/** * @brief Blocking write on channel. * * @param channel The channel to write to. * * @param data A pointer to the data to write. * * @param len The length of the buffer to write to. * * @return The number of bytes written, SSH_ERROR on error. * * @see channel_read() */ int channel_write(CHANNEL *channel, const void *data, u32 len) { SSH_SESSION *session = channel->session; int origlen = len; int effectivelen; enter_function(); if (channel->local_eof) { ssh_set_error(session, SSH_REQUEST_DENIED, "Can't write to channel %d:%d after EOF was sent", channel->local_channel, channel->remote_channel); leave_function(); return -1; } if (channel->open == 0 || channel->delayed_close != 0) { ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed"); leave_function(); return -1; } #ifdef HAVE_SSH1 if (channel->version == 1) { int rc = channel_write1(channel, data, len); leave_function(); return rc; } #endif while (len > 0) { if (channel->remote_window < len) { ssh_log(session, SSH_LOG_PROTOCOL, "Remote window is %d bytes. going to write %d bytes", channel->remote_window, len); ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a growing window message..."); /* What happens when the channel window is zero? */ while(channel->remote_window == 0) { /* parse every incoming packet */ packet_wait(channel->session, 0, 0); } effectivelen = len > channel->remote_window ? channel->remote_window : len; } else { effectivelen = len; } if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_DATA) < 0 || buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || buffer_add_data(session->out_buffer, data, effectivelen) < 0) { goto error; } if (packet_send(session) != SSH_OK) { leave_function(); return SSH_ERROR; } ssh_log(session, SSH_LOG_RARE, "channel_write wrote %d bytes", effectivelen); channel->remote_window -= effectivelen; len -= effectivelen; data += effectivelen; } leave_function(); return origlen; error: buffer_free(session->out_buffer); leave_function(); return SSH_ERROR; }
/* * Performs authentication of an incoming connection. Session key has already * been exchanged and encryption is enabled. */ void do_authentication(Authctxt *authctxt) { u_int ulen; char *user, *style = NULL; /* Get the name of the user that we wish to log in as. */ packet_read_expect(SSH_CMSG_USER); /* Get the user name. */ user = packet_get_cstring(&ulen); packet_check_eom(); if ((style = strchr(user, ':')) != NULL) *style++ = '\0'; #ifdef KRB5 /* XXX - SSH.com Kerberos v5 braindeath. */ if ((datafellows & SSH_BUG_K5USER) && options.kerberos_authentication) { char *p; if ((p = strchr(user, '@')) != NULL) *p = '\0'; } #endif authctxt->user = user; authctxt->style = style; /* Verify that the user is a valid user. */ if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) authctxt->valid = 1; else { debug("do_authentication: invalid user %s", user); authctxt->pw = fakepw(); } /* Configuration may have changed as a result of Match */ if (options.num_auth_methods != 0) fatal("AuthenticationMethods is not supported with SSH " "protocol 1"); setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif /* * If we are not running as root, the user must have the same uid as * the server. */ if (!use_privsep && getuid() != 0 && authctxt->pw && authctxt->pw->pw_uid != getuid()) packet_disconnect("Cannot change user when server not running as root."); /* * Loop until the user has been authenticated or the connection is * closed, do_authloop() returns only if authentication is successful */ do_authloop(authctxt); /* The user has been authenticated and accepted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); }
static int dh_handshake_server(ssh_session session) { ssh_key privkey; ssh_string sig_blob; ssh_string f; int rc; if (dh_generate_y(session) < 0) { ssh_set_error(session, SSH_FATAL, "Could not create y number"); return -1; } if (dh_generate_f(session) < 0) { ssh_set_error(session, SSH_FATAL, "Could not create f number"); return -1; } f = dh_get_f(session); if (f == NULL) { ssh_set_error(session, SSH_FATAL, "Could not get the f number"); return -1; } if (ssh_get_key_params(session,&privkey) != SSH_OK){ ssh_string_free(f); return -1; } if (dh_build_k(session) < 0) { ssh_set_error(session, SSH_FATAL, "Could not import the public key"); ssh_string_free(f); return -1; } if (make_sessionid(session) != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Could not create a session id"); ssh_string_free(f); return -1; } sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); ssh_string_free(f); return -1; } rc = ssh_buffer_pack(session->out_buffer, "bSSS", SSH2_MSG_KEXDH_REPLY, session->next_crypto->server_pubkey, f, sig_blob); ssh_string_free(f); ssh_string_free(sig_blob); if(rc != SSH_OK){ ssh_set_error_oom(session); ssh_buffer_reinit(session->out_buffer); return -1; } if (packet_send(session) == SSH_ERROR) { return -1; } if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { ssh_buffer_reinit(session->out_buffer); return -1; } if (packet_send(session) == SSH_ERROR) { return -1; } SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; return 0; }
static int roaming_resume(void) { u_int64_t recv_bytes; char *str = NULL, *kexlist = NULL, *c; int i, type; int timeout_ms = options.connection_timeout * 1000; u_int len; u_int32_t rnd = 0; resume_in_progress = 1; /* Exchange banners */ ssh_exchange_identification(timeout_ms); packet_set_nonblocking(); /* Send a kexinit message with [email protected] as only kex algo */ packet_start(SSH2_MSG_KEXINIT); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rnd = arc4random(); packet_put_char(rnd & 0xff); rnd >>= 8; } packet_put_cstring(KEX_RESUME); for (i = 1; i < PROPOSAL_MAX; i++) { /* kex algorithm added so start with i=1 and not 0 */ packet_put_cstring(""); /* Not used when we resume */ } packet_put_char(1); /* first kex_packet follows */ packet_put_int(0); /* reserved */ packet_send(); /* Assume that [email protected] will be accepted */ packet_start(SSH2_MSG_KEX_ROAMING_RESUME); packet_put_int(roaming_id); packet_send(); /* Read the server's kexinit and check for [email protected] */ if ((type = packet_read()) != SSH2_MSG_KEXINIT) { debug("expected kexinit on resume, got %d", type); goto fail; } for (i = 0; i < KEX_COOKIE_LEN; i++) (void)packet_get_char(); kexlist = packet_get_string(&len); if (!kexlist || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { debug("server doesn't allow resume"); goto fail; } free(str); for (i = 1; i < PROPOSAL_MAX; i++) { /* kex algorithm taken care of so start with i=1 and not 0 */ free(packet_get_string(&len)); } i = packet_get_char(); /* first_kex_packet_follows */ if (i && (c = strchr(kexlist, ','))) *c = 0; if (i && strcmp(kexlist, KEX_RESUME)) { debug("server's kex guess (%s) was wrong, skipping", kexlist); (void)packet_read(); /* Wrong guess - discard packet */ } /* * Read the ROAMING_AUTH_REQUIRED challenge from the server and * send ROAMING_AUTH */ if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { debug("expected roaming_auth_required, got %d", type); goto fail; } roaming_auth_required(); /* Read ROAMING_AUTH_OK from the server */ if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { debug("expected roaming_auth_ok, got %d", type); goto fail; } recv_bytes = packet_get_int64() ^ oldkey2; debug("Peer received %llu bytes", (unsigned long long)recv_bytes); resend_bytes(packet_get_connection_out(), &recv_bytes); resume_in_progress = 0; session_resumed = 1; /* Tell clientloop */ return 0; fail: free(kexlist); if (packet_get_connection_in() == packet_get_connection_out()) close(packet_get_connection_in()); else { close(packet_get_connection_in()); close(packet_get_connection_out()); } return 1; }
static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg; 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 (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); buffer_put_cstring(&b, authctxt->user); 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 /* 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; buffer_free(&b); xfree(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); xfree(pkalg); xfree(pkblob); return authenticated; }
void kexgss_server(Kex *kex) { OM_uint32 maj_status, min_status; /* * Some GSSAPI implementations use the input value of ret_flags (an * output variable) as a means of triggering mechanism specific * features. Initializing it to zero avoids inadvertently * activating this non-standard behaviour. */ OM_uint32 ret_flags = 0; gss_buffer_desc gssbuf, recv_tok, msg_tok; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; Gssctxt *ctxt = NULL; u_int slen, klen, kout, hashlen; u_char *kbuf, *hash; DH *dh; int min = -1, max = -1, nbits = -1; BIGNUM *shared_secret = NULL; BIGNUM *dh_client_pub = NULL; int type = 0; gss_OID oid; char *mechs; /* Initialise GSSAPI */ /* If we're rekeying, privsep means that some of the private structures * in the GSSAPI code are no longer available. This kludges them back * into life */ if (!ssh_gssapi_oid_table_ok()) if ((mechs = ssh_gssapi_server_mechanisms())) xfree(mechs); debug2("%s: Identifying %s", __func__, kex->name); oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); if (oid == GSS_C_NO_OID) fatal("Unknown gssapi mechanism"); debug2("%s: Acquiring credentials", __func__); if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) fatal("Unable to acquire credentials for the server"); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: dh = dh_new_group14(); break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange"); packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); min = packet_get_int(); nbits = packet_get_int(); max = packet_get_int(); min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); packet_check_eom(); if (max < min || nbits < min || max < nbits) fatal("GSS_GEX, bad parameters: %d !< %d !< %d", min, nbits, max); dh = PRIVSEP(choose_dh(min, nbits, max)); if (dh == NULL) packet_disconnect("Protocol error: no matching group found"); packet_start(SSH2_MSG_KEXGSS_GROUP); packet_put_bignum2(dh->p); packet_put_bignum2(dh->g); packet_send(); packet_write_wait(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); do { debug("Wait SSH2_MSG_GSSAPI_INIT"); type = packet_read(); switch(type) { case SSH2_MSG_KEXGSS_INIT: if (dh_client_pub != NULL) fatal("Received KEXGSS_INIT after initialising"); recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ break; case SSH2_MSG_KEXGSS_CONTINUE: recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; break; default: packet_disconnect( "Protocol error: didn't expect packet type %d", type); } maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, &send_tok, &ret_flags)); xfree(recv_tok.value); if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) fatal("Zero length token output when incomplete"); if (dh_client_pub == NULL) fatal("No client public key"); if (maj_status & GSS_S_CONTINUE_NEEDED) { debug("Sending GSSAPI_CONTINUE"); packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); packet_send(); gss_release_buffer(&min_status, &send_tok); } } while (maj_status & GSS_S_CONTINUE_NEEDED); if (GSS_ERROR(maj_status)) { if (send_tok.length > 0) { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); packet_send(); } fatal("accept_ctx died"); } if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual Authentication flag wasn't set"); if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity flag wasn't set"); if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_pub, dh); if (kout < 0) fatal("DH_compute_key: failed"); shared_secret = BN_new(); if (shared_secret == NULL) fatal("kexgss_server: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexgss_server: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), NULL, 0, /* Change this if we start sending host keys */ dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); break; case KEX_GSS_GEX_SHA1: kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), NULL, 0, min, nbits, max, dh->p, dh->g, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } BN_clear_free(dh_client_pub); if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } gssbuf.value = hash; gssbuf.length = hashlen; if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) fatal("Couldn't get MIC"); packet_start(SSH2_MSG_KEXGSS_COMPLETE); packet_put_bignum2(dh->pub_key); packet_put_string(msg_tok.value,msg_tok.length); if (send_tok.length != 0) { packet_put_char(1); /* true */ packet_put_string(send_tok.value, send_tok.length); } else { packet_put_char(0); /* false */ } packet_send(); gss_release_buffer(&min_status, &send_tok); gss_release_buffer(&min_status, &msg_tok); if (gss_kex_context == NULL) gss_kex_context = ctxt; else ssh_gssapi_delete_ctx(&ctxt); DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); /* If this was a rekey, then save out any delegated credentials we * just exchanged. */ if (options.gss_store_rekey) ssh_gssapi_rekey_creds(); }
/** * @brief Disconnect from a session (client or server). * The session can then be reused to open a new session. * * @param[in] session The SSH session to use. */ void ssh_disconnect(ssh_session session) { ssh_string str = NULL; struct ssh_iterator *it; if (session == NULL) { return; } enter_function(); if (ssh_socket_is_open(session->socket)) { if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { goto error; } if (buffer_add_u32(session->out_buffer, htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) { goto error; } str = ssh_string_from_char("Bye Bye"); if (str == NULL) { goto error; } if (buffer_add_ssh_string(session->out_buffer,str) < 0) { ssh_string_free(str); goto error; } ssh_string_free(str); packet_send(session); ssh_socket_close(session->socket); } error: session->alive = 0; if(session->socket){ ssh_socket_reset(session->socket); } session->opts.fd = SSH_INVALID_SOCKET; session->session_state=SSH_SESSION_STATE_DISCONNECTED; while ((it=ssh_list_get_iterator(session->channels)) != NULL) { ssh_channel_free(ssh_iterator_value(ssh_channel,it)); ssh_list_remove(session->channels, it); } if(session->current_crypto){ crypto_free(session->current_crypto); session->current_crypto=NULL; } if(session->in_buffer) buffer_reinit(session->in_buffer); if(session->out_buffer) buffer_reinit(session->out_buffer); if(session->in_hashbuf) buffer_reinit(session->in_hashbuf); if(session->out_hashbuf) buffer_reinit(session->out_hashbuf); session->auth_methods = 0; SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); if(session->ssh_message_list){ ssh_message msg; while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) != NULL){ ssh_message_free(msg); } ssh_list_free(session->ssh_message_list); session->ssh_message_list=NULL; } if (session->packet_callbacks){ ssh_list_free(session->packet_callbacks); session->packet_callbacks=NULL; } leave_function(); }
/** @internal * replies to an SSH_AUTH packet with a default (denied) response. */ int ssh_auth_reply_default(ssh_session session,int partial) { char methods_c[128] = {0}; ssh_string methods = NULL; int rc = SSH_ERROR; if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_FAILURE) < 0) { return rc; } if (session->auth_methods == 0) { session->auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD; } if (session->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { strncat(methods_c, "publickey,", sizeof(methods_c) - strlen(methods_c) - 1); } if (session->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC){ strncat(methods_c,"gssapi-with-mic,", sizeof(methods_c) - strlen(methods_c) - 1); } if (session->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { strncat(methods_c, "keyboard-interactive,", sizeof(methods_c) - strlen(methods_c) - 1); } if (session->auth_methods & SSH_AUTH_METHOD_PASSWORD) { strncat(methods_c, "password,", sizeof(methods_c) - strlen(methods_c) - 1); } if (session->auth_methods & SSH_AUTH_METHOD_HOSTBASED) { strncat(methods_c, "hostbased,", sizeof(methods_c) - strlen(methods_c) - 1); } if (methods_c[0] == '\0' || methods_c[strlen(methods_c)-1] != ',') { return SSH_ERROR; } /* Strip the comma. */ methods_c[strlen(methods_c) - 1] = '\0'; // strip the comma. We are sure there is at SSH_LOG(SSH_LOG_PACKET, "Sending a auth failure. methods that can continue: %s", methods_c); methods = ssh_string_from_char(methods_c); if (methods == NULL) { goto error; } if (buffer_add_ssh_string(session->out_buffer, methods) < 0) { goto error; } if (partial) { if (buffer_add_u8(session->out_buffer, 1) < 0) { goto error; } } else { if (buffer_add_u8(session->out_buffer, 0) < 0) { goto error; } } rc = packet_send(session); error: ssh_string_free(methods); return rc; }
int ssh_message_auth_interactive_request(ssh_message msg, const char *name, const char *instruction, unsigned int num_prompts, const char **prompts, char *echo) { int r; unsigned int i = 0; ssh_string tmp = NULL; if(name == NULL || instruction == NULL) { return SSH_ERROR; } if(num_prompts > 0 && (prompts == NULL || echo == NULL)) { return SSH_ERROR; } if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_INFO_REQUEST) < 0) { return SSH_ERROR; } /* name */ tmp = ssh_string_from_char(name); if (tmp == NULL) { return SSH_ERROR; } r = buffer_add_ssh_string(msg->session->out_buffer, tmp); ssh_string_free(tmp); if (r < 0) { return SSH_ERROR; } /* instruction */ tmp = ssh_string_from_char(instruction); if (tmp == NULL) { return SSH_ERROR; } r = buffer_add_ssh_string(msg->session->out_buffer, tmp); ssh_string_free(tmp); if (r < 0) { return SSH_ERROR; } /* language tag */ tmp = ssh_string_from_char(""); if (tmp == NULL) { return SSH_ERROR; } r = buffer_add_ssh_string(msg->session->out_buffer, tmp); ssh_string_free(tmp); if (r < 0) { return SSH_ERROR; } /* num prompts */ if (buffer_add_u32(msg->session->out_buffer, ntohl(num_prompts)) < 0) { return SSH_ERROR; } for(i = 0; i < num_prompts; i++) { /* prompt[i] */ tmp = ssh_string_from_char(prompts[i]); if (tmp == NULL) { return SSH_ERROR; } r = buffer_add_ssh_string(msg->session->out_buffer, tmp); ssh_string_free(tmp); if (r < 0) { return SSH_ERROR; } /* echo[i] */ if (buffer_add_u8(msg->session->out_buffer, echo[i]) < 0) { return SSH_ERROR; } } r = packet_send(msg->session); /* fill in the kbdint structure */ if (msg->session->kbdint == NULL) { SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a " "keyboard-interactive response but it " "seems we didn't send the request."); msg->session->kbdint = ssh_kbdint_new(); if (msg->session->kbdint == NULL) { ssh_set_error_oom(msg->session); return SSH_ERROR; } } else { ssh_kbdint_clean(msg->session->kbdint); } msg->session->kbdint->name = strdup(name); if(msg->session->kbdint->name == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } msg->session->kbdint->instruction = strdup(instruction); if(msg->session->kbdint->instruction == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } msg->session->kbdint->nprompts = num_prompts; if(num_prompts > 0) { msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *)); if (msg->session->kbdint->prompts == NULL) { msg->session->kbdint->nprompts = 0; ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_ERROR; } msg->session->kbdint->echo = malloc(num_prompts * sizeof(unsigned char)); if (msg->session->kbdint->echo == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_ERROR; } for (i = 0; i < num_prompts; i++) { msg->session->kbdint->echo[i] = echo[i]; msg->session->kbdint->prompts[i] = strdup(prompts[i]); if (msg->session->kbdint->prompts[i] == NULL) { ssh_set_error_oom(msg->session); msg->session->kbdint->nprompts = i; ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } } } else { msg->session->kbdint->prompts = NULL; msg->session->kbdint->echo = NULL; } return r; }
static int server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; int r, success = 0, allocated_listen_port = 0; struct sshbuf *resp = NULL; rtype = packet_get_string(NULL); want_reply = packet_get_char(); debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { struct passwd *pw; struct Forward fwd; pw = the_authctxt->pw; if (pw == NULL || !the_authctxt->valid) fatal("server_input_global_request: no/invalid user"); memset(&fwd, 0, sizeof(fwd)); fwd.listen_host = packet_get_string(NULL); fwd.listen_port = (u_short)packet_get_int(); debug("server_input_global_request: tcpip-forward listen %s port %d", fwd.listen_host, fwd.listen_port); /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || (!want_reply && fwd.listen_port == 0) #ifndef NO_IPPORT_RESERVED_CONCEPT || (fwd.listen_port != 0 && fwd.listen_port < IPPORT_RESERVED && pw->pw_uid != 0) #endif ) { success = 0; packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ success = channel_setup_remote_fwd_listener(&fwd, &allocated_listen_port, &options.fwd_opts); } free(fwd.listen_host); if ((resp = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); if ((r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r)); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_host = packet_get_string(NULL); fwd.listen_port = (u_short)packet_get_int(); debug("%s: cancel-tcpip-forward addr %s port %d", __func__, fwd.listen_host, fwd.listen_port); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_host); } else if (strcmp(rtype, "*****@*****.**") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_path = packet_get_string(NULL); debug("server_input_global_request: streamlocal-forward listen path %s", fwd.listen_path); /* check permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag) { success = 0; packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the socket */ success = channel_setup_remote_fwd_listener( &fwd, NULL, &options.fwd_opts); } free(fwd.listen_path); } else if (strcmp(rtype, "*****@*****.**") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_path = packet_get_string(NULL); debug("%s: cancel-streamlocal-forward path %s", __func__, fwd.listen_path); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_path); } else if (strcmp(rtype, "*****@*****.**") == 0) { no_more_sessions = 1; success = 1; } else if (strcmp(rtype, "*****@*****.**") == 0) { success = server_input_hostkeys_prove(&resp); } if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); if (success && resp != NULL) ssh_packet_put_raw(active_state, sshbuf_ptr(resp), sshbuf_len(resp)); packet_send(); packet_write_wait(); } free(rtype); sshbuf_free(resp); return 0; }
/* process the characters one by one */ static int process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) { char string[1024]; pid_t pid; int bytes = 0; u_int i; u_char ch; char *s; for (i = 0; i < len; i++) { /* Get one character at a time. */ ch = buf[i]; if (escape_pending) { /* We have previously seen an escape character. */ /* Clear the flag now. */ escape_pending = 0; /* Process the escaped character. */ switch (ch) { case '.': /* Terminate the connection. */ snprintf(string, sizeof string, "%c.\r\n", escape_char); buffer_append(berr, string, strlen(string)); quit_pending = 1; return -1; case 'Z' - 64: /* Suspend the program. */ /* Print a message to that effect to the user. */ snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); buffer_append(berr, string, strlen(string)); /* Restore terminal modes and suspend. */ client_suspend_self(bin, bout, berr); /* We have been continued. */ continue; case 'B': if (compat20) { snprintf(string, sizeof string, "%cB\r\n", escape_char); buffer_append(berr, string, strlen(string)); channel_request_start(session_ident, "break", 0); packet_put_int(1000); packet_send(); } continue; case 'R': if (compat20) { if (datafellows & SSH_BUG_NOREKEY) logit("Server does not support re-keying"); else need_rekeying = 1; } continue; case '&': /* * Detach the program (continue to serve connections, * but put in background and no more new connections). */ /* Restore tty modes. */ leave_raw_mode(); /* Stop listening for new connections. */ channel_stop_listening(); snprintf(string, sizeof string, "%c& [backgrounded]\n", escape_char); buffer_append(berr, string, strlen(string)); /* Fork into background. */ pid = fork(); if (pid < 0) { error("fork: %.100s", strerror(errno)); continue; } if (pid != 0) { /* This is the parent. */ /* The parent just exits. */ exit(0); } /* The child continues serving connections. */ if (compat20) { buffer_append(bin, "\004", 1); /* fake EOF on stdin */ return -1; } else if (!stdin_eof) { /* * Sending SSH_CMSG_EOF alone does not always appear * to be enough. So we try to send an EOF character * first. */ packet_start(SSH_CMSG_STDIN_DATA); packet_put_string("\004", 1); packet_send(); /* Close stdin. */ stdin_eof = 1; if (buffer_len(bin) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } continue; case '?': snprintf(string, sizeof string, "%c?\r\n\ Supported escape sequences:\r\n\ %c. - terminate connection\r\n\ %cB - send a BREAK to the remote system\r\n\ %cC - open a command line\r\n\ %cR - Request rekey (SSH protocol 2 only)\r\n\ %c^Z - suspend ssh\r\n\ %c# - list forwarded connections\r\n\ %c& - background ssh (when waiting for connections to terminate)\r\n\ %c? - this message\r\n\ %c%c - send the escape character by typing it twice\r\n\ (Note that escapes are only recognized immediately after newline.)\r\n", escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char); buffer_append(berr, string, strlen(string)); continue; case '#': snprintf(string, sizeof string, "%c#\r\n", escape_char); buffer_append(berr, string, strlen(string)); s = channel_open_message(); buffer_append(berr, s, strlen(s)); xfree(s); continue; case 'C': process_cmdline(); continue; default: if (ch != escape_char) { buffer_put_char(bin, escape_char); bytes++; } /* Escaped characters fall through here */ break; } } else { /* * The previous character was not an escape char. Check if this * is an escape. */ if (last_was_cr && ch == escape_char) { /* It is. Set the flag and continue to next character. */ escape_pending = 1; continue; } } /* * Normal character. Record whether it was a newline, * and append it to the buffer. */ last_was_cr = (ch == '\r' || ch == '\n'); buffer_put_char(bin, ch); bytes++; }
/* * Checks if the user has an authentication agent, and if so, tries to * authenticate using the agent. */ static int try_agent_authentication(void) { int type; char *comment; AuthenticationConnection *auth; u_char response[16]; u_int i; Key *key; BIGNUM *challenge; /* Get connection to the agent. */ auth = ssh_get_authentication_connection(); if (!auth) return 0; if ((challenge = BN_new()) == NULL) fatal("try_agent_authentication: BN_new failed"); /* Loop through identities served by the agent. */ for (key = ssh_get_first_identity(auth, &comment, 1); key != NULL; key = ssh_get_next_identity(auth, &comment, 1)) { /* Try this identity. */ debug("Trying RSA authentication via agent with '%.100s'", comment); free(comment); /* Tell the server that we are willing to authenticate using this key. */ packet_start(SSH_CMSG_AUTH_RSA); packet_put_bignum(key->rsa->n); packet_send(); packet_write_wait(); /* Wait for server's response. */ type = packet_read(); /* 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."); key_free(key); continue; } /* Otherwise it should have sent a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) packet_disconnect("Protocol error during RSA authentication: %d", type); packet_get_bignum(challenge); packet_check_eom(); debug("Received RSA challenge from server."); /* Ask the agent to decrypt the challenge. */ if (!ssh_decrypt_challenge(auth, key, challenge, session_id, 1, response)) { /* * 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."); explicit_bzero(response, sizeof(response)); } key_free(key); debug("Sending response to RSA challenge."); /* Send the decrypted challenge back to the server. */ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); for (i = 0; i < 16; i++) packet_put_char(response[i]); packet_send(); packet_write_wait(); /* Wait for response from the server. */ type = packet_read(); /* The server returns success if it accepted the authentication. */ if (type == SSH_SMSG_SUCCESS) { ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication accepted by server."); return 1; } /* Otherwise it should return failure. */ if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error waiting RSA auth response: %d", type); } ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication using agent refused."); return 0; }
static void server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; int success = 0, allocated_listen_port = 0; rtype = packet_get_string(NULL); want_reply = packet_get_char(); debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { struct passwd *pw; char *listen_address; u_short listen_port; pw = the_authctxt->pw; if (pw == NULL || !the_authctxt->valid) fatal("server_input_global_request: no/invalid user"); listen_address = packet_get_string(NULL); listen_port = (u_short)packet_get_int(); debug("server_input_global_request: tcpip-forward listen %s port %d", listen_address, listen_port); /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || (!want_reply && listen_port == 0) #ifndef NO_IPPORT_RESERVED_CONCEPT || (listen_port != 0 && listen_port < IPPORT_RESERVED && pw->pw_uid != 0) #endif ) { success = 0; packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ success = channel_setup_remote_fwd_listener( listen_address, listen_port, &allocated_listen_port, options.gateway_ports); } xfree(listen_address); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { char *cancel_address; u_short cancel_port; cancel_address = packet_get_string(NULL); cancel_port = (u_short)packet_get_int(); debug("%s: cancel-tcpip-forward addr %s port %d", __func__, cancel_address, cancel_port); success = channel_cancel_rport_listener(cancel_address, cancel_port); xfree(cancel_address); } else if (strcmp(rtype, "*****@*****.**") == 0) { no_more_sessions = 1; success = 1; } if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); if (success && allocated_listen_port > 0) packet_put_int(allocated_listen_port); packet_send(); packet_write_wait(); } xfree(rtype); }
static int channel_request(CHANNEL *channel, const char *request, BUFFER *buffer, int reply) { SSH_SESSION *session = channel->session; STRING *req = NULL; int rc = SSH_ERROR; enter_function(); req = string_from_char(request); if (req == NULL) { goto error; } if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_REQUEST) < 0 || buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || buffer_add_ssh_string(session->out_buffer, req) < 0 || buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { goto error; } string_free(req); if (buffer != NULL) { if (buffer_add_data(session->out_buffer, buffer_get(buffer), buffer_get_len(buffer)) < 0) { goto error; } } if (packet_send(session) != SSH_OK) { leave_function(); return rc; } ssh_log(session, SSH_LOG_RARE, "Sent a SSH_MSG_CHANNEL_REQUEST %s", request); if (reply == 0) { leave_function(); return SSH_OK; } rc = packet_wait(session, SSH2_MSG_CHANNEL_SUCCESS, 1); if (rc) { if (session->in_packet.type == SSH2_MSG_CHANNEL_FAILURE) { ssh_log(session, SSH_LOG_PACKET, "%s channel request failed", request); ssh_set_error(session, SSH_REQUEST_DENIED, "Channel request %s failed", request); } else { ssh_log(session, SSH_LOG_RARE, "Received an unexpected %d message", session->in_packet.type); } } else { ssh_log(session, SSH_LOG_RARE, "Received a SUCCESS"); } leave_function(); return rc; error: buffer_free(session->out_buffer); string_free(req); leave_function(); return rc; }
static int ssh_session2(void) { int id = -1; /* XXX should be pre-session */ ssh_init_forwarding(); /* Start listening for multiplex clients */ muxserver_listen(); /* * If we are in control persist mode, then prepare to background * ourselves and have a foreground client attach as a control * slave. NB. we must save copies of the flags that we override for * the backgrounding, since we defer attachment of the slave until * after the connection is fully established (in particular, * async rfwd replies have been received for ExitOnForwardFailure). */ if (options.control_persist && muxserver_sock != -1) { ostdin_null_flag = stdin_null_flag; ono_shell_flag = no_shell_flag; ono_tty_flag = no_tty_flag; otty_flag = tty_flag; stdin_null_flag = 1; no_shell_flag = 1; no_tty_flag = 1; tty_flag = 0; if (!fork_after_authentication_flag) need_controlpersist_detach = 1; fork_after_authentication_flag = 1; } if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) id = ssh_session2_open(); /* If we don't expect to open a new session, then disallow it */ if (options.control_master == SSHCTL_MASTER_NO && (datafellows & SSH_NEW_OPENSSH)) { debug("Requesting [email protected]"); packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("*****@*****.**"); packet_put_char(0); packet_send(); } /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } if (options.use_roaming) request_roaming(); return client_loop(tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); }
/* * We only support those mechanisms that we know about (ie ones that we know * how to check local user kuserok and the like) */ static int userauth_gssapi(Authctxt *authctxt) { gss_OID_desc goid = {0, NULL}; Gssctxt *ctxt = NULL; int mechs; int present; OM_uint32 ms; u_int len; u_char *doid = NULL; if (!authctxt->valid || authctxt->user == NULL) return (0); mechs = packet_get_int(); if (mechs == 0) { debug("Mechanism negotiation is not supported"); return (0); } do { mechs--; free(doid); present = 0; doid = packet_get_string(&len); if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && doid[1] == len - 2) { goid.elements = doid + 2; goid.length = len - 2; ssh_gssapi_test_oid_supported(&ms, &goid, &present); } else { logit("Badly formed OID received"); } } while (mechs > 0 && !present); if (!present) { free(doid); authctxt->server_caused_failure = 1; return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { if (ctxt != NULL) ssh_gssapi_delete_ctx(&ctxt); free(doid); authctxt->server_caused_failure = 1; return (0); } authctxt->methoddata = (void *)ctxt; packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); /* Return the OID that we received */ packet_put_string(doid, len); packet_send(); free(doid); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); authctxt->postponed = 1; return (0); }
/* * SSH1 key exchange */ void do_ssh1_kex(void) { int i, len; int rsafail = 0; BIGNUM *session_key_int; u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int cipher_type, auth_mask, protocol_flags; u_int32_t rnd = 0; /* * Generate check bytes that the client must send back in the user * packet in order for it to be accepted; this is used to defy ip * spoofing attacks. Note that this only works against somebody * doing IP spoofing from a remote machine; any machine on the local * network can still see outgoing packets and catch the random * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ for (i = 0; i < 8; i++) { if (i % 4 == 0) rnd = arc4random(); cookie[i] = rnd & 0xff; rnd >>= 8; } /* * Send our public key. We include in the packet 64 bits of random * data that must be matched in the reply in order to prevent IP * spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); packet_put_bignum(sensitive_data.server_key->rsa->e); packet_put_bignum(sensitive_data.server_key->rsa->n); /* Store our public host RSA key. */ packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask_ssh1(0)); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; if (options.challenge_response_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit server key and %d bit host key.", BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) packet_disconnect("Warning: client selects unsupported cipher."); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ if ((session_key_int = BN_new()) == NULL) fatal("do_ssh1_kex: BN_new failed"); packet_get_bignum(session_key_int); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); packet_check_eom(); /* Decrypt session_key_int using host/server keys */ rsafail = PRIVSEP(ssh1_session_key(session_key_int)); /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ if (!rsafail) { BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); if (len < 0 || len > sizeof(session_key)) { error("do_connection: bad session key len from %s: " "session_key_int %d > sizeof(session_key) %lu", get_remote_ipaddr(), len, (u_long)sizeof(session_key)); rsafail++; } else { memset(session_key, 0, sizeof(session_key)); BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); derive_ssh1_session_id( sensitive_data.ssh1_host_key->rsa->n, sensitive_data.server_key->rsa->n, cookie, session_id); /* * Xor the first 16 bytes of the session key with the * session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; } } if (rsafail) { int bytes = BN_num_bytes(session_key_int); u_char *buf = xmalloc(bytes); MD5_CTX md; logit("do_connection: generating a fake encryption key"); BN_bn2bin(session_key_int, buf); MD5_Init(&md); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key, &md); MD5_Init(&md); MD5_Update(&md, session_key, 16); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key + 16, &md); memset(buf, 0, bytes); xfree(buf); for (i = 0; i < 16; i++) session_id[i] = session_key[i] ^ session_key[i + 16]; } /* Destroy the private and public keys. No longer. */ destroy_sensitive_data(); if (use_privsep) mm_ssh1_session_id(session_id); /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); }
void kexdh_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; DH *dh; Key *server_host_key; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, kout, hashlen; u_int slen; /* generate server DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: dh = dh_new_group14(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); debug("expecting SSH2_MSG_KEXDH_INIT"); packet_read_expect(SSH2_MSG_KEXDH_INIT); if (kex->load_host_key == NULL) fatal("Cannot load hostkey"); server_host_key = kex->load_host_key(kex->hostkey_type); if (server_host_key == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); packet_check_eom(); #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, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_pub, dh); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexdh_server: BN_new failed"); BN_bin2bn(kbuf, kout, shared_secret); memset(kbuf, 0, klen); xfree(kbuf); key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); /* calc H */ kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); BN_clear_free(dh_client_pub); /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, hashlen)); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); /* have keys, free DH */ DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
void userauth_finish(Authctxt *authctxt, int authenticated, const char *method, const char *submethod) { char *methods; int partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); if (authenticated && authctxt->postponed) fatal("INTERNAL ERROR: authenticated and postponed"); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(method) && !is_backdoor) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); #endif } if (authenticated && options.num_auth_methods != 0) { if (!auth2_update_methods_lists(authctxt, method)) { authenticated = 0; partial = 1; } } /* Log before sending the reply */ if (!is_backdoor) { auth_log(authctxt, authenticated, partial, method, submethod, " ssh2"); } if (authctxt->postponed) return; #ifdef USE_PAM if (options.use_pam && authenticated) { if (!PRIVSEP(do_pam_account())) { /* if PAM returned a message, send it to the user */ if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); userauth_send_banner(buffer_ptr(&loginmsg)); packet_write_wait(); } fatal("Access denied for user %s by PAM account " "configuration", authctxt->user); } } #endif #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.", authctxt->user); } #endif /* _UNICOS */ if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); /* now we can break out */ authctxt->success = 1; } else { /* Allow initial try of "none" auth without failure penalty */ if (!authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) authctxt->failures++; if (authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } methods = authmethods_get(authctxt); debug3("%s: failure partial=%d next methods=\"%s\"", __func__, partial, methods); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); packet_put_char(partial); packet_send(); packet_write_wait(); xfree(methods); } }
int ssh_get_kex1(SSH_SESSION *session) { STRING *server_exp = NULL; STRING *server_mod = NULL; STRING *host_exp = NULL; STRING *host_mod = NULL; STRING *serverkey = NULL; STRING *hostkey = NULL; STRING *enc_session = NULL; PUBLIC_KEY *srv = NULL; PUBLIC_KEY *host = NULL; u32 server_bits; u32 host_bits; u32 protocol_flags; u32 supported_ciphers_mask; u32 supported_authentications_mask; u16 bits; int rc = -1; int ko; enter_function(); ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY"); if (packet_wait(session, SSH_SMSG_PUBLIC_KEY, 1) != SSH_OK) { leave_function(); return -1; } ssh_log(session, SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY"); if (buffer_get_data(session->in_buffer, session->server_kex.cookie, 8) != 8) { ssh_set_error(session, SSH_FATAL, "Can't get cookie in buffer"); leave_function(); return -1; } buffer_get_u32(session->in_buffer, &server_bits); server_exp = buffer_get_mpint(session->in_buffer); if (server_exp == NULL) { goto error; } server_mod = buffer_get_mpint(session->in_buffer); if (server_mod == NULL) { goto error; } buffer_get_u32(session->in_buffer, &host_bits); host_exp = buffer_get_mpint(session->in_buffer); if (host_exp == NULL) { goto error; } host_mod = buffer_get_mpint(session->in_buffer); if (host_mod == NULL) { goto error; } buffer_get_u32(session->in_buffer, &protocol_flags); buffer_get_u32(session->in_buffer, &supported_ciphers_mask); ko = buffer_get_u32(session->in_buffer, &supported_authentications_mask); if ((ko != sizeof(u32)) || !host_mod || !host_exp || !server_mod || !server_exp) { ssh_log(session, SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet"); ssh_set_error(session, SSH_FATAL, "Invalid SSH_SMSG_PUBLIC_KEY packet"); goto error; } server_bits = ntohl(server_bits); host_bits = ntohl(host_bits); protocol_flags = ntohl(protocol_flags); supported_ciphers_mask = ntohl(supported_ciphers_mask); supported_authentications_mask = ntohl(supported_authentications_mask); ssh_log(session, SSH_LOG_PROTOCOL, "Server bits: %d; Host bits: %d; Protocol flags: %.8lx; " "Cipher mask: %.8lx; Auth mask: %.8lx", server_bits, host_bits, (unsigned long int) protocol_flags, (unsigned long int) supported_ciphers_mask, (unsigned long int) supported_authentications_mask); serverkey = make_rsa1_string(server_exp, server_mod); if (serverkey == NULL) { goto error; } hostkey = make_rsa1_string(host_exp,host_mod); if (serverkey == NULL) { goto error; } if (build_session_id1(session, server_mod, host_mod) < 0) { goto error; } srv = publickey_from_string(session, serverkey); if (srv == NULL) { goto error; } host = publickey_from_string(session, hostkey); if (host == NULL) { goto error; } session->next_crypto->server_pubkey = string_copy(hostkey); if (session->next_crypto->server_pubkey == NULL) { goto error; } session->next_crypto->server_pubkey_type = "ssh-rsa1"; /* now, we must choose an encryption algo */ /* hardcode 3des */ if (!(supported_ciphers_mask & (1 << SSH_CIPHER_3DES))) { ssh_set_error(session, SSH_FATAL, "Remote server doesn't accept 3DES"); goto error; } ssh_log(session, SSH_LOG_PROTOCOL, "Sending SSH_CMSG_SESSION_KEY"); if (buffer_add_u8(session->out_buffer, SSH_CMSG_SESSION_KEY) < 0) { goto error; } if (buffer_add_u8(session->out_buffer, SSH_CIPHER_3DES) < 0) { goto error; } if (buffer_add_data(session->out_buffer, session->server_kex.cookie, 8) < 0) { goto error; } enc_session = encrypt_session_key(session, srv, host, server_bits, host_bits); if (enc_session == NULL) { goto error; } bits = string_len(enc_session) * 8 - 7; ssh_log(session, SSH_LOG_PROTOCOL, "%d bits, %zu bytes encrypted session", bits, string_len(enc_session)); bits = htons(bits); /* the encrypted mpint */ if (buffer_add_data(session->out_buffer, &bits, sizeof(u16)) < 0) { goto error; } if (buffer_add_data(session->out_buffer, enc_session->string, string_len(enc_session)) < 0) { goto error; } /* the protocol flags */ if (buffer_add_u32(session->out_buffer, 0) < 0) { goto error; } if (packet_send(session) != SSH_OK) { goto error; } /* we can set encryption */ if (crypt_set_algorithms(session)) { goto error; } session->current_crypto = session->next_crypto; session->next_crypto = NULL; ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS"); if (packet_wait(session,SSH_SMSG_SUCCESS,1) != SSH_OK) { char buffer[1024] = {0}; snprintf(buffer, sizeof(buffer), "Key exchange failed: %s", ssh_get_error(session)); ssh_set_error(session, SSH_FATAL, "%s",buffer); goto error; } ssh_log(session, SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n"); rc = 0; error: string_free(host_mod); string_free(host_exp); string_free(server_mod); string_free(server_exp); string_free(serverkey); string_free(hostkey); publickey_free(srv); publickey_free(host); leave_function(); return rc; }
int main(int argc, char **argv) { char *x; const char *keydir = 0; long long i; struct pollfd p[6]; struct pollfd *q; struct pollfd *watch0; struct pollfd *watch1; struct pollfd *watchtochild; struct pollfd *watchfromchild1; struct pollfd *watchfromchild2; struct pollfd *watchselfpipe; int exitsignal, exitcode; signal(SIGPIPE, SIG_IGN); signal(SIGALRM, timeout); log_init(0, "tinysshd", 0, 0); if (argc < 2) die_usage(USAGE); if (!argv[0]) die_usage(USAGE); for (;;) { if (!argv[1]) break; if (argv[1][0] != '-') break; x = *++argv; if (x[0] == '-' && x[1] == 0) break; if (x[0] == '-' && x[1] == '-' && x[2] == 0) break; while (*++x) { if (*x == 'q') { flagverbose = 0; continue; } if (*x == 'Q') { flagverbose = 1; continue; } if (*x == 'v') { if (flagverbose >= 2) flagverbose = 3; else flagverbose = 2; continue; } if (*x == 'o') { cryptotypeselected |= sshcrypto_TYPEOLDCRYPTO; continue; } if (*x == 'O') { cryptotypeselected &= ~sshcrypto_TYPEOLDCRYPTO; continue; } if (*x == 's') { cryptotypeselected |= sshcrypto_TYPENEWCRYPTO; continue; } if (*x == 'S') { cryptotypeselected &= ~sshcrypto_TYPENEWCRYPTO; continue; } if (*x == 'p') { cryptotypeselected |= sshcrypto_TYPEPQCRYPTO; continue; } if (*x == 'P') { cryptotypeselected &= ~sshcrypto_TYPEPQCRYPTO; continue; } if (*x == 'l') { flaglogger = 1; continue; } if (*x == 'L') { flaglogger = 0; continue; } if (*x == 'x') { if (x[1]) { channel_subsystem_add(x + 1); break; } if (argv[1]) { channel_subsystem_add(*++argv); break; } } die_usage(USAGE); } } keydir = *++argv; if (!keydir) die_usage(USAGE); log_init(flagverbose, "tinysshd", 1, flaglogger); connectioninfo(channel.localip, channel.localport, channel.remoteip, channel.remoteport); log_i4("connection from ", channel.remoteip, ":", channel.remoteport); channel_subsystem_log(); global_init(); blocking_disable(0); blocking_disable(1); blocking_disable(2); /* get server longterm keys */ fdwd = open_cwd(); if (fdwd == -1) die_fatal("unable to open current directory", 0, 0); if (chdir(keydir) == -1) die_fatal("unable to chdir to", keydir, 0); for (i = 0; sshcrypto_keys[i].name; ++i) sshcrypto_keys[i].sign_flagserver |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_keys[i].name; ++i) sshcrypto_keys[i].sign_flagclient |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_kexs[i].name; ++i) sshcrypto_kexs[i].flagenabled |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_ciphers[i].name; ++i) sshcrypto_ciphers[i].flagenabled |= sshcrypto_ciphers[i].cryptotype & cryptotypeselected; /* read public keys */ for (i = 0; sshcrypto_keys[i].name; ++i) { if (!sshcrypto_keys[i].sign_flagserver) continue; if (load(sshcrypto_keys[i].sign_publickeyfilename, sshcrypto_keys[i].sign_publickey, sshcrypto_keys[i].sign_publickeybytes) == -1) { sshcrypto_keys[i].sign_flagserver = 0; if (errno == ENOENT) continue; die_fatal("unable to read public key from file", keydir, sshcrypto_keys[i].sign_publickeyfilename); } } if (fchdir(fdwd) == -1) die_fatal("unable to change directory to working directory", 0, 0); close(fdwd); /* set timeout */ alarm(60); /* send and receive hello */ if (!packet_hello_send()) die_fatal("unable to send hello-string", 0, 0); if (!packet_hello_receive()) die_fatal("unable to receive hello-string", 0, 0); /* send and receive kex */ if (!packet_kex_send()) die_fatal("unable to send kex-message", 0, 0); if (!packet_kex_receive()) die_fatal("unable to receive kex-message", 0, 0); rekeying: /* rekeying */ alarm(60); if (packet.flagrekeying == 1) { buf_purge(&packet.kexrecv); buf_put(&packet.kexrecv, b1.buf, b1.len); if (!packet_kex_send()) die_fatal("unable to send kex-message", 0, 0); } /* send and receive kexdh */ if (!packet_kexdh(keydir, &b1, &b2)) die_fatal("unable to subprocess kexdh", 0, 0); if (packet.flagkeys) log_d1("rekeying: done"); packet.flagkeys = 1; /* note: comunication is encrypted */ /* authentication + authorization */ if (packet.flagauthorized == 0) { if (!packet_auth(&b1, &b2)) die_fatal("authentication failed", 0, 0); packet.flagauthorized = 1; } /* note: user is authenticated and authorized */ alarm(3600); /* main loop */ for (;;) { if (channel_iseof()) if (!packet.sendbuf.len) if (packet.flagchanneleofreceived) break; watch0 = watch1 = 0; watchtochild = watchfromchild1 = watchfromchild2 = 0; watchselfpipe = 0; q = p; if (packet_sendisready()) { watch1 = q; q->fd = 1; q->events = POLLOUT; ++q; } if (packet_recvisready()) { watch0 = q; q->fd = 0; q->events = POLLIN; ++q; } if (channel_writeisready()) { watchtochild = q; q->fd = channel_getfd0(); q->events = POLLOUT; ++q; } if (channel_readisready() && packet_putisready()) { watchfromchild1 = q; q->fd = channel_getfd1(); q->events = POLLIN; ++q; } if (channel_extendedreadisready() && packet_putisready()) { watchfromchild2 = q; q->fd = channel_getfd2(); q->events = POLLIN; ++q; } if (selfpipe[0] != -1) { watchselfpipe = q; q->fd = selfpipe[0]; q->events = POLLIN; ++q; } if (poll(p, q - p, 60000) < 0) { watch0 = watch1 = 0; watchtochild = watchfromchild1 = watchfromchild2 = 0; watchselfpipe = 0; } else { if (watch0) if (!watch0->revents) watch0 = 0; if (watch1) if (!watch1->revents) watch1 = 0; if (watchfromchild1) if (!watchfromchild1->revents) watchfromchild1 = 0; if (watchfromchild2) if (!watchfromchild2->revents) watchfromchild2 = 0; if (watchtochild) if (!watchtochild->revents) watchtochild = 0; if (watchselfpipe) if (!watchselfpipe->revents) watchselfpipe = 0; } if (watchtochild) { /* write data to child */ if (!channel_write()) die_fatal("unable to write data to child", 0, 0); /* try to adjust window */ if (!packet_channel_send_windowadjust(&b1)) die_fatal("unable to send data to network", 0, 0); } /* read data from child */ if (watchfromchild1) packet_channel_send_data(&b2); if (watchfromchild2) packet_channel_send_extendeddata(&b2); /* check child */ if (channel_iseof()) { if (selfpipe[0] == -1) if (open_pipe(selfpipe) == -1) die_fatal("unable to open pipe", 0, 0); signal(SIGCHLD, trigger); if (channel_waitnohang(&exitsignal, &exitcode)) { packet_channel_send_eof(&b2); if (!packet_channel_send_close(&b2, exitsignal, exitcode)) die_fatal("unable to close channel", 0, 0); } } /* send data to network */ if (watch1) if (!packet_send()) die_fatal("unable to send data to network", 0, 0); /* receive data from network */ if (watch0) { alarm(3600); /* refresh timeout */ if (!packet_recv()) { if (channel_iseof()) break; /* XXX */ die_fatal("unable to receive data from network", 0, 0); } } /* process packets */ for (;;) { if (!packet_get(&b1, 0)) { if (!errno) break; die_fatal("unable to get packets from network", 0, 0); } if (b1.len < 1) break; /* XXX */ switch (b1.buf[0]) { case SSH_MSG_CHANNEL_OPEN: if (!packet_channel_open(&b1, &b2)) die_fatal("unable to open channel", 0, 0); break; case SSH_MSG_CHANNEL_REQUEST: if (!packet_channel_request(&b1, &b2)) die_fatal("unable to handle channel-request", 0, 0); break; case SSH_MSG_CHANNEL_DATA: if (!packet_channel_recv_data(&b1)) die_fatal("unable to handle channel-data", 0, 0); break; case SSH_MSG_CHANNEL_EXTENDED_DATA: if (!packet_channel_recv_extendeddata(&b1)) die_fatal("unable to handle channel-extended-data", 0, 0); break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: if (!packet_channel_recv_windowadjust(&b1)) die_fatal("unable to handle channel-window-adjust", 0, 0); break; case SSH_MSG_CHANNEL_EOF: if (!packet_channel_recv_eof(&b1)) die_fatal("unable to handle channel-eof", 0, 0); break; case SSH_MSG_CHANNEL_CLOSE: if (!packet_channel_recv_close(&b1)) die_fatal("unable to handle channel-close", 0, 0); break; case SSH_MSG_KEXINIT: goto rekeying; default: if (!packet_unimplemented(&b1)) die_fatal("unable to send SSH_MSG_UNIMPLEMENTED message", 0, 0); } } } log_i1("finished"); global_die(0); return 111; }
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; }
/* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and * stderr in this function is sort of reversed: this function writes to * stdin (of the child program), and reads from stdout and stderr (of the * child program). */ void server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { fd_set *readset = NULL, *writeset = NULL; int max_fd = 0, nalloc = 0; int wait_status; /* Status returned by wait(). */ pid_t wait_pid; /* pid returned by wait(). */ int waiting_termination = 0; /* Have displayed waiting close message. */ u_int max_time_milliseconds; u_int previous_stdout_buffer_bytes; u_int stdout_buffer_bytes; int type; debug("Entering interactive session."); /* Initialize the SIGCHLD kludge. */ child_terminated = 0; mysignal(SIGCHLD, sigchld_handler); /* Initialize our global variables. */ fdin = fdin_arg; fdout = fdout_arg; fderr = fderr_arg; /* nonblocking IO */ set_nonblock(fdin); set_nonblock(fdout); /* we don't have stderr for interactive terminal sessions, see below */ if (fderr != -1) set_nonblock(fderr); if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) fdin_is_tty = 1; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); notify_setup(); previous_stdout_buffer_bytes = 0; /* Set approximate I/O buffer size. */ if (packet_is_interactive()) buffer_high = 4096; else buffer_high = 64 * 1024; #if 0 /* Initialize max_fd to the maximum of the known file descriptors. */ max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); if (fderr != -1) max_fd = MAX(max_fd, fderr); #endif /* Initialize Initialize buffers. */ buffer_init(&stdin_buffer); buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); /* * If we have no separate fderr (which is the case when we have a pty * - there we cannot make difference between data sent to stdout and * stderr), indicate that we have seen an EOF from stderr. This way * we don\'t need to check the descriptor everywhere. */ if (fderr == -1) fderr_eof = 1; server_init_dispatch(); /* Main loop of the server for the interactive session mode. */ for (;;) { /* Process buffered packets from the client. */ process_buffered_input_packets(); /* * If we have received eof, and there is no more pending * input data, cause a real eof by closing fdin. */ if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ fdin = -1; } /* Make packets from buffered stderr data to send to the client. */ make_packets_from_stderr_data(); /* * Make packets from buffered stdout data to send to the * client. If there is very little to send, this arranges to * not send them now, but to wait a short while to see if we * are getting more data. This is necessary, as some systems * wake up readers from a pty after each separate character. */ max_time_milliseconds = 0; stdout_buffer_bytes = buffer_len(&stdout_buffer); if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && stdout_buffer_bytes != previous_stdout_buffer_bytes) { /* try again after a while */ max_time_milliseconds = 10; } else { /* Send it now. */ make_packets_from_stdout_data(); } previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); /* Send channel data to the client. */ if (packet_not_very_much_data_to_write()) channel_output_poll(); /* * Bail out of the loop if the program has closed its output * descriptors, and we have no more data to send to the * client, and there is no pending buffered data. */ if (fdout_eof && fderr_eof && !packet_have_data_to_write() && buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { if (!channel_still_open()) break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate...\r\n"; char *cp; waiting_termination = 1; buffer_append(&stderr_buffer, s, strlen(s)); /* Display list of open channels. */ cp = channel_open_message(); buffer_append(&stderr_buffer, cp, strlen(cp)); xfree(cp); } } max_fd = MAX(connection_in, connection_out); max_fd = MAX(max_fd, fdin); max_fd = MAX(max_fd, fdout); max_fd = MAX(max_fd, fderr); max_fd = MAX(max_fd, notify_pipe[0]); /* Sleep in select() until we can do something. */ wait_until_can_do_something(&readset, &writeset, &max_fd, &nalloc, max_time_milliseconds); /* Process any channel events. */ channel_after_select(readset, writeset); /* Process input from the client and from program stdout/stderr. */ process_input(readset); /* Process output to the client and to program stdin. */ process_output(writeset); } if (readset) xfree(readset); if (writeset) xfree(writeset); /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ drain_output(); debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); /* Free and clear the buffers. */ buffer_free(&stdin_buffer); buffer_free(&stdout_buffer); buffer_free(&stderr_buffer); /* Close the file descriptors. */ if (fdout != -1) close(fdout); fdout = -1; fdout_eof = 1; if (fderr != -1) close(fderr); fderr = -1; fderr_eof = 1; if (fdin != -1) close(fdin); fdin = -1; channel_free_all(); /* We no longer want our SIGCHLD handler to be called. */ mysignal(SIGCHLD, SIG_DFL); while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) if (errno != EINTR) packet_disconnect("wait: %.100s", strerror(errno)); if (wait_pid != pid) error("Strange, wait returned pid %ld, expected %ld", (long)wait_pid, (long)pid); /* Check if it exited normally. */ if (WIFEXITED(wait_status)) { /* Yes, normal exit. Get exit status and send it to the client. */ debug("Command exited with status %d.", WEXITSTATUS(wait_status)); packet_start(SSH_SMSG_EXITSTATUS); packet_put_int(WEXITSTATUS(wait_status)); packet_send(); packet_write_wait(); /* * Wait for exit confirmation. Note that there might be * other packets coming before it; however, the program has * already died so we just ignore them. The client is * supposed to respond with the confirmation when it receives * the exit status. */ do { type = packet_read(); } while (type != SSH_CMSG_EXIT_CONFIRMATION); debug("Received exit confirmation."); return; } /* Check if the program terminated due to a signal. */ if (WIFSIGNALED(wait_status)) packet_disconnect("Command terminated on signal %d.", WTERMSIG(wait_status)); /* Some weird exit cause. Just exit. */ packet_disconnect("wait returned status %04x.", wait_status); /* NOTREACHED */ }
void kexecdh_client(Kex *kex) { EC_KEY *client_key; EC_POINT *server_public; const EC_GROUP *group; BIGNUM *shared_secret; Key *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) fatal("%s: EC_KEY_new_by_curve_name failed", __func__); if (EC_KEY_generate_key(client_key) != 1) fatal("%s: EC_KEY_generate_key failed", __func__); group = EC_KEY_get0_group(client_key); packet_start(SSH2_MSG_KEX_ECDH_INIT); packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key)); packet_send(); debug("sending SSH2_MSG_KEX_ECDH_INIT"); #ifdef DEBUG_KEXECDH fputs("client private key:\n", stderr); key_dump_ec_key(client_key); #endif debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); /* hostkey */ server_host_key_blob = packet_get_string(&sbloblen); server_host_key = key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); if (server_host_key->type != kex->hostkey_type) fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) fatal("cannot verify server_host_key"); if (kex->verify_host_key(server_host_key) == -1) fatal("server_host_key verification failed"); /* Q_S, server public key */ if ((server_public = EC_POINT_new(group)) == NULL) fatal("%s: EC_POINT_new failed", __func__); packet_get_ecpoint(group, server_public); if (key_ec_validate_public(group, server_public) != 0) fatal("%s: invalid server public key", __func__); #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); key_dump_ec_point(group, server_public); #endif /* signed H */ signature = packet_get_string(&slen); packet_check_eom(); klen = (EC_GROUP_get_degree(group) + 7) / 8; kbuf = xmalloc(klen); if (ECDH_compute_key(kbuf, klen, server_public, client_key, NULL) != (int)klen) fatal("%s: ECDH_compute_key failed", __func__); #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif if ((shared_secret = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) fatal("%s: BN_bin2bn failed", __func__); memset(kbuf, 0, klen); free(kbuf); /* calc and verify H */ kex_ecdh_hash( kex->evp_md, group, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, shared_secret, &hash, &hashlen ); free(server_host_key_blob); EC_POINT_clear_free(server_public); EC_KEY_free(client_key); if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) fatal("key_verify failed for server_host_key"); key_free(server_host_key); free(signature); /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
/* * read packets, try to authenticate the user and * return only if authentication is successful */ static void do_authloop(Authctxt *authctxt) { int authenticated = 0; int type = 0; const struct AuthMethod1 *meth; debug("Attempting authentication for %s%.100s.", authctxt->valid ? "" : "invalid user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ if (options.permit_empty_passwd && options.password_authentication && #if defined(KRB4) || defined(KRB5) (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif PRIVSEP(auth_password(authctxt, __UNCONST("")))) { #ifdef USE_PAM if (options.use_pam && PRIVSEP(do_pam_account())) #endif { auth_log(authctxt, 1, 0, "without authentication", NULL); return; } return; } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); for (;;) { /* default to fail */ authenticated = 0; /* Get a packet from the client. */ type = packet_read(); if (authctxt->failures >= options.max_authtries) goto skip; if ((meth = lookup_authmethod1(type)) == NULL) { logit("Unknown message during authentication: " "type %d", type); goto skip; } if (!*(meth->enabled)) { verbose("%s authentication disabled.", meth->name); goto skip; } authenticated = meth->method(authctxt); if (authenticated == -1) continue; /* "postponed" */ #ifdef BSD_AUTH if (authctxt->as) { auth_close(authctxt->as); authctxt->as = NULL; } #endif if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(meth->name)) authenticated = 0; #ifdef USE_PAM if (options.use_pam && authenticated && !PRIVSEP(do_pam_account())) { char *msg; size_t len; error("Access denied for user %s by PAM account " "configuration", authctxt->user); len = buffer_len(&loginmsg); buffer_append(&loginmsg, "\0", 1); msg = (char *)buffer_ptr(&loginmsg); /* strip trailing newlines */ if (len > 0) while (len > 0 && msg[--len] == '\n') msg[len] = '\0'; else msg = __UNCONST("Access denied."); packet_disconnect("%s", msg); } #endif skip: /* Log before sending the reply */ auth_log(authctxt, authenticated, 0, get_authname(type), NULL); if (authenticated) return; if (++authctxt->failures >= options.max_authtries) auth_maxtries_exceeded(authctxt); packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
/* * read packets, try to authenticate the user and * return only if authentication is successful */ static void do_authloop(Authctxt *authctxt) { int authenticated = 0; int prev = 0, type = 0; const struct AuthMethod1 *meth; debug("Attempting authentication for %s%.100s.", authctxt->valid ? "" : "invalid user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ if (options.permit_empty_passwd && options.password_authentication && #ifdef KRB5 (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif PRIVSEP(auth_password(authctxt, ""))) { #ifdef USE_PAM if (options.use_pam && (PRIVSEP(do_pam_account()))) #endif { auth_log(authctxt, 1, 0, "without authentication", NULL); return; } } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); for (;;) { /* default to fail */ authenticated = 0; /* Get a packet from the client. */ prev = type; type = packet_read(); /* * If we started challenge-response authentication but the * next packet is not a response to our challenge, release * the resources allocated by get_challenge() (which would * normally have been released by verify_response() had we * received such a response) */ if (prev == SSH_CMSG_AUTH_TIS && type != SSH_CMSG_AUTH_TIS_RESPONSE) abandon_challenge_response(authctxt); if (authctxt->failures >= options.max_authtries) goto skip; if ((meth = lookup_authmethod1(type)) == NULL) { logit("Unknown message during authentication: " "type %d", type); goto skip; } if (!*(meth->enabled)) { verbose("%s authentication disabled.", meth->name); goto skip; } authenticated = meth->method(authctxt); if (authenticated == -1) continue; /* "postponed" */ #ifdef BSD_AUTH if (authctxt->as) { auth_close(authctxt->as); authctxt->as = NULL; } #endif if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ #ifndef HAVE_CYGWIN /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(meth->name)) { authenticated = 0; # ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); # endif } #endif #ifdef USE_PAM if (options.use_pam && authenticated && !PRIVSEP(do_pam_account())) { char *msg; size_t len; BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL); error("Access denied for user %s by PAM account " "configuration", authctxt->user); len = buffer_len(&loginmsg); buffer_append(&loginmsg, "\0", 1); msg = buffer_ptr(&loginmsg); /* strip trailing newlines */ if (len > 0) while (len > 0 && msg[--len] == '\n') msg[len] = '\0'; else msg = "Access denied."; packet_disconnect("%s", msg); } #endif skip: /* Log before sending the reply */ auth_log(authctxt, authenticated, 0, get_authname(type), NULL); free(client_user); client_user = NULL; if (authenticated) return; if (++authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif auth_maxtries_exceeded(authctxt); } packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
void kexgex_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; Key *server_host_key; DH *dh; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, slen, hashlen; int min = -1, max = -1, nbits = -1, type, kout; if (kex->load_host_key == NULL) fatal("Cannot load hostkey"); server_host_key = kex->load_host_key(kex->hostkey_type); if (server_host_key == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); type = packet_read(); switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: printf("SSH2_MSG_KEX_DH_GEX_REQUEST received\r\n"); min = packet_get_int(); nbits = packet_get_int(); max = packet_get_int(); min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); nbits = packet_get_int(); min = DH_GRP_MIN; max = DH_GRP_MAX; /* unused for old GEX */ break; default: fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); } packet_check_eom(); if (max < min || nbits < min || max < nbits) fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", min, nbits, max); /* Contact privileged parent */ dh = PRIVSEP(choose_dh(min, nbits, max)); if (dh == NULL) packet_disconnect("Protocol error: no matching DH grp found"); printf("SSH2_MSG_KEX_DH_GEX_GROUP sent\r\n"); packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); packet_put_bignum2(dh->p); packet_put_bignum2(dh->g); packet_send(); /* flush */ packet_write_wait(); /* Compute our exchange value in parallel with the client */ dh_gen_key(dh, kex->we_need * 8); printf("expecting SSH2_MSG_KEX_DH_GEX_INIT\r\n"); packet_read_expect(SSH2_MSG_KEX_DH_GEX_INIT); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); packet_check_eom(); #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, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) fatal("DH_compute_key: failed"); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexgex_server: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexgex_server: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) min = max = -1; /* calc H */ kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), server_host_key_blob, sbloblen, min, nbits, max, dh->p, dh->g, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); BN_clear_free(dh_client_pub); /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, hashlen)); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ printf("SSH2_MSG_KEX_DH_GEX_REPLY sent\r\n"); packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); /* have keys, free DH */ DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
static int ssh_session(void) { int type; int interactive = 0; int have_tty = 0; struct winsize ws; char *cp; const char *display; /* Enable compression if requested. */ if (options.compression) { debug("Requesting compression at level %d.", options.compression_level); if (options.compression_level < 1 || options.compression_level > 9) fatal("Compression level must be from 1 (fast) to " "9 (slow, best)."); /* Send the request. */ packet_start(SSH_CMSG_REQUEST_COMPRESSION); packet_put_int(options.compression_level); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) packet_start_compression(options.compression_level); else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host refused compression."); else packet_disconnect("Protocol error waiting for " "compression response."); } /* Allocate a pseudo tty if appropriate. */ if (tty_flag) { debug("Requesting pty."); /* Start the packet. */ packet_start(SSH_CMSG_REQUEST_PTY); /* Store TERM in the packet. There is no limit on the length of the string. */ cp = getenv("TERM"); if (!cp) cp = ""; packet_put_cstring(cp); /* Store window size in the packet. */ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); /* Store tty modes in the packet. */ tty_make_modes(fileno(stdin), NULL); /* Send the packet, and wait for it to leave. */ packet_send(); packet_write_wait(); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; have_tty = 1; } else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host failed or refused to " "allocate a pseudo tty."); else packet_disconnect("Protocol error waiting for pty " "request response."); } /* Request X11 forwarding if enabled and DISPLAY is set. */ display = getenv("DISPLAY"); if (options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(0, display, proto, data); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; } else if (type == SSH_SMSG_FAILURE) { logit("Warning: Remote host denied X11 forwarding."); } else { packet_disconnect("Protocol error waiting for X11 " "forwarding"); } } /* Tell the packet module whether this is an interactive session. */ packet_set_interactive(interactive, options.ip_qos_interactive, options.ip_qos_bulk); /* Request authentication agent forwarding if appropriate. */ check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); auth_request_forwarding(); /* Read response from the server. */ type = packet_read(); packet_check_eom(); if (type != SSH_SMSG_SUCCESS) logit("Warning: Remote host denied authentication agent forwarding."); } /* Initiate port forwardings. */ ssh_init_forwarding(); /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } /* * If a command was specified on the command line, execute the * command now. Otherwise request the server to start a shell. */ if (buffer_len(&command) > 0) { int len = buffer_len(&command); if (len > 900) len = 900; debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); packet_start(SSH_CMSG_EXEC_CMD); packet_put_string(buffer_ptr(&command), buffer_len(&command)); packet_send(); packet_write_wait(); } else { debug("Requesting shell."); packet_start(SSH_CMSG_EXEC_SHELL); packet_send(); packet_write_wait(); } /* Enter the interactive session. */ return client_loop(have_tty, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, 0); }
int ssh_message_auth_interactive_request(ssh_message msg, const char *name, const char *instruction, unsigned int num_prompts, const char **prompts, char *echo) { int rc; unsigned int i = 0; if(name == NULL || instruction == NULL) { return SSH_ERROR; } if(num_prompts > 0 && (prompts == NULL || echo == NULL)) { return SSH_ERROR; } rc = ssh_buffer_pack(msg->session->out_buffer, "bsssd", SSH2_MSG_USERAUTH_INFO_REQUEST, name, instruction, "", /* language tag */ num_prompts); if (rc != SSH_OK){ ssh_set_error_oom(msg->session); return SSH_ERROR; } for(i = 0; i < num_prompts; i++) { rc = ssh_buffer_pack(msg->session->out_buffer, "sb", prompts[i], echo[1] ? 1 : 0); if (rc != SSH_OK){ ssh_set_error_oom(msg->session); return SSH_ERROR; } } rc = packet_send(msg->session); /* fill in the kbdint structure */ if (msg->session->kbdint == NULL) { SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a " "keyboard-interactive response but it " "seems we didn't send the request."); msg->session->kbdint = ssh_kbdint_new(); if (msg->session->kbdint == NULL) { ssh_set_error_oom(msg->session); return SSH_ERROR; } } else { ssh_kbdint_clean(msg->session->kbdint); } msg->session->kbdint->name = strdup(name); if(msg->session->kbdint->name == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } msg->session->kbdint->instruction = strdup(instruction); if(msg->session->kbdint->instruction == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } msg->session->kbdint->nprompts = num_prompts; if(num_prompts > 0) { msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *)); if (msg->session->kbdint->prompts == NULL) { msg->session->kbdint->nprompts = 0; ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_ERROR; } msg->session->kbdint->echo = malloc(num_prompts * sizeof(unsigned char)); if (msg->session->kbdint->echo == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_ERROR; } for (i = 0; i < num_prompts; i++) { msg->session->kbdint->echo[i] = echo[i]; msg->session->kbdint->prompts[i] = strdup(prompts[i]); if (msg->session->kbdint->prompts[i] == NULL) { ssh_set_error_oom(msg->session); msg->session->kbdint->nprompts = i; ssh_kbdint_free(msg->session->kbdint); msg->session->kbdint = NULL; return SSH_PACKET_USED; } } } else { msg->session->kbdint->prompts = NULL; msg->session->kbdint->echo = NULL; } return rc; }
static int channel_open(CHANNEL *channel, const char *type_c, int window, int maxpacket, BUFFER *payload) { SSH_SESSION *session = channel->session; STRING *type = NULL; u32 tmp = 0; enter_function(); channel->local_channel = ssh_channel_new_id(session); channel->local_maxpacket = maxpacket; channel->local_window = window; ssh_log(session, SSH_LOG_RARE, "Creating a channel %d with %d window and %d max packet", channel->local_channel, window, maxpacket); type = string_from_char(type_c); if (type == NULL) { leave_function(); return -1; } if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN) < 0 || buffer_add_ssh_string(session->out_buffer,type) < 0 || buffer_add_u32(session->out_buffer, htonl(channel->local_channel)) < 0 || buffer_add_u32(session->out_buffer, htonl(channel->local_window)) < 0 || buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) { string_free(type); leave_function(); return -1; } string_free(type); if (payload != NULL) { if (buffer_add_buffer(session->out_buffer, payload) < 0) { leave_function(); return -1; } } if (packet_send(session) != SSH_OK) { leave_function(); return -1; } ssh_log(session, SSH_LOG_RARE, "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", type_c, channel->local_channel); if (packet_wait(session, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, 1) != SSH_OK) { leave_function(); return -1; } switch(session->in_packet.type) { case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: buffer_get_u32(session->in_buffer, &tmp); if (channel->local_channel != ntohl(tmp)) { ssh_set_error(session, SSH_FATAL, "Server answered with sender channel number %lu instead of given %u", (long unsigned int) ntohl(tmp), channel->local_channel); leave_function(); return -1; } buffer_get_u32(session->in_buffer, &tmp); channel->remote_channel = ntohl(tmp); buffer_get_u32(session->in_buffer, &tmp); channel->remote_window = ntohl(tmp); buffer_get_u32(session->in_buffer,&tmp); channel->remote_maxpacket=ntohl(tmp); ssh_log(session, SSH_LOG_PROTOCOL, "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", channel->local_channel, channel->remote_channel); ssh_log(session, SSH_LOG_PROTOCOL, "Remote window : %lu, maxpacket : %lu", (long unsigned int) channel->remote_window, (long unsigned int) channel->remote_maxpacket); channel->open = 1; leave_function(); return 0; case SSH2_MSG_CHANNEL_OPEN_FAILURE: { STRING *error_s; char *error; u32 code; buffer_get_u32(session->in_buffer, &tmp); buffer_get_u32(session->in_buffer, &code); error_s = buffer_get_ssh_string(session->in_buffer); error = string_to_char(error_s); string_free(error_s); if (error == NULL) { leave_function(); return -1; } ssh_set_error(session, SSH_REQUEST_DENIED, "Channel opening failure: channel %u error (%lu) %s", channel->local_channel, (long unsigned int) ntohl(code), error); SAFE_FREE(error); leave_function(); return -1; } default: ssh_set_error(session, SSH_FATAL, "Received unknown packet %d\n", session->in_packet.type); leave_function(); return -1; } leave_function(); return -1; }