Fallback on invalid SSH key

This commit is contained in:
mueller_minki
2026-10-04 19:47:32 +02:00
parent b8a265a95d
commit c40fed0591
3 changed files with 87 additions and 20 deletions

View File

@@ -639,8 +639,9 @@ static void sink_accept(struct csink *s)
/* Transparent source spoofing */
// Runs ip(8) without a shell. Returns its exit status, -1 if it did not run.
static int run_ip(const char *const argv[])
// Runs a program without a shell, output discarded. Returns its exit status,
// -1 if it did not run.
static int run_cmd(const char *const argv[])
{
pid_t pid = fork();
if (pid < 0)
@@ -651,7 +652,7 @@ static int run_ip(const char *const argv[])
dup2(nul, STDOUT_FILENO);
dup2(nul, STDERR_FILENO);
}
execvp("ip", (char *const *)argv);
execvp(argv[0], (char *const *)argv);
_exit(127);
}
int st;
@@ -676,16 +677,16 @@ static void spoof_routing(int add)
// Remove copies left by an earlier run, then add exactly one.
for (int k = 0; k < 16; k++) {
const char *del[] = { "ip", fam, "rule", "del", "pref", tab, NULL };
if (run_ip(del) != 0)
if (run_cmd(del) != 0)
break;
}
const char *flush[] = { "ip", fam, "route", "flush", "table", tab, NULL };
run_ip(flush);
run_cmd(flush);
if (!add)
continue;
const char *rule[] = { "ip", fam, "rule", "add", "pref", tab, "from", fams[i][1], "iif", "lo", "lookup", tab, NULL };
const char *route[] = { "ip", fam, "route", "replace", "local", fams[i][2], "dev", "lo", "table", tab, NULL };
int ok = run_ip(rule) == 0 && run_ip(route) == 0;
int ok = run_cmd(rule) == 0 && run_cmd(route) == 0;
if (i == 0)
ok4 = ok;
else if (!ok)
@@ -835,23 +836,86 @@ static int process_ctl_input(void)
/* Session setup */
// libssh2 before 1.11 signs RSA keys only with SHA-1 ("ssh-rsa"), which
// OpenSSH 8.8+ rejects by default.
static int rsa_usable(void)
{
return libssh2_version(0x010b00) != NULL;
}
// Identifies RSA keys by the public key file or a PEM private key header.
static int key_is_rsa(const char *path)
{
char pub[300], head[64] = "";
snprintf(pub, sizeof(pub), "%s.pub", path);
FILE *f = fopen(pub, "r");
if (!f)
f = fopen(path, "r");
if (!f)
return 0;
size_t n = fread(head, 1, sizeof(head) - 1, f);
head[n] = '\0';
fclose(f);
return !strncmp(head, "ssh-rsa ", 8) || strstr(head, "BEGIN RSA PRIVATE KEY") != NULL;
}
// Creates PB_CLIENT_KEY with ssh-keygen unless it exists. Returns 0 if usable.
static int ensure_own_key(void)
{
if (access(PB_CLIENT_KEY, R_OK) == 0)
return 0;
char host[64], comment[96];
if (gethostname(host, sizeof(host)) < 0)
strcpy(host, "client");
host[sizeof(host) - 1] = '\0';
snprintf(comment, sizeof(comment), "patchbay@%s", host);
const char *argv[] = { "ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", comment, "-f", PB_CLIENT_KEY, NULL };
if (run_cmd(argv) != 0 || access(PB_CLIENT_KEY, R_OK) != 0) {
log_err("cannot create %s with ssh-keygen (is openssh-client installed?)", PB_CLIENT_KEY);
return -1;
}
log_warn("created %s; add its public key in the web UI (patchbayd --pubkey)", PB_CLIENT_KEY);
return 0;
}
int client_identity(const struct pb_config *c, char *out, size_t outsz)
{
const char *cands[] = { c->identity_file, "/root/.ssh/id_ed25519", "/root/.ssh/id_rsa",
"/etc/ssh/ssh_host_ed25519_key", NULL };
for (int i = 0; cands[i]; i++) {
if (!cands[i][0])
continue;
if (access(cands[i], R_OK) == 0) {
snprintf(out, outsz, "%s", cands[i]);
return 0;
}
if (i == 0) {
log_err("IdentityFile %s is not readable", cands[i]);
if (c->identity_file[0]) {
if (access(c->identity_file, R_OK) != 0) {
log_err("IdentityFile %s is not readable", c->identity_file);
return -1;
}
if (key_is_rsa(c->identity_file) && !rsa_usable())
log_warn("IdentityFile %s is an RSA key, which libssh2 %s can only sign with SHA-1; "
"the target will reject it. Use an ed25519 key or libssh2 1.11+", c->identity_file,
libssh2_version(0));
snprintf(out, outsz, "%s", c->identity_file);
return 0;
}
return -1;
const char *cands[] = { "/root/.ssh/id_ed25519", "/root/.ssh/id_rsa", "/etc/ssh/ssh_host_ed25519_key", NULL };
for (int i = 0; cands[i]; i++) {
if (access(cands[i], R_OK) != 0)
continue;
if (key_is_rsa(cands[i]) && !rsa_usable()) {
// A key the target rejects is worse than none: use our own instead.
static int told;
if (!told++)
log_warn("%s is an RSA key that libssh2 %s cannot sign acceptably; using %s instead", cands[i],
libssh2_version(0), PB_CLIENT_KEY);
if (ensure_own_key() < 0)
return -1;
snprintf(out, outsz, "%s", PB_CLIENT_KEY);
return 0;
}
snprintf(out, outsz, "%s", cands[i]);
return 0;
}
// No usable key at all: create our own (or reuse one created earlier).
if (ensure_own_key() < 0)
return -1;
snprintf(out, outsz, "%s", PB_CLIENT_KEY);
return 0;
}
static int verify_host_key(void)

View File

@@ -4,6 +4,7 @@
#define PB_DEFAULT_CONF "/etc/patchbay/patchbay.conf"
#define PB_DEFAULT_DB "/etc/patchbay/patchbay.db"
#define PB_RUN_DIR "/run/patchbay"
#define PB_CLIENT_KEY "/etc/patchbay/id_ed25519" // created when root only has an unusable RSA key
enum pb_role { ROLE_CLIENT, ROLE_TARGET };