static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *userstyle; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* 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("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", 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; } 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, strlen(options.pubkey_key_types), 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { 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)) && 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("test whether pkalg/pkblob are acceptable"); 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))) { 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("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); return authenticated; }
/* 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; }
/* * Checks whether key is allowed in output of command. * returns 1 if the key is allowed or 0 otherwise. */ static int user_key_command_allowed2(struct passwd *user_pw, Key *key) { FILE *f = NULL; int r, ok, found_key = 0; struct passwd *pw; int i, uid_swapped = 0, ac = 0; pid_t pid; char *username = NULL, *key_fp = NULL, *keytext = NULL; char *tmp, *command = NULL, **av = NULL; void (*osigchld)(int); if (options.authorized_keys_command == NULL) return 0; if (options.authorized_keys_command_user == NULL) { error("No user for AuthorizedKeysCommand specified, skipping"); return 0; } /* * NB. all returns later this function should go via "out" to * ensure the original SIGCHLD handler is restored properly. */ osigchld = signal(SIGCHLD, SIG_DFL); /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_keys_command_user, "u", user_pw->pw_name, (char *)NULL); pw = getpwnam(username); if (pw == NULL) { error("AuthorizedKeysCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; } /* Prepare AuthorizedKeysCommand */ if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((r = sshkey_to_base64(key, &keytext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } /* Turn the command into an argument vector */ if (split_argv(options.authorized_keys_command, &ac, &av) != 0) { error("AuthorizedKeysCommand \"%s\" contains invalid quotes", command); goto out; } if (ac == 0) { error("AuthorizedKeysCommand \"%s\" yielded no arguments", command); goto out; } for (i = 1; i < ac; i++) { tmp = percent_expand(av[i], "u", user_pw->pw_name, "h", user_pw->pw_dir, "t", sshkey_ssh_name(key), "f", key_fp, "k", keytext, (char *)NULL); if (tmp == NULL) fatal("%s: percent_expand failed", __func__); free(av[i]); av[i] = tmp; } /* Prepare a printable command for logs, etc. */ command = assemble_argv(ac, av); /* * If AuthorizedKeysCommand was run without arguments * then fall back to the old behaviour of passing the * target username as a single argument. */ if (ac == 1) { av = xreallocarray(av, ac + 2, sizeof(*av)); av[1] = xstrdup(user_pw->pw_name); av[2] = NULL; /* Fix up command too, since it is used in log messages */ free(command); xasprintf(&command, "%s %s", av[0], av[1]); } if ((pid = subprocess("AuthorizedKeysCommand", pw, command, ac, av, &f)) == 0) goto out; uid_swapped = 1; temporarily_use_uid(pw); ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); fclose(f); f = NULL; if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0) goto out; /* Read completed successfully */ found_key = ok; out: if (f != NULL) fclose(f); signal(SIGCHLD, osigchld); for (i = 0; i < ac; i++) free(av[i]); free(av); if (uid_swapped) restore_uid(); free(command); free(username); free(key_fp); free(keytext); return found_key; }
int hostfile_replace_entries(const char *filename, const char *host, const char *ip, struct sshkey **keys, size_t nkeys, int store_hash, int quiet, int hash_alg) { int r, fd, oerrno = 0; int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; struct host_delete_ctx ctx; char *fp, *temp = NULL, *back = NULL; mode_t omask; size_t i; omask = umask(077); memset(&ctx, 0, sizeof(ctx)); ctx.host = host; ctx.quiet = quiet; if ((ctx.skip_keys = calloc(nkeys, sizeof(*ctx.skip_keys))) == NULL) return SSH_ERR_ALLOC_FAIL; ctx.keys = keys; ctx.nkeys = nkeys; ctx.modified = 0; /* * Prepare temporary file for in-place deletion. */ if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) < 0 || (r = asprintf(&back, "%s.old", filename)) < 0) { r = SSH_ERR_ALLOC_FAIL; goto fail; } if ((fd = mkstemp(temp)) == -1) { oerrno = errno; error("%s: mkstemp: %s", __func__, strerror(oerrno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if ((ctx.out = fdopen(fd, "w")) == NULL) { oerrno = errno; close(fd); error("%s: fdopen: %s", __func__, strerror(oerrno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } /* Remove all entries for the specified host from the file */ if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, ip, HKF_WANT_PARSE_KEY)) != 0) { error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); goto fail; } /* Add the requested keys */ for (i = 0; i < nkeys; i++) { if (ctx.skip_keys[i]) continue; if ((fp = sshkey_fingerprint(keys[i], hash_alg, SSH_FP_DEFAULT)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } do_log2(loglevel, "%s%sAdding new key for %s to %s: %s %s", quiet ? __func__ : "", quiet ? ": " : "", host, filename, sshkey_ssh_name(keys[i]), fp); free(fp); if (!write_host_entry(ctx.out, host, ip, keys[i], store_hash)) { r = SSH_ERR_INTERNAL_ERROR; goto fail; } ctx.modified = 1; } fclose(ctx.out); ctx.out = NULL; if (ctx.modified) { /* Backup the original file and replace it with the temporary */ if (unlink(back) == -1 && errno != ENOENT) { oerrno = errno; error("%s: unlink %.100s: %s", __func__, back, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if (link(filename, back) == -1) { oerrno = errno; error("%s: link %.100s to %.100s: %s", __func__, filename, back, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if (rename(temp, filename) == -1) { oerrno = errno; error("%s: rename \"%s\" to \"%s\": %s", __func__, temp, filename, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } } else { /* No changes made; just delete the temporary file */ if (unlink(temp) != 0) error("%s: unlink \"%s\": %s", __func__, temp, strerror(errno)); } /* success */ r = 0; fail: if (temp != NULL && r != 0) unlink(temp); free(temp); free(back); if (ctx.out != NULL) fclose(ctx.out); free(ctx.skip_keys); umask(omask); if (r == SSH_ERR_SYSTEM_ERROR) errno = oerrno; return r; }
static int userauth_pubkey(Authctxt *authctxt) { #ifdef WIN32_FIXME int loginStat = 1; char currentUser[MAX_PATH] = {0}; DWORD currentUserSize = sizeof(currentUser); int targetIsCurrent = 0; # ifdef USE_NTCREATETOKEN int doOpenSSHVerify = 1; # else int doOpenSSHVerify = 0; # endif #endif Buffer b; Key *key = NULL; char *pkalg, *userstyle; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* 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("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", 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; } 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) { 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; /* * On pure win32 try to logon using lsa first. */ #ifdef WIN32_FIXME authctxt -> hTokenLsa_ = NULL; authctxt -> methoddata = NULL; /* * Retrieve name of current login user (i.e. sshd process owner). */ GetUserName(currentUser, ¤tUserSize); /* * Try to get token from lsa, but only if targetUser != currentUser. * Owerthise we already have targetUser's token in current thread, so * we only need key verify from original OpenSSH code. */ targetIsCurrent = (strcmp(currentUser, authctxt -> user) == 0); if (targetIsCurrent) { doOpenSSHVerify = 1; } else { loginStat = LsaLogon(&authctxt -> hTokenLsa_, HomeDirLsaW, authctxt -> user, pkblob, blen, sig, slen, buffer_ptr(&b), buffer_len(&b), datafellows); /* * If lsa logon process success. */ if (loginStat == 0) { /* * And user authorized OK. */ if (authctxt -> hTokenLsa_) { doOpenSSHVerify = 0; /* * This is part of openssh authorization needed for parsing * 'options' block in key. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt -> pw, key, 1))) // PRAGMA:TODO { authenticated = 1; } else { authenticated = 0; } buffer_free(&b); free(sig); } } } if (doOpenSSHVerify) { /* * If lsa fails, test for correct signature using openssh code. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0)) //PRAGMA:TODO && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; } } /* * Original code. */ #else /* #ifdef WIN32_FIXME */ 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); #endif /* else #ifdef WIN32_FIXME. */ } else { debug("test whether pkalg/pkblob are acceptable"); 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 */ #ifndef WIN32_FIXME if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) #endif { 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("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); return authenticated; }
/* * Checks whether principal is allowed in output of command. * returns 1 if the principal is allowed or 0 otherwise. */ static int match_principals_command(struct passwd *user_pw, const struct sshkey *key) { const struct sshkey_cert *cert = key->cert; FILE *f = NULL; int r, ok, found_principal = 0; struct passwd *pw; int i, ac = 0, uid_swapped = 0; pid_t pid; char *tmp, *username = NULL, *command = NULL, **av = NULL; char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL; char serial_s[16]; void (*osigchld)(int); if (options.authorized_principals_command == NULL) return 0; if (options.authorized_principals_command_user == NULL) { error("No user for AuthorizedPrincipalsCommand specified, " "skipping"); return 0; } /* * NB. all returns later this function should go via "out" to * ensure the original SIGCHLD handler is restored properly. */ osigchld = signal(SIGCHLD, SIG_DFL); /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_principals_command_user, "u", user_pw->pw_name, (char *)NULL); pw = getpwnam(username); if (pw == NULL) { error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; } /* Turn the command into an argument vector */ if (split_argv(options.authorized_principals_command, &ac, &av) != 0) { error("AuthorizedPrincipalsCommand \"%s\" contains " "invalid quotes", command); goto out; } if (ac == 0) { error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", command); goto out; } if ((ca_fp = sshkey_fingerprint(cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } if ((r = sshkey_to_base64(key, &keytext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } snprintf(serial_s, sizeof(serial_s), "%llu", (unsigned long long)cert->serial); for (i = 1; i < ac; i++) { tmp = percent_expand(av[i], "u", user_pw->pw_name, "h", user_pw->pw_dir, "t", sshkey_ssh_name(key), "T", sshkey_ssh_name(cert->signature_key), "f", key_fp, "F", ca_fp, "k", keytext, "K", catext, "i", cert->key_id, "s", serial_s, (char *)NULL); if (tmp == NULL) fatal("%s: percent_expand failed", __func__); free(av[i]); av[i] = tmp; } /* Prepare a printable command for logs, etc. */ command = assemble_argv(ac, av); if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, ac, av, &f)) == 0) goto out; uid_swapped = 1; temporarily_use_uid(pw); ok = process_principals(f, NULL, pw, cert); fclose(f); f = NULL; if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0) goto out; /* Read completed successfully */ found_principal = ok; out: if (f != NULL) fclose(f); signal(SIGCHLD, osigchld); for (i = 0; i < ac; i++) free(av[i]); free(av); if (uid_swapped) restore_uid(); free(command); free(username); free(ca_fp); free(key_fp); free(catext); free(keytext); return found_principal; }
/* 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_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; }
int mm_answer_keyallowed(int sock, struct sshbuf *m) { struct ssh *ssh = active_state; /* XXX */ struct sshkey *key; char *cuser, *chost; u_char *blob; size_t bloblen; enum mm_keytype type = 0; int r, allowed = 0; debug3("%s entering", __func__); if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_cstring(m, &cuser, NULL)) != 0 || (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || (r = sshbuf_get_string(m, &blob, &bloblen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if ((r = sshkey_from_blob(blob, bloblen, &key)) != 0) fatal("%s: cannot parse key: %s", __func__, ssh_err(r)); if ((compat20 && type == MM_RSAHOSTKEY) || (!compat20 && type != MM_RSAHOSTKEY)) fatal("%s: key type and protocol mismatch", __func__); if (key != NULL && authctxt->valid) { /* These should not make it past the privsep child */ if (sshkey_type_plain(key->type) == KEY_RSA && (ssh->compat & SSH_BUG_RSASIGMD5) != 0) fatal("%s: passed a SSH_BUG_RSASIGMD5 key", __func__); switch (type) { case MM_USERKEY: allowed = options.pubkey_authentication && !auth2_userkey_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, strlen(options.pubkey_key_types), 0) == 1 && user_key_allowed(authctxt->pw, key); pubkey_auth_info(authctxt, key, NULL); auth_method = "publickey"; if (options.pubkey_authentication && allowed != 1) auth_clear_options(); break; case MM_HOSTKEY: allowed = options.hostbased_authentication && match_pattern_list(sshkey_ssh_name(key), options.hostbased_key_types, strlen(options.hostbased_key_types), 0) == 1 && hostbased_key_allowed(authctxt->pw, cuser, chost, key); pubkey_auth_info(authctxt, key, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); auth_method = "hostbased"; break; #ifdef WITH_SSH1 case MM_RSAHOSTKEY: key->type = KEY_RSA1; /* XXX */ allowed = options.rhosts_rsa_authentication && auth_rhosts_rsa_key_allowed(authctxt->pw, cuser, chost, key); if (options.rhosts_rsa_authentication && allowed != 1) auth_clear_options(); auth_method = "rsa"; break; #endif default: fatal("%s: unknown key type %d", __func__, type); break; } } if (key != NULL) sshkey_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); if (allowed) { /* Save temporarily for comparison in verify */ key_blob = blob; key_bloblen = bloblen; key_blobtype = type; hostbased_cuser = cuser; hostbased_chost = chost; } else { /* Log failed attempt */ auth_log(authctxt, 0, 0, auth_method, NULL); free(blob); free(cuser); free(chost); } debug3("%s: key %p is %s", __func__, key, allowed ? "allowed" : "not allowed"); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, allowed)) != 0 || (r = sshbuf_put_u32(m, forced_command != NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); if (type == MM_RSAHOSTKEY) monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); return (0); }
int mm_answer_keyallowed(int sock, Buffer *m) { struct sshkey *key; char *cuser, *chost; u_char *blob; u_int bloblen, pubkey_auth_attempt; enum mm_keytype type = 0; int allowed = 0; debug3("%s entering", __func__); type = buffer_get_int(m); cuser = buffer_get_string(m, NULL); chost = buffer_get_string(m, NULL); blob = buffer_get_string(m, &bloblen); pubkey_auth_attempt = buffer_get_int(m); key = key_from_blob(blob, bloblen); debug3("%s: key_from_blob: %p", __func__, key); if (key != NULL && authctxt->valid) { /* These should not make it past the privsep child */ if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) fatal("%s: passed a SSH_BUG_RSASIGMD5 key", __func__); switch (type) { case MM_USERKEY: allowed = options.pubkey_authentication && !auth2_key_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) == 1 && user_key_allowed(authctxt->pw, key, pubkey_auth_attempt); auth_method = "publickey"; if (options.pubkey_authentication && (!pubkey_auth_attempt || allowed != 1)) auth_clear_options(); break; case MM_HOSTKEY: allowed = options.hostbased_authentication && !auth2_key_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.hostbased_key_types, 0) == 1 && hostbased_key_allowed(authctxt->pw, cuser, chost, key); auth2_record_info(authctxt, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); auth_method = "hostbased"; break; default: fatal("%s: unknown key type %d", __func__, type); break; } } debug3("%s: key is %s", __func__, allowed ? "allowed" : "not allowed"); auth2_record_key(authctxt, 0, key); sshkey_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); if (allowed) { /* Save temporarily for comparison in verify */ key_blob = blob; key_bloblen = bloblen; key_blobtype = type; hostbased_cuser = cuser; hostbased_chost = chost; } else { /* Log failed attempt */ auth_log(authctxt, 0, 0, auth_method, NULL); free(blob); free(cuser); free(chost); } buffer_clear(m); buffer_put_int(m, allowed); buffer_put_int(m, forced_command != NULL); mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); return (0); }
static void do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c, int direction, int packet_index, const char *dump_path, struct sshbuf *replace_data) { struct ssh *client = NULL, *server = NULL, *server2 = NULL; struct sshkey *pubkey = NULL; struct sshbuf *state; struct kex_params kex_params; char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; char *keyname = NULL; TEST_START("sshkey_from_private"); ASSERT_INT_EQ(sshkey_from_private(prvkey, &pubkey), 0); TEST_DONE(); TEST_START("ssh_init"); memcpy(kex_params.proposal, myproposal, sizeof(myproposal)); if (kex != NULL) kex_params.proposal[PROPOSAL_KEX_ALGS] = strdup(kex); keyname = strdup(sshkey_ssh_name(prvkey)); ASSERT_PTR_NE(keyname, NULL); kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0); ASSERT_PTR_NE(client, NULL); ASSERT_PTR_NE(server, NULL); TEST_DONE(); TEST_START("ssh_add_hostkey"); ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0); ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0); TEST_DONE(); TEST_START("kex"); run_kex(client, server, s2c, c2s, direction, packet_index, dump_path, replace_data); TEST_DONE(); TEST_START("rekeying client"); ASSERT_INT_EQ(kex_send_kexinit(client), 0); run_kex(client, server, s2c, c2s, direction, packet_index, dump_path, replace_data); TEST_DONE(); TEST_START("rekeying server"); ASSERT_INT_EQ(kex_send_kexinit(server), 0); run_kex(client, server, s2c, c2s, direction, packet_index, dump_path, replace_data); TEST_DONE(); TEST_START("ssh_packet_get_state"); state = sshbuf_new(); ASSERT_PTR_NE(state, NULL); ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0); ASSERT_INT_GE(sshbuf_len(state), 1); TEST_DONE(); TEST_START("ssh_packet_set_state"); server2 = NULL; ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0); ASSERT_PTR_NE(server2, NULL); ASSERT_INT_EQ(ssh_add_hostkey(server2, prvkey), 0); kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */ ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0); ASSERT_INT_EQ(sshbuf_len(state), 0); sshbuf_free(state); ASSERT_PTR_NE(server2->kex, NULL); /* XXX we need to set the callbacks */ server2->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; server2->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; #ifdef OPENSSL_HAS_ECC server2->kex->kex[KEX_ECDH_SHA2] = kexecdh_server; #endif server2->kex->kex[KEX_C25519_SHA256] = kexc25519_server; server2->kex->load_host_public_key = server->kex->load_host_public_key; server2->kex->load_host_private_key = server->kex->load_host_private_key; server2->kex->sign = server->kex->sign; TEST_DONE(); TEST_START("rekeying server2"); ASSERT_INT_EQ(kex_send_kexinit(server2), 0); run_kex(client, server2, s2c, c2s, direction, packet_index, dump_path, replace_data); ASSERT_INT_EQ(kex_send_kexinit(client), 0); run_kex(client, server2, s2c, c2s, direction, packet_index, dump_path, replace_data); TEST_DONE(); TEST_START("cleanup"); sshkey_free(pubkey); ssh_free(client); ssh_free(server); ssh_free(server2); free(keyname); TEST_DONE(); }