From 5a18868b2d506b9e2254047d8d9fb9df5b2a8ae0 Mon Sep 17 00:00:00 2001 From: des Date: Sun, 7 Feb 2016 11:38:54 +0000 Subject: MFH (r265214, r294333, r294407, r294467): misc prop fixes MFH (r285975, r287143): register mergeinfo for security fixes MFH (r294497, r294498, r295139): internal documentation MFH (r294328): upgrade to openssh 6.7p1, re-add libwrap MFH (r294332): upgrade to openssh 6.8p1 MFH (r294367): update pam_ssh for api changes MFH (r294909): switch usedns back on MFH (r294336): upgrade to openssh 6.9p1 MFH (r294495): re-enable dsa keys MFH (r294464): upgrade to openssh 7.0p1 MFH (r294496): upgrade to openssh 7.1p2 Approved by: re (gjb) Relnotes: yes --- crypto/openssh/sshconnect2.c | 651 ++++++++++++++++++++++++++----------------- 1 file changed, 403 insertions(+), 248 deletions(-) (limited to 'crypto/openssh/sshconnect2.c') diff --git a/crypto/openssh/sshconnect2.c b/crypto/openssh/sshconnect2.c index ec3ad6a..7751031 100644 --- a/crypto/openssh/sshconnect2.c +++ b/crypto/openssh/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.204 2014/02/02 03:44:32 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.226 2015/07/30 00:01:34 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -61,8 +61,8 @@ #include "dh.h" #include "authfd.h" #include "log.h" -#include "readconf.h" #include "misc.h" +#include "readconf.h" #include "match.h" #include "dispatch.h" #include "canohost.h" @@ -70,6 +70,7 @@ #include "pathnames.h" #include "uidswap.h" #include "hostfile.h" +#include "ssherr.h" #ifdef GSSAPI #include "ssh-gss.h" @@ -90,10 +91,8 @@ u_int session_id2_len = 0; char *xxx_host; struct sockaddr *xxx_hostaddr; -Kex *xxx_kex = NULL; - static int -verify_host_key_callback(Key *hostkey) +verify_host_key_callback(Key *hostkey, struct ssh *ssh) { if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) fatal("Host key verification failed."); @@ -131,16 +130,17 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) } while (0) while ((alg = strsep(&avail, ",")) && *alg != '\0') { - if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) fatal("%s: unknown alg %s", __func__, alg); if (lookup_key_in_hostkeys_by_type(hostkeys, - key_type_plain(ktype), NULL)) + sshkey_type_plain(ktype), NULL)) ALG_APPEND(first, alg); else ALG_APPEND(last, alg); } #undef ALG_APPEND - xasprintf(&ret, "%s%s%s", first, *first == '\0' ? "" : ",", last); + xasprintf(&ret, "%s%s%s", first, + (*first == '\0' || *last == '\0') ? "" : ",", last); if (*first != '\0') debug3("%s: prefer hostkeyalgs: %s", __func__, first); @@ -156,23 +156,19 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) void ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) { - Kex *kex; + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + struct kex *kex; + int r; xxx_host = host; xxx_hostaddr = hostaddr; - if (options.ciphers == (char *)-1) { - logit("No valid ciphers for protocol version 2 given, using defaults."); - options.ciphers = NULL; - } - if (options.ciphers != NULL) { - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; - } + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( + options.kex_algorithms); myproposal[PROPOSAL_ENC_ALGS_CTOS] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); + compat_cipher_proposal(options.ciphers); myproposal[PROPOSAL_ENC_ALGS_STOC] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); + compat_cipher_proposal(options.ciphers); if (options.compression) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none"; @@ -180,43 +176,46 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib"; } - if (options.macs != NULL) { - myproposal[PROPOSAL_MAC_ALGS_CTOS] = - myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; - } - if (options.hostkeyalgorithms != NULL) + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; + if (options.hostkeyalgorithms != NULL) { + if (kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options.hostkeyalgorithms) != 0) + fatal("%s: kex_assemble_namelist", __func__); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(options.hostkeyalgorithms); - else { + } else { + /* Enforce default */ + options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); /* Prefer algorithms that we already have keys for */ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( order_hostkeyalgs(host, hostaddr, port)); } - if (options.kex_algorithms != NULL) - myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( - myproposal[PROPOSAL_KEX_ALGS]); if (options.rekey_limit || options.rekey_interval) packet_set_rekey_limits((u_int32_t)options.rekey_limit, (time_t)options.rekey_interval); /* start key exchange */ - kex = kex_setup(myproposal); + if ((r = kex_setup(active_state, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); + kex = active_state->kex; +#ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; +# ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_client; +# endif +#endif kex->kex[KEX_C25519_SHA256] = kexc25519_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; - xxx_kex = kex; - - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); if (options.use_roaming && !kex->roaming) { debug("Roaming not allowed by server"); @@ -239,15 +238,15 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) * Authenticate user */ -typedef struct Authctxt Authctxt; -typedef struct Authmethod Authmethod; +typedef struct cauthctxt Authctxt; +typedef struct cauthmethod Authmethod; typedef struct identity Identity; typedef struct idlist Idlist; struct identity { TAILQ_ENTRY(identity) next; - AuthenticationConnection *ac; /* set if agent supports key */ - Key *key; /* public/private key */ + int agent_fd; /* >=0 if agent supports key */ + struct sshkey *key; /* public/private key */ char *filename; /* comment for agent-only keys */ int tried; int isprivate; /* key points to the private key */ @@ -255,25 +254,29 @@ struct identity { }; TAILQ_HEAD(idlist, identity); -struct Authctxt { +struct cauthctxt { const char *server_user; const char *local_user; const char *host; const char *service; - Authmethod *method; + struct cauthmethod *method; sig_atomic_t success; char *authlist; + int attempt; /* pubkey */ - Idlist keys; - AuthenticationConnection *agent; + struct idlist keys; + int agent_fd; /* hostbased */ Sensitive *sensitive; + char *oktypes, *ktypes; + const char *active_ktype; /* kbd-interactive */ int info_req_seen; /* generic */ void *methoddata; }; -struct Authmethod { + +struct cauthmethod { char *name; /* string to compare against server's list */ int (*userauth)(Authctxt *authctxt); void (*cleanup)(Authctxt *authctxt); @@ -281,14 +284,14 @@ struct Authmethod { int *batch_flag; /* flag in option struct that disables method */ }; -void input_userauth_success(int, u_int32_t, void *); -void input_userauth_success_unexpected(int, u_int32_t, void *); -void input_userauth_failure(int, u_int32_t, void *); -void input_userauth_banner(int, u_int32_t, void *); -void input_userauth_error(int, u_int32_t, void *); -void input_userauth_info_req(int, u_int32_t, void *); -void input_userauth_pk_ok(int, u_int32_t, void *); -void input_userauth_passwd_changereq(int, u_int32_t, void *); +int input_userauth_success(int, u_int32_t, void *); +int input_userauth_success_unexpected(int, u_int32_t, void *); +int input_userauth_failure(int, u_int32_t, void *); +int input_userauth_banner(int, u_int32_t, void *); +int input_userauth_error(int, u_int32_t, void *); +int input_userauth_info_req(int, u_int32_t, void *); +int input_userauth_pk_ok(int, u_int32_t, void *); +int input_userauth_passwd_changereq(int, u_int32_t, void *); int userauth_none(Authctxt *); int userauth_pubkey(Authctxt *); @@ -298,11 +301,11 @@ int userauth_hostbased(Authctxt *); #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt); -void input_gssapi_response(int type, u_int32_t, void *); -void input_gssapi_token(int type, u_int32_t, void *); -void input_gssapi_hash(int type, u_int32_t, void *); -void input_gssapi_error(int, u_int32_t, void *); -void input_gssapi_errtok(int, u_int32_t, void *); +int input_gssapi_response(int type, u_int32_t, void *); +int input_gssapi_token(int type, u_int32_t, void *); +int input_gssapi_hash(int type, u_int32_t, void *); +int input_gssapi_error(int, u_int32_t, void *); +int input_gssapi_errtok(int, u_int32_t, void *); #endif void userauth(Authctxt *, char *); @@ -395,7 +398,9 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, authctxt.authlist = NULL; authctxt.methoddata = NULL; authctxt.sensitive = sensitive; + authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL; authctxt.info_req_seen = 0; + authctxt.agent_fd = -1; if (authctxt.method == NULL) fatal("ssh_userauth2: internal error: cannot send userauth none request"); @@ -450,15 +455,16 @@ userauth(Authctxt *authctxt, char *authlist) } /* ARGSUSED */ -void +int input_userauth_error(int type, u_int32_t seq, void *ctxt) { fatal("input_userauth_error: bad message during authentication: " "type %d", type); + return 0; } /* ARGSUSED */ -void +int input_userauth_banner(int type, u_int32_t seq, void *ctxt) { char *msg, *raw, *lang; @@ -477,10 +483,11 @@ input_userauth_banner(int type, u_int32_t seq, void *ctxt) } free(raw); free(lang); + return 0; } /* ARGSUSED */ -void +int input_userauth_success(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -494,9 +501,10 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt) free(authctxt->methoddata); authctxt->methoddata = NULL; authctxt->success = 1; /* break out */ + return 0; } -void +int input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -506,10 +514,11 @@ input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) fatal("Unexpected authentication success during %s.", authctxt->method->name); + return 0; } /* ARGSUSED */ -void +int input_userauth_failure(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -532,10 +541,11 @@ input_userauth_failure(int type, u_int32_t seq, void *ctxt) debug("Authentications that can continue: %s", authlist); userauth(authctxt, authlist); + return 0; } /* ARGSUSED */ -void +int input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -579,7 +589,9 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) key->type, pktype); goto done; } - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + goto done; debug2("input_userauth_pk_ok: fp %s", fp); free(fp); @@ -603,6 +615,7 @@ done: /* try another method if we did not send a packet */ if (sent == 0) userauth(authctxt, NULL); + return 0; } #ifdef GSSAPI @@ -718,7 +731,7 @@ process_gssapi_token(void *ctxt, gss_buffer_t recv_tok) } /* ARGSUSED */ -void +int input_gssapi_response(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -739,7 +752,7 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) free(oidv); debug("Badly encoded mechanism OID received"); userauth(authctxt, NULL); - return; + return 0; } if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) @@ -753,12 +766,13 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) /* Start again with next method on list */ debug("Trying to start again"); userauth(authctxt, NULL); - return; + return 0; } + return 0; } /* ARGSUSED */ -void +int input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -781,12 +795,13 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt) if (GSS_ERROR(status)) { /* Start again with the next method in the list */ userauth(authctxt, NULL); - return; + return 0; } + return 0; } /* ARGSUSED */ -void +int input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -813,10 +828,11 @@ input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) gss_release_buffer(&ms, &send_tok); /* Server will be returning a failed packet after this one */ + return 0; } /* ARGSUSED */ -void +int input_gssapi_error(int type, u_int32_t plen, void *ctxt) { char *msg; @@ -832,6 +848,7 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) debug("Server GSSAPI Error:\n%s", msg); free(msg); free(lang); + return 0; } #endif /* GSSAPI */ @@ -886,7 +903,7 @@ userauth_passwd(Authctxt *authctxt) * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST */ /* ARGSUSED */ -void +int input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; @@ -927,7 +944,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) password = read_passphrase(prompt, RP_ALLOW_EOF); if (password == NULL) { /* bail out */ - return; + return 0; } snprintf(prompt, sizeof(prompt), "Retype %.30s@%.128s's new password: ", @@ -950,30 +967,33 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); + return 0; } static int -identity_sign(Identity *id, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) +identity_sign(struct identity *id, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { Key *prv; int ret; /* the agent supports this key */ - if (id->ac) - return (ssh_agent_sign(id->ac, id->key, sigp, lenp, - data, datalen)); + if (id->agent_fd) + return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, + data, datalen, compat); + /* * we have already loaded the private key or * the private key is stored in external hardware */ - if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) - return (key_sign(id->key, sigp, lenp, data, datalen)); + if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) + return (sshkey_sign(id->key, sigp, lenp, data, datalen, + compat)); /* load the private key from the file */ if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) - return (-1); - ret = key_sign(prv, sigp, lenp, data, datalen); - key_free(prv); + return (-1); /* XXX return decent error code */ + ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat); + sshkey_free(prv); return (ret); } @@ -982,13 +1002,16 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) { Buffer b; u_char *blob, *signature; - u_int bloblen, slen; + u_int bloblen; + size_t slen; u_int skip = 0; int ret = -1; int have_sig = 1; char *fp; - fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + return 0; debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); free(fp); @@ -1023,8 +1046,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) /* generate signature */ ret = identity_sign(id, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); - if (ret == -1) { + buffer_ptr(&b), buffer_len(&b), datafellows); + if (ret != 0) { free(blob); buffer_free(&b); return 0; @@ -1099,7 +1122,7 @@ load_identity_file(char *filename, int userprovided) { Key *private; char prompt[300], *passphrase; - int perm_ok = 0, quit, i; + int r, perm_ok = 0, quit = 0, i; struct stat st; if (stat(filename, &st) < 0) { @@ -1107,33 +1130,50 @@ load_identity_file(char *filename, int userprovided) filename, strerror(errno)); return NULL; } - private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); - if (!perm_ok) { - if (private != NULL) - key_free(private); - return NULL; - } - if (private == NULL) { - if (options.batch_mode) - return NULL; - snprintf(prompt, sizeof prompt, - "Enter passphrase for key '%.100s': ", filename); - for (i = 0; i < options.number_of_password_prompts; i++) { + snprintf(prompt, sizeof prompt, + "Enter passphrase for key '%.100s': ", filename); + for (i = 0; i <= options.number_of_password_prompts; i++) { + if (i == 0) + passphrase = ""; + else { passphrase = read_passphrase(prompt, 0); - if (strcmp(passphrase, "") != 0) { - private = key_load_private_type(KEY_UNSPEC, - filename, passphrase, NULL, NULL); - quit = 0; - } else { + if (*passphrase == '\0') { debug2("no passphrase given, try next key"); + free(passphrase); + break; + } + } + switch ((r = sshkey_load_private_type(KEY_UNSPEC, filename, + passphrase, &private, NULL, &perm_ok))) { + case 0: + break; + case SSH_ERR_KEY_WRONG_PASSPHRASE: + if (options.batch_mode) { + quit = 1; + break; + } + if (i != 0) + debug2("bad passphrase given, try again..."); + break; + case SSH_ERR_SYSTEM_ERROR: + if (errno == ENOENT) { + debug2("Load key \"%s\": %s", + filename, ssh_err(r)); quit = 1; + break; } + /* FALLTHROUGH */ + default: + error("Load key \"%s\": %s", filename, ssh_err(r)); + quit = 1; + break; + } + if (i > 0) { explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); - if (private != NULL || quit) - break; - debug2("bad passphrase given, try again..."); } + if (private != NULL || quit) + break; } return private; } @@ -1147,12 +1187,12 @@ load_identity_file(char *filename, int userprovided) static void pubkey_prepare(Authctxt *authctxt) { - Identity *id, *id2, *tmp; - Idlist agent, files, *preferred; - Key *key; - AuthenticationConnection *ac; - char *comment; - int i, found; + struct identity *id, *id2, *tmp; + struct idlist agent, files, *preferred; + struct sshkey *key; + int agent_fd, i, r, found; + size_t j; + struct ssh_identitylist *idlist; TAILQ_INIT(&agent); /* keys from the agent */ TAILQ_INIT(&files); /* keys from the config file */ @@ -1175,14 +1215,14 @@ pubkey_prepare(Authctxt *authctxt) } /* Prefer PKCS11 keys that are explicitly listed */ TAILQ_FOREACH_SAFE(id, &files, next, tmp) { - if (id->key == NULL || (id->key->flags & KEY_FLAG_EXT) == 0) + if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) continue; found = 0; TAILQ_FOREACH(id2, &files, next) { if (id2->key == NULL || - (id2->key->flags & KEY_FLAG_EXT) != 0) + (id2->key->flags & SSHKEY_FLAG_EXT) == 0) continue; - if (key_equal(id->key, id2->key)) { + if (sshkey_equal(id->key, id2->key)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); found = 1; @@ -1197,37 +1237,48 @@ pubkey_prepare(Authctxt *authctxt) } } /* list of keys supported by the agent */ - if ((ac = ssh_get_authentication_connection())) { - for (key = ssh_get_first_identity(ac, &comment, 2); - key != NULL; - key = ssh_get_next_identity(ac, &comment, 2)) { + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { + if (r != SSH_ERR_AGENT_NOT_PRESENT) + debug("%s: ssh_get_authentication_socket: %s", + __func__, ssh_err(r)); + } else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) { + if (r != SSH_ERR_AGENT_NO_IDENTITIES) + debug("%s: ssh_fetch_identitylist: %s", + __func__, ssh_err(r)); + } else { + for (j = 0; j < idlist->nkeys; j++) { found = 0; TAILQ_FOREACH(id, &files, next) { - /* agent keys from the config file are preferred */ - if (key_equal(key, id->key)) { - key_free(key); - free(comment); + /* + * agent keys from the config file are + * preferred + */ + if (sshkey_equal(idlist->keys[j], id->key)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); - id->ac = ac; + id->agent_fd = agent_fd; found = 1; break; } } if (!found && !options.identities_only) { id = xcalloc(1, sizeof(*id)); - id->key = key; - id->filename = comment; - id->ac = ac; + /* XXX "steals" key/comment from idlist */ + id->key = idlist->keys[j]; + id->filename = idlist->comments[j]; + idlist->keys[j] = NULL; + idlist->comments[j] = NULL; + id->agent_fd = agent_fd; TAILQ_INSERT_TAIL(&agent, id, next); } } + ssh_free_identitylist(idlist); /* append remaining agent keys */ for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { TAILQ_REMOVE(&agent, id, next); TAILQ_INSERT_TAIL(preferred, id, next); } - authctxt->agent = ac; + authctxt->agent_fd = agent_fd; } /* append remaining keys from the config file */ for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { @@ -1245,18 +1296,38 @@ pubkey_cleanup(Authctxt *authctxt) { Identity *id; - if (authctxt->agent != NULL) - ssh_close_authentication_connection(authctxt->agent); + if (authctxt->agent_fd != -1) + ssh_close_authentication_socket(authctxt->agent_fd); for (id = TAILQ_FIRST(&authctxt->keys); id; id = TAILQ_FIRST(&authctxt->keys)) { TAILQ_REMOVE(&authctxt->keys, id, next); if (id->key) - key_free(id->key); + sshkey_free(id->key); free(id->filename); free(id); } } +static int +try_identity(Identity *id) +{ + if (!id->key) + return (0); + if (match_pattern_list(sshkey_ssh_name(id->key), + options.pubkey_key_types, 0) != 1) { + debug("Skipping %s key %s for not in PubkeyAcceptedKeyTypes", + sshkey_ssh_name(id->key), id->filename); + return (0); + } + if (key_type_plain(id->key->type) == KEY_RSA && + (datafellows & SSH_BUG_RSASIGMD5) != 0) { + debug("Skipped %s key %s for RSA/MD5 server", + key_type(id->key), id->filename); + return (0); + } + return (id->key->type != KEY_RSA1); +} + int userauth_pubkey(Authctxt *authctxt) { @@ -1275,11 +1346,7 @@ userauth_pubkey(Authctxt *authctxt) * private key instead */ if (id->key != NULL) { - if (key_type_plain(id->key->type) == KEY_RSA && - (datafellows & SSH_BUG_RSASIGMD5) != 0) { - debug("Skipped %s key %s for RSA/MD5 server", - key_type(id->key), id->filename); - } else if (id->key->type != KEY_RSA1) { + if (try_identity(id)) { debug("Offering %s public key: %s", key_type(id->key), id->filename); sent = send_pubkey_test(authctxt, id); @@ -1289,13 +1356,8 @@ userauth_pubkey(Authctxt *authctxt) id->key = load_identity_file(id->filename, id->userprovided); if (id->key != NULL) { - id->isprivate = 1; - if (key_type_plain(id->key->type) == KEY_RSA && - (datafellows & SSH_BUG_RSASIGMD5) != 0) { - debug("Skipped %s key %s for RSA/MD5 " - "server", key_type(id->key), - id->filename); - } else { + if (try_identity(id)) { + id->isprivate = 1; sent = sign_and_send_pubkey( authctxt, id); } @@ -1343,7 +1405,7 @@ userauth_kbdint(Authctxt *authctxt) /* * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ -void +int input_userauth_info_req(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -1395,81 +1457,120 @@ input_userauth_info_req(int type, u_int32_t seq, void *ctxt) packet_add_padding(64); packet_send(); + return 0; } static int -ssh_keysign(Key *key, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) +ssh_keysign(struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen) { - Buffer b; + struct sshbuf *b; struct stat st; pid_t pid; - int to[2], from[2], status, version = 2; + int i, r, to[2], from[2], status, sock = packet_get_connection_in(); + u_char rversion = 0, version = 2; + void (*osigchld)(int); - debug2("ssh_keysign called"); + *sigp = NULL; + *lenp = 0; if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) { - error("ssh_keysign: not installed: %s", strerror(errno)); + error("%s: not installed: %s", __func__, strerror(errno)); + return -1; + } + if (fflush(stdout) != 0) { + error("%s: fflush: %s", __func__, strerror(errno)); return -1; } - if (fflush(stdout) != 0) - error("ssh_keysign: fflush: %s", strerror(errno)); if (pipe(to) < 0) { - error("ssh_keysign: pipe: %s", strerror(errno)); + error("%s: pipe: %s", __func__, strerror(errno)); return -1; } if (pipe(from) < 0) { - error("ssh_keysign: pipe: %s", strerror(errno)); + error("%s: pipe: %s", __func__, strerror(errno)); return -1; } if ((pid = fork()) < 0) { - error("ssh_keysign: fork: %s", strerror(errno)); + error("%s: fork: %s", __func__, strerror(errno)); return -1; } + osigchld = signal(SIGCHLD, SIG_DFL); if (pid == 0) { /* keep the socket on exec */ - fcntl(packet_get_connection_in(), F_SETFD, 0); + fcntl(sock, F_SETFD, 0); permanently_drop_suid(getuid()); close(from[0]); if (dup2(from[1], STDOUT_FILENO) < 0) - fatal("ssh_keysign: dup2: %s", strerror(errno)); + fatal("%s: dup2: %s", __func__, strerror(errno)); close(to[1]); if (dup2(to[0], STDIN_FILENO) < 0) - fatal("ssh_keysign: dup2: %s", strerror(errno)); + fatal("%s: dup2: %s", __func__, strerror(errno)); close(from[1]); close(to[0]); + /* Close everything but stdio and the socket */ + for (i = STDERR_FILENO + 1; i < sock; i++) + close(i); + closefrom(sock + 1); + debug3("%s: [child] pid=%ld, exec %s", + __func__, (long)getpid(), _PATH_SSH_KEY_SIGN); execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *) 0); - fatal("ssh_keysign: exec(%s): %s", _PATH_SSH_KEY_SIGN, + fatal("%s: exec(%s): %s", __func__, _PATH_SSH_KEY_SIGN, strerror(errno)); } close(from[1]); close(to[0]); - buffer_init(&b); - buffer_put_int(&b, packet_get_connection_in()); /* send # of socket */ - buffer_put_string(&b, data, datalen); - if (ssh_msg_send(to[1], version, &b) == -1) - fatal("ssh_keysign: couldn't send request"); - - if (ssh_msg_recv(from[0], &b) < 0) { - error("ssh_keysign: no reply"); - buffer_free(&b); - return -1; - } + if ((b = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + /* send # of sock, data to be signed */ + if ((r = sshbuf_put_u32(b, sock) != 0) || + (r = sshbuf_put_string(b, data, datalen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (ssh_msg_send(to[1], version, b) == -1) + fatal("%s: couldn't send request", __func__); + sshbuf_reset(b); + r = ssh_msg_recv(from[0], b); close(from[0]); close(to[1]); + if (r < 0) { + error("%s: no reply", __func__); + goto fail; + } - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - break; - - if (buffer_get_char(&b) != version) { - error("ssh_keysign: bad version"); - buffer_free(&b); + errno = 0; + while (waitpid(pid, &status, 0) < 0) { + if (errno != EINTR) { + error("%s: waitpid %ld: %s", + __func__, (long)pid, strerror(errno)); + goto fail; + } + } + if (!WIFEXITED(status)) { + error("%s: exited abnormally", __func__); + goto fail; + } + if (WEXITSTATUS(status) != 0) { + error("%s: exited with status %d", + __func__, WEXITSTATUS(status)); + goto fail; + } + if ((r = sshbuf_get_u8(b, &rversion)) != 0) { + error("%s: buffer error: %s", __func__, ssh_err(r)); + goto fail; + } + if (rversion != version) { + error("%s: bad version", __func__); + goto fail; + } + if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { + error("%s: buffer error: %s", __func__, ssh_err(r)); + fail: + signal(SIGCHLD, osigchld); + sshbuf_free(b); return -1; } - *sigp = buffer_get_string(&b, lenp); - buffer_free(&b); + signal(SIGCHLD, osigchld); + sshbuf_free(b); return 0; } @@ -1477,94 +1578,148 @@ ssh_keysign(Key *key, u_char **sigp, u_int *lenp, int userauth_hostbased(Authctxt *authctxt) { - Key *private = NULL; - Sensitive *sensitive = authctxt->sensitive; - Buffer b; - u_char *signature, *blob; - char *chost, *pkalg, *p; + struct ssh *ssh = active_state; + struct sshkey *private = NULL; + struct sshbuf *b = NULL; const char *service; - u_int blen, slen; - int ok, i, found = 0; - - /* check for a useful key */ - for (i = 0; i < sensitive->nkeys; i++) { - private = sensitive->keys[i]; - if (private && private->type != KEY_RSA1) { - found = 1; + u_char *sig = NULL, *keyblob = NULL; + char *fp = NULL, *chost = NULL, *lname = NULL; + size_t siglen = 0, keylen = 0; + int i, r, success = 0; + + if (authctxt->ktypes == NULL) { + authctxt->oktypes = xstrdup(options.hostbased_key_types); + authctxt->ktypes = authctxt->oktypes; + } + + /* + * Work through each listed type pattern in HostbasedKeyTypes, + * trying each hostkey that matches the type in turn. + */ + for (;;) { + if (authctxt->active_ktype == NULL) + authctxt->active_ktype = strsep(&authctxt->ktypes, ","); + if (authctxt->active_ktype == NULL || + *authctxt->active_ktype == '\0') + break; + debug3("%s: trying key type %s", __func__, + authctxt->active_ktype); + + /* check for a useful key */ + private = NULL; + for (i = 0; i < authctxt->sensitive->nkeys; i++) { + if (authctxt->sensitive->keys[i] == NULL || + authctxt->sensitive->keys[i]->type == KEY_RSA1 || + authctxt->sensitive->keys[i]->type == KEY_UNSPEC) + continue; + if (match_pattern_list( + sshkey_ssh_name(authctxt->sensitive->keys[i]), + authctxt->active_ktype, 0) != 1) + continue; /* we take and free the key */ - sensitive->keys[i] = NULL; + private = authctxt->sensitive->keys[i]; + authctxt->sensitive->keys[i] = NULL; break; } + /* Found one */ + if (private != NULL) + break; + /* No more keys of this type; advance */ + authctxt->active_ktype = NULL; } - if (!found) { + if (private == NULL) { + free(authctxt->oktypes); + authctxt->oktypes = authctxt->ktypes = NULL; + authctxt->active_ktype = NULL; debug("No more client hostkeys for hostbased authentication."); - return 0; + goto out; } - if (key_to_blob(private, &blob, &blen) == 0) { - key_free(private); - return 0; + + if ((fp = sshkey_fingerprint(private, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) { + error("%s: sshkey_fingerprint failed", __func__); + goto out; } + debug("%s: trying hostkey %s %s", + __func__, sshkey_ssh_name(private), fp); + /* figure out a name for the client host */ - p = get_local_name(packet_get_connection_in()); - if (p == NULL) { - error("userauth_hostbased: cannot get local ipaddr/name"); - key_free(private); - free(blob); - return 0; + if ((lname = get_local_name(packet_get_connection_in())) == NULL) { + error("%s: cannot get local ipaddr/name", __func__); + goto out; } - xasprintf(&chost, "%s.", p); - debug2("userauth_hostbased: chost %s", chost); - free(p); + + /* XXX sshbuf_put_stringf? */ + xasprintf(&chost, "%s.", lname); + debug2("%s: chost %s", __func__, chost); service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : authctxt->service; - pkalg = xstrdup(key_ssh_name(private)); - buffer_init(&b); + /* construct data */ - buffer_put_string(&b, session_id2, session_id2_len); - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, authctxt->server_user); - buffer_put_cstring(&b, service); - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_cstring(&b, pkalg); - buffer_put_string(&b, blob, blen); - buffer_put_cstring(&b, chost); - buffer_put_cstring(&b, authctxt->local_user); + if ((b = sshbuf_new()) == NULL) { + error("%s: sshbuf_new failed", __func__); + goto out; + } + if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); + goto out; + } + 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->server_user)) != 0 || + (r = sshbuf_put_cstring(b, service)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || + (r = sshbuf_put_cstring(b, key_ssh_name(private))) != 0 || + (r = sshbuf_put_string(b, keyblob, keylen)) != 0 || + (r = sshbuf_put_cstring(b, chost)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) { + error("%s: buffer error: %s", __func__, ssh_err(r)); + goto out; + } + #ifdef DEBUG_PK - buffer_dump(&b); + sshbuf_dump(b, stderr); #endif - if (sensitive->external_keysign) - ok = ssh_keysign(private, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); - else - ok = key_sign(private, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); - key_free(private); - buffer_free(&b); - if (ok != 0) { - error("key_sign failed"); - free(chost); - free(pkalg); - free(blob); - return 0; + if (authctxt->sensitive->external_keysign) + r = ssh_keysign(private, &sig, &siglen, + sshbuf_ptr(b), sshbuf_len(b)); + else if ((r = sshkey_sign(private, &sig, &siglen, + sshbuf_ptr(b), sshbuf_len(b), datafellows)) != 0) + debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); + if (r != 0) { + error("sign using hostkey %s %s failed", + sshkey_ssh_name(private), fp); + goto out; } - packet_start(SSH2_MSG_USERAUTH_REQUEST); - packet_put_cstring(authctxt->server_user); - packet_put_cstring(authctxt->service); - packet_put_cstring(authctxt->method->name); - packet_put_cstring(pkalg); - packet_put_string(blob, blen); - packet_put_cstring(chost); - packet_put_cstring(authctxt->local_user); - packet_put_string(signature, slen); - explicit_bzero(signature, slen); - free(signature); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_cstring(ssh, key_ssh_name(private))) != 0 || + (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 || + (r = sshpkt_put_cstring(ssh, chost)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 || + (r = sshpkt_put_string(ssh, sig, siglen)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error("%s: packet error: %s", __func__, ssh_err(r)); + goto out; + } + success = 1; + + out: + if (sig != NULL) { + explicit_bzero(sig, siglen); + free(sig); + } + free(keyblob); + free(lname); + free(fp); free(chost); - free(pkalg); - free(blob); + sshkey_free(private); + sshbuf_free(b); - packet_send(); - return 1; + return success; } /* find auth method */ -- cgit v1.1