mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
v26.10.3 (#1692)
* feat: shell controller, TabStage, layout and theme plugins with repair view * feat: move the classic layout into its own bundled plugin * feat: move color themes into a bundled themes plugin * fix: plugin tests and type checks work with a linked sdk * fix: home tab, layout switch and missing layout fallbacks for looks * chore: add classic layout and themes to the readme * chore: drop example plugin links from the plugin guide * feat: plugin filters by type, paged browse, hide plugins needing newer termix * feat: move desktop, status, import, audit and branding features into plugins * feat: host lock api so a plugin can load hosts from a config file (#1581) * chore: add hosts config to the plugin table * feat: login policy hook for plugins and optional https redirect (#1584) * chore: add login security to the plugin table * feat: api key socket tickets so a master can open remote sessions (#1654) * feat: sync moves to a plugin, desktop gets multiple accounts (#1580, #1654) * chore: add sync to the plugin table * feat: move the sync screens and wizard out of core into the plugin (#1580) * fix: add a neutral linked desktop hook for plugins (#1580) * fix: show the linked account's name and admin flag in the app (#1580) * feat: ssh transport providers, core ssh relay and plugin folders api * feat: support FIDO2 security keys through ssh agent * feat: add companion panels beside host tabs * feat: mTLS client certificates for desktop server links * feat: native web view for plugins in the desktop app * fix: detect the desktop link window by Electron's default user agent Fixes #1675 * fix: changelog for desktop link fix * fix: keep text primary keys in the plugin data export * fix: ship sharp binaries for Windows arm64 builds * feat: client cert on the sync link types (sdk 1.0.18) * chore: update translations * feat: themed title bar for the desktop app (#1690) * chore: changelog for the themed title bar * fix: draw the title bar in the sidebar color * feat: per host layout for the terminal panel bar * chore: sdk 1.0.19 for the panel bar layout * chore: bundle all 43 plugins for 26.10.3 * chore: update translations --------- Co-authored-by: SirHephaistos <60616553+SirHephaistos@users.noreply.github.com> Co-authored-by: Giovanny Aranda <garanda21@gmail.com>
This commit is contained in:
577 files changed
+76479
-101831
No files matched your search
@@ -0,0 +1,149 @@
|
||||
const fs = require("node:fs");
|
||||
|
||||
const SK_ED = "sk-ssh-ed25519@openssh.com";
|
||||
const SK_EC = "sk-ecdsa-sha2-nistp256@openssh.com";
|
||||
|
||||
function replaceOnce(source, from, to, label) {
|
||||
const first = source.indexOf(from);
|
||||
if (first === -1 || source.indexOf(from, first + 1) !== -1) {
|
||||
throw new Error(`ssh2 changed (${label}); review the security key patch`);
|
||||
}
|
||||
return source.replace(from, () => to);
|
||||
}
|
||||
|
||||
function patchKeyParserSource(source) {
|
||||
if (source.includes("// termix-sk")) return source;
|
||||
let out = replaceOnce(
|
||||
source,
|
||||
` case 'ecdsa-sha2-nistp521':
|
||||
return true;
|
||||
case 'ssh-ed25519':
|
||||
if (eddsaSupported)
|
||||
return true;`,
|
||||
` case 'ecdsa-sha2-nistp521':
|
||||
return true;
|
||||
case '${SK_EC}': // termix-sk
|
||||
return true;
|
||||
case '${SK_ED}':
|
||||
case 'ssh-ed25519':
|
||||
if (eddsaSupported)
|
||||
return true;`,
|
||||
"key types",
|
||||
);
|
||||
out = replaceOnce(
|
||||
out,
|
||||
` default:
|
||||
return new Error(\`Unsupported OpenSSH public key type: \${baseType}\`);
|
||||
}
|
||||
|
||||
return new OpenSSH_Public(`,
|
||||
` case '${SK_ED}':
|
||||
case '${SK_EC}': {
|
||||
// Public blob only; the security key signs, so there is no private half.
|
||||
const rest = data.subarray(data._pos || 0);
|
||||
const typeLen = Buffer.byteLength(baseType);
|
||||
pubSSH = Buffer.allocUnsafe(4 + typeLen + rest.length);
|
||||
writeUInt32BE(pubSSH, typeLen, 0);
|
||||
pubSSH.utf8Write(baseType, 4, typeLen);
|
||||
pubSSH.set(rest, 4 + typeLen);
|
||||
algo = baseType === '${SK_ED}' ? null : 'sha256';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return new Error(\`Unsupported OpenSSH public key type: \${baseType}\`);
|
||||
}
|
||||
|
||||
return new OpenSSH_Public(`,
|
||||
"parseDER",
|
||||
);
|
||||
return out;
|
||||
}
|
||||
|
||||
function patchAgentSource(source) {
|
||||
if (source.includes("skTrailer")) return source;
|
||||
return replaceOnce(
|
||||
source,
|
||||
` binaryParser.init(signature, 0);
|
||||
binaryParser.readString(true);
|
||||
signature = binaryParser.readString();
|
||||
binaryParser.clear();`,
|
||||
` binaryParser.init(signature, 0);
|
||||
binaryParser.readString(true);
|
||||
signature = binaryParser.readString();
|
||||
// Security key signatures end with flags and a counter.
|
||||
const skTrailer = signature === undefined
|
||||
? undefined
|
||||
: binaryParser.readRaw();
|
||||
binaryParser.clear();
|
||||
if (skTrailer && skTrailer.length)
|
||||
signature.skTrailer = Buffer.from(skTrailer);`,
|
||||
"agent sign response",
|
||||
);
|
||||
}
|
||||
|
||||
function patchProtocolSource(source) {
|
||||
if (source.includes("skTrailer")) return source;
|
||||
const start = source.indexOf(" authPK(username, pubKey, keyAlgo, cbSign) {");
|
||||
const end = source.indexOf(" authHostbased(", start);
|
||||
if (start === -1 || end === -1) {
|
||||
throw new Error("ssh2 changed (authPK); review the security key patch");
|
||||
}
|
||||
let region = source.slice(start, end);
|
||||
region = replaceOnce(
|
||||
region,
|
||||
` signature = convertSignature(signature, keyType);`,
|
||||
` const skTrailer = signature && signature.skTrailer;
|
||||
signature = convertSignature(signature, keyType);`,
|
||||
"authPK sign",
|
||||
);
|
||||
region = replaceOnce(
|
||||
region,
|
||||
` + 4 + algoLen + 4 + sigLen
|
||||
);`,
|
||||
` + 4 + algoLen + 4 + sigLen + (skTrailer ? skTrailer.length : 0)
|
||||
);`,
|
||||
"authPK alloc",
|
||||
);
|
||||
region = replaceOnce(
|
||||
region,
|
||||
` writeUInt32BE(packet, 4 + algoLen + 4 + sigLen, p += pubKeyLen);`,
|
||||
` writeUInt32BE(
|
||||
packet,
|
||||
4 + algoLen + 4 + sigLen + (skTrailer ? skTrailer.length : 0),
|
||||
p += pubKeyLen
|
||||
);`,
|
||||
"authPK length",
|
||||
);
|
||||
region = replaceOnce(
|
||||
region,
|
||||
` packet.set(signature, p += 4);`,
|
||||
` packet.set(signature, p += 4);
|
||||
if (skTrailer)
|
||||
packet.set(skTrailer, p + sigLen);`,
|
||||
"authPK copy",
|
||||
);
|
||||
return source.slice(0, start) + region + source.slice(end);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
const targets = [
|
||||
["ssh2/lib/protocol/keyParser.js", patchKeyParserSource],
|
||||
["ssh2/lib/agent.js", patchAgentSource],
|
||||
["ssh2/lib/protocol/Protocol.js", patchProtocolSource],
|
||||
];
|
||||
for (const [id, patch] of targets) {
|
||||
const target = require.resolve(id);
|
||||
const source = fs.readFileSync(target, "utf8");
|
||||
const result = patch(source);
|
||||
if (result !== source) fs.writeFileSync(target, result);
|
||||
}
|
||||
console.log(
|
||||
"[patch-ssh2-sk] Security key public keys and signatures enabled",
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
patchKeyParserSource,
|
||||
patchAgentSource,
|
||||
patchProtocolSource,
|
||||
};
|
||||
Reference in new issue
Block a user