static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; int r, authenticated; const char *displayname; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); /* * We don't need to check the status, because we're only enabled in * the dispatcher once the exchange is complete */ if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); if ((!use_privsep || mm_is_monitor()) && (displayname = ssh_gssapi_displayname()) != NULL) auth2_record_info(authctxt, "%s", displayname); authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); return 0; }
static Channel * server_request_direct_tcpip(struct ssh *ssh) { Channel *c = NULL; char *target, *originator; u_int target_port, originator_port; int r; if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &target_port)) != 0 || (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); debug("server_request_direct_tcpip: originator %s port %d, target %s " "port %d", originator, originator_port, target, target_port); /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag) { c = channel_connect_to_port(ssh, target, target_port, "direct-tcpip", "direct-tcpip"); } else { logit("refused local port forward: " "originator %s port %d, target %s port %d", originator, originator_port, target, target_port); } free(originator); free(target); return c; }
static Channel * server_request_session(struct ssh *ssh) { Channel *c; int r; debug("input_session_request"); if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (no_more_sessions) { ssh_packet_disconnect(ssh, "Possible attack: attempt to open a session " "after additional sessions disabled"); } /* * A server session has no fd to read or write until a * CHANNEL_REQUEST for a shell is made, so we set the type to * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all * CHANNEL_REQUEST messages is registered. */ c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 0, "server-session", 1); if (session_open(ssh, c->self) != 1) { debug("session open failed, free channel %d", c->self); channel_free(c); return NULL; } channel_register_cleanup(c->self, session_close_by_channel, 0); return c; }
static int userauth_none(struct ssh *ssh) { int r; none_enabled = 0; if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (options.permit_empty_passwd && options.password_authentication) return (PRIVSEP(auth_password(ssh->authctxt, ""))); return (0); }
static int input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; BIGNUM *p = NULL, *g = NULL; int r, bits; debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); if ((p = BN_new()) == NULL || (g = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshpkt_get_bignum2(ssh, p)) != 0 || (r = sshpkt_get_bignum2(ssh, g)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; if ((bits = BN_num_bits(p)) < 0 || (u_int)bits < kex->min || (u_int)bits > kex->max) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } if ((kex->dh = dh_new_group(g, p)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } p = g = NULL; /* belong to kex->dh now */ /* generate and send 'e', client DH public key */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); r = 0; out: if (p) BN_clear_free(p); if (g) BN_clear_free(g); return r; }
static int input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; int r; u_int min = 0, max = 0, nbits = 0; debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); if ((r = sshpkt_get_u32(ssh, &min)) != 0 || (r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_u32(ssh, &max)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; kex->min = min; kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); if (kex->max < kex->min || kex->nbits < kex->min || kex->max < kex->nbits) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } /* Contact privileged parent */ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); if (kex->dh == NULL) { sshpkt_disconnect(ssh, "no matching DH grp found"); r = SSH_ERR_ALLOC_FAIL; goto out; } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; /* Compute our exchange value in parallel with the client */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) goto out; debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; out: return r; }
/* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; const u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) if ((r = sshpkt_get_u8(ssh, NULL)) != 0) return r; for (i = 0; i < PROPOSAL_MAX; i++) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; /* * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported * KEX method has the server move first, but a server might be using * a custom method or one that we otherwise don't support. We should * be prepared to remember first_kex_follows here so we can eat a * packet later. * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means * for cases where the server *doesn't* go first. I guess we should * ignore it when it is set for these cases, which is what we do now. */ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); return SSH_ERR_INTERNAL_ERROR; }
static int server_input_eof(int type, u_int32_t seq, struct ssh *ssh) { int r; /* * Eof from the client. The stdin descriptor to the * program will be closed when all buffered data has * drained. */ debug("EOF received for stdin."); r = sshpkt_get_end(ssh); stdin_eof = 1; return r; }
static int server_input_window_size(int type, u_int32_t seq, struct ssh *ssh) { u_int row, col, xpixel, ypixel; int r; if ((r = sshpkt_get_u32(ssh, &row)) != 0 || (r = sshpkt_get_u32(ssh, &col)) != 0 || (r = sshpkt_get_u32(ssh, &xpixel)) != 0 || (r = sshpkt_get_u32(ssh, &ypixel)) != 0 || (r = sshpkt_get_end(ssh))) return r; debug("Window change received."); if (fdin != -1) pty_change_window_size(fdin, row, col, xpixel, ypixel); return 0; }
static int kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; int r; debug("SSH2_MSG_NEWKEYS received"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); if ((r = sshpkt_get_end(ssh)) != 0) return r; kex->done = 1; sshbuf_reset(kex->peer); /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; return 0; }
/* * Attempt J-PAKE authentication. */ static int userauth_jpake(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; int r, authenticated = 0; if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); debug("[email protected] requested"); if (authctxt->user != NULL) { if (authctxt->jpake_ctx == NULL) authctxt->jpake_ctx = jpake_new(); if (options.zero_knowledge_password_authentication) authenticated = auth2_jpake_start(ssh); } return authenticated; }
static int kex_input_newkeys(ncrack_ssh_state *nstate) { //struct ssh *ssh = ctxt; struct kex *kex = nstate->kex; int r; debug("SSH2_MSG_NEWKEYS received"); //ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); if ((r = sshpkt_get_end(nstate)) != 0) return r; kex->done = 1; sshbuf_reset(kex->peer); /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; return 0; }
/* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) if ((r = sshpkt_get_u8(ssh, NULL)) != 0) return r; for (i = 0; i < PROPOSAL_MAX; i++) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || (r = sshpkt_get_u32(ssh, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); return SSH_ERR_INTERNAL_ERROR; }
static int server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) { Channel *c; int r, success = 0; u_int id; u_char reply; char *rtype = NULL; if ((r = sshpkt_get_u32(ssh, &id)) != 0 || (r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &reply)) != 0) goto out; debug("server_input_channel_req: channel %u request %s reply %d", id, rtype, reply); if ((c = channel_lookup(id)) == NULL) ssh_packet_disconnect(ssh, "server_input_channel_req: " "unknown channel %u", id); if (!strcmp(rtype, "*****@*****.**")) { if ((r = sshpkt_get_end(ssh)) != 0) goto out; chan_rcvd_eow(c); } else if ((c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) success = session_input_channel_req(c, rtype); if (reply && !(c->flags & CHAN_CLOSE_SENT)) { if ((r = sshpkt_start(ssh, success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; } r = 0; out: if (rtype) free(rtype); return r; }
/*ARGSUSED*/ static int input_service_request(int type, u_int32_t seq, struct ssh *ssh) { struct authctxt *authctxt = ssh->authctxt; char *service = NULL; int r, acceptit = 0; if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; if (authctxt == NULL) fatal("input_service_request: no authctxt"); if (strcmp(service, "ssh-userauth") == 0) { if (!authctxt->success) { acceptit = 1; /* now we can handle user-auth requests */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); } } /* XXX all other service requests are denied */ if (acceptit) { if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 || (r = sshpkt_put_cstring(ssh, service)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; ssh_packet_write_wait(ssh); } else { debug("bad service request %s", service); ssh_packet_disconnect(ssh, "bad service request %s", service); } r = 0; out: free(service); return r; }
static int userauth_passwd(struct ssh *ssh) { char *password; int authenticated = 0, r; u_char change; size_t len; if ((r = sshpkt_get_u8(ssh, &change)) != 0 || (r = sshpkt_get_cstring(ssh, &password, &len)) != 0 || (change && (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0) || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (change) logit("password change not supported"); else if (PRIVSEP(auth_password(ssh, password)) == 1) authenticated = 1; explicit_bzero(password, len); free(password); return authenticated; }
int kex_input_ext_info(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; u_int32_t i, ninfo; char *name, *val, *found; int r; debug("SSH2_MSG_EXT_INFO received"); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { free(name); return r; } debug("%s: %s=<%s>", __func__, name, val); if (strcmp(name, "server-sig-algs") == 0) { found = match_list("rsa-sha2-256", val, NULL); if (found) { kex->rsa_sha2 = 256; free(found); } found = match_list("rsa-sha2-512", val, NULL); if (found) { kex->rsa_sha2 = 512; free(found); } } free(name); free(val); } return sshpkt_get_end(ssh); }
static int server_input_stdin_data(int type, u_int32_t seq, struct ssh *ssh) { u_char *data = NULL; size_t data_len; int r; /* Stdin data from the client. Append it to the buffer. */ /* Ignore any data if the client has closed stdin. */ if (fdin == -1) return 0; if ((r = sshpkt_get_string(ssh, &data, &data_len)) != 0 || (r = sshpkt_get_end(ssh)) != 0 || (r = sshbuf_put(stdin_buffer, data, data_len)) != 0) goto out; r = 0; out: if (data) { explicit_bzero(data, data_len); free(data); } return r; }
static int input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status; int r; u_char *p; size_t len; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); recv_tok.value = p; recv_tok.length = len; /* Push the error token into GSSAPI to see what it says */ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL)); free(recv_tok.value); /* We can't return anything to the client, even if we wanted to */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); /* The client will have already moved on to the next auth */ gss_release_buffer(&maj_status, &send_tok); return 0; }
int auth_rsa_challenge_dialog(struct sshkey *key) { struct ssh *ssh = active_state; BIGNUM *challenge, *encrypted_challenge; u_char response[16]; int r, success; if ((encrypted_challenge = BN_new()) == NULL) fatal("auth_rsa_challenge_dialog: BN_new() failed"); challenge = PRIVSEP(auth_rsa_generate_challenge(key)); /* Encrypt the challenge with the public key. */ if ((r = rsa_public_encrypt(encrypted_challenge, challenge, key->rsa)) != 0) fatal("%s: rsa_public_encrypt: %s", __func__, ssh_err(r)); /* Send the encrypted challenge to the client. */ if ((r = sshpkt_start(ssh, SSH_SMSG_AUTH_RSA_CHALLENGE)) != 0 || (r = sshpkt_put_bignum1(ssh, encrypted_challenge)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); BN_clear_free(encrypted_challenge); ssh_packet_write_wait(ssh); /* Wait for a response. */ ssh_packet_read_expect(ssh, SSH_CMSG_AUTH_RSA_RESPONSE); if ((r = sshpkt_get(ssh, &response, sizeof(response))) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); BN_clear_free(challenge); return (success); }
static int input_kex_c25519_init(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; struct sshkey *server_host_private, *server_host_public; struct sshbuf *shared_secret = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; u_char server_key[CURVE25519_SIZE]; u_char *client_pubkey = NULL; u_char server_pubkey[CURVE25519_SIZE]; u_char hash[SSH_DIGEST_MAX_LENGTH]; size_t slen, pklen, sbloblen, hashlen; int r; /* generate private key */ kexc25519_keygen(server_key, server_pubkey); #ifdef DEBUG_KEXECDH dump_digest("server private key:", server_key, sizeof(server_key)); #endif if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } server_host_public = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; } if ((r = sshpkt_get_string(ssh, &client_pubkey, &pklen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; if (pklen != CURVE25519_SIZE) { r = SSH_ERR_SIGNATURE_INVALID; goto out; } #ifdef DEBUG_KEXECDH dump_digest("client public key:", client_pubkey, CURVE25519_SIZE); #endif if ((shared_secret = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = kexc25519_shared_key(server_key, client_pubkey, shared_secret)) < 0) goto out; /* calc H */ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) goto out; hashlen = sizeof(hash); if ((r = kex_c25519_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, client_pubkey, server_pubkey, sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), hash, &hashlen)) < 0) goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) goto out; /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || (r = sshpkt_put_string(ssh, server_pubkey, sizeof(server_pubkey))) != 0 || (r = sshpkt_put_string(ssh, signature, slen)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); explicit_bzero(server_key, sizeof(server_key)); free(server_host_key_blob); free(signature); free(client_pubkey); sshbuf_free(shared_secret); return r; }
static int input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; const EC_GROUP *group; EC_POINT *server_public = NULL; EC_KEY *client_key; BIGNUM *shared_secret = NULL; struct sshkey *server_host_key = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf = NULL; u_char hash[SSH_DIGEST_MAX_LENGTH]; size_t slen, sbloblen; size_t klen = 0, hashlen; int r; if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } group = kex->ec_group; client_key = kex->ec_client_key; /* hostkey */ if ((r = sshpkt_get_string(ssh, &server_host_key_blob, &sbloblen)) != 0 || (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; if (server_host_key->type != kex->hostkey_type || (kex->hostkey_type == KEY_ECDSA && server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } if (kex->verify_host_key(server_host_key, ssh) == -1) { r = SSH_ERR_SIGNATURE_INVALID; goto out; } /* Q_S, server public key */ /* signed H */ if ((server_public = EC_POINT_new(group)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshpkt_get_ec(ssh, server_public, group)) != 0 || (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); sshkey_dump_ec_point(group, server_public); #endif if (sshkey_ec_validate_public(group, server_public) != 0) { sshpkt_disconnect(ssh, "invalid server public key"); r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } klen = (EC_GROUP_get_degree(group) + 7) / 8; if ((kbuf = malloc(klen)) == NULL || (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (ECDH_compute_key(kbuf, klen, server_public, client_key, NULL) != (int)klen || BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif /* calc and verify H */ hashlen = sizeof(hash); if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->my), sshbuf_len(kex->my), sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, shared_secret, hash, &hashlen)) != 0) goto out; if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, ssh->compat)) != 0) goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); if (kex->ec_client_key) { EC_KEY_free(kex->ec_client_key); kex->ec_client_key = NULL; } if (server_public) EC_POINT_clear_free(server_public); if (kbuf) { explicit_bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); sshkey_free(server_host_key); free(server_host_key_blob); free(signature); return r; }
int input_kex_dh_init(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; struct sshkey *server_host_public, *server_host_private; u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; u_char hash[SSH_DIGEST_MAX_LENGTH]; size_t sbloblen, slen; size_t klen = 0, hashlen; int kout, r; if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } server_host_public = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; } /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { sshpkt_disconnect(ssh, "bad client public DH value"); r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } klen = DH_size(kex->dh); if ((kbuf = malloc(klen)) == NULL || (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || BN_bin2bn(kbuf, kout, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) goto out; /* calc H */ hashlen = sizeof(hash); if ((r = kex_dh_hash( kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, dh_client_pub, kex->dh->pub_key, shared_secret, hash, &hashlen)) != 0) goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 || (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ (r = sshpkt_put_string(ssh, signature, slen)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); DH_free(kex->dh); kex->dh = NULL; if (dh_client_pub) BN_clear_free(dh_client_pub); if (kbuf) { explicit_bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); free(server_host_key_blob); free(signature); return r; }
static int input_userauth_info_response(int type, u_int32_t seq, struct ssh *ssh) { struct authctxt *authctxt = ssh->authctxt; struct kbdintctxt *kbdintctxt; int authenticated = 0, res; int r; u_int i, nresp; const char *devicename = NULL; char **response = NULL; if (authctxt == NULL) fatal("input_userauth_info_response: no authctxt"); kbdintctxt = authctxt->kbdintctxt; if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) fatal("input_userauth_info_response: no kbdintctxt"); if (kbdintctxt->device == NULL) fatal("input_userauth_info_response: no device"); authctxt->postponed = 0; /* reset */ if ((r = sshpkt_get_u32(ssh, &nresp)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (nresp != kbdintctxt->nreq) fatal("input_userauth_info_response: wrong number of replies"); if (nresp > 100) fatal("input_userauth_info_response: too many replies"); if (nresp > 0) { response = xcalloc(nresp, sizeof(char *)); for (i = 0; i < nresp; i++) if ((r = sshpkt_get_cstring(ssh, &response[i], NULL)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); for (i = 0; i < nresp; i++) { explicit_bzero(response[i], strlen(response[i])); free(response[i]); } free(response); switch (res) { case 0: /* Success! */ authenticated = authctxt->valid ? 1 : 0; break; case 1: /* Authentication needs further interaction */ if (send_userauth_info_request(ssh) == 1) authctxt->postponed = 1; break; default: /* Failure! */ break; } devicename = kbdintctxt->device->name; if (!authctxt->postponed) { if (authenticated) { auth2_challenge_stop(ssh); } else { /* start next device */ /* may set authctxt->postponed */ auth2_challenge_start(ssh); } } userauth_finish(ssh, authenticated, "keyboard-interactive", devicename); return 0; }
static int input_kex_c25519_reply(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; struct sshkey *server_host_key = NULL; struct sshbuf *shared_secret = NULL; u_char *server_pubkey = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *hash; size_t slen, pklen, sbloblen, hashlen; int r; if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } /* hostkey */ if ((r = sshpkt_get_string(ssh, &server_host_key_blob, &sbloblen)) != 0 || (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; if (server_host_key->type != kex->hostkey_type) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } if (kex->verify_host_key(server_host_key, ssh) == -1) { r = SSH_ERR_SIGNATURE_INVALID; goto out; } /* Q_S, server public key */ /* signed H */ if ((r = sshpkt_get_string(ssh, &server_pubkey, &pklen)) != 0 || (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; if (pklen != CURVE25519_SIZE) { r = SSH_ERR_SIGNATURE_INVALID; goto out; } #ifdef DEBUG_KEXECDH dump_digest("server public key:", server_pubkey, CURVE25519_SIZE); #endif if ((shared_secret = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = kexc25519_shared_key(kex->c25519_client_key, server_pubkey, shared_secret)) < 0) goto out; /* calc and verify H */ if ((r = kex_c25519_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->my), sshbuf_len(kex->my), sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, kex->c25519_client_pubkey, server_pubkey, sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), &hash, &hashlen)) < 0) goto out; if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, ssh->compat)) != 0) goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } if ((r = kex_derive_keys(ssh, hash, hashlen, sshbuf_ptr(shared_secret), sshbuf_len(shared_secret))) == 0) r = kex_send_newkeys(ssh); r = 0; out: explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); free(server_host_key_blob); free(server_pubkey); free(signature); sshkey_free(server_host_key); sshbuf_free(shared_secret); return r; }
static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status, flags; u_char *p; size_t len; int r; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); recv_tok.value = p; recv_tok.length = len; maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, &flags)); free(p); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); userauth_finish(ssh, 0, "gssapi-with-mic", NULL); } else { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if (maj_status == GSS_S_COMPLETE) { ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); if (flags & GSS_C_INTEG_FLAG) ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, &input_gssapi_mic); else ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &input_gssapi_exchange_complete); } } gss_release_buffer(&min_status, &send_tok); return 0; }
/* * Checks if the user has an authentication agent, and if so, tries to * authenticate using the agent. */ static int try_agent_authentication(struct ssh *ssh) { int r, type, agent_fd, ret = 0; u_char response[16]; size_t i; BIGNUM *challenge; struct ssh_identitylist *idlist = NULL; /* Get connection to the agent. */ 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)); return 0; } if ((challenge = BN_new()) == NULL) fatal("try_agent_authentication: BN_new failed"); /* Loop through identities served by the agent. */ if ((r = ssh_fetch_identitylist(agent_fd, 1, &idlist)) != 0) { if (r != SSH_ERR_AGENT_NO_IDENTITIES) debug("%s: ssh_fetch_identitylist: %s", __func__, ssh_err(r)); goto out; } for (i = 0; i < idlist->nkeys; i++) { /* Try this identity. */ debug("Trying RSA authentication via agent with '%.100s'", idlist->comments[i]); /* * Tell the server that we are willing to authenticate * using this key. */ if ((r = sshpkt_start(ssh, SSH_CMSG_AUTH_RSA)) != 0 || (r = sshpkt_put_bignum1(ssh, idlist->keys[i]->rsa->n)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); /* Wait for server's response. */ type = ssh_packet_read(ssh); /* The server sends failure if it doesn't like our key or does not support RSA authentication. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our key."); continue; } /* Otherwise it should have sent a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) ssh_packet_disconnect(ssh, "Protocol error during RSA " "authentication: %d", type); if ((r = sshpkt_get_bignum1(ssh, challenge)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); debug("Received RSA challenge from server."); /* Ask the agent to decrypt the challenge. */ if ((r = ssh_decrypt_challenge(agent_fd, idlist->keys[i], challenge, session_id, response)) != 0) { /* * The agent failed to authenticate this identifier * although it advertised it supports this. Just * return a wrong value. */ logit("Authentication agent failed to decrypt " "challenge: %s", ssh_err(r)); memset(response, 0, sizeof(response)); } debug("Sending response to RSA challenge."); /* Send the decrypted challenge back to the server. */ if ((r = sshpkt_start(ssh, SSH_CMSG_AUTH_RSA_RESPONSE)) != 0 || (r = sshpkt_put(ssh, &response, sizeof(response))) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); /* Wait for response from the server. */ type = ssh_packet_read(ssh); /* * The server returns success if it accepted the * authentication. */ if (type == SSH_SMSG_SUCCESS) { debug("RSA authentication accepted by server."); ret = 1; break; } else if (type != SSH_SMSG_FAILURE) ssh_packet_disconnect(ssh, "Protocol error waiting RSA auth " "response: %d", type); } if (ret != 1) debug("RSA authentication using agent refused."); out: ssh_free_identitylist(idlist); ssh_close_authentication_socket(agent_fd); BN_clear_free(challenge); return ret; }
static int input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; EC_POINT *client_public; EC_KEY *server_key = NULL; const EC_GROUP *group; const EC_POINT *public_key; BIGNUM *shared_secret = NULL; struct sshkey *server_host_private, *server_host_public; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf = NULL; u_char hash[SSH_DIGEST_MAX_LENGTH]; size_t slen, sbloblen; size_t klen = 0, hashlen; int r; if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (EC_KEY_generate_key(server_key) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } group = EC_KEY_get0_group(server_key); #ifdef DEBUG_KEXECDH fputs("server private key:\n", stderr); sshkey_dump_ec_key(server_key); #endif if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } server_host_public = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; } if ((client_public = EC_POINT_new(group)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshpkt_get_ec(ssh, client_public, group)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXECDH fputs("client public key:\n", stderr); sshkey_dump_ec_point(group, client_public); #endif if (sshkey_ec_validate_public(group, client_public) != 0) { sshpkt_disconnect(ssh, "invalid client public key"); r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } /* Calculate shared_secret */ klen = (EC_GROUP_get_degree(group) + 7) / 8; if ((kbuf = malloc(klen)) == NULL || (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (ECDH_compute_key(kbuf, klen, client_public, server_key, NULL) != (int)klen || BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif /* calc H */ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, &sbloblen)) != 0) goto out; hashlen = sizeof(hash); if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, client_public, EC_KEY_get0_public_key(server_key), shared_secret, hash, &hashlen)) != 0) goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) goto out; /* destroy_sensitive_data(); */ public_key = EC_KEY_get0_public_key(server_key); /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || (r = sshpkt_put_string(ssh, signature, slen)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: explicit_bzero(hash, sizeof(hash)); if (kex->ec_client_key) { EC_KEY_free(kex->ec_client_key); kex->ec_client_key = NULL; } if (server_key) EC_KEY_free(server_key); if (kbuf) { explicit_bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); free(server_host_key_blob); free(signature); return r; }
static int input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; struct sshkey *server_host_key; u_char *kbuf = NULL, *hash, *signature = NULL, *server_host_key_blob = NULL; size_t klen = 0, slen, sbloblen, hashlen; int kout, r; debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } /* key, cert */ if ((r = sshpkt_get_string(ssh, &server_host_key_blob, &sbloblen)) != 0 || (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; if (server_host_key->type != kex->hostkey_type) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } if (kex->verify_host_key(server_host_key, ssh) == -1) { r = SSH_ERR_SIGNATURE_INVALID; goto out; } /* DH parameter f, server public DH key */ if ((dh_server_pub = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* signed H */ if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 || (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif if (!dh_pub_is_valid(kex->dh, dh_server_pub)) { sshpkt_disconnect(ssh, "bad server public DH value"); r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } klen = DH_size(kex->dh); if ((kbuf = malloc(klen)) == NULL || (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || BN_bin2bn(kbuf, kout, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if (ssh->compat & SSH_OLD_DHGEX) kex->min = kex->max = -1; /* calc and verify H */ if ((r = kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, sshbuf_ptr(kex->my), sshbuf_len(kex->my), sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, kex->min, kex->nbits, kex->max, kex->dh->p, kex->dh->g, kex->dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen)) != 0) goto out; if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, ssh->compat)) != 0) goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = malloc(kex->session_id_len); if (kex->session_id == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(kex->session_id, hash, kex->session_id_len); } if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); out: DH_free(kex->dh); kex->dh = NULL; if (server_host_key_blob) free(server_host_key_blob); if (server_host_key) sshkey_free(server_host_key); if (dh_server_pub) BN_clear_free(dh_server_pub); if (kbuf) { bzero(kbuf, klen); free(kbuf); } if (shared_secret) BN_clear_free(shared_secret); if (signature) free(signature); return r; }
static int input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; int r, min = -1, max = -1, nbits = -1; switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); if ((r = sshpkt_get_u32(ssh, &min)) != 0 || (r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_u32(ssh, &max)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; kex->min = min; kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; /* unused for old GEX */ kex->min = min = DH_GRP_MIN; kex->max = max = DH_GRP_MAX; break; default: r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (kex->max < kex->min || kex->nbits < kex->min || kex->max < kex->nbits) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } /* Contact privileged parent */ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); if (kex->dh == NULL) { sshpkt_disconnect(ssh, "no matching DH grp found"); r = SSH_ERR_ALLOC_FAIL; goto out; } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; /* Compute our exchange value in parallel with the client */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) goto out; /* old KEX does not use min/max in kexgex_hash() */ if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) kex->min = kex->max = -1; debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; out: return r; }