X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/36a5b38eb813fdd1d8da0731b33aa967a066d8b8..5f4fdfae1aad347ce0a5e84495b7db5f38b3798b:/sshd.c diff --git a/sshd.c b/sshd.c index f3169078..03a9ce12 100644 --- a/sshd.c +++ b/sshd.c @@ -11,15 +11,7 @@ */ #include "includes.h" -RCSID("$Id$"); - -#ifdef HAVE_POLL_H -# include -#else /* HAVE_POLL_H */ -# ifdef HAVE_SYS_POLL_H -# include -# endif /* HAVE_SYS_POLL_H */ -#endif /* HAVE_POLL_H */ +RCSID("$OpenBSD: sshd.c,v 1.79 2000/01/18 13:45:05 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -53,6 +45,16 @@ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = SERVER_CONFIG_FILE; +/* + * Flag indicating whether IPv4 or IPv6. This can be set on the command line. + * Default value is AF_UNSPEC means both IPv4 and IPv6. + */ +#ifdef IPV4_DEFAULT +int IPv4or6 = AF_INET; +#else +int IPv4or6 = AF_UNSPEC; +#endif + /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system @@ -74,10 +76,12 @@ char *av0; char **saved_argv; /* - * This is set to the socket that the server is listening; this is used in - * the SIGHUP signal handler. + * The sockets that the server is listening; this is used in the SIGHUP + * signal handler. */ -int listen_sock; +#define MAX_LISTEN_SOCKS 16 +int listen_socks[MAX_LISTEN_SOCKS]; +int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, @@ -127,8 +131,8 @@ int received_sighup = 0; RSA *public_key; /* Prototypes for various functions defined later in this file. */ -void do_connection(); -void do_authentication(char *user); +void do_ssh_kex(); +void do_authentication(); void do_authloop(struct passwd * pw); void do_fake_authloop(char *user); void do_authenticated(struct passwd * pw); @@ -143,165 +147,17 @@ void do_child(const char *command, struct passwd * pw, const char *term, const char *display, const char *auth_proto, const char *auth_data, const char *ttyname); -#ifdef HAVE_LIBPAM -static int pamconv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr); -int do_pam_auth(const char *user, const char *password); -void do_pam_account(char *username, char *remote_user); -void do_pam_session(char *username, char *ttyname); -void pam_cleanup_proc(void *context); - -static struct pam_conv conv = { - pamconv, - NULL -}; -struct pam_handle_t *pamh = NULL; -const char *pampasswd = NULL; -char *pamconv_msg = NULL; - -static int pamconv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) -{ - struct pam_response *reply; - int count; - size_t msg_len; - char *p; - - /* PAM will free this later */ - reply = malloc(num_msg * sizeof(*reply)); - if (reply == NULL) - return PAM_CONV_ERR; - - for(count = 0; count < num_msg; count++) { - switch (msg[count]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - if (pampasswd == NULL) { - free(reply); - return PAM_CONV_ERR; - } - reply[count].resp_retcode = PAM_SUCCESS; - reply[count].resp = xstrdup(pampasswd); - break; - - case PAM_TEXT_INFO: - reply[count].resp_retcode = PAM_SUCCESS; - reply[count].resp = xstrdup(""); - - if (msg[count]->msg == NULL) - break; - - debug("Adding PAM message: %s", msg[count]->msg); - - msg_len = strlen(msg[count]->msg); - if (pamconv_msg) { - size_t n = strlen(pamconv_msg); - pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2); - p = pamconv_msg + n; - } else { - pamconv_msg = p = xmalloc(msg_len + 2); - } - memcpy(p, msg[count]->msg, msg_len); - p[msg_len] = '\n'; - p[msg_len + 1] = '\0'; - break; - - case PAM_PROMPT_ECHO_ON: - case PAM_ERROR_MSG: - default: - free(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - - return PAM_SUCCESS; -} - -void pam_cleanup_proc(void *context) -{ - int pam_retval; - - if (pamh != NULL) - { - pam_retval = pam_close_session((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) { - log("Cannot close PAM session: %.200s", - PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - } - - pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); - if (pam_retval != PAM_SUCCESS) { - log("Cannot release PAM authentication: %.200s", - PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - } - } -} - -int do_pam_auth(const char *user, const char *password) -{ - int pam_retval; - - if ((options.permit_empty_passwd == 0) && (password[0] == '\0') - return 0; - - pampasswd = password; - - pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); - if (pam_retval == PAM_SUCCESS) { - debug("PAM Password authentication accepted for user \"%.100s\"", user); - return 1; - } else { - debug("PAM Password authentication for \"%.100s\" failed: %s", - user, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - return 0; - } -} - -void do_pam_account(char *username, char *remote_user) -{ - int pam_retval; - - debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname()); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, - get_canonical_hostname()); - if (pam_retval != PAM_SUCCESS) { - log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } - - if (remote_user != NULL) { - debug("PAM setting ruser to \"%.200s\"", remote_user); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); - if (pam_retval != PAM_SUCCESS) { - log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } - } - - pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) { - log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } -} - -void do_pam_session(char *username, char *ttyname) +/* + * Close all listening sockets + */ +void +close_listen_socks(void) { - int pam_retval; - - if (ttyname != NULL) { - debug("PAM setting tty to \"%.200s\"", ttyname); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_TTY, ttyname); - if (pam_retval != PAM_SUCCESS) - fatal("PAM set tty failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - } - - pam_retval = pam_open_session((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) - fatal("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + int i; + for (i = 0; i < num_listen_socks; i++) + close(listen_socks[i]); + num_listen_socks = -1; } -#endif /* HAVE_LIBPAM */ /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; @@ -323,7 +179,7 @@ void sighup_restart() { log("Received SIGHUP; restarting."); - close(listen_sock); + close_listen_socks(); execv(saved_argv[0], saved_argv); log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); exit(1); @@ -338,7 +194,7 @@ void sigterm_handler(int sig) { log("Received signal %d; terminating.", sig); - close(listen_sock); + close_listen_socks(); exit(255); } @@ -445,11 +301,12 @@ main(int ac, char **av) { extern char *optarg; extern int optind; - int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; + int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, pid, on = 1; + socklen_t fromlen; int remote_major, remote_minor; int silentrsa = 0; - struct pollfd fds; - struct sockaddr_in sin; + fd_set *fdset; + struct sockaddr_storage from; char buf[100]; /* Must not be larger than remote_version. */ char remote_version[100]; /* Must be at least as big as buf. */ const char *remote_ip; @@ -457,6 +314,9 @@ main(int ac, char **av) char *comment; FILE *f; struct linger linger; + struct addrinfo *ai; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int listen_sock, maxfd; /* Save argv[0]. */ saved_argv = av; @@ -469,8 +329,14 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ")) != EOF) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) { switch (opt) { + case '4': + IPv4or6 = AF_INET; + break; + case '6': + IPv4or6 = AF_INET6; + break; case 'f': config_file_name = optarg; break; @@ -491,7 +357,10 @@ main(int ac, char **av) options.server_key_bits = atoi(optarg); break; case 'p': - options.port = atoi(optarg); + options.ports_from_cmdline = 1; + if (options.num_ports >= MAX_PORTS) + fatal("too many ports.\n"); + options.ports[options.num_ports++] = atoi(optarg); break; case 'g': options.login_grace_time = atoi(optarg); @@ -510,6 +379,9 @@ main(int ac, char **av) case '?': default: fprintf(stderr, "sshd version %s\n", SSH_VERSION); +#ifdef RSAREF + fprintf(stderr, "Compiled with RSAref.\n"); +#endif fprintf(stderr, "Usage: %s [options]\n", av0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); @@ -521,11 +393,22 @@ main(int ac, char **av) fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); fprintf(stderr, " -h file File from which to read host key (default: %s)\n", - HOST_KEY_FILE); + HOST_KEY_FILE); + fprintf(stderr, " -4 Use IPv4 only\n"); + fprintf(stderr, " -6 Use IPv6 only\n"); exit(1); } } + /* + * Force logging to stderr until we have loaded the private host + * key (unless started from inetd) + */ + log_init(av0, + options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, + !inetd_flag); + /* check if RSA support exists */ if (rsa_alive() == 0) { if (silentrsa == 0) @@ -545,18 +428,11 @@ main(int ac, char **av) fprintf(stderr, "Bad server key size.\n"); exit(1); } - if (options.port < 1 || options.port > 65535) { - fprintf(stderr, "Bad port number.\n"); - exit(1); - } /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } - /* Force logging to stderr while loading the private host key - unless started from inetd */ - log_init(av0, options.log_level, options.log_facility, !inetd_flag); debug("sshd version %.100s", SSH_VERSION); @@ -645,32 +521,66 @@ main(int ac, char **av) arc4random_stir(); log("RSA key generation complete."); } else { - /* Create socket for listening. */ - listen_sock = socket(AF_INET, SOCK_STREAM, 0); - if (listen_sock < 0) - fatal("socket: %.100s", strerror(errno)); - - /* Set socket options. We try to make the port reusable - and have it close as fast as possible without waiting - in unnecessary wait states on close. */ - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, - sizeof(on)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger, - sizeof(linger)); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr = options.listen_addr; - sin.sin_port = htons(options.port); - - if (bind(listen_sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { - error("bind: %.100s", strerror(errno)); - shutdown(listen_sock, SHUT_RDWR); - close(listen_sock); - fatal("Bind to port %d failed.", options.port); + for (ai = options.listen_addrs; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (num_listen_socks >= MAX_LISTEN_SOCKS) + fatal("Too many listen sockets. " + "Enlarge MAX_LISTEN_SOCKS"); + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("getnameinfo failed"); + continue; + } + /* Create socket for listening. */ + listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (listen_sock < 0) { + /* kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { + error("listen_sock O_NONBLOCK: %s", strerror(errno)); + close(listen_sock); + continue; + } + /* + * Set socket options. We try to make the port + * reusable and have it close as fast as possible + * without waiting in unnecessary wait states on + * close. + */ + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, + (void *) &linger, sizeof(linger)); + + debug("Bind to port %s on %s.", strport, ntop); + + /* Bind the socket to the desired port. */ + if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("Bind to port %s on %s failed: %.200s.", + strport, ntop, strerror(errno)); + close(listen_sock); + continue; + } + listen_socks[num_listen_socks] = listen_sock; + num_listen_socks++; + + /* Start listening on the port. */ + log("Server listening on %s port %s.", ntop, strport); + if (listen(listen_sock, 5) < 0) + fatal("listen: %.100s", strerror(errno)); + } + freeaddrinfo(options.listen_addrs); + + if (!num_listen_socks) + fatal("Cannot bind any address."); + if (!debug_flag) { /* * Record our pid in /etc/sshd_pid to make it easier @@ -686,10 +596,6 @@ main(int ac, char **av) } } - log("Server listening on port %d.", options.port); - if (listen(listen_sock, 5) < 0) - fatal("listen: %.100s", strerror(errno)); - public_key = RSA_new(); sensitive_data.private_key = RSA_new(); @@ -711,6 +617,14 @@ main(int ac, char **av) /* Arrange SIGCHLD to be caught. */ signal(SIGCHLD, main_sigchld_handler); + /* setup fd set for listen */ + maxfd = 0; + for (i = 0; i < num_listen_socks; i++) + if (listen_socks[i] > maxfd) + maxfd = listen_socks[i]; + fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + fdset = (fd_set *)xmalloc(fdsetsz); + /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. @@ -718,26 +632,28 @@ main(int ac, char **av) for (;;) { if (received_sighup) sighup_restart(); - /* Wait in poll until there is a connection. */ - memset(&fds, 0, sizeof(fds)); - fds.fd = listen_sock; - fds.events = POLLIN; - if (poll(&fds, 1, -1) == -1) { - if (errno == EINTR) - continue; - fatal("poll: %.100s", strerror(errno)); - /*NOTREACHED*/ - } - if (fds.revents == 0) + /* Wait in select until there is a connection. */ + memset(fdset, 0, fdsetsz); + for (i = 0; i < num_listen_socks; i++) + FD_SET(listen_socks[i], fdset); + if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { + if (errno != EINTR) + error("select: %.100s", strerror(errno)); continue; - aux = sizeof(sin); - newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux); - if (received_sighup) - sighup_restart(); - if (newsock < 0) { - if (errno == EINTR) + } + for (i = 0; i < num_listen_socks; i++) { + if (!FD_ISSET(listen_socks[i], fdset)) continue; - error("accept: %.100s", strerror(errno)); + fromlen = sizeof(from); + newsock = accept(listen_socks[i], (struct sockaddr *)&from, + &fromlen); + if (newsock < 0) { + if (errno != EINTR && errno != EWOULDBLOCK) + error("accept: %.100s", strerror(errno)); + continue; + } + if (fcntl(newsock, F_SETFL, 0) < 0) { + error("newsock del O_NONBLOCK: %s", strerror(errno)); continue; } /* @@ -751,7 +667,7 @@ main(int ac, char **av) * connection without forking. */ debug("Server will not fork when running in debugging mode."); - close(listen_sock); + close_listen_socks(); sock_in = newsock; sock_out = newsock; pid = getpid(); @@ -768,7 +684,7 @@ main(int ac, char **av) * accepted socket. Reinitialize logging (since our pid has * changed). We break out of the loop to handle the connection. */ - close(listen_sock); + close_listen_socks(); sock_in = newsock; sock_out = newsock; log_init(av0, options.log_level, options.log_facility, log_stderr); @@ -789,6 +705,10 @@ main(int ac, char **av) /* Close the new socket (the child is now taking care of it). */ close(newsock); + } /* for (i = 0; i < num_listen_socks; i++) */ + /* child process check (or debug mode) */ + if (num_listen_socks < 0) + break; } } @@ -827,6 +747,7 @@ main(int ac, char **av) /* Check whether logins are denied from this host. */ #ifdef LIBWRAP + /* XXX LIBWRAP noes not know about IPv6 */ { struct request_info req; @@ -838,12 +759,11 @@ main(int ac, char **av) close(sock_out); refuse(&req); } - verbose("Connection from %.500s port %d", eval_client(&req), remote_port); +/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */ } -#else +#endif /* LIBWRAP */ /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); -#endif /* LIBWRAP */ /* * We don\'t want to listen forever unless the other side @@ -865,12 +785,12 @@ main(int ac, char **av) snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); if (atomicio(write, sock_out, buf, strlen(buf)) != strlen(buf)) - fatal("Could not write ident string to %s.", get_remote_ipaddr()); + fatal("Could not write ident string to %s.", remote_ip); /* Read other side\'s version identification. */ for (i = 0; i < sizeof(buf) - 1; i++) { if (read(sock_in, &buf[i], 1) != 1) - fatal("Did not receive ident string from %s.", get_remote_ipaddr()); + fatal("Did not receive ident string from %s.", remote_ip); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; @@ -897,7 +817,7 @@ main(int ac, char **av) close(sock_in); close(sock_out); fatal("Bad protocol version identification '%.100s' from %s", - buf, get_remote_ipaddr()); + buf, remote_ip); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); @@ -908,19 +828,15 @@ main(int ac, char **av) close(sock_in); close(sock_out); fatal("Protocol major versions differ for %s: %d vs. %d", - get_remote_ipaddr(), - PROTOCOL_MAJOR, remote_major); + remote_ip, PROTOCOL_MAJOR, remote_major); } /* Check that the client has sufficiently high software version. */ if (remote_major == 1 && remote_minor < 3) packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); if (remote_major == 1 && remote_minor == 3) { + /* note that this disables agent-forwarding */ enable_compat13(); - if (strcmp(remote_version, "OpenSSH-1.1") != 0) { - debug("Agent forwarding disabled, remote version is not compatible."); - no_agent_forwarding_flag = 1; - } } /* * Check that the connection comes from a privileged port. Rhosts- @@ -934,10 +850,21 @@ main(int ac, char **av) options.rhosts_authentication = 0; options.rhosts_rsa_authentication = 0; } +#ifdef KRB4 + if (!packet_connection_is_ipv4() && + options.kerberos_authentication) { + debug("Kerberos Authentication disabled, only available for IPv4."); + options.kerberos_authentication = 0; + } +#endif /* KRB4 */ + packet_set_nonblocking(); - /* Handle the connection. */ - do_connection(); + /* perform the key exchange */ + do_ssh_kex(); + + /* authenticate user and start session */ + do_authentication(); #ifdef KRB4 /* Cleanup user's ticket cache file. */ @@ -952,42 +879,26 @@ main(int ac, char **av) /* The connection has been terminated. */ verbose("Closing connection to %.100s", remote_ip); -#ifdef HAVE_LIBPAM - { - int retval; - - if (pamh != NULL) { - debug("Closing PAM session."); - retval = pam_close_session((pam_handle_t *)pamh, 0); - - debug("Terminating PAM library."); - if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) - log("Cannot release PAM authentication."); - - fatal_remove_cleanup(&pam_cleanup_proc, NULL); - } - } -#endif /* HAVE_LIBPAM */ +#ifdef USE_PAM + finish_pam(); +#endif /* USE_PAM */ packet_close(); exit(0); } /* - * Process an incoming connection. Protocol version identifiers have already - * been exchanged. This sends server key and performs the key exchange. - * Server and host keys will no longer be needed after this functions. + * SSH1 key exchange */ void -do_connection() +do_ssh_kex() { int i, len; + int plen, slen; BIGNUM *session_key_int; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; - unsigned char check_bytes[8]; - char *user; + unsigned char cookie[8]; unsigned int cipher_type, auth_mask, protocol_flags; - int plen, slen, ulen; u_int32_t rand = 0; /* @@ -1002,7 +913,7 @@ do_connection() for (i = 0; i < 8; i++) { if (i % 4 == 0) rand = arc4random(); - check_bytes[i] = rand & 0xff; + cookie[i] = rand & 0xff; rand >>= 8; } @@ -1013,7 +924,7 @@ do_connection() */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) - packet_put_char(check_bytes[i]); + packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(public_key->n)); @@ -1076,7 +987,7 @@ do_connection() /* 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 (check_bytes[i] != packet_get_char()) + if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); @@ -1124,10 +1035,15 @@ do_connection() sensitive_data.private_key); } - compute_session_id(session_id, check_bytes, + compute_session_id(session_id, cookie, sensitive_data.host_key->n, sensitive_data.private_key->n); + /* Destroy the private and public keys. They will no longer be needed. */ + RSA_free(public_key); + RSA_free(sensitive_data.private_key); + RSA_free(sensitive_data.host_key); + /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the @@ -1142,13 +1058,13 @@ do_connection() memset(session_key, 0, sizeof(session_key)); BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + /* Destroy the decrypted integer. It is no longer needed. */ + BN_clear_free(session_key_int); + /* 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]; - /* 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); @@ -1161,24 +1077,9 @@ do_connection() packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); - - /* Get the name of the user that we wish to log in as. */ - packet_read_expect(&plen, SSH_CMSG_USER); - - /* Get the user name. */ - user = packet_get_string(&ulen); - packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); - - /* Destroy the private and public keys. They will no longer be needed. */ - RSA_free(public_key); - RSA_free(sensitive_data.private_key); - RSA_free(sensitive_data.host_key); - - setproctitle("%s", user); - /* Do the authentication. */ - do_authentication(user); } + /* * Check if the user is allowed to log in via ssh. If user is listed in * DenyUsers or user's primary group is listed in DenyGroups, false will @@ -1254,13 +1155,23 @@ allowed_user(struct passwd * pw) /* * Performs authentication of an incoming connection. Session key has already - * been exchanged and encryption is enabled. User is the user name to log - * in as (received from the client). + * been exchanged and encryption is enabled. */ void -do_authentication(char *user) +do_authentication() { struct passwd *pw, pwcopy; + int plen, ulen; + char *user; + + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(&plen, SSH_CMSG_USER); + + /* Get the user name. */ + user = packet_get_string(&ulen); + packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); + + setproctitle("%s", user); #ifdef AFS /* If machine has AFS, set process authentication group. */ @@ -1285,18 +1196,8 @@ do_authentication(char *user) pwcopy.pw_shell = xstrdup(pw->pw_shell); pw = &pwcopy; -#ifdef HAVE_LIBPAM - { - int pam_retval; - - debug("Starting up PAM with username \"%.200s\"", pw->pw_name); - - pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh); - if (pam_retval != PAM_SUCCESS) - fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); - - fatal_add_cleanup(&pam_cleanup_proc, NULL); - } +#ifdef USE_PAM + start_pam(pw); #endif /* @@ -1313,11 +1214,11 @@ do_authentication(char *user) #ifdef KRB4 (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif /* KRB4 */ -#ifdef HAVE_LIBPAM - do_pam_auth(pw->pw_name, "")) { -#else /* HAVE_LIBPAM */ +#ifdef USE_PAM + auth_pam_password(pw, "")) { +#else /* USE_PAM */ auth_password(pw, "")) { -#endif /* HAVE_LIBPAM */ +#endif /* USE_PAM */ /* Authentication with empty password succeeded. */ log("Login for user %s from %.100s, accepted without authentication.", pw->pw_name, get_remote_ipaddr()); @@ -1457,9 +1358,6 @@ do_authloop(struct passwd * pw) authenticated = auth_rhosts(pw, client_user); snprintf(user, sizeof user, " ruser %s", client_user); -#ifndef HAVE_LIBPAM - xfree(client_user); -#endif /* HAVE_LIBPAM */ break; case SSH_CMSG_AUTH_RHOSTS_RSA: @@ -1492,9 +1390,6 @@ do_authloop(struct passwd * pw) BN_clear_free(client_host_key_n); snprintf(user, sizeof user, " ruser %s", client_user); -#ifndef HAVE_LIBPAM - xfree(client_user); -#endif /* HAVE_LIBPAM */ break; case SSH_CMSG_AUTH_RSA: @@ -1523,13 +1418,13 @@ do_authloop(struct passwd * pw) password = packet_get_string(&dlen); packet_integrity_check(plen, 4 + dlen, type); -#ifdef HAVE_LIBPAM +#ifdef USE_PAM /* Do PAM auth with password */ - authenticated = do_pam_auth(pw->pw_name, password); -#else /* HAVE_LIBPAM */ + authenticated = auth_pam_password(pw, password); +#else /* USE_PAM */ /* Try authentication with the password. */ authenticated = auth_password(pw, password); -#endif /* HAVE_LIBPAM */ +#endif /* USE_PAM */ memset(password, 0, strlen(password)); xfree(password); break; @@ -1595,29 +1490,24 @@ do_authloop(struct passwd * pw) get_remote_port(), user); -#ifndef HAVE_LIBPAM - if (authenticated) - return; - - if (attempt > AUTH_FAIL_MAX) - packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); -#else /* HAVE_LIBPAM */ if (authenticated) { - do_pam_account(pw->pw_name, client_user); - - if (client_user != NULL) - xfree(client_user); +#ifdef USE_PAM + if (!do_pam_account(pw->pw_name, client_user)) + { + if (client_user != NULL) + xfree(client_user); + do_fake_authloop(pw->pw_name); + } +#endif /* USE_PAM */ return; } - if (attempt > AUTH_FAIL_MAX) { - if (client_user != NULL) - xfree(client_user); + if (client_user != NULL) + xfree(client_user); + if (attempt > AUTH_FAIL_MAX) packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); - } -#endif /* HAVE_LIBPAM */ /* Send a message indicating that the authentication attempt failed. */ packet_start(SSH_SMSG_FAILURE); @@ -1652,8 +1542,10 @@ do_fake_authloop(char *user) for (attempt = 1;; attempt++) { /* Read a packet. This will not return if the client disconnects. */ int plen; +#ifndef SKEY + (void)packet_read(&plen); +#else /* SKEY */ int type = packet_read(&plen); -#ifdef SKEY int dlen; char *password, *skeyinfo; /* Try to send a fake s/key challenge. */ @@ -1823,10 +1715,10 @@ do_authenticated(struct passwd * pw) /* Indicate that we now have a pty. */ have_pty = 1; -#ifdef HAVE_LIBPAM +#ifdef USE_PAM /* do the pam_open_session since we have the pty */ - do_pam_session(pw->pw_name,ttyname); -#endif /* HAVE_LIBPAM */ + do_pam_session(pw->pw_name, ttyname); +#endif /* USE_PAM */ break; @@ -1876,7 +1768,7 @@ do_authenticated(struct passwd * pw) #endif /* XAUTH_PATH */ case SSH_CMSG_AGENT_REQUEST_FORWARDING: - if (no_agent_forwarding_flag) { + if (no_agent_forwarding_flag || compat13) { debug("Authentication agent forwarding not permitted for this authentication."); goto fail; } @@ -1903,6 +1795,9 @@ do_authenticated(struct passwd * pw) packet_set_interactive(have_pty || display != NULL, options.keepalives); +#ifdef USE_PAM + do_pam_setcred(); +#endif /* USE_PAM */ if (forced_command != NULL) goto do_forced_command; debug("Forking shell."); @@ -1918,6 +1813,9 @@ do_authenticated(struct passwd * pw) packet_set_interactive(have_pty || display != NULL, options.keepalives); +#ifdef USE_PAM + do_pam_setcred(); +#endif /* USE_PAM */ if (forced_command != NULL) goto do_forced_command; /* Get command from the packet. */ @@ -2126,8 +2024,8 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd, char line[256]; struct stat st; int quiet_login; - struct sockaddr_in from; - int fromlen; + struct sockaddr_storage from; + socklen_t fromlen; struct pty_cleanup_context cleanup_context; /* Get remote host name. */ @@ -2188,17 +2086,16 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd, } /* Record that there was a login on that terminal. */ record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, - &from); + (struct sockaddr *)&from); /* Check if .hushlogin exists. */ snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); quiet_login = stat(line, &st) >= 0; -#ifdef HAVE_LIBPAM - /* output the results of the pamconv() */ - if (!quiet_login && pamconv_msg != NULL) - fprintf(stderr, pamconv_msg); -#endif +#ifdef USE_PAM + if (!quiet_login) + print_pam_messages(); +#endif /* USE_PAM */ /* * If the user has logged in before, display the time of last @@ -2363,6 +2260,39 @@ read_environment_file(char ***env, unsigned int *envsize, fclose(f); } +#ifdef USE_PAM +/* + * Sets any environment variables which have been specified by PAM + */ +void do_pam_environment(char ***env, int *envsize) +{ + char *equals, var_name[512], var_val[512]; + char **pam_env; + int i; + + if ((pam_env = fetch_pam_environment()) == NULL) + return; + + for(i = 0; pam_env[i] != NULL; i++) { + if ((equals = strstr(pam_env[i], "=")) == NULL) + continue; + + if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) + { + memset(var_name, '\0', sizeof(var_name)); + memset(var_val, '\0', sizeof(var_val)); + + strncpy(var_name, pam_env[i], equals - pam_env[i]); + strcpy(var_val, equals + 1); + + debug("PAM environment: %s=%s", var_name, var_val); + + child_set_env(env, envsize, var_name, var_val); + } + } +} +#endif /* USE_PAM */ + /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group @@ -2382,7 +2312,7 @@ do_child(const char *command, struct passwd * pw, const char *term, struct stat st; char *argv[10]; -#ifndef HAVE_LIBPAM /* pam_nologin handles this */ +#ifndef USE_PAM /* pam_nologin handles this */ /* Check /etc/nologin. */ f = fopen("/etc/nologin", "r"); if (f) { @@ -2393,13 +2323,11 @@ do_child(const char *command, struct passwd * pw, const char *term, if (pw->pw_uid != 0) exit(254); } -#endif /* HAVE_LIBPAM */ +#endif /* USE_PAM */ -#ifdef HAVE_SETLOGIN /* Set login name in the kernel. */ if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); -#endif /* HAVE_SETLOGIN */ /* Set uid, gid, and groups. */ /* Login(1) does this as well, and it needs uid 0 for the "-h" @@ -2479,7 +2407,7 @@ do_child(const char *command, struct passwd * pw, const char *term, } snprintf(buf, sizeof buf, "%.50s %d %d", - get_remote_ipaddr(), get_remote_port(), options.port); + get_remote_ipaddr(), get_remote_port(), get_local_port()); child_set_env(&env, &envsize, "SSH_CLIENT", buf); if (ttyname) @@ -2498,26 +2426,10 @@ do_child(const char *command, struct passwd * pw, const char *term, } #endif /* KRB4 */ -#ifdef HAVE_LIBPAM +#ifdef USE_PAM /* Pull in any environment variables that may have been set by PAM. */ - { - char *equals, var_name[512], var_val[512]; - char **pam_env = pam_getenvlist((pam_handle_t *)pamh); - int i; - for(i = 0; pam_env && pam_env[i]; i++) { - equals = strstr(pam_env[i], "="); - if ((strlen(pam_env[i]) < (sizeof(var_name) - 1)) && (equals != NULL)) - { - debug("PAM environment: %s=%s", var_name, var_val); - memset(var_name, '\0', sizeof(var_name)); - memset(var_val, '\0', sizeof(var_val)); - strncpy(var_name, pam_env[i], equals - pam_env[i]); - strcpy(var_val, equals + 1); - child_set_env(&env, &envsize, var_name, var_val); - } - } - } -#endif /* HAVE_LIBPAM */ + do_pam_environment(&env, &envsize); +#endif /* USE_PAM */ if (xauthfile) child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); @@ -2562,7 +2474,6 @@ do_child(const char *command, struct passwd * pw, const char *term, * descriptors left by system functions. They will be closed later. */ endpwent(); - endhostent(); /* * Close any extra open file descriptors so that we don\'t have them