/* print all known host keys for a given host, but skip keys of given type */ static int show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) { int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; int i, ret = 0; char *fp, *ra; const struct hostkey_entry *found; for (i = 0; type[i] != -1; i++) { if (type[i] == key->type) continue; if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; fp = sshkey_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); ra = sshkey_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); logit("WARNING: %s key found for host %s\n" "in %s:%lu\n" "%s key fingerprint %s.", sshkey_type(found->key), found->host, found->file, found->line, sshkey_type(found->key), fp); if (options.visual_host_key) logit("%s", ra); xfree(ra); xfree(fp); ret = 1; } return ret; }
/* * Formats any key left in authctxt->auth_method_key for inclusion in * auth_log()'s message. Also includes authxtct->auth_method_info if present. */ static char * format_method_key(Authctxt *authctxt) { const struct sshkey *key = authctxt->auth_method_key; const char *methinfo = authctxt->auth_method_info; char *fp, *ret = NULL; if (key == NULL) return NULL; if (sshkey_is_cert(key)) { fp = sshkey_fingerprint(key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT); xasprintf(&ret, "%s ID %s (serial %llu) CA %s %s%s%s", sshkey_type(key), key->cert->key_id, (unsigned long long)key->cert->serial, sshkey_type(key->cert->signature_key), fp == NULL ? "(null)" : fp, methinfo == NULL ? "" : ", ", methinfo == NULL ? "" : methinfo); free(fp); } else { fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); xasprintf(&ret, "%s %s%s%s", sshkey_type(key), fp == NULL ? "(null)" : fp, methinfo == NULL ? "" : ", ", methinfo == NULL ? "" : methinfo); free(fp); } return ret; }
static int host_delete(struct hostkey_foreach_line *l, void *_ctx) { struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx; int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; size_t i; if (l->status == HKF_STATUS_MATCHED) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ fprintf(ctx->out, "%s\n", l->line); return 0; } /* XXX might need a knob for this later */ /* Don't remove RSA1 keys */ if (l->key->type == KEY_RSA1) { fprintf(ctx->out, "%s\n", l->line); return 0; } /* * If this line contains one of the keys that we will be * adding later, then don't change it and mark the key for * skipping. */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(ctx->keys[i], l->key)) { ctx->skip_keys[i] = 1; fprintf(ctx->out, "%s\n", l->line); debug3("%s: %s key already at %s:%ld", __func__, sshkey_type(l->key), l->path, l->linenum); return 0; } } /* * Hostname matches and has no CA/revoke marker, delete it * by *not* writing the line to ctx->out. */ do_log2(loglevel, "%s%s%s:%ld: Removed %s key for host %s", ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", l->path, l->linenum, sshkey_type(l->key), ctx->host); ctx->modified = 1; return 0; } /* Retain non-matching hosts and invalid lines when deleting */ if (l->status == HKF_STATUS_INVALID) { do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry", ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", l->path, l->linenum); } fprintf(ctx->out, "%s\n", l->line); return 0; }
/* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) { int flags = 0; char *fp; fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); debug("Server host key: %s %s", sshkey_type(host_key), fp); xfree(fp); /* XXX certs are not yet supported for DNS */ if (!sshkey_is_cert(host_key) && options.verify_host_key_dns && verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { if (options.verify_host_key_dns == 1 && flags & DNS_VERIFY_MATCH && flags & DNS_VERIFY_SECURE) return 0; if (flags & DNS_VERIFY_MATCH) { matching_host_key_dns = 1; } else { warn_changed_key(host_key); error("Update the SSHFP RR in DNS with the new " "host key to get rid of this message."); } } } return check_host_key(host, hostaddr, options.port, host_key, RDRW, options.user_hostfiles, options.num_user_hostfiles, options.system_hostfiles, options.num_system_hostfiles); }
static int record_hostkey(struct hostkey_foreach_line *l, void *_ctx) { struct load_callback_ctx *ctx = (struct load_callback_ctx *)_ctx; struct hostkeys *hostkeys = ctx->hostkeys; struct hostkey_entry *tmp; if (l->status == HKF_STATUS_INVALID) { /* XXX make this verbose() in the future */ debug("%s:%ld: parse error in hostkeys file", l->path, l->linenum); return 0; } debug3("%s: found %skey type %s in file %s:%lu", __func__, l->marker == MRK_NONE ? "" : (l->marker == MRK_CA ? "ca " : "revoked "), sshkey_type(l->key), l->path, l->linenum); if ((tmp = reallocarray(hostkeys->entries, hostkeys->num_entries + 1, sizeof(*hostkeys->entries))) == NULL) return SSH_ERR_ALLOC_FAIL; hostkeys->entries = tmp; hostkeys->entries[hostkeys->num_entries].host = xstrdup(ctx->host); hostkeys->entries[hostkeys->num_entries].file = xstrdup(l->path); hostkeys->entries[hostkeys->num_entries].line = l->linenum; hostkeys->entries[hostkeys->num_entries].key = l->key; l->key = NULL; /* steal it */ hostkeys->entries[hostkeys->num_entries].marker = l->marker; hostkeys->num_entries++; ctx->num_loaded++; return 0; }
/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ int auth_key_is_revoked(struct sshkey *key) { char *fp = NULL; int r; if (options.revoked_keys_file == NULL) return 0; if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { r = SSH_ERR_ALLOC_FAIL; error("%s: fingerprint key: %s", __func__, ssh_err(r)); goto out; } r = sshkey_check_revoked(key, options.revoked_keys_file); switch (r) { case 0: break; /* not revoked */ case SSH_ERR_KEY_REVOKED: error("Authentication key %s %s revoked by file %s", sshkey_type(key), fp, options.revoked_keys_file); goto out; default: error("Error checking authentication key %s %s in " "revoked keys file %s: %s", sshkey_type(key), fp, options.revoked_keys_file, ssh_err(r)); goto out; } /* Success */ r = 0; out: free(fp); return r == 0 ? 0 : 1; }
static void signature_tests(struct sshkey *k, struct sshkey *bad) { u_char i, buf[2049]; size_t lens[] = { 1, 2, 7, 8, 9, 15, 16, 17, 31, 32, 33, 127, 128, 129, 255, 256, 257, 1023, 1024, 1025, 2047, 2048, 2049 }; for (i = 0; i < (sizeof(lens)/sizeof(lens[0])); i++) { test_subtest_info("%s key, banana length %zu", sshkey_type(k), lens[i]); banana(buf, lens[i]); signature_test(k, bad, buf, lens[i]); } }
/* * Performs the RSA authentication dialog with the client. This returns * 0 if the client could not be authenticated, and 1 if authentication was * successful. This may exit if there is a serious protocol violation. */ int auth_rsa(Authctxt *authctxt, BIGNUM *client_n) { struct ssh *ssh = active_state; struct sshkey *key; char *fp; struct passwd *pw = authctxt->pw; /* no user given */ if (!authctxt->valid) return 0; if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) { auth_clear_options(); return (0); } /* Perform the challenge-response dialog for this key. */ if (!auth_rsa_challenge_dialog(key)) { /* Wrong response. */ verbose("Wrong response to RSA authentication challenge."); ssh_packet_send_debug(ssh, "Wrong response to RSA authentication challenge."); /* * Break out of the loop. Otherwise we might send * another challenge and break the protocol. */ sshkey_free(key); return (0); } /* * Correct response. The client has been successfully * authenticated. Note that we have not yet processed the * options; this will be reset if the options cause the * authentication to be rejected. */ fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", sshkey_type(key), fp); xfree(fp); sshkey_free(key); ssh_packet_send_debug(ssh, "RSA authentication accepted."); return (1); }
/* Checks whether a key has already been previously used for authentication */ int auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) { u_int i; char *fp; for (i = 0; i < authctxt->nprev_keys; i++) { if (sshkey_equal_public(key, authctxt->prev_keys[i])) { fp = sshkey_fingerprint(authctxt->prev_keys[i], options.fingerprint_hash, SSH_FP_DEFAULT); debug3("%s: key already used: %s %s", __func__, sshkey_type(authctxt->prev_keys[i]), fp == NULL ? "UNKNOWN" : fp); free(fp); return 1; } } return 0; }
static void warn_changed_key(struct sshkey *host_key) { char *fp; fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); error("It is also possible that a host key has just been changed."); error("The fingerprint for the %s key sent by the remote host is\n%s.", sshkey_type(host_key), fp); error("Please contact your system administrator."); xfree(fp); }
/* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { int r = -1, flags = 0; char *fp = NULL; struct sshkey *plain = NULL; if ((fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint host key: %s", __func__, ssh_err(r)); r = -1; goto out; } debug("Server host key: %s %s", compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); if (sshkey_equal(previous_host_key, host_key)) { debug2("%s: server host key %s %s matches cached key", __func__, sshkey_type(host_key), fp); r = 0; goto out; } /* Check in RevokedHostKeys file if specified */ if (options.revoked_host_keys != NULL) { r = sshkey_check_revoked(host_key, options.revoked_host_keys); switch (r) { case 0: break; /* not revoked */ case SSH_ERR_KEY_REVOKED: error("Host key %s %s revoked by file %s", sshkey_type(host_key), fp, options.revoked_host_keys); r = -1; goto out; default: error("Error checking host key %s %s in " "revoked keys file %s: %s", sshkey_type(host_key), fp, options.revoked_host_keys, ssh_err(r)); r = -1; goto out; } } if (options.verify_host_key_dns) { /* * XXX certs are not yet supported for DNS, so downgrade * them and try the plain key. */ if ((r = sshkey_from_private(host_key, &plain)) != 0) goto out; if (sshkey_is_cert(plain)) sshkey_drop_cert(plain); if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { if (options.verify_host_key_dns == 1 && flags & DNS_VERIFY_MATCH && flags & DNS_VERIFY_SECURE) { r = 0; goto out; } if (flags & DNS_VERIFY_MATCH) { matching_host_key_dns = 1; } else { warn_changed_key(plain); error("Update the SSHFP RR in DNS " "with the new host key to get rid " "of this message."); } } } } r = check_host_key(host, hostaddr, options.port, host_key, RDRW, options.user_hostfiles, options.num_user_hostfiles, options.system_hostfiles, options.num_system_hostfiles); out: sshkey_free(plain); free(fp); if (r == 0 && host_key != NULL) { key_free(previous_host_key); previous_host_key = key_from_private(host_key); } return r; }
void load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) { FILE *f; char line[8192]; u_long linenum = 0, num_loaded = 0; char *cp, *cp2, *hashed_host; HostkeyMarker marker; struct sshkey *key; u_int kbits; if ((f = fopen(path, "r")) == NULL) return; debug3("%s: loading entries for host \"%.100s\" from file \"%s\"", __func__, host, path); while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { cp = line; /* Skip any leading whitespace, comments and empty lines. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; if ((marker = check_markers(&cp)) == MRK_ERROR) { verbose("%s: invalid marker at %s:%lu", __func__, path, linenum); continue; } /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; /* Check if the host name matches. */ if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { if (*cp != HASH_DELIM) continue; hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); if (hashed_host == NULL) { debug("Invalid hashed host line %lu of %s", linenum, path); continue; } if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) continue; } /* Got a match. Skip host name. */ cp = cp2; /* * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { error("%s: sshkey_new failed", __func__); break; } if (!hostfile_read_key(&cp, &kbits, key)) { sshkey_free(key); if ((key = sshkey_new(KEY_RSA1)) == NULL) { error("%s: sshkey_new failed", __func__); break; } if (!hostfile_read_key(&cp, &kbits, key)) { sshkey_free(key); continue; } } if (!hostfile_check_key(kbits, key, host, path, linenum)) continue; debug3("%s: found %skey type %s in file %s:%lu", __func__, marker == MRK_NONE ? "" : (marker == MRK_CA ? "ca " : "revoked "), sshkey_type(key), path, linenum); hostkeys->entries = xrealloc(hostkeys->entries, hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); hostkeys->entries[hostkeys->num_entries].file = xstrdup(path); hostkeys->entries[hostkeys->num_entries].line = linenum; hostkeys->entries[hostkeys->num_entries].key = key; hostkeys->entries[hostkeys->num_entries].marker = marker; hostkeys->num_entries++; num_loaded++; } debug3("%s: loaded %lu keys", __func__, num_loaded); fclose(f); return; }
/* return 1 if given hostkey is allowed */ int hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, struct sshkey *key) { struct ssh *ssh = active_state; /* XXX */ const char *resolvedname, *ipaddr, *lookup, *reason; HostStatus host_status; int len; char *fp; if (auth_key_is_revoked(key)) return 0; resolvedname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); debug2("%s: chost %s resolvedname %s ipaddr %s", __func__, chost, resolvedname, ipaddr); if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { debug2("stripping trailing dot from chost %s", chost); chost[len - 1] = '\0'; } if (options.hostbased_uses_name_from_packet_only) { if (auth_rhosts2(pw, cuser, chost, chost) == 0) { debug2("%s: auth_rhosts2 refused " "user \"%.100s\" host \"%.100s\" (from packet)", __func__, cuser, chost); return 0; } lookup = chost; } else { if (strcasecmp(resolvedname, chost) != 0) logit("userauth_hostbased mismatch: " "client sends %s, but we resolve %s to %s", chost, ipaddr, resolvedname); if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) { debug2("%s: auth_rhosts2 refused " "user \"%.100s\" host \"%.100s\" addr \"%.100s\"", __func__, cuser, resolvedname, ipaddr); return 0; } lookup = resolvedname; } debug2("%s: access allowed by auth_rhosts2", __func__); if (sshkey_is_cert(key) && sshkey_cert_check_authority(key, 1, 0, lookup, &reason)) { error("%s", reason); auth_debug_add("%s", reason); return 0; } host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); /* backward compat if no key has been found. */ if (host_status == HOST_NEW) { host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE2, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE2); } if (host_status == HOST_OK) { if (sshkey_is_cert(key)) { if ((fp = sshkey_fingerprint(key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint fail", __func__); verbose("Accepted certificate ID \"%s\" signed by " "%s CA %s from %s@%s", key->cert->key_id, sshkey_type(key->cert->signature_key), fp, cuser, lookup); } else { if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint fail", __func__); verbose("Accepted %s public key %s from %s@%s", sshkey_type(key), fp, cuser, lookup); } free(fp); } return (host_status == HOST_OK); }
static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, struct sshkey *host_key, int readonly, char **user_hostfiles, u_int num_user_hostfiles, char **system_hostfiles, u_int num_system_hostfiles) { HostStatus host_status; HostStatus ip_status; struct sshkey *raw_key = NULL; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; char msg[1024]; const char *type; const struct hostkey_entry *host_found, *ip_found; int len, cancelled_forwarding = 0; int local = sockaddr_is_local(hostaddr); int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; struct hostkeys *host_hostkeys, *ip_hostkeys; u_int i; /* * Force accepting of the host key for loopback/localhost. The * problem is that if the home directory is NFS-mounted to multiple * machines, localhost will refer to a different machine in each of * them, and the user will get bogus HOST_CHANGED warnings. This * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ if (options.no_host_authentication_for_localhost == 1 && local && options.host_key_alias == NULL) { debug("Forcing accepting of host key for " "loopback/localhost."); return 0; } /* * Prepare the hostname and address strings used for hostkey lookup. * In some cases, these will have a port number appended. */ get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); /* * Turn off check_host_ip if the connection is to localhost, via proxy * command or if we don't have a hostname to compare with */ if (options.check_host_ip && (local || strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) options.check_host_ip = 0; host_hostkeys = init_hostkeys(); for (i = 0; i < num_user_hostfiles; i++) load_hostkeys(host_hostkeys, host, user_hostfiles[i]); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(host_hostkeys, host, system_hostfiles[i]); ip_hostkeys = NULL; if (!want_cert && options.check_host_ip) { ip_hostkeys = init_hostkeys(); for (i = 0; i < num_user_hostfiles; i++) load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); } retry: /* Reload these as they may have changed on cert->key downgrade */ want_cert = sshkey_is_cert(host_key); type = sshkey_type(host_key); /* * Check if the host key is present in the user's list of known * hosts or in the systemwide list. */ host_status = check_key_in_hostkeys(host_hostkeys, host_key, &host_found); /* * Also perform check for the ip address, skip the check if we are * localhost, looking for a certificate, or the hostname was an ip * address to begin with. */ if (!want_cert && ip_hostkeys != NULL) { ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, &ip_found); if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || (ip_found != NULL && !sshkey_equal(ip_found->key, host_found->key)))) host_ip_differ = 1; } else ip_status = host_status; switch (host_status) { case HOST_OK: /* The host is known and the key matches. */ debug("Host '%.200s' is known and matches the %s host %s.", host, type, want_cert ? "certificate" : "key"); debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", host_found->file, host_found->line); if (want_cert && !check_host_cert(hostname, host_key)) goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { if (readonly || want_cert) logit("%s host key for IP address " "'%.128s' not in list of known hosts.", type, ip); else if (!add_host_to_hostfile(user_hostfiles[0], ip, host_key, options.hash_known_hosts)) logit("Failed to add the %s host key for IP " "address '%.128s' to the list of known " "hosts (%.30s).", type, ip, user_hostfiles[0]); else logit("Warning: Permanently added the %s host " "key for IP address '%.128s' to the list " "of known hosts.", type, ip); } else if (options.visual_host_key) { fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); ra = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); logit("Host key fingerprint is %s\n%s\n", fp, ra); xfree(ra); xfree(fp); } break; case HOST_NEW: if (options.host_key_alias == NULL && port != 0 && port != SSH_DEFAULT_PORT) { debug("checking without port identifier"); if (check_host_key(hostname, hostaddr, 0, host_key, ROQUIET, user_hostfiles, num_user_hostfiles, system_hostfiles, num_system_hostfiles) == 0) { debug("found matching key w/out port"); break; } } if (readonly || want_cert) goto fail; /* The host is new. */ if (options.strict_host_key_checking == 1) { /* * User has requested strict host key checking. We * will not add the host key automatically. The only * alternative left is to abort. */ error("No %s host key is known for %.200s and you " "have requested strict checking.", type, host); goto fail; } else if (options.strict_host_key_checking == 2) { char msg1[1024], msg2[1024]; if (show_other_keys(host_hostkeys, host_key)) snprintf(msg1, sizeof(msg1), "\nbut keys of different type are already" " known for this host."); else snprintf(msg1, sizeof(msg1), "."); /* The default */ fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); ra = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); msg2[0] = '\0'; if (options.verify_host_key_dns) { if (matching_host_key_dns) snprintf(msg2, sizeof(msg2), "Matching host key fingerprint" " found in DNS.\n"); else snprintf(msg2, sizeof(msg2), "No matching host key fingerprint" " found in DNS.\n"); } snprintf(msg, sizeof(msg), "The authenticity of host '%.200s (%s)' can't be " "established%s\n" "%s key fingerprint is %s.%s%s\n%s" "Are you sure you want to continue connecting " "(yes/no)? ", host, ip, msg1, type, fp, options.visual_host_key ? "\n" : "", options.visual_host_key ? ra : "", msg2); xfree(ra); xfree(fp); if (!confirm(msg)) goto fail; } /* * If not in strict mode, add the key automatically to the * local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; if (options.hash_known_hosts) { /* Add hash of host and IP separately */ r = add_host_to_hostfile(user_hostfiles[0], host, host_key, options.hash_known_hosts) && add_host_to_hostfile(user_hostfiles[0], ip, host_key, options.hash_known_hosts); } else { /* Add unhashed "host,ip" */ r = add_host_to_hostfile(user_hostfiles[0], hostline, host_key, options.hash_known_hosts); } } else { r = add_host_to_hostfile(user_hostfiles[0], host, host_key, options.hash_known_hosts); hostp = host; } if (!r) logit("Failed to add the host to the list of known " "hosts (%.500s).", user_hostfiles[0]); else logit("Warning: Permanently added '%.200s' (%s) to the " "list of known hosts.", hostp, type); break; case HOST_REVOKED: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REVOKED HOST KEY DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s is marked as revoked.", type, host); error("This could mean that a stolen key is being used to"); error("impersonate this host."); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ if (options.strict_host_key_checking) { error("%s host key for %.200s was revoked and you have " "requested strict checking.", type, host); goto fail; } goto continue_unsafe; case HOST_CHANGED: if (want_cert) { /* * This is only a debug() since it is valid to have * CAs with wildcard DNS matches that don't match * all hosts that one might visit. */ debug("Host certificate authority does not " "match %s in %s:%lu", CA_MARKER, host_found->file, host_found->line); goto fail; } if (readonly == ROQUIET) goto fail; if (options.check_host_ip && host_ip_differ) { char *key_msg; if (ip_status == HOST_NEW) key_msg = "is unknown"; else if (ip_status == HOST_OK) key_msg = "is unchanged"; else key_msg = "has a different value"; error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s has changed,", type, host); error("and the key for the corresponding IP address %s", ip); error("%s. This could either mean that", key_msg); error("DNS SPOOFING is happening or the IP address for the host"); error("and its host key have changed at the same time."); if (ip_status != HOST_NEW) error("Offending key for IP in %s:%lu", ip_found->file, ip_found->line); } /* The host key has changed. */ warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfiles[0]); error("Offending %s key in %s:%lu", sshkey_type(host_found->key), host_found->file, host_found->line); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ if (options.strict_host_key_checking) { error("%s host key for %.200s has changed and you have " "requested strict checking.", type, host); goto fail; } continue_unsafe: /* * If strict host key checking has not been requested, allow * the connection but without MITM-able authentication or * forwarding. */ if (options.password_authentication) { error("Password authentication is disabled to avoid " "man-in-the-middle attacks."); options.password_authentication = 0; cancelled_forwarding = 1; } if (options.kbd_interactive_authentication) { error("Keyboard-interactive authentication is disabled" " to avoid man-in-the-middle attacks."); options.kbd_interactive_authentication = 0; options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.challenge_response_authentication) { error("Challenge/response authentication is disabled" " to avoid man-in-the-middle attacks."); options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.forward_agent) { error("Agent forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_agent = 0; cancelled_forwarding = 1; } if (options.forward_x11) { error("X11 forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_x11 = 0; cancelled_forwarding = 1; } if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { error("Port forwarding is disabled to avoid " "man-in-the-middle attacks."); options.num_local_forwards = options.num_remote_forwards = 0; cancelled_forwarding = 1; } if (options.tun_open != SSH_TUNMODE_NO) { error("Tunnel forwarding is disabled to avoid " "man-in-the-middle attacks."); options.tun_open = SSH_TUNMODE_NO; cancelled_forwarding = 1; } if (options.exit_on_forward_failure && cancelled_forwarding) fatal("Error: forwarding disabled due to host key " "check failure"); /* * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an * identifying sentence, tell that the host identifies itself * by that sentence, and ask the user if he/she wishes to * accept the authentication. */ break; case HOST_FOUND: fatal("internal error"); break; } if (options.check_host_ip && host_status != HOST_CHANGED && ip_status == HOST_CHANGED) { snprintf(msg, sizeof(msg), "Warning: the %s host key for '%.200s' " "differs from the key for the IP address '%.128s'" "\nOffending key for IP in %s:%lu", type, host, ip, ip_found->file, ip_found->line); if (host_status == HOST_OK) { len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, "\nMatching host key in %s:%lu", host_found->file, host_found->line); } if (options.strict_host_key_checking == 1) { logit("%s", msg); error("Exiting, you have requested strict checking."); goto fail; } else if (options.strict_host_key_checking == 2) { strlcat(msg, "\nAre you sure you want " "to continue connecting (yes/no)? ", sizeof(msg)); if (!confirm(msg)) goto fail; } else { logit("%s", msg); } } xfree(ip); xfree(host); if (host_hostkeys != NULL) free_hostkeys(host_hostkeys); if (ip_hostkeys != NULL) free_hostkeys(ip_hostkeys); return 0; fail: if (want_cert && host_status != HOST_REVOKED) { /* * No matching certificate. Downgrade cert to raw key and * search normally. */ debug("No matching CA found. Retry with plain key"); if ((r = sshkey_from_private(host_key, &raw_key)) != 0) fatal("%s: sshkey_from_private: %s", __func__, ssh_err(r)); if ((r = sshkey_drop_cert(raw_key)) != 0) fatal("Couldn't drop certificate: %s", ssh_err(r)); host_key = raw_key; goto retry; } if (raw_key != NULL) sshkey_free(raw_key); xfree(ip); xfree(host); if (host_hostkeys != NULL) free_hostkeys(host_hostkeys); if (ip_hostkeys != NULL) free_hostkeys(ip_hostkeys); return -1; }
static int userauth_hostbased(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; struct sshbuf *b; struct sshkey *key = NULL; char *pkalg, *cuser, *chost; u_char *pkblob, *sig; size_t alen, blen, slen; int r, pktype, authenticated = 0; if (!authctxt->valid) { debug2("%s: disabled because of invalid user", __func__); return 0; } /* XXX use sshkey_froms() */ if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 || (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 || (r = sshpkt_get_string(ssh, &sig, &slen)) != 0) fatal("%s: packet parsing: %s", __func__, ssh_err(r)); debug("%s: cuser %s chost %s pkalg %s slen %zu", __func__, cuser, chost, pkalg, slen); #ifdef DEBUG_PK debug("signature:"); sshbuf_dump_data(sig, siglen, stderr); #endif pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("%s: unsupported public key algorithm: %s", __func__, pkalg); goto done; } if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { error("%s: key_from_blob: %s", __func__, ssh_err(r)); goto done; } if (key == NULL) { error("%s: cannot decode key: %s", __func__, pkalg); goto done; } if (key->type != pktype) { error("%s: type mismatch for decoded key " "(received %d, expected %d)", __func__, key->type, pktype); goto done; } if (sshkey_type_plain(key->type) == KEY_RSA && (ssh->compat & SSH_BUG_RSASIGMD5) != 0) { error("Refusing RSA key because peer uses unsafe " "signature format"); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.hostbased_key_types, 0) != 1) { logit("%s: key type %s not in HostbasedAcceptedKeyTypes", __func__, sshkey_type(key)); goto done; } if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* reconstruct packet */ if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 || (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshbuf_put_cstring(b, authctxt->user)) != 0 || (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || (r = sshbuf_put_cstring(b, "hostbased")) != 0 || (r = sshbuf_put_string(b, pkalg, alen)) != 0 || (r = sshbuf_put_string(b, pkblob, blen)) != 0 || (r = sshbuf_put_cstring(b, chost)) != 0 || (r = sshbuf_put_cstring(b, cuser)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif auth2_record_info(authctxt, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); /* test for allowed key and correct signature */ authenticated = 0; if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat)) == 0) authenticated = 1; auth2_record_key(authctxt, authenticated, key); sshbuf_free(b); done: debug2("%s: authenticated %d", __func__, authenticated); sshkey_free(key); free(pkalg); free(pkblob); free(cuser); free(chost); free(sig); return authenticated; }
/* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) { u_int i; int r = -1, flags = 0; char valid[64], *fp = NULL, *cafp = NULL; struct sshkey *plain = NULL; if ((fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint host key: %s", __func__, ssh_err(r)); r = -1; goto out; } if (sshkey_is_cert(host_key)) { if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint CA key: %s", __func__, ssh_err(r)); r = -1; goto out; } sshkey_format_cert_validity(host_key->cert, valid, sizeof(valid)); debug("Server host certificate: %s %s, serial %llu " "ID \"%s\" CA %s %s valid %s", sshkey_ssh_name(host_key), fp, (unsigned long long)host_key->cert->serial, host_key->cert->key_id, sshkey_ssh_name(host_key->cert->signature_key), cafp, valid); for (i = 0; i < host_key->cert->nprincipals; i++) { debug2("Server host certificate hostname: %s", host_key->cert->principals[i]); } } else { debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); } if (sshkey_equal(previous_host_key, host_key)) { debug2("%s: server host key %s %s matches cached key", __func__, sshkey_type(host_key), fp); r = 0; goto out; } /* Check in RevokedHostKeys file if specified */ if (options.revoked_host_keys != NULL) { r = sshkey_check_revoked(host_key, options.revoked_host_keys); switch (r) { case 0: break; /* not revoked */ case SSH_ERR_KEY_REVOKED: error("Host key %s %s revoked by file %s", sshkey_type(host_key), fp, options.revoked_host_keys); r = -1; goto out; default: error("Error checking host key %s %s in " "revoked keys file %s: %s", sshkey_type(host_key), fp, options.revoked_host_keys, ssh_err(r)); r = -1; goto out; } } if (options.verify_host_key_dns) { /* * XXX certs are not yet supported for DNS, so downgrade * them and try the plain key. */ if ((r = sshkey_from_private(host_key, &plain)) != 0) goto out; if (sshkey_is_cert(plain)) sshkey_drop_cert(plain); if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { if (options.verify_host_key_dns == 1 && flags & DNS_VERIFY_MATCH && flags & DNS_VERIFY_SECURE) { r = 0; goto out; } if (flags & DNS_VERIFY_MATCH) { matching_host_key_dns = 1; } else { warn_changed_key(plain); error("Update the SSHFP RR in DNS " "with the new host key to get rid " "of this message."); } } } } r = check_host_key(host, hostaddr, options.port, host_key, RDRW, options.user_hostfiles, options.num_user_hostfiles, options.system_hostfiles, options.num_system_hostfiles); out: sshkey_free(plain); free(fp); free(cafp); if (r == 0 && host_key != NULL) { sshkey_free(previous_host_key); r = sshkey_from_private(host_key, &previous_host_key); } return r; }
static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *userstyle, *fp = NULL; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("%s: disabled because of invalid user", __func__); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("%s: SSH_BUG_PKAUTH", __func__); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("%s: unsupported public key algorithm: %s", __func__, pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("%s: cannot decode key: %s", __func__, pkalg); goto done; } if (key->type != pktype) { error("%s: type mismatch for decoded key " "(received %d, expected %d)", __func__, key->type, pktype); goto done; } if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { logit("Refusing RSA key because client uses unsafe " "signature scheme"); goto done; } fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); if (auth2_userkey_already_used(authctxt, key)) { logit("refusing previously-used %s key", key_type(key)); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { debug3("%s: have signature for %s %s", __func__, sshkey_type(key), fp); sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); buffer_put_cstring(&b, userstyle); free(userstyle); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif pubkey_auth_info(authctxt, key, NULL); /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); key = NULL; /* Don't free below */ } buffer_free(&b); free(sig); } else { debug("%s: test whether pkalg/pkblob are acceptable for %s %s", __func__, sshkey_type(key), fp); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); free(fp); return authenticated; }
int main(int argc, char **argv) { struct sshbuf *b; Options options; #define NUM_KEYTYPES 4 struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; u_char *signature, *data, rver; char *host, *fp; size_t slen, dlen; #ifdef WITH_OPENSSL u_int32_t rnd[256]; #endif if (pledge("stdio rpath getpw dns id", NULL) != 0) fatal("%s: pledge: %s", __progname, strerror(errno)); /* Ensure that stdin and stdout are connected */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) exit(1); /* Leave /dev/null fd iff it is attached to stderr */ if (fd > 2) close(fd); i = 0; /* XXX This really needs to read sshd_config for the paths */ key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); original_real_uid = getuid(); /* XXX readconf.c needs this */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("getpwuid failed"); pw = pwcopy(pw); permanently_set_uid(pw); seed_rng(); #ifdef DEBUG_SSH_KEYSIGN log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); #endif /* verify that ssh-keysign is enabled by the admin */ initialize_options(&options); (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0); fill_default_options(&options); if (options.enable_ssh_keysign != 1) fatal("ssh-keysign not enabled in %s", _PATH_HOST_CONFIG_FILE); for (i = found = 0; i < NUM_KEYTYPES; i++) { if (key_fd[i] != -1) found = 1; } if (found == 0) fatal("could not open any host key"); #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); #endif found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { keys[i] = NULL; if (key_fd[i] == -1) continue; r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC, NULL, &key, NULL); close(key_fd[i]); if (r != 0) debug("parse key %d: %s", i, ssh_err(r)); else if (key != NULL) { keys[i] = key; found = 1; } } if (!found) fatal("no hostkey found"); if (pledge("stdio dns", NULL) != 0) fatal("%s: pledge: %s", __progname, strerror(errno)); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); if (ssh_msg_recv(STDIN_FILENO, b) < 0) fatal("ssh_msg_recv failed"); if ((r = sshbuf_get_u8(b, &rver)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (rver != version) fatal("bad version: received %d, expected %d", rver, version); if ((r = sshbuf_get_u32(b, (u_int *)&fd)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (fd < 0 || fd == STDIN_FILENO || fd == STDOUT_FILENO) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) fatal("cannot get local name for fd"); if ((r = sshbuf_get_string(b, &data, &dlen)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (valid_request(pw, host, &key, data, dlen) < 0) fatal("not a valid request"); free(host); found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { if (keys[i] != NULL && sshkey_equal_public(key, keys[i])) { found = 1; break; } } if (!found) { if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __progname); fatal("no matching hostkey found for key %s %s", sshkey_type(key), fp ? fp : ""); } if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, NULL, 0)) != 0) fatal("sshkey_sign failed: %s", ssh_err(r)); free(data); /* send reply */ sshbuf_reset(b); if ((r = sshbuf_put_string(b, signature, slen)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (ssh_msg_send(STDOUT_FILENO, version, b) == -1) fatal("ssh_msg_send failed"); return (0); }
static int server_input_hostkeys_prove(struct sshbuf **respp) { struct ssh *ssh = active_state; /* XXX */ struct sshbuf *resp = NULL; struct sshbuf *sigbuf = NULL; struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; int r, ndx, success = 0; const u_char *blob; u_char *sig = 0; size_t blen, slen; if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || (r = sshkey_from_blob(blob, blen, &key)) != 0) { error("%s: couldn't parse key: %s", __func__, ssh_err(r)); goto out; } /* * Better check that this is actually one of our hostkeys * before attempting to sign anything with it. */ if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { error("%s: unknown host %s key", __func__, sshkey_type(key)); goto out; } /* * XXX refactor: make kex->sign just use an index rather * than passing in public and private keys */ if ((key_prv = get_hostkey_by_index(ndx)) == NULL && (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { error("%s: can't retrieve hostkey %d", __func__, ndx); goto out; } sshbuf_reset(sigbuf); free(sig); sig = NULL; if ((r = sshbuf_put_cstring(sigbuf, "*****@*****.**")) != 0 || (r = sshbuf_put_string(sigbuf, ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0 || (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), 0)) != 0 || (r = sshbuf_put_string(resp, sig, slen)) != 0) { error("%s: couldn't prepare signature: %s", __func__, ssh_err(r)); goto out; } } /* Success */ *respp = resp; resp = NULL; /* don't free it */ success = 1; out: free(sig); sshbuf_free(resp); sshbuf_free(sigbuf); sshkey_free(key); return success; }