/* * Make packets from buffered stdout data, and buffer it for sending to the * client. */ static void make_packets_from_stdout_data(void) { u_int len; /* Send buffered stdout data to the client. */ while (buffer_len(&stdout_buffer) > 0 && packet_not_very_much_data_to_write()) { len = buffer_len(&stdout_buffer); if (packet_is_interactive()) { if (len > 512) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) len = packet_get_maxsize(); } packet_start(SSH_SMSG_STDOUT_DATA); packet_put_string(buffer_ptr(&stdout_buffer), len); packet_send(); buffer_consume(&stdout_buffer, len); stdout_bytes += len; } }
static void client_alive_check(void) { int channel_id; /* timeout, check to see how many we have had */ if (packet_inc_alive_timeouts() > options.client_alive_count_max) { logit("Timeout, client not responding."); cleanup_exit(255); } /* * send a bogus global/channel request with "wantreply", * we should get back a failure */ if ((channel_id = channel_find_open()) == -1) { packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("*****@*****.**"); packet_put_char(1); /* boolean: want reply */ } else { channel_request_start(channel_id, "*****@*****.**", 1); } packet_send(); }
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); } free(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); free(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(); } free(rtype); }
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, 0); /* 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_stdio_forwarding(); 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); }
/* * 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'; 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. */ #ifndef HAVE_CYGWIN if (!use_privsep && getuid() != 0 && authctxt->pw && authctxt->pw->pw_uid != getuid()) packet_disconnect("Cannot change user when server not running as root."); #endif /* * 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 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; }
/* * 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); }
/* * 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); xfree(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. */ log("Authentication agent failed to decrypt challenge."); memset(response, 0, 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 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; }
void kexgss_client(Kex *kex) { gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; Gssctxt *ctxt; OM_uint32 maj_status, min_status, ret_flags; u_int klen, kout, slen = 0, hashlen, strlen; DH *dh; BIGNUM *dh_server_pub = NULL; BIGNUM *shared_secret = NULL; BIGNUM *p = NULL; BIGNUM *g = NULL; u_char *kbuf, *hash; u_char *serverhostkey = NULL; u_char *empty = ""; char *msg; char *lang; int type = 0; int first = 1; int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; /* Initialise our GSSAPI world */ ssh_gssapi_build_ctx(&ctxt); if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) == GSS_C_NO_OID) fatal("Couldn't identify host exchange"); if (ssh_gssapi_import_name(ctxt, kex->gss_host)) fatal("Couldn't import hostname"); if (kex->gss_client && ssh_gssapi_client_identity(ctxt, kex->gss_client)) fatal("Couldn't acquire client credentials"); 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\n"); nbits = dh_estimate(kex->we_need * 8); packet_start(SSH2_MSG_KEXGSS_GROUPREQ); packet_put_int(min); packet_put_int(nbits); packet_put_int(max); packet_send(); packet_read_expect(SSH2_MSG_KEXGSS_GROUP); if ((p = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(p); if ((g = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(g); packet_check_eom(); if (BN_num_bits(p) < min || BN_num_bits(p) > max) fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", min, BN_num_bits(p), max); dh = dh_new_group(g, p); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } /* Step 1 - e is dh->pub_key */ dh_gen_key(dh, kex->we_need * 8); /* This is f, we initialise it now to make life easier */ dh_server_pub = BN_new(); if (dh_server_pub == NULL) fatal("dh_server_pub == NULL"); token_ptr = GSS_C_NO_BUFFER; do { debug("Calling gss_init_sec_context"); maj_status = ssh_gssapi_init_ctx(ctxt, kex->gss_deleg_creds, token_ptr, &send_tok, &ret_flags); 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); } fatal("gss_init_context failed"); } /* If we've got an old receive buffer get rid of it */ if (token_ptr != GSS_C_NO_BUFFER) xfree(recv_tok.value); if (maj_status == GSS_S_COMPLETE) { /* If mutual state flag is not true, kex fails */ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual authentication failed"); /* If integ avail flag is not true kex fails */ if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity check failed"); } /* * If we have data to send, then the last message that we * received cannot have been a 'complete'. */ if (send_tok.length != 0) { if (first) { packet_start(SSH2_MSG_KEXGSS_INIT); packet_put_string(send_tok.value, send_tok.length); packet_put_bignum2(dh->pub_key); first = 0; } else { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); } packet_send(); gss_release_buffer(&min_status, &send_tok); /* If we've sent them data, they should reply */ do { type = packet_read(); if (type == SSH2_MSG_KEXGSS_HOSTKEY) { debug("Received KEXGSS_HOSTKEY"); if (serverhostkey) fatal("Server host key received more than once"); serverhostkey = packet_get_string(&slen); } } while (type == SSH2_MSG_KEXGSS_HOSTKEY); switch (type) { case SSH2_MSG_KEXGSS_CONTINUE: debug("Received GSSAPI_CONTINUE"); if (maj_status == GSS_S_COMPLETE) fatal("GSSAPI Continue received from server when complete"); recv_tok.value = packet_get_string(&strlen); recv_tok.length = strlen; break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); packet_get_bignum2(dh_server_pub); msg_tok.value = packet_get_string(&strlen); msg_tok.length = strlen; /* Is there a token included? */ if (packet_get_char()) { recv_tok.value= packet_get_string(&strlen); recv_tok.length = strlen; /* If we're already complete - protocol error */ if (maj_status == GSS_S_COMPLETE) packet_disconnect("Protocol error: received token when complete"); } else { /* No token included */ if (maj_status != GSS_S_COMPLETE) packet_disconnect("Protocol error: did not receive final token"); } break; case SSH2_MSG_KEXGSS_ERROR: debug("Received Error"); maj_status = packet_get_int(); min_status = packet_get_int(); msg = packet_get_string(NULL); lang = packet_get_string(NULL); fatal("GSSAPI Error: \n%.400s",msg); default: packet_disconnect("Protocol error: didn't expect packet type %d", type); } token_ptr = &recv_tok; } else { /* No data, and not complete */ if (maj_status != GSS_S_COMPLETE) fatal("Not complete, and no token output"); } } while (maj_status & GSS_S_CONTINUE_NEEDED); /* * We _must_ have received a COMPLETE message in reply from the * server, which will have set dh_server_pub and msg_tok */ if (type != SSH2_MSG_KEXGSS_COMPLETE) fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); /* Check f in range [1, p-1] */ if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); /* compute K=f^x mod p */ klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_server_pub, dh); if (kout < 0) fatal("DH_compute_key: failed"); shared_secret = BN_new(); if (shared_secret == NULL) fatal("kexgss_client: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_client: 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->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, dh->pub_key, /* e */ dh_server_pub, /* f */ shared_secret, /* K */ &hash, &hashlen ); break; case KEX_GSS_GEX_SHA1: kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, min, nbits, max, dh->p, dh->g, dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen ); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } gssbuf.value = hash; gssbuf.length = hashlen; /* Verify that the hash matches the MIC we just got. */ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) packet_disconnect("Hash's MIC didn't verify"); xfree(msg_tok.value); DH_free(dh); if (serverhostkey) xfree(serverhostkey); BN_clear_free(dh_server_pub); /* 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); } if (kex->gss_deleg_creds) ssh_gssapi_credentials_updated(ctxt); if (gss_kex_context == NULL) gss_kex_context = ctxt; else ssh_gssapi_delete_ctx(&ctxt); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
static int userauth_pubkey(Authctxt *authctxt) { #ifdef WIN32_FIXME int loginStat = 1; char currentUser[MAX_PATH] = {0}; DWORD currentUserSize = sizeof(currentUser); int targetIsCurrent = 0; # ifdef USE_NTCREATETOKEN int doOpenSSHVerify = 1; # else int doOpenSSHVerify = 0; # endif #endif Buffer b; Key *key = NULL; char *pkalg, *userstyle; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { logit("Refusing RSA key because client uses unsafe " "signature scheme"); goto done; } if (auth2_userkey_already_used(authctxt, key)) { logit("refusing previously-used %s key", key_type(key)); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); buffer_put_cstring(&b, userstyle); free(userstyle); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif pubkey_auth_info(authctxt, key, NULL); /* test for correct signature */ authenticated = 0; /* * On pure win32 try to logon using lsa first. */ #ifdef WIN32_FIXME authctxt -> hTokenLsa_ = NULL; authctxt -> methoddata = NULL; /* * Retrieve name of current login user (i.e. sshd process owner). */ GetUserName(currentUser, ¤tUserSize); /* * Try to get token from lsa, but only if targetUser != currentUser. * Owerthise we already have targetUser's token in current thread, so * we only need key verify from original OpenSSH code. */ targetIsCurrent = (strcmp(currentUser, authctxt -> user) == 0); if (targetIsCurrent) { doOpenSSHVerify = 1; } else { loginStat = LsaLogon(&authctxt -> hTokenLsa_, HomeDirLsaW, authctxt -> user, pkblob, blen, sig, slen, buffer_ptr(&b), buffer_len(&b), datafellows); /* * If lsa logon process success. */ if (loginStat == 0) { /* * And user authorized OK. */ if (authctxt -> hTokenLsa_) { doOpenSSHVerify = 0; /* * This is part of openssh authorization needed for parsing * 'options' block in key. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt -> pw, key, 1))) // PRAGMA:TODO { authenticated = 1; } else { authenticated = 0; } buffer_free(&b); free(sig); } } } if (doOpenSSHVerify) { /* * If lsa fails, test for correct signature using openssh code. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0)) //PRAGMA:TODO && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; } } /* * Original code. */ #else /* #ifdef WIN32_FIXME */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); key = NULL; /* Don't free below */ } buffer_free(&b); free(sig); #endif /* else #ifdef WIN32_FIXME. */ } else { debug("test whether pkalg/pkblob are acceptable"); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ #ifndef WIN32_FIXME if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) #endif { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); return authenticated; }
/* * 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 oid = {0, NULL}; Gssctxt *ctxt = NULL; int mechs; gss_OID_set supported; int present; OM_uint32 ms; u_int len; 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); } ssh_gssapi_supported_oids(&supported); do { mechs--; if (doid) xfree(doid); doid = packet_get_string(&len); if (doid[0] != SSH_GSS_OIDTYPE || doid[1] != len-2) { logit("Mechanism OID received using the old encoding form"); oid.elements = doid; oid.length = len; } else { oid.elements = doid + 2; oid.length = len - 2; } gss_test_oid_set_member(&ms, &oid, supported, &present); } while (mechs > 0 && !present); gss_release_oid_set(&ms, &supported); if (!present) { xfree(doid); return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &oid)))) { xfree(doid); return (0); } authctxt->methoddata=(void *)ctxt; packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); /* Return OID in same format as we received it*/ packet_put_string(doid, len); packet_send(); xfree(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); }
/* * read packets, try to authenticate the user and * return only if authentication is successful */ static void do_authloop(Authctxt *authctxt) { int authenticated = 0; u_int bits; Key *client_host_key; BIGNUM *n; char *client_user, *password; char info[1024]; u_int dlen; u_int ulen; int type = 0; struct passwd *pw = authctxt->pw; debug("Attempting authentication for %s%.100s.", authctxt->valid ? "" : "illegal user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ if (options.password_authentication && #if defined(KRB4) || defined(KRB5) (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif PRIVSEP(auth_password(authctxt, ""))) { auth_log(authctxt, 1, "without authentication", ""); return; } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); client_user = NULL; for ( ;; ) { /* default to fail */ authenticated = 0; info[0] = '\0'; /* Get a packet from the client. */ authctxt->v1_auth_type = type = packet_read(); authctxt->v1_auth_name = get_authname(type); authctxt->attempt++; /* Process the packet. */ switch (type) { #if defined(KRB4) || defined(KRB5) case SSH_CMSG_AUTH_KERBEROS: if (!options.kerberos_authentication) { verbose("Kerberos authentication disabled."); } else { char *kdata = packet_get_string(&dlen); packet_check_eom(); if (kdata[0] == 4) { /* KRB_PROT_VERSION */ #ifdef KRB4 KTEXT_ST tkt, reply; tkt.length = dlen; if (tkt.length < MAX_KTXT_LEN) memcpy(tkt.dat, kdata, tkt.length); if (PRIVSEP(auth_krb4(authctxt, &tkt, &client_user, &reply))) { authenticated = 1; snprintf(info, sizeof(info), " tktuser %.100s", client_user); packet_start( SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.dat, reply.length); packet_send(); packet_write_wait(); } #endif /* KRB4 */ } else { #ifdef KRB5 krb5_data tkt, reply; tkt.length = dlen; tkt.data = kdata; if (PRIVSEP(auth_krb5(authctxt, &tkt, &client_user, &reply))) { authenticated = 1; snprintf(info, sizeof(info), " tktuser %.100s", client_user); /* Send response to client */ packet_start( SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.data, reply.length); packet_send(); packet_write_wait(); if (reply.length) xfree(reply.data); } #endif /* KRB5 */ } xfree(kdata); } break; #endif /* KRB4 || KRB5 */ #if defined(AFS) || defined(KRB5) /* XXX - punt on backward compatibility here. */ case SSH_CMSG_HAVE_KERBEROS_TGT: packet_send_debug("Kerberos TGT passing disabled before authentication."); break; #ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: packet_send_debug("AFS token passing disabled before authentication."); break; #endif /* AFS */ #endif /* AFS || KRB5 */ case SSH_CMSG_AUTH_RHOSTS: if (!options.rhosts_authentication) { verbose("Rhosts authentication disabled."); break; } /* * Get client user name. Note that we just have to * trust the client; this is one reason why rhosts * authentication is insecure. (Another is * IP-spoofing on a local network.) */ client_user = packet_get_string(&ulen); packet_check_eom(); /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ authenticated = auth_rhosts(pw, client_user); snprintf(info, sizeof info, " ruser %.100s", client_user); break; case SSH_CMSG_AUTH_RHOSTS_RSA: if (!options.rhosts_rsa_authentication) { verbose("Rhosts with RSA authentication disabled."); break; } /* * Get client user name. Note that we just have to * trust the client; root on the client machine can * claim to be any user. */ client_user = packet_get_string(&ulen); /* Get the client host key. */ client_host_key = key_new(KEY_RSA1); bits = packet_get_int(); packet_get_bignum(client_host_key->rsa->e); packet_get_bignum(client_host_key->rsa->n); if (bits != BN_num_bits(client_host_key->rsa->n)) verbose("Warning: keysize mismatch for client_host_key: " "actual %d, announced %d", BN_num_bits(client_host_key->rsa->n), bits); packet_check_eom(); authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); key_free(client_host_key); snprintf(info, sizeof info, " ruser %.100s", client_user); break; case SSH_CMSG_AUTH_RSA: if (!options.rsa_authentication) { verbose("RSA authentication disabled."); break; } /* RSA authentication requested. */ if ((n = BN_new()) == NULL) fatal("do_authloop: BN_new failed"); packet_get_bignum(n); packet_check_eom(); authenticated = auth_rsa(pw, n); BN_clear_free(n); break; case SSH_CMSG_AUTH_PASSWORD: authctxt->init_attempt++; if (!options.password_authentication) { verbose("Password authentication disabled."); break; } /* * Read user password. It is in plain text, but was * transmitted over the encrypted channel so it is * not visible to an outside observer. */ password = packet_get_string(&dlen); packet_check_eom(); /* Try authentication with the password. */ if (authctxt->init_failures < options.max_init_auth_tries) authenticated = PRIVSEP(auth_password(authctxt, password)); memset(password, 0, strlen(password)); xfree(password); break; case SSH_CMSG_AUTH_TIS: debug("rcvd SSH_CMSG_AUTH_TIS"); if (options.challenge_response_authentication == 1) { char *challenge = get_challenge(authctxt); if (challenge != NULL) { debug("sending challenge '%s'", challenge); packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); packet_put_cstring(challenge); xfree(challenge); packet_send(); packet_write_wait(); continue; } } break; case SSH_CMSG_AUTH_TIS_RESPONSE: debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); if (options.challenge_response_authentication == 1) { char *response = packet_get_string(&dlen); debug("got response '%s'", response); packet_check_eom(); authenticated = verify_response(authctxt, response); memset(response, 'r', dlen); xfree(response); } break; default: /* * Any unknown messages will be ignored (and failure * returned) during authentication. */ log("Unknown message during authentication: type %d", type); break; } #ifdef BSD_AUTH if (authctxt->as) { auth_close(authctxt->as); authctxt->as = NULL; } #endif if (!authctxt->valid && authenticated) { authenticated = 0; log("Ignoring authenticated invalid user %s", authctxt->user); } #ifdef _UNICOS if (type == SSH_CMSG_AUTH_PASSWORD && !authenticated) cray_login_failure(authctxt->user, IA_UDBERR); if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (authenticated && !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, pw)) { packet_disconnect("Authentication rejected for uid %d.", pw == NULL ? -1 : pw->pw_uid); authenticated = 0; } #else /* Special handling for root */ if (!use_privsep && authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(get_authname(type))) authenticated = 0; #endif #ifdef USE_PAM /* XXX PAM and PRIVSEP don't mix */ if (use_privsep && authenticated) fatal("Privsep is not supported"); if (authenticated && type != SSH_CMSG_AUTH_PASSWORD) authenticated = do_pam_non_initial_userauth(authctxt); else if (authenticated && !AUTHPAM_DONE(authctxt)) authenticated = 0; if (!authenticated) authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED); #endif /* USE_PAM */ /* Log before sending the reply */ auth_log(authctxt, authenticated, get_authname(type), info); if (client_user != NULL) { xfree(client_user); client_user = NULL; } if (authenticated) return; if (type == SSH_CMSG_AUTH_PASSWORD) authctxt->init_failures++; if (authctxt->failures++ > options.max_auth_tries) { #ifdef HAVE_BSM fatal_remove_cleanup(audit_failed_login_cleanup, authctxt); audit_sshd_login_failure(&ah, PAM_MAXTRIES); #endif /* HAVE_BSM */ packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
/* * Performs authentication of an incoming connection. Session key has already * been exchanged and encryption is enabled. */ Authctxt * do_authentication(void) { 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_string(&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 = authctxt_new(); authctxt->user = user; authctxt->style = style; #ifdef HAVE_BSM fatal_add_cleanup(audit_failed_login_cleanup, authctxt); #endif /* HAVE_BSM */ /* Verify that the user is a valid user. */ if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) { authctxt->valid = 1; } else { authctxt->valid = 0; debug("do_authentication: illegal user %s", user); } setproctitle("%s%s", authctxt->pw ? user : "******", use_privsep ? " [net]" : ""); #if 0 #ifdef USE_PAM PRIVSEP(start_pam(authctxt->pw == NULL ? "NOUSER" : user)); #endif #endif /* * If we are not running as root, the user must have the same uid as * the server. (Unless you are running Windows) */ #ifndef HAVE_CYGWIN if (!use_privsep && getuid() != 0 && authctxt->pw && authctxt->pw->pw_uid != getuid()) packet_disconnect("Cannot change user when server not running as root."); #endif /* * 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(); return (authctxt); }
/* * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST */ void input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; char *info, *lang, *password = NULL, *retype = NULL; char prompt[150]; debug2("input_userauth_passwd_changereq"); if (authctxt == NULL) fatal("input_userauth_passwd_changereq: " "no authentication context"); info = packet_get_utf8_string(NULL); if (strlen(info) != 0) { info = g11n_filter_string(info); log("%s", info); } xfree(info); lang = packet_get_string(NULL); xfree(lang); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(1); /* additional info */ snprintf(prompt, sizeof(prompt), gettext("Enter %.30s@%.128s's old password: "******"Enter %.30s@%.128s's new password: "******"Retype %.30s@%.128s's new password: "******"Mismatch; try again, EOF to quit."); password = NULL; } memset(retype, 0, strlen(retype)); xfree(retype); } packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); packet_add_padding(64); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); }
struct packet *new_tcp_packet(int socket_fd, int address_family, enum direction_t direction, enum ip_ecn_t ecn, const char *flags, u32 start_sequence, u16 tcp_payload_bytes, u32 ack_sequence, s32 window, const struct tcp_options *tcp_options, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ /* Calculate lengths in bytes of all sections of the packet */ const int ip_option_bytes = 0; const int tcp_option_bytes = tcp_options ? tcp_options->length : 0; const int ip_header_bytes = (ip_header_len(address_family) + ip_option_bytes); const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes; const int ip_bytes = ip_header_bytes + tcp_header_bytes + tcp_payload_bytes; /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { asprintf(error, "IP options are not padded correctly " "to ensure IP header is a multiple of 4 bytes: " "%d excess bytes", ip_option_bytes & 0x3); printf("1\n"); return NULL; } if (tcp_option_bytes & 0x3) { asprintf(error, "TCP options are not padded correctly " "to ensure TCP header is a multiple of 4 bytes: " "%d excess bytes", tcp_option_bytes & 0x3); printf("2\n"); return NULL; } assert((tcp_header_bytes & 0x3) == 0); assert((ip_header_bytes & 0x3) == 0); if (tcp_header_bytes > MAX_TCP_HEADER_BYTES) { asprintf(error, "TCP header too large"); return NULL; } if (ip_bytes > MAX_TCP_DATAGRAM_BYTES) { asprintf(error, "TCP segment too large"); return NULL; } if (!is_tcp_flags_spec_valid(flags, error)){ return NULL; } /* Allocate and zero out a packet object of the desired size */ packet = packet_new(ip_bytes); memset(packet->buffer, 0, ip_bytes); packet->ip_bytes = ip_bytes; packet->direction = direction; packet->socket_script_fd = socket_fd; packet->flags = 0; packet->ecn = ecn; /* Set IP header fields */ set_packet_ip_header(packet, address_family, ip_bytes, direction, ecn, IPPROTO_TCP); /* Find the start of TCP sections of the packet */ packet->tcp = (struct tcp *) (packet_start(packet) + ip_header_bytes); u8 *tcp_option_start = (u8 *) (packet->tcp + 1); /* Set TCP header fields */ packet->tcp->src_port = htons(0); packet->tcp->dst_port = htons(0); packet->tcp->seq = htonl(start_sequence); packet->tcp->ack_seq = htonl(ack_sequence); packet->tcp->doff = tcp_header_bytes / 4; if (window == -1) { if (direction == DIRECTION_INBOUND) { asprintf(error, "window must be specified" " for inbound packets"); return NULL; } packet->tcp->window = 0; packet->flags |= FLAG_WIN_NOCHECK; } else { packet->tcp->window = htons(window); } packet->tcp->check = 0; packet->tcp->urg_ptr = 0; packet->tcp->fin = is_tcp_flag_set('F', flags); packet->tcp->syn = is_tcp_flag_set('S', flags); packet->tcp->rst = is_tcp_flag_set('R', flags); packet->tcp->psh = is_tcp_flag_set('P', flags); packet->tcp->ack = is_tcp_flag_set('.', flags); packet->tcp->urg = 0; packet->tcp->ece = is_tcp_flag_set('E', flags); packet->tcp->cwr = is_tcp_flag_set('W', flags); if (tcp_options == NULL) { packet->flags |= FLAG_OPTIONS_NOCHECK; } else if (tcp_options->length > 0) { /* Copy TCP options into packet */ memcpy(tcp_option_start, tcp_options->data, tcp_options->length); } return packet; }
/* * 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(); } }
/* 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++; }
void kexgex_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; Key *server_host_public, *server_host_private; DH *dh; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, slen, hashlen; int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; int type, kout; if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) fatal("Cannot load hostkey"); server_host_public = kex->load_host_public_key(kex->hostkey_type); if (server_host_public == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); server_host_private = kex->load_host_private_key(kex->hostkey_type); type = packet_read(); switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); omin = min = packet_get_int(); onbits = nbits = packet_get_int(); omax = max = packet_get_int(); min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); onbits = nbits = packet_get_int(); /* unused for old GEX */ omin = min = DH_GRP_MIN; omax = max = DH_GRP_MAX; break; default: fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); } packet_check_eom(); if (omax < omin || onbits < omin || omax < onbits) fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", omin, onbits, omax); /* Contact privileged parent */ dh = PRIVSEP(choose_dh(min, nbits, max)); if (dh == NULL) packet_disconnect("Protocol error: no matching DH grp found"); debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); 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); debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); 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); free(kbuf); key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) omin = min = omax = 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, omin, onbits, omax, 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 */ kex->sign(server_host_private, server_host_public, &signature, &slen, hash, hashlen); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); 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(); free(signature); free(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 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)) { 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, submethod)) { authenticated = 0; partial = 1; } } /* Log before sending the reply */ auth_log(authctxt, authenticated, partial, method, submethod); 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(); free(methods); } }
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; }
/* * 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; 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 packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
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); }
void kexdh_client(Kex *kex) { BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; DH *dh; Key *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; int kout; /* generate and send 'e', client 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); packet_start(SSH2_MSG_KEXDH_INIT); packet_put_bignum2(dh->pub_key); packet_send(); debug("sending SSH2_MSG_KEXDH_INIT"); #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif debug("expecting SSH2_MSG_KEXDH_REPLY"); packet_read_expect(SSH2_MSG_KEXDH_REPLY); /* key, cert */ 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"); /* DH parameter f, server public DH key */ if ((dh_server_pub = BN_new()) == NULL) fatal("dh_server_pub == NULL"); packet_get_bignum2(dh_server_pub); #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif /* signed H */ signature = packet_get_string(&slen); packet_check_eom(); if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); if ((kout = DH_compute_key(kbuf, dh_server_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("kexdh_client: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_client: BN_bin2bn failed"); memset(kbuf, 0, klen); free(kbuf); /* calc and verify H */ kex_dh_hash( 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, dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen ); free(server_host_key_blob); BN_clear_free(dh_server_pub); DH_free(dh); 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); }
/* * 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(); }
static int ssh_session2(void) { int id = -1; /* XXX should be pre-session */ if (!options.control_persist) ssh_init_stdio_forwarding(); ssh_init_forwarding(); /* Start listening for multiplex clients */ muxserver_listen(); /* * If we are in control persist mode and have a working mux listen * socket, 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; orequest_tty = options.request_tty; otty_flag = tty_flag; stdin_null_flag = 1; no_shell_flag = 1; tty_flag = 0; if (!fork_after_authentication_flag) need_controlpersist_detach = 1; fork_after_authentication_flag = 1; } /* * ControlPersist mux listen socket setup failed, attempt the * stdio forward setup that we skipped earlier. */ if (options.control_persist && muxserver_sock == -1) ssh_init_stdio_forwarding(); 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); }
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(); }
/* * 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; u_int 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_int64_t 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); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_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... (press ~& to background)\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)); free(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); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } /* 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); } free(readset); free(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 */ }
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; }