X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/e7c0f9d591754e0c5512aa3cb5543dbb4e45605b..95f1eccc45470eefc565e2d58694cba940857df3:/sshd.c?ds=sidebyside diff --git a/sshd.c b/sshd.c index e603161e..3fdc1e0d 100644 --- a/sshd.c +++ b/sshd.c @@ -1,25 +1,20 @@ /* - -sshd.c - -Author: Tatu Ylonen - -Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - All rights reserved - -Created: Fri Mar 17 17:09:28 1995 ylo - -This program is the ssh daemon. It listens for connections from clients, and -performs authentication, executes use commands or shell, and forwards -information to/from the application to the user client over an encrypted -connection. This can also handle forwarding of X11, TCP/IP, and authentication -agent connections. - -*/ + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Fri Mar 17 17:09:28 1995 ylo + * This program is the ssh daemon. It listens for connections from clients, and + * performs authentication, executes use commands or shell, and forwards + * information to/from the application to the user client over an encrypted + * connection. This can also handle forwarding of X11, TCP/IP, and authentication + * agent connections. + */ #include "includes.h" RCSID("$Id$"); +#include + #include "xmalloc.h" #include "rsa.h" #include "ssh.h" @@ -52,10 +47,12 @@ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = SERVER_CONFIG_FILE; -/* 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 - log, the daemon will not go to background, and will exit after processing - the first connection. */ +/* + * 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 + * log, the daemon will not go to background, and will exit after processing + * the first connection. + */ int debug_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ @@ -70,40 +67,50 @@ char *av0; /* Saved arguments to main(). */ char **saved_argv; -/* This is set to the socket that the server is listening; this is used in - the SIGHUP signal handler. */ +/* + * This is set to the socket that the server is listening; this is used in + * the SIGHUP signal handler. + */ int listen_sock; -/* Flags set in auth-rsa from authorized_keys flags. These are set in - auth-rsa.c. */ +/* + * the client's version string, passed by sshd2 in compat mode. if != NULL, + * sshd will skip the version-number exchange + */ +char *client_version_string = NULL; + +/* Flags set in auth-rsa from authorized_keys flags. These are set in auth-rsa.c. */ int no_port_forwarding_flag = 0; int no_agent_forwarding_flag = 0; int no_x11_forwarding_flag = 0; int no_pty_flag = 0; -char *forced_command = NULL; /* RSA authentication "command=" option. */ -struct envstring *custom_environment = NULL; - /* RSA authentication "environment=" options. */ + +/* RSA authentication "command=" option. */ +char *forced_command = NULL; + +/* RSA authentication "environment=" options. */ +struct envstring *custom_environment = NULL; /* Session id for the current session. */ unsigned char session_id[16]; -/* Any really sensitive data in the application is contained in this structure. - The idea is that this structure could be locked into memory so that the - pages do not get written into swap. However, there are some problems. - The private key contains BIGNUMs, and we do not (in principle) have - access to the internals of them, and locking just the structure is not - very useful. Currently, memory locking is not implemented. */ -struct -{ - /* Private part of server key. */ - RSA *private_key; - - /* Private part of host key. */ - RSA *host_key; +/* + * Any really sensitive data in the application is contained in this + * structure. The idea is that this structure could be locked into memory so + * that the pages do not get written into swap. However, there are some + * problems. The private key contains BIGNUMs, and we do not (in principle) + * have access to the internals of them, and locking just the structure is + * not very useful. Currently, memory locking is not implemented. + */ +struct { + RSA *private_key; /* Private part of server key. */ + RSA *host_key; /* Private part of host key. */ } sensitive_data; -/* Flag indicating whether the current session key has been used. This flag - is set whenever the key is used, and cleared when the key is regenerated. */ +/* + * Flag indicating whether the current session key has been used. This flag + * is set whenever the key is used, and cleared when the key is regenerated. + */ int key_used = 0; /* This is set to true when SIGHUP is received. */ @@ -116,2444 +123,2568 @@ RSA *public_key; /* Prototypes for various functions defined later in this file. */ void do_connection(); void do_authentication(char *user); -void do_authloop(struct passwd *pw); +void do_authloop(struct passwd * pw); void do_fake_authloop(char *user); -void do_authenticated(struct passwd *pw); -void do_exec_pty(const char *command, int ptyfd, int ttyfd, - const char *ttyname, struct passwd *pw, const char *term, - const char *display, const char *auth_proto, - const char *auth_data); -void do_exec_no_pty(const char *command, struct passwd *pw, - const char *display, const char *auth_proto, - const char *auth_data); -void do_child(const char *command, struct passwd *pw, const char *term, +void do_authenticated(struct passwd * pw); +void do_exec_pty(const char *command, int ptyfd, int ttyfd, + const char *ttyname, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data); +void do_exec_no_pty(const char *command, struct passwd * pw, + const char *display, const char *auth_proto, + const char *auth_data); +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); -void do_pam_account_and_session(const char *username, - const char *remote_user, const char *remote_host); + struct pam_response **resp, void *appdata_ptr); +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 + 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 **resp, void *appdata_ptr) { - int count = 0; - struct pam_response *reply = NULL; - - /* 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); - if (pamconv_msg == NULL) - { - pamconv_msg = malloc(strlen(msg[count]->msg) + 2); - - if (pamconv_msg == NULL) - return PAM_CONV_ERR; - - strncpy(pamconv_msg, msg[count]->msg, strlen(msg[count]->msg)); - pamconv_msg[strlen(msg[count]->msg)] = '\n'; - pamconv_msg[strlen(msg[count]->msg) + 1] = '\0'; - } else - { - pamconv_msg = realloc(pamconv_msg, strlen(pamconv_msg) + strlen(msg[count]->msg) + 2); - strncat(pamconv_msg, msg[count]->msg, strlen(msg[count]->msg)); - pamconv_msg[strlen(pamconv_msg)] = '\n'; - pamconv_msg[strlen(pamconv_msg) + 1] = '\0'; + 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; + } } - break; - - case PAM_PROMPT_ECHO_ON: - case PAM_ERROR_MSG: - default: - free(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - - return PAM_SUCCESS; + + *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 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)); + } + } } -void do_pam_account_and_session(const char *username, const char *remote_user, const char *remote_host) +void do_pam_account(char *username, char *remote_user) { - int pam_retval; - - if (remote_host != NULL) - { - debug("PAM setting rhost to \"%.200s\"", remote_host); - pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host); - 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); - } - - pam_retval = pam_open_session((pam_handle_t *)pamh, 0); - if (pam_retval != PAM_SUCCESS) - { - log("PAM session setup failed: %.200s", pam_strerror((pam_handle_t *)pamh, pam_retval)); - do_fake_authloop(username); - } -} -#endif /* HAVE_LIBPAM */ + 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); + } -/* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; - the effect is to reread the configuration file (and to regenerate - the server key). */ + 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); + } + } -void sighup_handler(int sig) -{ - received_sighup = 1; - signal(SIGHUP, sighup_handler); + 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); + } } -/* Called from the main program after receiving SIGHUP. Restarts the - server. */ - -void sighup_restart() +void do_pam_session(char *username, char *ttyname) { - log("Received SIGHUP; restarting."); - close(listen_sock); - execv(saved_argv[0], saved_argv); - log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); - exit(1); -} + int pam_retval; -/* Generic signal handler for terminating signals in the master daemon. - These close the listen socket; not closing it seems to cause "Address - already in use" problems on some machines, which is inconvenient. */ + 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)); + } -void sigterm_handler(int sig) -{ - log("Received signal %d; terminating.", sig); - close(listen_sock); - exit(255); + 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)); } +#endif /* HAVE_LIBPAM */ -/* SIGCHLD handler. This is called whenever a child dies. This will then - reap any zombies left by exited c. */ - -void main_sigchld_handler(int sig) +/* + * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; + * the effect is to reread the configuration file (and to regenerate + * the server key). + */ +void +sighup_handler(int sig) { - int save_errno = errno; - int status; - wait(&status); - signal(SIGCHLD, main_sigchld_handler); - errno = save_errno; + received_sighup = 1; + signal(SIGHUP, sighup_handler); } -/* Signal handler for the alarm after the login grace period has expired. */ - -void grace_alarm_handler(int sig) +/* + * Called from the main program after receiving SIGHUP. + * Restarts the server. + */ +void +sighup_restart() { - /* Close the connection. */ - packet_close(); - - /* Log error and exit. */ - fatal("Timeout before authentication."); + log("Received SIGHUP; restarting."); + close(listen_sock); + execv(saved_argv[0], saved_argv); + log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); + exit(1); } -/* Signal handler for the key regeneration alarm. Note that this - alarm only occurs in the daemon waiting for connections, and it does not - do anything with the private key or random state before forking. Thus there - should be no concurrency control/asynchronous execution problems. */ +/* + * Generic signal handler for terminating signals in the master daemon. + * These close the listen socket; not closing it seems to cause "Address + * already in use" problems on some machines, which is inconvenient. + */ +void +sigterm_handler(int sig) +{ + log("Received signal %d; terminating.", sig); + close(listen_sock); + exit(255); +} -void key_regeneration_alarm(int sig) +/* + * SIGCHLD handler. This is called whenever a child dies. This will then + * reap any zombies left by exited c. + */ +void +main_sigchld_handler(int sig) { - int save_errno = errno; + int save_errno = errno; + int status; - /* Check if we should generate a new key. */ - if (key_used) - { - /* This should really be done in the background. */ - log("Generating new %d bit RSA key.", options.server_key_bits); + while (waitpid(-1, &status, WNOHANG) > 0) + ; - if (sensitive_data.private_key != NULL) - RSA_free(sensitive_data.private_key); - sensitive_data.private_key = RSA_new(); + signal(SIGCHLD, main_sigchld_handler); + errno = save_errno; +} - if (public_key != NULL) - RSA_free(public_key); - public_key = RSA_new(); - - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - key_used = 0; - log("RSA key generation complete."); - } - - /* Reschedule the alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - errno = save_errno; +/* + * Signal handler for the alarm after the login grace period has expired. + */ +void +grace_alarm_handler(int sig) +{ + /* Close the connection. */ + packet_close(); + + /* Log error and exit. */ + fatal("Timeout before authentication for %s.", get_remote_ipaddr()); } -/* Main program for the daemon. */ +/* + * convert ssh auth msg type into description + */ +char * +get_authname(int type) +{ + switch (type) { + case SSH_CMSG_AUTH_PASSWORD: + return "password"; + case SSH_CMSG_AUTH_RSA: + return "rsa"; + case SSH_CMSG_AUTH_RHOSTS_RSA: + return "rhosts-rsa"; + case SSH_CMSG_AUTH_RHOSTS: + return "rhosts"; +#ifdef KRB4 + case SSH_CMSG_AUTH_KERBEROS: + return "kerberos"; +#endif +#ifdef SKEY + case SSH_CMSG_AUTH_TIS_RESPONSE: + return "s/key"; +#endif + } + fatal("get_authname: unknown auth %d: internal error", type); + return NULL; +} + +/* + * Signal handler for the key regeneration alarm. Note that this + * alarm only occurs in the daemon waiting for connections, and it does not + * do anything with the private key or random state before forking. + * Thus there should be no concurrency control/asynchronous execution + * problems. + */ +void +key_regeneration_alarm(int sig) +{ + int save_errno = errno; + + /* Check if we should generate a new key. */ + if (key_used) { + /* This should really be done in the background. */ + log("Generating new %d bit RSA key.", options.server_key_bits); + + if (sensitive_data.private_key != NULL) + RSA_free(sensitive_data.private_key); + sensitive_data.private_key = RSA_new(); + + if (public_key != NULL) + RSA_free(public_key); + public_key = RSA_new(); + + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + key_used = 0; + log("RSA key generation complete."); + } + /* Reschedule the alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + errno = save_errno; +} +/* + * Main program for the daemon. + */ int main(int ac, char **av) { - extern char *optarg; - extern int optind; - int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; - int remote_major, remote_minor; - int silentrsa = 0; - struct sockaddr_in sin; - char buf[100]; /* Must not be larger than remote_version. */ - char remote_version[100]; /* Must be at least as big as buf. */ - int remote_port; - char *comment; - FILE *f; - struct linger linger; - - /* Save argv[0]. */ - saved_argv = av; - if (strchr(av[0], '/')) - av0 = strrchr(av[0], '/') + 1; - else - av0 = av[0]; - - /* Initialize configuration options to their default values. */ - initialize_server_options(&options); - - /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:diqQ")) != EOF) - { - switch (opt) - { - case 'f': - config_file_name = optarg; - break; - case 'd': - debug_flag = 1; - options.log_level = SYSLOG_LEVEL_DEBUG; - break; - case 'i': - inetd_flag = 1; - break; - case 'Q': - silentrsa = 1; - break; - case 'q': - options.log_level = SYSLOG_LEVEL_QUIET; - break; - case 'b': - options.server_key_bits = atoi(optarg); - break; - case 'p': - options.port = atoi(optarg); - break; - case 'g': - options.login_grace_time = atoi(optarg); - break; - case 'k': - options.key_regeneration_time = atoi(optarg); - break; - case 'h': - options.host_key_file = optarg; - break; - case '?': - default: - fprintf(stderr, "sshd version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s [options]\n", av0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); - fprintf(stderr, " -d Debugging mode\n"); - fprintf(stderr, " -i Started from inetd\n"); - fprintf(stderr, " -q Quiet (no logging)\n"); - fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); - fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); - 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); - exit(1); + extern char *optarg; + extern int optind; + int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; + int remote_major, remote_minor; + int silentrsa = 0; + struct pollfd fds; + struct sockaddr_in sin; + 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; + int remote_port; + char *comment; + FILE *f; + struct linger linger; + + /* Save argv[0]. */ + saved_argv = av; + if (strchr(av[0], '/')) + av0 = strrchr(av[0], '/') + 1; + else + av0 = av[0]; + + /* Initialize configuration options to their default values. */ + initialize_server_options(&options); + + /* Parse command-line arguments. */ + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ")) != EOF) { + switch (opt) { + case 'f': + config_file_name = optarg; + break; + case 'd': + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG; + break; + case 'i': + inetd_flag = 1; + break; + case 'Q': + silentrsa = 1; + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'b': + options.server_key_bits = atoi(optarg); + break; + case 'p': + options.port = atoi(optarg); + break; + case 'g': + options.login_grace_time = atoi(optarg); + break; + case 'k': + options.key_regeneration_time = atoi(optarg); + break; + case 'h': + options.host_key_file = optarg; + break; + case 'V': + client_version_string = optarg; + /* only makes sense with inetd_flag, i.e. no listen() */ + inetd_flag = 1; + break; + case '?': + default: + fprintf(stderr, "sshd version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [options]\n", av0); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); + fprintf(stderr, " -d Debugging mode\n"); + fprintf(stderr, " -i Started from inetd\n"); + fprintf(stderr, " -q Quiet (no logging)\n"); + fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); + fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); + 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); + exit(1); + } } - } - - /* check if RSA support exists */ - if (rsa_alive() == 0) { - if (silentrsa == 0) - printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); - log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); - exit(1); - } - - /* Read server configuration options from the configuration file. */ - read_server_config(&options, config_file_name); - - /* Fill in default values for those options not explicitly set. */ - fill_default_server_options(&options); - - /* Check certain values for sanity. */ - if (options.server_key_bits < 512 || - options.server_key_bits > 32768) - { - 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); - } - - /* Initialize the log (it is reinitialized below in case we forked). */ - - if (debug_flag && !inetd_flag) - log_stderr = 1; - - log_init(av0, options.log_level, options.log_facility, log_stderr); - - debug("sshd version %.100s", SSH_VERSION); - - sensitive_data.host_key = RSA_new(); - /* Load the host key. It must have empty passphrase. */ - if (!load_private_key(options.host_key_file, "", - sensitive_data.host_key, &comment)) - { - if (debug_flag) - fprintf(stderr, "Could not load host key: %s: %s\n", - options.host_key_file, strerror(errno)); - else - { - int err = errno; - /* force logging */ - log_init(av0, SYSLOG_LEVEL_DEBUG, options.log_facility, log_stderr); - error("Could not load host key: %.200s: %.100s", - options.host_key_file, strerror(err)); + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + if (silentrsa == 0) + printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); + log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); + exit(1); + } + /* Read server configuration options from the configuration file. */ + read_server_config(&options, config_file_name); + + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + + /* Check certain values for sanity. */ + if (options.server_key_bits < 512 || + options.server_key_bits > 32768) { + 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); } - exit(1); - } - xfree(comment); - - /* If not in debugging mode, and not started from inetd, disconnect from - the controlling terminal, and fork. The original process exits. */ - if (!debug_flag && !inetd_flag) - { + /* 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); + + sensitive_data.host_key = RSA_new(); + errno = 0; + /* Load the host key. It must have empty passphrase. */ + if (!load_private_key(options.host_key_file, "", + sensitive_data.host_key, &comment)) { + error("Could not load host key: %.200s: %.100s", + options.host_key_file, strerror(errno)); + exit(1); + } + xfree(comment); + + /* Initialize the log (it is reinitialized below in case we + forked). */ + if (debug_flag && !inetd_flag) + log_stderr = 1; + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* If not in debugging mode, and not started from inetd, + disconnect from the controlling terminal, and fork. The + original process exits. */ + if (!debug_flag && !inetd_flag) { #ifdef TIOCNOTTY - int fd; + int fd; #endif /* TIOCNOTTY */ - if (daemon(0, 0) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); - - /* Disconnect from the controlling tty. */ + if (daemon(0, 0) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); + + /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY - fd = open("/dev/tty", O_RDWR|O_NOCTTY); - if (fd >= 0) - { - (void)ioctl(fd, TIOCNOTTY, NULL); - close(fd); - } + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } #endif /* TIOCNOTTY */ - } - - /* Reinitialize the log (because of the fork above). */ - log_init(av0, options.log_level, options.log_facility, log_stderr); - - /* Check that server and host key lengths differ sufficiently. This is - necessary to make double encryption work with rsaref. Oh, I hate - software patents. I dont know if this can go? Niels */ - if (options.server_key_bits > - BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && - options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) - { - options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; - debug("Forcing server key to %d bits to make it differ from host key.", - options.server_key_bits); - } - - /* Do not display messages to stdout in RSA code. */ - rsa_set_verbose(0); - - /* Initialize the random number generator. */ - arc4random_stir(); - - /* Chdir to the root directory so that the current disk can be unmounted - if desired. */ - chdir("/"); - - /* Close connection cleanly after attack. */ - cipher_attack_detected = packet_disconnect; - - /* Start listening for a socket, unless started from inetd. */ - if (inetd_flag) - { - int s1, s2; - s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ - s2 = dup(s1); - sock_in = dup(0); - sock_out = dup(1); - /* We intentionally do not close the descriptors 0, 1, and 2 as our - code for setting the descriptors won\'t work if ttyfd happens to - be one of those. */ - debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - /* Generate an rsa key. */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - 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)); - - /* Initialize the socket address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr = options.listen_addr; - sin.sin_port = htons(options.port); - - /* Bind the socket to the desired 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); } - - if (!debug_flag) - { - /* Record our pid in /etc/sshd_pid to make it easier to kill the - correct sshd. We don\'t want to do this before the bind above - because the bind will fail if there already is a daemon, and this - will overwrite any old pid in the file. */ - f = fopen(SSH_DAEMON_PID_FILE, "w"); - if (f) - { - fprintf(f, "%u\n", (unsigned int)getpid()); - fclose(f); - } + /* Reinitialize the log (because of the fork above). */ + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* Check that server and host key lengths differ sufficiently. + This is necessary to make double encryption work with rsaref. + Oh, I hate software patents. I dont know if this can go? Niels */ + if (options.server_key_bits > + BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && + options.server_key_bits < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + options.server_key_bits = + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + debug("Forcing server key to %d bits to make it differ from host key.", + options.server_key_bits); } - - /* Start listening on the port. */ - 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(); - /* Generate an rsa key. */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); - - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - - /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ - signal(SIGHUP, sighup_handler); - signal(SIGTERM, sigterm_handler); - signal(SIGQUIT, sigterm_handler); - - /* Arrange SIGCHLD to be caught. */ - signal(SIGCHLD, main_sigchld_handler); - - /* Stay listening for connections until the system crashes or the - daemon is killed with a signal. */ - for (;;) - { - if (received_sighup) - sighup_restart(); - /* Wait in accept until there is a connection. */ - aux = sizeof(sin); - newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux); - if (received_sighup) - sighup_restart(); - if (newsock < 0) - { - if (errno == EINTR) - continue; - error("accept: %.100s", strerror(errno)); - continue; - } - - /* Got connection. Fork a child to handle it, unless we are in - debugging mode. */ - if (debug_flag) - { - /* In debugging mode. Close the listening socket, and start - processing the connection without forking. */ - debug("Server will not fork when running in debugging mode."); - close(listen_sock); - sock_in = newsock; - sock_out = newsock; - pid = getpid(); - break; - } - else - { - /* Normal production daemon. Fork, and have the child process - the connection. The parent continues listening. */ - if ((pid = fork()) == 0) - { - /* Child. Close the listening socket, and start using - the accepted socket. Reinitialize logging (since our - pid has changed). We break out of the loop to handle - the connection. */ - close(listen_sock); - sock_in = newsock; - sock_out = newsock; - log_init(av0, options.log_level, options.log_facility, log_stderr); - break; + /* Do not display messages to stdout in RSA code. */ + rsa_set_verbose(0); + + /* Initialize the random number generator. */ + arc4random_stir(); + + /* Chdir to the root directory so that the current disk can be + unmounted if desired. */ + chdir("/"); + + /* Close connection cleanly after attack. */ + cipher_attack_detected = packet_disconnect; + + /* Start listening for a socket, unless started from inetd. */ + if (inetd_flag) { + int s1, s2; + s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ + s2 = dup(s1); + sock_in = dup(0); + sock_out = dup(1); + /* We intentionally do not close the descriptors 0, 1, and 2 + as our code for setting the descriptors won\'t work + if ttyfd happens to be one of those. */ + debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); + + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + 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); + } + if (!debug_flag) { + /* + * Record our pid in /etc/sshd_pid to make it easier + * to kill the correct sshd. We don\'t want to do + * this before the bind above because the bind will + * fail if there already is a daemon, and this will + * overwrite any old pid in the file. + */ + f = fopen(SSH_DAEMON_PID_FILE, "w"); + if (f) { + fprintf(f, "%u\n", (unsigned int) getpid()); + fclose(f); + } } - } - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %d.", pid); + 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(); + + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); + + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + + /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ + signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGQUIT, sigterm_handler); + + /* Arrange SIGCHLD to be caught. */ + signal(SIGCHLD, main_sigchld_handler); + + /* + * Stay listening for connections until the system crashes or + * the daemon is killed with a signal. + */ + 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) + continue; + aux = sizeof(sin); + newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux); + if (received_sighup) + sighup_restart(); + if (newsock < 0) { + if (errno == EINTR) + continue; + error("accept: %.100s", strerror(errno)); + continue; + } + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* + * In debugging mode. Close the listening + * socket, and start processing the + * connection without forking. + */ + debug("Server will not fork when running in debugging mode."); + close(listen_sock); + sock_in = newsock; + sock_out = newsock; + pid = getpid(); + break; + } else { + /* + * Normal production daemon. Fork, and have + * the child process the connection. The + * parent continues listening. + */ + if ((pid = fork()) == 0) { + /* + * Child. Close the listening socket, and start using the + * accepted socket. Reinitialize logging (since our pid has + * changed). We break out of the loop to handle the connection. + */ + close(listen_sock); + sock_in = newsock; + sock_out = newsock; + log_init(av0, options.log_level, options.log_facility, log_stderr); + break; + } + } + + /* Parent. Stay in the loop. */ + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %d.", pid); + + /* Mark that the key has been used (it was "given" to the child). */ + key_used = 1; + + arc4random_stir(); + + /* Close the new socket (the child is now taking care of it). */ + close(newsock); + } + } - /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + /* This is the child processing a new connection. */ + + /* + * Disable the key regeneration alarm. We will not regenerate the + * key since we are no longer in a position to give it to anyone. We + * will not restart on SIGHUP since it no longer makes sense. + */ + alarm(0); + signal(SIGALRM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + /* + * Set socket options for the connection. We want the socket to + * close as fast as possible without waiting for anything. If the + * connection is not a socket, these will do nothing. + */ + /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + + /* + * Register our connection. This turns encryption off because we do + * not have a key. + */ + packet_set_connection(sock_in, sock_out); + + remote_port = get_remote_port(); + remote_ip = get_remote_ipaddr(); + + /* Check whether logins are denied from this host. */ +#ifdef LIBWRAP + { + struct request_info req; - arc4random_stir(); + request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); + fromhost(&req); - /* Close the new socket (the child is now taking care of it). */ - close(newsock); + if (!hosts_access(&req)) { + close(sock_in); + close(sock_out); + refuse(&req); + } + verbose("Connection from %.500s port %d", eval_client(&req), remote_port); } - } - - /* This is the child processing a new connection. */ - - /* Disable the key regeneration alarm. We will not regenerate the key - since we are no longer in a position to give it to anyone. We will - not restart on SIGHUP since it no longer makes sense. */ - alarm(0); - signal(SIGALRM, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - - /* Set socket options for the connection. We want the socket to close - as fast as possible without waiting for anything. If the connection - is not a socket, these will do nothing. */ - /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); - - /* Register our connection. This turns encryption off because we do not - have a key. */ - packet_set_connection(sock_in, sock_out); - - remote_port = get_remote_port(); - - /* Check whether logins are denied from this host. */ -#ifdef LIBWRAP - { - struct request_info req; - - request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); - fromhost(&req); - - if (!hosts_access(&req)) { - close(sock_in); - close(sock_out); - refuse(&req); - } - log("Connection from %.500s port %d", eval_client(&req), remote_port); - } #else - /* Log the connection. */ - log("Connection from %.100s port %d", get_remote_ipaddr(), remote_port); + /* 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 successfully - authenticates itself. So we set up an alarm which is cleared after - successful authentication. A limit of zero indicates no limit. - Note that we don\'t set the alarm in debugging mode; it is just annoying - to have the server exit just when you are about to discover the bug. */ - signal(SIGALRM, grace_alarm_handler); - if (!debug_flag) - alarm(options.login_grace_time); - - /* Send our protocol version identification. */ - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", - PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); - if (write(sock_out, buf, strlen(buf)) != strlen(buf)) - fatal("Could not write ident string."); - - /* 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."); - if (buf[i] == '\r') - { - buf[i] = '\n'; - buf[i + 1] = 0; - break; + /* + * We don\'t want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is + * cleared after successful authentication. A limit of zero + * indicates no limit. Note that we don\'t set the alarm in debugging + * mode; it is just annoying to have the server exit just when you + * are about to discover the bug. + */ + signal(SIGALRM, grace_alarm_handler); + if (!debug_flag) + alarm(options.login_grace_time); + + if (client_version_string != NULL) { + /* we are exec'ed by sshd2, so skip exchange of protocol version */ + strlcpy(buf, client_version_string, sizeof(buf)); + } else { + /* Send our protocol version identification. */ + 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()); + + /* 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()); + if (buf[i] == '\r') { + buf[i] = '\n'; + buf[i + 1] = 0; + break; + } + if (buf[i] == '\n') { + /* buf[i] == '\n' */ + buf[i + 1] = 0; + break; + } + } + buf[sizeof(buf) - 1] = 0; } - if (buf[i] == '\n') - { - /* buf[i] == '\n' */ - buf[i + 1] = 0; - break; + + /* + * Check that the versions match. In future this might accept + * several versions and set appropriate flags to handle them. + */ + if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, + remote_version) != 3) { + char *s = "Protocol mismatch.\n"; + + (void) atomicio(write, sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + fatal("Bad protocol version identification '%.100s' from %s", + buf, get_remote_ipaddr()); + } + debug("Client protocol version %d.%d; client software version %.100s", + remote_major, remote_minor, remote_version); + if (remote_major != PROTOCOL_MAJOR) { + char *s = "Protocol major versions differ.\n"; + + (void) atomicio(write, sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + fatal("Protocol major versions differ for %s: %d vs. %d", + get_remote_ipaddr(), + 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) { + enable_compat13(); + if (strcmp(remote_version, "OpenSSH-1.1") != 0) { + debug("Agent forwarding disabled, remote version is not compatible."); + no_agent_forwarding_flag = 1; + } } - } - buf[sizeof(buf) - 1] = 0; - - /* Check that the versions match. In future this might accept several - versions and set appropriate flags to handle them. */ - if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, - remote_version) != 3) - { - const char *s = "Protocol mismatch.\n"; - (void) write(sock_out, s, strlen(s)); - close(sock_in); - close(sock_out); - fatal("Bad protocol version identification: %.100s", buf); - } - debug("Client protocol version %d.%d; client software version %.100s", - remote_major, remote_minor, remote_version); - if (remote_major != PROTOCOL_MAJOR) - { - const char *s = "Protocol major versions differ.\n"; - (void) write(sock_out, s, strlen(s)); - close(sock_in); - close(sock_out); - fatal("Protocol major versions differ: %d vs. %d", - 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) { - 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- and Rhosts-RSA-Authentication only make sense - from priviledged programs. - Of course, if the intruder has root access on his local machine, - he can connect from any port. So do not use these authentication - methods from machines that you do not trust. */ - if (remote_port >= IPPORT_RESERVED || - remote_port < IPPORT_RESERVED / 2) - { - options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; - } - - packet_set_nonblocking(); - - /* Handle the connection. */ - do_connection(); + /* + * Check that the connection comes from a privileged port. Rhosts- + * and Rhosts-RSA-Authentication only make sense from priviledged + * programs. Of course, if the intruder has root access on his local + * machine, he can connect from any port. So do not use these + * authentication methods from machines that you do not trust. + */ + if (remote_port >= IPPORT_RESERVED || + remote_port < IPPORT_RESERVED / 2) { + options.rhosts_authentication = 0; + options.rhosts_rsa_authentication = 0; + } + packet_set_nonblocking(); + + /* Handle the connection. */ + do_connection(); #ifdef KRB4 - /* Cleanup user's ticket cache file. */ - if (options.kerberos_ticket_cleanup) - (void) dest_tkt(); + /* Cleanup user's ticket cache file. */ + if (options.kerberos_ticket_cleanup) + (void) dest_tkt(); #endif /* KRB4 */ - /* Cleanup user's local Xauthority file. */ - if (xauthfile) unlink(xauthfile); + /* Cleanup user's local Xauthority file. */ + if (xauthfile) + unlink(xauthfile); - /* The connection has been terminated. */ - log("Closing connection to %.100s", inet_ntoa(sin.sin_addr)); + /* 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 */ + { + int retval; - packet_close(); + if (pamh != NULL) { + debug("Closing PAM session."); + retval = pam_close_session((pam_handle_t *)pamh, 0); - exit(0); -} + debug("Terminating PAM library."); + if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) + log("Cannot release PAM authentication."); -/* 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. */ + fatal_remove_cleanup(&pam_cleanup_proc, NULL); + } + } +#endif /* HAVE_LIBPAM */ + 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. + */ void do_connection() { - int i, len; - BIGNUM *session_key_int; - unsigned char session_key[SSH_SESSION_KEY_LENGTH]; - unsigned char check_bytes[8]; - char *user; - unsigned int cipher_type, auth_mask, protocol_flags; - int plen, slen; - u_int32_t rand = 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) - rand = arc4random(); - check_bytes[i] = rand & 0xff; - rand >>= 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(check_bytes[i]); - - /* Store our public server RSA key. */ - packet_put_int(BN_num_bits(public_key->n)); - packet_put_bignum(public_key->e); - packet_put_bignum(public_key->n); - - /* Store our public host RSA key. */ - packet_put_int(BN_num_bits(sensitive_data.host_key->n)); - packet_put_bignum(sensitive_data.host_key->e); - packet_put_bignum(sensitive_data.host_key->n); - - /* Put protocol flags. */ - packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); - - /* Declare which ciphers we support. */ - packet_put_int(cipher_mask()); - - /* Declare supported authentication types. */ - auth_mask = 0; - if (options.rhosts_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS; - if (options.rhosts_rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; - if (options.rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RSA; + int i, len; + BIGNUM *session_key_int; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + unsigned char check_bytes[8]; + char *user; + unsigned int cipher_type, auth_mask, protocol_flags; + int plen, slen, ulen; + u_int32_t rand = 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) + rand = arc4random(); + check_bytes[i] = rand & 0xff; + rand >>= 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(check_bytes[i]); + + /* Store our public server RSA key. */ + packet_put_int(BN_num_bits(public_key->n)); + packet_put_bignum(public_key->e); + packet_put_bignum(public_key->n); + + /* Store our public host RSA key. */ + packet_put_int(BN_num_bits(sensitive_data.host_key->n)); + packet_put_bignum(sensitive_data.host_key->e); + packet_put_bignum(sensitive_data.host_key->n); + + /* Put protocol flags. */ + packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); + + /* Declare which ciphers we support. */ + packet_put_int(cipher_mask()); + + /* Declare supported authentication types. */ + auth_mask = 0; + if (options.rhosts_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS; + if (options.rhosts_rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; + if (options.rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RSA; #ifdef KRB4 - if (options.kerberos_authentication) - auth_mask |= 1 << SSH_AUTH_KERBEROS; + if (options.kerberos_authentication) + auth_mask |= 1 << SSH_AUTH_KERBEROS; #endif #ifdef AFS - if (options.kerberos_tgt_passing) - auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; - if (options.afs_token_passing) - auth_mask |= 1 << SSH_PASS_AFS_TOKEN; + if (options.kerberos_tgt_passing) + auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; + if (options.afs_token_passing) + auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif - 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 public key and %d bit host key.", - BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); - - /* Read clients reply (cipher type and session key). */ - packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); - - /* Get cipher type. */ - cipher_type = packet_get_char(); - - /* 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()) - packet_disconnect("IP Spoofing check bytes do not match."); - - debug("Encryption type: %.200s", cipher_name(cipher_type)); - - /* Get the encrypted integer. */ - session_key_int = BN_new(); - packet_get_bignum(session_key_int, &slen); - - /* Get protocol flags. */ - protocol_flags = packet_get_int(); - packet_set_protocol_flags(protocol_flags); - - packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); - - /* Decrypt it using our private server key and private host key (key with - larger modulus first). */ - if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) - { - /* Private key has bigger modulus. */ - if (BN_num_bits(sensitive_data.private_key->n) < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", - BN_num_bits(sensitive_data.private_key->n), - BN_num_bits(sensitive_data.host_key->n), - SSH_KEY_BITS_RESERVED); - } - - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - } - else - { - /* Host key has bigger modulus (or they are equal). */ - if (BN_num_bits(sensitive_data.host_key->n) < - BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", - BN_num_bits(sensitive_data.host_key->n), - BN_num_bits(sensitive_data.private_key->n), - SSH_KEY_BITS_RESERVED); - } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - } - - /* Compute session id for this session. */ - compute_session_id(session_id, check_bytes, - BN_num_bits(sensitive_data.host_key->n), - sensitive_data.host_key->n, - BN_num_bits(sensitive_data.private_key->n), - sensitive_data.private_key->n); - - /* 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. */ - BN_mask_bits(session_key_int, sizeof(session_key) * 8); - len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) - fatal("do_connection: bad len: session_key_int %d > sizeof(session_key) %d", - len, sizeof(session_key)); - memset(session_key, 0, sizeof(session_key)); - BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); - - /* 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, 0); - - /* 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 acknowledgement packet. Note that this packet is sent - encrypted. */ - 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. */ - { - int ulen; - 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); -} +#ifdef SKEY + if (options.skey_authentication == 1) + auth_mask |= 1 << SSH_AUTH_TIS; +#endif + 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 public key and %d bit host key.", + BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); + + /* Read clients reply (cipher type and session key). */ + packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); + + /* Get cipher type and check whether we accept this. */ + cipher_type = packet_get_char(); + + if (!(cipher_mask() & (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 (check_bytes[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. */ + session_key_int = BN_new(); + packet_get_bignum(session_key_int, &slen); + + protocol_flags = packet_get_int(); + packet_set_protocol_flags(protocol_flags); + + packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); + + /* + * Decrypt it using our private server key and private host key (key + * with larger modulus first). + */ + if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { + /* Private key has bigger modulus. */ + if (BN_num_bits(sensitive_data.private_key->n) < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.private_key->n), + BN_num_bits(sensitive_data.host_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + } else { + /* Host key has bigger modulus (or they are equal). */ + if (BN_num_bits(sensitive_data.host_key->n) < + BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.host_key->n), + BN_num_bits(sensitive_data.private_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + } + + compute_session_id(session_id, check_bytes, + sensitive_data.host_key->n, + sensitive_data.private_key->n); + + /* + * 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. + */ + BN_mask_bits(session_key_int, sizeof(session_key) * 8); + len = BN_num_bytes(session_key_int); + if (len < 0 || len > sizeof(session_key)) + fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", + get_remote_ipaddr(), + len, sizeof(session_key)); + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + + /* 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); + + /* Destroy our copy of the session key. It is no longer needed. */ + memset(session_key, 0, sizeof(session_key)); -/* 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 - be returned. If AllowUsers isn't empty and user isn't listed there, or - if AllowGroups isn't empty and user isn't listed there, false will be - returned. Otherwise true is returned. - XXX This function should also check if user has a valid shell */ + debug("Received session key; encryption turned on."); + + /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ + 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 + * be returned. If AllowUsers isn't empty and user isn't listed there, or + * if AllowGroups isn't empty and user isn't listed there, false will be + * returned. Otherwise true is returned. + * XXX This function should also check if user has a valid shell + */ static int -allowed_user(struct passwd *pw) +allowed_user(struct passwd * pw) { - struct group *grp; - int i; - - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw) - return 0; - - /* XXX Should check for valid login shell */ - - /* Return false if user is listed in DenyUsers */ - if (options.num_deny_users > 0) - { - if (!pw->pw_name) - return 0; - for (i = 0; i < options.num_deny_users; i++) - if (match_pattern(pw->pw_name, options.deny_users[i])) - return 0; - } - - /* Return false if AllowUsers isn't empty and user isn't listed there */ - if (options.num_allow_users > 0) - { - if (!pw->pw_name) - return 0; - for (i = 0; i < options.num_allow_users; i++) - if (match_pattern(pw->pw_name, options.allow_users[i])) - break; - /* i < options.num_allow_users iff we break for loop */ - if (i >= options.num_allow_users) - return 0; - } - - /* Get the primary group name if we need it. Return false if it fails */ - if (options.num_deny_groups > 0 || options.num_allow_groups > 0 ) - { - grp = getgrgid(pw->pw_gid); - if (!grp) - return 0; - - /* Return false if user's group is listed in DenyGroups */ - if (options.num_deny_groups > 0) - { - if (!grp->gr_name) - return 0; - for (i = 0; i < options.num_deny_groups; i++) - if (match_pattern(grp->gr_name, options.deny_groups[i])) - return 0; - } - - /* Return false if AllowGroups isn't empty and user's group isn't - listed there */ - if (options.num_allow_groups > 0) - { - if (!grp->gr_name) - return 0; - for (i = 0; i < options.num_allow_groups; i++) - if (match_pattern(grp->gr_name, options.allow_groups[i])) - break; - /* i < options.num_allow_groups iff we break for loop */ - if (i >= options.num_allow_groups) - return 0; - } - } - - /* We found no reason not to let this user try to log on... */ - return 1; + struct group *grp; + int i; + + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (!pw) + return 0; + + /* XXX Should check for valid login shell */ + + /* Return false if user is listed in DenyUsers */ + if (options.num_deny_users > 0) { + if (!pw->pw_name) + return 0; + for (i = 0; i < options.num_deny_users; i++) + if (match_pattern(pw->pw_name, options.deny_users[i])) + return 0; + } + /* Return false if AllowUsers isn't empty and user isn't listed there */ + if (options.num_allow_users > 0) { + if (!pw->pw_name) + return 0; + for (i = 0; i < options.num_allow_users; i++) + if (match_pattern(pw->pw_name, options.allow_users[i])) + break; + /* i < options.num_allow_users iff we break for loop */ + if (i >= options.num_allow_users) + return 0; + } + /* Get the primary group name if we need it. Return false if it fails */ + if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { + grp = getgrgid(pw->pw_gid); + if (!grp) + return 0; + + /* Return false if user's group is listed in DenyGroups */ + if (options.num_deny_groups > 0) { + if (!grp->gr_name) + return 0; + for (i = 0; i < options.num_deny_groups; i++) + if (match_pattern(grp->gr_name, options.deny_groups[i])) + return 0; + } + /* + * Return false if AllowGroups isn't empty and user's group + * isn't listed there + */ + if (options.num_allow_groups > 0) { + if (!grp->gr_name) + return 0; + for (i = 0; i < options.num_allow_groups; i++) + if (match_pattern(grp->gr_name, options.allow_groups[i])) + break; + /* i < options.num_allow_groups iff we break for + loop */ + if (i >= options.num_allow_groups) + return 0; + } + } + /* We found no reason not to let this user try to log on... */ + return 1; } -/* 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). */ - +/* + * 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). + */ void do_authentication(char *user) { - struct passwd *pw, pwcopy; + struct passwd *pw, pwcopy; #ifdef AFS - /* If machine has AFS, set process authentication group. */ - if (k_hasafs()) { - k_setpag(); - k_unlog(); - } + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } #endif /* AFS */ - - /* Verify that the user is a valid user. */ - pw = getpwnam(user); - if (!pw || !allowed_user(pw)) - do_fake_authloop(user); - - /* Take a copy of the returned structure. */ - memset(&pwcopy, 0, sizeof(pwcopy)); - pwcopy.pw_name = xstrdup(pw->pw_name); - pwcopy.pw_passwd = xstrdup(pw->pw_passwd); - pwcopy.pw_uid = pw->pw_uid; - pwcopy.pw_gid = pw->pw_gid; - pwcopy.pw_dir = xstrdup(pw->pw_dir); - pwcopy.pw_shell = xstrdup(pw->pw_shell); - pw = &pwcopy; + + /* Verify that the user is a valid user. */ + pw = getpwnam(user); + if (!pw || !allowed_user(pw)) + do_fake_authloop(user); + + /* Take a copy of the returned structure. */ + memset(&pwcopy, 0, sizeof(pwcopy)); + pwcopy.pw_name = xstrdup(pw->pw_name); + pwcopy.pw_passwd = xstrdup(pw->pw_passwd); + pwcopy.pw_uid = pw->pw_uid; + pwcopy.pw_gid = pw->pw_gid; + pwcopy.pw_dir = xstrdup(pw->pw_dir); + pwcopy.pw_shell = xstrdup(pw->pw_shell); + pw = &pwcopy; #ifdef HAVE_LIBPAM - debug("Starting up PAM with username \"%.200s\"", pw->pw_name); + { + int pam_retval; - if (pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh) != PAM_SUCCESS) - fatal("PAM initialisation failed: %.200s", pam_strerror((pam_handle_t *)pamh, pam_retval)); + debug("Starting up PAM with username \"%.200s\"", pw->pw_name); - fatal_add_cleanup(&pam_cleanup_proc, NULL); + 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); + } #endif - /* If we are not running as root, the user must have the same uid as the - server. */ - if (getuid() != 0 && pw->pw_uid != getuid()) - packet_disconnect("Cannot change user when server not running as root."); + /* + * If we are not running as root, the user must have the same uid as + * the server. + */ + if (getuid() != 0 && pw->pw_uid != getuid()) + packet_disconnect("Cannot change user when server not running as root."); - debug("Attempting authentication for %.100s.", user); + debug("Attempting authentication for %.100s.", user); - /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && + /* If the user has no password, accept authentication immediately. */ + if (options.password_authentication && #ifdef KRB4 - (!options.kerberos_authentication || options.kerberos_or_local_passwd) && + (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif /* KRB4 */ - auth_password(pw, "")) - { - /* Authentication with empty password succeeded. */ - debug("Login for user %.100s accepted without authentication.", user); - } else { - /* Loop until the user has been authenticated or the connection is closed, - do_authloop() returns only if authentication is successfull */ - do_authloop(pw); - } - - /* XXX log unified auth message */ - - /* Check if the user is logging in as root and root logins are disallowed. */ - if (pw->pw_uid == 0 && !options.permit_root_login) - { - if (forced_command) - log("Root login accepted for forced command."); - else - packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", - get_canonical_hostname()); - } - - /* The user has been authenticated and accepted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - /* Perform session preparation. */ - do_authenticated(pw); + auth_password(pw, "")) { + /* Authentication with empty password succeeded. */ + log("Login for user %s from %.100s, accepted without authentication.", + pw->pw_name, get_remote_ipaddr()); + } else { + /* Loop until the user has been authenticated or the + connection is closed, do_authloop() returns only if + authentication is successfull */ + do_authloop(pw); + } + + /* Check if the user is logging in as root and root logins are disallowed. */ + if (pw->pw_uid == 0 && !options.permit_root_login) { + if (forced_command) + log("Root login accepted for forced command."); + else + packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + /* The user has been authenticated and accepted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Perform session preparation. */ + do_authenticated(pw); } -#define MAX_AUTH_FAILURES 5 +#define AUTH_FAIL_MAX 6 +#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) +#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" -/* read packets and try to authenticate local user *pw. - return if authentication is successfull */ +/* + * read packets and try to authenticate local user *pw. + * return if authentication is successfull + */ void -do_authloop(struct passwd *pw) +do_authloop(struct passwd * pw) { - int authentication_failures = 0; - unsigned int client_host_key_bits; - BIGNUM *client_host_key_e, *client_host_key_n; - BIGNUM *n; - char *client_user, *password; - int plen, dlen, nlen, ulen, elen; - - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - for (;;) { - int authenticated = 0; - - /* Get a packet from the client. */ - int type = packet_read(&plen); - - /* Process the packet. */ - switch (type) - { + int attempt = 0; + unsigned int bits; + BIGNUM *client_host_key_e, *client_host_key_n; + BIGNUM *n; + char *client_user = NULL, *password = NULL; + char user[1024]; + int plen, dlen, nlen, ulen, elen; + int type = 0; + void (*authlog) (const char *fmt,...) = verbose; +#ifdef HAVE_LIBPAM + int pam_retval; +#endif /* HAVE_LIBPAM */ + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + for (attempt = 1;; attempt++) { + int authenticated = 0; + strlcpy(user, "", sizeof user); + + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { #ifdef AFS - case SSH_CMSG_HAVE_KERBEROS_TGT: - if (!options.kerberos_tgt_passing) - { - /* packet_get_all(); */ - log("Kerberos tgt passing disabled."); - break; - } - else { - /* Accept Kerberos tgt. */ - char *tgt = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - if (!auth_kerberos_tgt(pw, tgt)) - debug("Kerberos tgt REFUSED for %s", pw->pw_name); - xfree(tgt); - } - continue; - - case SSH_CMSG_HAVE_AFS_TOKEN: - if (!options.afs_token_passing || !k_hasafs()) { - /* packet_get_all(); */ - log("AFS token passing disabled."); - break; - } - else { - /* Accept AFS token. */ - char *token_string = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - if (!auth_afs_token(pw, token_string)) - debug("AFS token REFUSED for %s", pw->pw_name); - xfree(token_string); - } - continue; + case SSH_CMSG_HAVE_KERBEROS_TGT: + if (!options.kerberos_tgt_passing) { + /* packet_get_all(); */ + verbose("Kerberos tgt passing disabled."); + break; + } else { + /* Accept Kerberos tgt. */ + char *tgt = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + if (!auth_kerberos_tgt(pw, tgt)) + verbose("Kerberos tgt REFUSED for %s", pw->pw_name); + xfree(tgt); + } + continue; + + case SSH_CMSG_HAVE_AFS_TOKEN: + if (!options.afs_token_passing || !k_hasafs()) { + /* packet_get_all(); */ + verbose("AFS token passing disabled."); + break; + } else { + /* Accept AFS token. */ + char *token_string = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + if (!auth_afs_token(pw, token_string)) + verbose("AFS token REFUSED for %s", pw->pw_name); + xfree(token_string); + } + continue; #endif /* AFS */ - #ifdef KRB4 - case SSH_CMSG_AUTH_KERBEROS: - if (!options.kerberos_authentication) - { - /* packet_get_all(); */ - log("Kerberos authentication disabled."); - break; - } - else { - /* Try Kerberos v4 authentication. */ - KTEXT_ST auth; - char *tkt_user = NULL; - char *kdata = packet_get_string((unsigned int *)&auth.length); - packet_integrity_check(plen, 4 + auth.length, type); - - if (auth.length < MAX_KTXT_LEN) - memcpy(auth.dat, kdata, auth.length); - xfree(kdata); - - authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); - - log("Kerberos authentication %s%s for account %s from %s", - authenticated ? "accepted " : "failed", - tkt_user != NULL ? tkt_user : "", - pw->pw_name, get_canonical_hostname()); - if (authenticated) - xfree(tkt_user); - } - break; + case SSH_CMSG_AUTH_KERBEROS: + if (!options.kerberos_authentication) { + /* packet_get_all(); */ + verbose("Kerberos authentication disabled."); + break; + } else { + /* Try Kerberos v4 authentication. */ + KTEXT_ST auth; + char *tkt_user = NULL; + char *kdata = packet_get_string((unsigned int *) &auth.length); + packet_integrity_check(plen, 4 + auth.length, type); + + if (auth.length < MAX_KTXT_LEN) + memcpy(auth.dat, kdata, auth.length); + xfree(kdata); + + authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); + + if (authenticated) { + snprintf(user, sizeof user, " tktuser %s", tkt_user); + xfree(tkt_user); + } + } + break; #endif /* KRB4 */ - - case SSH_CMSG_AUTH_RHOSTS: - if (!options.rhosts_authentication) - { - log("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(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - - /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ - authenticated = auth_rhosts(pw, client_user); - - log("Rhosts authentication %s for %.100s, remote %.100s on %.700s.", - authenticated ? "accepted" : "failed", - pw->pw_name, client_user, get_canonical_hostname()); + + 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_integrity_check(plen, 4 + ulen, type); + + /* Try to authenticate using /etc/hosts.equiv and + .rhosts. */ + authenticated = auth_rhosts(pw, client_user); + + snprintf(user, sizeof user, " ruser %s", client_user); #ifndef HAVE_LIBPAM - xfree(client_user); + xfree(client_user); #endif /* HAVE_LIBPAM */ - break; - - case SSH_CMSG_AUTH_RHOSTS_RSA: - if (!options.rhosts_rsa_authentication) - { - log("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_e = BN_new(); - client_host_key_n = BN_new(); - client_host_key_bits = packet_get_int(); - packet_get_bignum(client_host_key_e, &elen); - packet_get_bignum(client_host_key_n, &nlen); - - packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); - - authenticated = auth_rhosts_rsa(pw, client_user, client_host_key_bits, - client_host_key_e, client_host_key_n); - log("Rhosts authentication %s for %.100s, remote %.100s.", - authenticated ? "accepted" : "failed", - pw->pw_name, 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_e = BN_new(); + client_host_key_n = BN_new(); + bits = packet_get_int(); + packet_get_bignum(client_host_key_e, &elen); + packet_get_bignum(client_host_key_n, &nlen); + + if (bits != BN_num_bits(client_host_key_n)) + error("Warning: keysize mismatch for client_host_key: " + "actual %d, announced %d", BN_num_bits(client_host_key_n), bits); + packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); + + authenticated = auth_rhosts_rsa(pw, client_user, + client_host_key_e, client_host_key_n); + BN_clear_free(client_host_key_e); + BN_clear_free(client_host_key_n); + + snprintf(user, sizeof user, " ruser %s", client_user); #ifndef HAVE_LIBPAM - xfree(client_user); + xfree(client_user); #endif /* HAVE_LIBPAM */ - BN_clear_free(client_host_key_e); - BN_clear_free(client_host_key_n); - break; - - case SSH_CMSG_AUTH_RSA: - if (!options.rsa_authentication) - { - log("RSA authentication disabled."); - break; - } - - /* RSA authentication requested. */ - n = BN_new(); - packet_get_bignum(n, &nlen); - packet_integrity_check(plen, nlen, type); - authenticated = auth_rsa(pw, n); - BN_clear_free(n); - log("RSA authentication %s for %.100s.", - authenticated ? "accepted" : "failed", - pw->pw_name); - break; - - case SSH_CMSG_AUTH_PASSWORD: - if (!options.password_authentication) - { - log("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_integrity_check(plen, 4 + dlen, type); - + break; + + case SSH_CMSG_AUTH_RSA: + if (!options.rsa_authentication) { + verbose("RSA authentication disabled."); + break; + } + /* RSA authentication requested. */ + n = BN_new(); + packet_get_bignum(n, &nlen); + packet_integrity_check(plen, nlen, type); + authenticated = auth_rsa(pw, n); + BN_clear_free(n); + break; + + case SSH_CMSG_AUTH_PASSWORD: + 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_integrity_check(plen, 4 + dlen, type); + #ifdef HAVE_LIBPAM - /* Do PAM auth with password */ - pampasswd = password; - pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); - if (pam_retval == PAM_SUCCESS) - { - log("PAM Password authentication accepted for user \"%.100s\"", user); - authenticated = 1; - break; - } - - log("PAM Password authentication for \"%.100s\" failed: %s", - user, pam_strerror((pam_handle_t *)pamh, pam_retval)); - break; + /* Do PAM auth with password */ + pampasswd = password; + pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); + if (pam_retval == PAM_SUCCESS) { + log("PAM Password authentication accepted for user \"%.100s\"", pw->pw_name); + memset(password, 0, strlen(password)); + xfree(password); + authenticated = 1; + break; + } + + log("PAM Password authentication for \"%.100s\" failed: %s", + pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); + memset(password, 0, strlen(password)); + xfree(password); + break; #else /* HAVE_LIBPAM */ - /* Try authentication with the password. */ - authenticated = auth_password(pw, password); + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); - memset(password, 0, strlen(password)); - xfree(password); - break; + memset(password, 0, strlen(password)); + xfree(password); + break; #endif /* HAVE_LIBPAM */ - - case SSH_CMSG_AUTH_TIS: - /* TIS Authentication is unsupported */ - log("TIS authentication disabled."); - break; - - default: - /* Any unknown messages will be ignored (and failure returned) - during authentication. */ - log("Unknown message during authentication: type %d", type); - break; /* Respond with a failure message. */ - } - - if (authenticated) - break; - if (++authentication_failures >= MAX_AUTH_FAILURES) - packet_disconnect("Too many authentication failures for %.100s from %.200s", - pw->pw_name, get_canonical_hostname()); - /* Send a message indicating that the authentication attempt failed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } -#ifdef HAVE_LIBPAM - do_pam_account_and_session(pw->pw_name, client_user, get_canonical_hostname()); +#ifdef SKEY + case SSH_CMSG_AUTH_TIS: + debug("rcvd SSH_CMSG_AUTH_TIS"); + if (options.skey_authentication == 1) { + char *skeyinfo = skey_keyinfo(pw->pw_name); + if (skeyinfo == NULL) { + debug("generating fake skeyinfo for %.100s.", pw->pw_name); + skeyinfo = skey_fake_keyinfo(pw->pw_name); + } + if (skeyinfo != NULL) { + /* we send our s/key- in tis-challenge messages */ + debug("sending challenge '%s'", skeyinfo); + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } + } + break; + case SSH_CMSG_AUTH_TIS_RESPONSE: + debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); + if (options.skey_authentication == 1) { + char *response = packet_get_string(&dlen); + debug("skey response == '%s'", response); + packet_integrity_check(plen, 4 + dlen, type); + authenticated = (skey_haskey(pw->pw_name) == 0 && + skey_passcheck(pw->pw_name, response) != -1); + xfree(response); + } + break; +#else + case SSH_CMSG_AUTH_TIS: + /* TIS Authentication is unsupported */ + log("TIS authentication unsupported."); + break; +#endif + + default: + /* + * Any unknown messages will be ignored (and failure + * returned) during authentication. + */ + log("Unknown message during authentication: type %d", type); + break; + } - /* Clean up */ - if (client_user != NULL) - xfree(client_user); + /* Raise logging level */ + if (authenticated || + attempt == AUTH_FAIL_LOG || + type == SSH_CMSG_AUTH_PASSWORD) + authlog = log; - if (password != NULL) - { - memset(password, 0, strlen(password)); - xfree(password); - } + authlog("%s %s for %.200s from %.200s port %d%s", + authenticated ? "Accepted" : "Failed", + get_authname(type), + pw->pw_uid == 0 ? "ROOT" : pw->pw_name, + get_remote_ipaddr(), + 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); + + return; + } + + if (attempt > AUTH_FAIL_MAX) { + if (client_user != NULL) + xfree(client_user); + + 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); + packet_send(); + packet_write_wait(); + } } -/* The user does not exist or access is denied, - but fake indication that authentication is needed. */ +/* + * The user does not exist or access is denied, + * but fake indication that authentication is needed. + */ void do_fake_authloop(char *user) { - int authentication_failures = 0; - - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - /* Keep reading packets, and always respond with a failure. This is to - avoid disclosing whether such a user really exists. */ - for (;;) - { - /* Read a packet. This will not return if the client disconnects. */ - int plen; - int type = packet_read(&plen); + int attempt = 0; + + log("Faking authloop for illegal user %.200s from %.200s port %d", + user, + get_remote_ipaddr(), + get_remote_port()); + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + /* + * Keep reading packets, and always respond with a failure. This is + * to avoid disclosing whether such a user really exists. + */ + for (attempt = 1;; attempt++) { + /* Read a packet. This will not return if the client disconnects. */ + int plen; + int type = packet_read(&plen); #ifdef SKEY - int passw_len; - char *password, *skeyinfo; - if (options.password_authentication && - options.skey_authentication == 1 && - type == SSH_CMSG_AUTH_PASSWORD && - (password = packet_get_string(&passw_len)) != NULL && - passw_len == 5 && - strncasecmp(password, "s/key", 5) == 0 && - (skeyinfo = skey_fake_keyinfo(user)) != NULL ){ - /* Send a fake s/key challenge. */ - packet_send_debug(skeyinfo); - } + int dlen; + char *password, *skeyinfo; + /* Try to send a fake s/key challenge. */ + if (options.skey_authentication == 1 && + (skeyinfo = skey_fake_keyinfo(user)) != NULL) { + if (type == SSH_CMSG_AUTH_TIS) { + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } else if (type == SSH_CMSG_AUTH_PASSWORD && + options.password_authentication && + (password = packet_get_string(&dlen)) != NULL && + dlen == 5 && + strncasecmp(password, "s/key", 5) == 0 ) { + packet_send_debug(skeyinfo); + } + } #endif - if (++authentication_failures >= MAX_AUTH_FAILURES) - packet_disconnect("Too many authentication failures for %.100s from %.200s", - user, get_canonical_hostname()); - /* Send failure. This should be indistinguishable from a failed - authentication. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } - /*NOTREACHED*/ - abort(); + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, user); + + /* + * Send failure. This should be indistinguishable from a + * failed authentication. + */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } + /* NOTREACHED */ + abort(); } -/* Remove local Xauthority file. */ +/* + * Remove local Xauthority file. + */ static void xauthfile_cleanup_proc(void *ignore) { - debug("xauthfile_cleanup_proc called"); + debug("xauthfile_cleanup_proc called"); - if (xauthfile != NULL) { - unlink(xauthfile); - xfree(xauthfile); - xauthfile = NULL; - } + if (xauthfile != NULL) { + unlink(xauthfile); + xfree(xauthfile); + xauthfile = NULL; + } } -/* Prepares for an interactive session. This is called after the user has - been successfully authenticated. During this message exchange, pseudo - terminals are allocated, X11, TCP/IP, and authentication agent forwardings - are requested, etc. */ - -void do_authenticated(struct passwd *pw) +/* + * Prepares for an interactive session. This is called after the user has + * been successfully authenticated. During this message exchange, pseudo + * terminals are allocated, X11, TCP/IP, and authentication agent forwardings + * are requested, etc. + */ +void +do_authenticated(struct passwd * pw) { - int type; - int compression_level = 0, enable_compression_after_reply = 0; - int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; - int row, col, xpixel, ypixel, screen; - char ttyname[64]; - char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL; - struct group *grp; - gid_t tty_gid; - mode_t tty_mode; - int n_bytes; - - /* Cancel the alarm we set to limit the time taken for authentication. */ - alarm(0); - - /* Inform the channel mechanism that we are the server side and that - the client may request to connect to any port at all. (The user could - do it anyway, and we wouldn\'t know what is permitted except by the - client telling us, so we can equally well trust the client not to request - anything bogus.) */ - channel_permit_all_opens(); - - /* We stay in this loop until the client requests to execute a shell or a - command. */ - while (1) - { - int plen, dlen; - - /* Get a packet from the client. */ - type = packet_read(&plen); - - /* Process the packet. */ - switch (type) - { - case SSH_CMSG_REQUEST_COMPRESSION: - packet_integrity_check(plen, 4, type); - compression_level = packet_get_int(); - if (compression_level < 1 || compression_level > 9) - { - packet_send_debug("Received illegal compression level %d.", - compression_level); - goto fail; - } - /* Enable compression after we have responded with SUCCESS. */ - enable_compression_after_reply = 1; - break; - - case SSH_CMSG_REQUEST_PTY: - if (no_pty_flag) - { - debug("Allocating a pty not permitted for this authentication."); - goto fail; - } - if (have_pty) - packet_disconnect("Protocol error: you already have a pty."); - - debug("Allocating pty."); - - /* Allocate a pty and open it. */ - if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) - { - error("Failed to allocate pty."); - goto fail; - } - - /* Determine the group to make the owner of the tty. */ - grp = getgrnam("tty"); - if (grp) - { - tty_gid = grp->gr_gid; - tty_mode = S_IRUSR|S_IWUSR|S_IWGRP; - } - else - { - tty_gid = pw->pw_gid; - tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH; - } - - /* Change ownership of the tty. */ - if (chown(ttyname, pw->pw_uid, tty_gid) < 0) - fatal("chown(%.100s, %d, %d) failed: %.100s", - ttyname, pw->pw_uid, tty_gid, strerror(errno)); - if (chmod(ttyname, tty_mode) < 0) - fatal("chmod(%.100s, 0%o) failed: %.100s", - ttyname, tty_mode, strerror(errno)); - - /* Get TERM from the packet. Note that the value may be of arbitrary - length. */ - - term = packet_get_string(&dlen); - packet_integrity_check(dlen, strlen(term), type); - /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ - /* Remaining bytes */ - n_bytes = plen - (4 + dlen + 4*4); - - if (strcmp(term, "") == 0) - term = NULL; - - /* Get window size from the packet. */ - row = packet_get_int(); - col = packet_get_int(); - xpixel = packet_get_int(); - ypixel = packet_get_int(); - pty_change_window_size(ptyfd, row, col, xpixel, ypixel); - - /* Get tty modes from the packet. */ - tty_parse_modes(ttyfd, &n_bytes); - packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); - - /* Indicate that we now have a pty. */ - have_pty = 1; - break; - - case SSH_CMSG_X11_REQUEST_FORWARDING: - if (!options.x11_forwarding) - { - packet_send_debug("X11 forwarding disabled in server configuration file."); - goto fail; - } + int type; + int compression_level = 0, enable_compression_after_reply = 0; + int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; + int row, col, xpixel, ypixel, screen; + char ttyname[64]; + char *command, *term = NULL, *display = NULL, *proto = NULL, + *data = NULL; + struct group *grp; + gid_t tty_gid; + mode_t tty_mode; + int n_bytes; + + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + + /* + * Inform the channel mechanism that we are the server side and that + * the client may request to connect to any port at all. (The user + * could do it anyway, and we wouldn\'t know what is permitted except + * by the client telling us, so we can equally well trust the client + * not to request anything bogus.) + */ + channel_permit_all_opens(); + + /* + * We stay in this loop until the client requests to execute a shell + * or a command. + */ + while (1) { + int plen, dlen; + + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { + case SSH_CMSG_REQUEST_COMPRESSION: + packet_integrity_check(plen, 4, type); + compression_level = packet_get_int(); + if (compression_level < 1 || compression_level > 9) { + packet_send_debug("Received illegal compression level %d.", + compression_level); + goto fail; + } + /* Enable compression after we have responded with SUCCESS. */ + enable_compression_after_reply = 1; + break; + + case SSH_CMSG_REQUEST_PTY: + if (no_pty_flag) { + debug("Allocating a pty not permitted for this authentication."); + goto fail; + } + if (have_pty) + packet_disconnect("Protocol error: you already have a pty."); + + debug("Allocating pty."); + + /* Allocate a pty and open it. */ + if (!pty_allocate(&ptyfd, &ttyfd, ttyname, + sizeof(ttyname))) { + error("Failed to allocate pty."); + goto fail; + } + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp) { + tty_gid = grp->gr_gid; + tty_mode = S_IRUSR | S_IWUSR | S_IWGRP; + } else { + tty_gid = pw->pw_gid; + tty_mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; + } + + /* Change ownership of the tty. */ + if (chown(ttyname, pw->pw_uid, tty_gid) < 0) + fatal("chown(%.100s, %d, %d) failed: %.100s", + ttyname, pw->pw_uid, tty_gid, strerror(errno)); + if (chmod(ttyname, tty_mode) < 0) + fatal("chmod(%.100s, 0%o) failed: %.100s", + ttyname, tty_mode, strerror(errno)); + + /* Get TERM from the packet. Note that the value may be of arbitrary length. */ + term = packet_get_string(&dlen); + packet_integrity_check(dlen, strlen(term), type); + /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ + /* Remaining bytes */ + n_bytes = plen - (4 + dlen + 4 * 4); + + if (strcmp(term, "") == 0) + term = NULL; + + /* Get window size from the packet. */ + row = packet_get_int(); + col = packet_get_int(); + xpixel = packet_get_int(); + ypixel = packet_get_int(); + pty_change_window_size(ptyfd, row, col, xpixel, ypixel); + + /* Get tty modes from the packet. */ + tty_parse_modes(ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); + + /* Indicate that we now have a pty. */ + have_pty = 1; + +#ifdef HAVE_LIBPAM + /* do the pam_open_session since we have the pty */ + do_pam_session(pw->pw_name,ttyname); +#endif /* HAVE_LIBPAM */ + + break; + + case SSH_CMSG_X11_REQUEST_FORWARDING: + if (!options.x11_forwarding) { + packet_send_debug("X11 forwarding disabled in server configuration file."); + goto fail; + } #ifdef XAUTH_PATH - if (no_x11_forwarding_flag) - { - packet_send_debug("X11 forwarding not permitted for this authentication."); - goto fail; - } - debug("Received request for X11 forwarding with auth spoofing."); - if (display) - packet_disconnect("Protocol error: X11 display already set."); - { - int proto_len, data_len; - proto = packet_get_string(&proto_len); - data = packet_get_string(&data_len); - packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type); - } - if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) - screen = packet_get_int(); - else - screen = 0; - display = x11_create_display_inet(screen); - if (!display) - goto fail; - - /* Setup to always have a local .Xauthority. */ - xauthfile = xmalloc(MAXPATHLEN); - snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); - - if ((xauthfd = mkstemp(xauthfile)) != -1) { - fchown(xauthfd, pw->pw_uid, pw->pw_gid); - close(xauthfd); - fatal_add_cleanup(xauthfile_cleanup_proc, NULL); - } - else { - xfree(xauthfile); - xauthfile = NULL; - } - break; + if (no_x11_forwarding_flag) { + packet_send_debug("X11 forwarding not permitted for this authentication."); + goto fail; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (display) + packet_disconnect("Protocol error: X11 display already set."); + { + int proto_len, data_len; + proto = packet_get_string(&proto_len); + data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type); + } + if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) + screen = packet_get_int(); + else + screen = 0; + display = x11_create_display_inet(screen, options.x11_display_offset); + if (!display) + goto fail; + + /* Setup to always have a local .Xauthority. */ + xauthfile = xmalloc(MAXPATHLEN); + snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); + + if ((xauthfd = mkstemp(xauthfile)) != -1) { + fchown(xauthfd, pw->pw_uid, pw->pw_gid); + close(xauthfd); + fatal_add_cleanup(xauthfile_cleanup_proc, NULL); + } else { + xfree(xauthfile); + xauthfile = NULL; + } + break; #else /* XAUTH_PATH */ - /* No xauth program; we won't accept forwarding with spoofing. */ - packet_send_debug("No xauth program; cannot forward with spoofing."); - goto fail; + packet_send_debug("No xauth program; cannot forward with spoofing."); + goto fail; #endif /* XAUTH_PATH */ - case SSH_CMSG_AGENT_REQUEST_FORWARDING: - if (no_agent_forwarding_flag) - { - debug("Authentication agent forwarding not permitted for this authentication."); - goto fail; - } - debug("Received authentication agent forwarding request."); - auth_input_request_forwarding(pw); - break; - - case SSH_CMSG_PORT_FORWARD_REQUEST: - if (no_port_forwarding_flag) - { - debug("Port forwarding not permitted for this authentication."); - goto fail; - } - debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(pw->pw_uid == 0); - break; - - case SSH_CMSG_EXEC_SHELL: - /* Set interactive/non-interactive mode. */ - packet_set_interactive(have_pty || display != NULL, - options.keepalives); - - if (forced_command != NULL) - goto do_forced_command; - debug("Forking shell."); - packet_integrity_check(plen, 0, type); - if (have_pty) - do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, - data); - else - do_exec_no_pty(NULL, pw, display, proto, data); - return; - - case SSH_CMSG_EXEC_CMD: - /* Set interactive/non-interactive mode. */ - packet_set_interactive(have_pty || display != NULL, - options.keepalives); - - if (forced_command != NULL) - goto do_forced_command; - /* Get command from the packet. */ - { - int dlen; - command = packet_get_string(&dlen); - debug("Executing command '%.500s'", command); - packet_integrity_check(plen, 4 + dlen, type); - } - if (have_pty) - do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, - proto, data); - else - do_exec_no_pty(command, pw, display, proto, data); - xfree(command); - return; - - case SSH_CMSG_MAX_PACKET_SIZE: - debug("The server does not support limiting packet size."); - goto fail; - - default: - /* Any unknown messages in this phase are ignored, and a failure - message is returned. */ - log("Unknown packet type received after authentication: %d", type); - goto fail; - } + case SSH_CMSG_AGENT_REQUEST_FORWARDING: + if (no_agent_forwarding_flag) { + debug("Authentication agent forwarding not permitted for this authentication."); + goto fail; + } + debug("Received authentication agent forwarding request."); + auth_input_request_forwarding(pw); + break; + + case SSH_CMSG_PORT_FORWARD_REQUEST: + if (no_port_forwarding_flag) { + debug("Port forwarding not permitted for this authentication."); + goto fail; + } + debug("Received TCP/IP port forwarding request."); + channel_input_port_forward_request(pw->pw_uid == 0); + break; + + case SSH_CMSG_MAX_PACKET_SIZE: + if (packet_set_maxsize(packet_get_int()) < 0) + goto fail; + break; + + case SSH_CMSG_EXEC_SHELL: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); + + if (forced_command != NULL) + goto do_forced_command; + debug("Forking shell."); + packet_integrity_check(plen, 0, type); + if (have_pty) + do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(NULL, pw, display, proto, data); + return; + + case SSH_CMSG_EXEC_CMD: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); + + if (forced_command != NULL) + goto do_forced_command; + /* Get command from the packet. */ + { + int dlen; + command = packet_get_string(&dlen); + debug("Executing command '%.500s'", command); + packet_integrity_check(plen, 4 + dlen, type); + } + if (have_pty) + do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(command, pw, display, proto, data); + xfree(command); + return; + + default: + /* + * Any unknown messages in this phase are ignored, + * and a failure message is returned. + */ + log("Unknown packet type received after authentication: %d", type); + goto fail; + } - /* The request was successfully processed. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); + /* The request was successfully processed. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); - /* Enable compression now that we have replied if appropriate. */ - if (enable_compression_after_reply) - { - enable_compression_after_reply = 0; - packet_start_compression(compression_level); - } + /* Enable compression now that we have replied if appropriate. */ + if (enable_compression_after_reply) { + enable_compression_after_reply = 0; + packet_start_compression(compression_level); + } + continue; - continue; - - fail: - /* The request failed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - continue; - - do_forced_command: - /* There is a forced command specified for this login. Execute it. */ - debug("Executing forced command: %.900s", forced_command); - if (have_pty) - do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, - proto, data); - else - do_exec_no_pty(forced_command, pw, display, proto, data); - return; - } -} +fail: + /* The request failed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + continue; -/* This is called to fork and execute a command when we have no tty. This - will call do_child from the child, and server_loop from the parent after - setting up file descriptors and such. */ +do_forced_command: + /* + * There is a forced command specified for this login. + * Execute it. + */ + debug("Executing forced command: %.900s", forced_command); + if (have_pty) + do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); + else + do_exec_no_pty(forced_command, pw, display, proto, data); + return; + } +} -void do_exec_no_pty(const char *command, struct passwd *pw, - const char *display, const char *auth_proto, - const char *auth_data) -{ - int pid; +/* + * This is called to fork and execute a command when we have no tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors and such. + */ +void +do_exec_no_pty(const char *command, struct passwd * pw, + const char *display, const char *auth_proto, + const char *auth_data) +{ + int pid; #ifdef USE_PIPES - int pin[2], pout[2], perr[2]; - /* Allocate pipes for communicating with the program. */ - if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) - packet_disconnect("Could not create pipes: %.100s", - strerror(errno)); + int pin[2], pout[2], perr[2]; + /* Allocate pipes for communicating with the program. */ + if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) + packet_disconnect("Could not create pipes: %.100s", + strerror(errno)); #else /* USE_PIPES */ - int inout[2], err[2]; - /* Uses socket pairs to communicate with the program. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || - socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) - packet_disconnect("Could not create socket pairs: %.100s", - strerror(errno)); + int inout[2], err[2]; + /* Uses socket pairs to communicate with the program. */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || + socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) + packet_disconnect("Could not create socket pairs: %.100s", + strerror(errno)); #endif /* USE_PIPES */ - - setproctitle("%s@notty", pw->pw_name); - /* Fork the child. */ - if ((pid = fork()) == 0) - { - /* Child. Reinitialize the log since the pid has changed. */ - log_init(av0, options.log_level, options.log_facility, log_stderr); + setproctitle("%s@notty", pw->pw_name); + + /* Fork the child. */ + if ((pid = fork()) == 0) { + /* Child. Reinitialize the log since the pid has changed. */ + log_init(av0, options.log_level, options.log_facility, log_stderr); - /* Create a new session and process group since the 4.4BSD setlogin() - affects the entire process group. */ - if (setsid() < 0) - error("setsid failed: %.100s", strerror(errno)); + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. + */ + if (setsid() < 0) + error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES - /* Redirect stdin. We close the parent side of the socket pair, - and make the child side the standard input. */ - close(pin[1]); - if (dup2(pin[0], 0) < 0) - perror("dup2 stdin"); - close(pin[0]); - - /* Redirect stdout. */ - close(pout[0]); - if (dup2(pout[1], 1) < 0) - perror("dup2 stdout"); - close(pout[1]); - - /* Redirect stderr. */ - close(perr[0]); - if (dup2(perr[1], 2) < 0) - perror("dup2 stderr"); - close(perr[1]); + /* + * Redirect stdin. We close the parent side of the socket + * pair, and make the child side the standard input. + */ + close(pin[1]); + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + close(pin[0]); + + /* Redirect stdout. */ + close(pout[0]); + if (dup2(pout[1], 1) < 0) + perror("dup2 stdout"); + close(pout[1]); + + /* Redirect stderr. */ + close(perr[0]); + if (dup2(perr[1], 2) < 0) + perror("dup2 stderr"); + close(perr[1]); #else /* USE_PIPES */ - /* Redirect stdin, stdout, and stderr. Stdin and stdout will use the - same socket, as some programs (particularly rdist) seem to depend - on it. */ - close(inout[1]); - close(err[1]); - if (dup2(inout[0], 0) < 0) /* stdin */ - perror("dup2 stdin"); - if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ - perror("dup2 stdout"); - if (dup2(err[0], 2) < 0) /* stderr */ - perror("dup2 stderr"); + /* + * Redirect stdin, stdout, and stderr. Stdin and stdout will + * use the same socket, as some programs (particularly rdist) + * seem to depend on it. + */ + close(inout[1]); + close(err[1]); + if (dup2(inout[0], 0) < 0) /* stdin */ + perror("dup2 stdin"); + if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ + perror("dup2 stdout"); + if (dup2(err[0], 2) < 0) /* stderr */ + perror("dup2 stderr"); #endif /* USE_PIPES */ - /* Do processing for the child (exec command etc). */ - do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); - /*NOTREACHED*/ - } - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); + /* Do processing for the child (exec command etc). */ + do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); #ifdef USE_PIPES - /* We are the parent. Close the child sides of the pipes. */ - close(pin[0]); - close(pout[1]); - close(perr[1]); - - /* Enter the interactive session. */ - server_loop(pid, pin[1], pout[0], perr[0]); - /* server_loop has closed pin[1], pout[1], and perr[1]. */ + /* We are the parent. Close the child sides of the pipes. */ + close(pin[0]); + close(pout[1]); + close(perr[1]); + + /* Enter the interactive session. */ + server_loop(pid, pin[1], pout[0], perr[0]); + /* server_loop has closed pin[1], pout[1], and perr[1]. */ #else /* USE_PIPES */ - /* We are the parent. Close the child sides of the socket pairs. */ - close(inout[0]); - close(err[0]); - - /* Enter the interactive session. Note: server_loop must be able to handle - the case that fdin and fdout are the same. */ - server_loop(pid, inout[1], inout[1], err[1]); - /* server_loop has closed inout[1] and err[1]. */ + /* We are the parent. Close the child sides of the socket pairs. */ + close(inout[0]); + close(err[0]); + + /* + * Enter the interactive session. Note: server_loop must be able to + * handle the case that fdin and fdout are the same. + */ + server_loop(pid, inout[1], inout[1], err[1]); + /* server_loop has closed inout[1] and err[1]. */ #endif /* USE_PIPES */ } -struct pty_cleanup_context -{ - const char *ttyname; - int pid; +struct pty_cleanup_context { + const char *ttyname; + int pid; }; -/* Function to perform cleanup if we get aborted abnormally (e.g., due to a - dropped connection). */ - -void pty_cleanup_proc(void *context) +/* + * Function to perform cleanup if we get aborted abnormally (e.g., due to a + * dropped connection). + */ +void +pty_cleanup_proc(void *context) { - struct pty_cleanup_context *cu = context; + struct pty_cleanup_context *cu = context; - debug("pty_cleanup_proc called"); + debug("pty_cleanup_proc called"); - /* Record that the user has logged out. */ - record_logout(cu->pid, cu->ttyname); + /* Record that the user has logged out. */ + record_logout(cu->pid, cu->ttyname); - /* Release the pseudo-tty. */ - pty_release(cu->ttyname); + /* Release the pseudo-tty. */ + pty_release(cu->ttyname); } -/* This is called to fork and execute a command when we have a tty. This - will call do_child from the child, and server_loop from the parent after - setting up file descriptors, controlling tty, updating wtmp, utmp, - lastlog, and other such operations. */ - -void do_exec_pty(const char *command, int ptyfd, int ttyfd, - const char *ttyname, struct passwd *pw, const char *term, - const char *display, const char *auth_proto, - const char *auth_data) +/* + * This is called to fork and execute a command when we have a tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors, controlling tty, updating wtmp, utmp, + * lastlog, and other such operations. + */ +void +do_exec_pty(const char *command, int ptyfd, int ttyfd, + const char *ttyname, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data) { - int pid, fdout; - const char *hostname; - time_t last_login_time; - char buf[100], *time_string; - FILE *f; - char line[256]; - struct stat st; - int quiet_login; - struct sockaddr_in from; - int fromlen; - struct pty_cleanup_context cleanup_context; - - /* Get remote host name. */ - hostname = get_canonical_hostname(); - - /* Get the time when the user last logged in. Buf will be set to contain - the hostname the last login was from. */ - if(!options.use_login) { - last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, - buf, sizeof(buf)); - } - - setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); - - /* Fork the child. */ - if ((pid = fork()) == 0) - { - pid = getpid(); - - /* Child. Reinitialize the log because the pid has changed. */ - log_init(av0, options.log_level, options.log_facility, log_stderr); - - /* Close the master side of the pseudo tty. */ - close(ptyfd); - - /* Make the pseudo tty our controlling tty. */ - pty_make_controlling_tty(&ttyfd, ttyname); - - /* Redirect stdin from the pseudo tty. */ - if (dup2(ttyfd, fileno(stdin)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Redirect stdout to the pseudo tty. */ - if (dup2(ttyfd, fileno(stdout)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Redirect stderr to the pseudo tty. */ - if (dup2(ttyfd, fileno(stderr)) < 0) - error("dup2 stdin failed: %.100s", strerror(errno)); - - /* Close the extra descriptor for the pseudo tty. */ - close(ttyfd); - - /* Get IP address of client. This is needed because we want to record - where the user logged in from. If the connection is not a socket, - let the ip address be 0.0.0.0. */ - memset(&from, 0, sizeof(from)); - if (packet_get_connection_in() == packet_get_connection_out()) - { - fromlen = sizeof(from); - if (getpeername(packet_get_connection_in(), - (struct sockaddr *)&from, &fromlen) < 0) { - debug("getpeername: %.100s", strerror(errno)); - fatal_cleanup(); - } + int pid, fdout; + const char *hostname; + time_t last_login_time; + char buf[100], *time_string; + FILE *f; + char line[256]; + struct stat st; + int quiet_login; + struct sockaddr_in from; + int fromlen; + struct pty_cleanup_context cleanup_context; + + /* Get remote host name. */ + hostname = get_canonical_hostname(); + + /* + * Get the time when the user last logged in. Buf will be set to + * contain the hostname the last login was from. + */ + if (!options.use_login) { + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + buf, sizeof(buf)); } + setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); + + /* Fork the child. */ + if ((pid = fork()) == 0) { + pid = getpid(); + + /* Child. Reinitialize the log because the pid has + changed. */ + log_init(av0, options.log_level, options.log_facility, log_stderr); + + /* Close the master side of the pseudo tty. */ + close(ptyfd); + + /* Make the pseudo tty our controlling tty. */ + pty_make_controlling_tty(&ttyfd, ttyname); + + /* Redirect stdin from the pseudo tty. */ + if (dup2(ttyfd, fileno(stdin)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stdout to the pseudo tty. */ + if (dup2(ttyfd, fileno(stdout)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stderr to the pseudo tty. */ + if (dup2(ttyfd, fileno(stderr)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Close the extra descriptor for the pseudo tty. */ + close(ttyfd); + + /* + * Get IP address of client. This is needed because we want + * to record where the user logged in from. If the + * connection is not a socket, let the ip address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + if (packet_get_connection_in() == packet_get_connection_out()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + /* Record that there was a login on that terminal. */ + record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, + &from); - /* Record that there was a login on that terminal. */ - record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, - &from); - - /* Check if .hushlogin exists. */ - snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); - quiet_login = stat(line, &st) >= 0; + /* 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); + /* output the results of the pamconv() */ + if (!quiet_login && pamconv_msg != NULL) + fprintf(stderr, pamconv_msg); #endif - - /* If the user has logged in before, display the time of last login. - However, don't display anything extra if a command has been - specified (so that ssh can be used to execute commands on a remote - machine without users knowing they are going to another machine). - Login(1) will do this for us as well, so check if login(1) is used */ - if (command == NULL && last_login_time != 0 && !quiet_login && - !options.use_login) - { - /* Convert the date to a string. */ - time_string = ctime(&last_login_time); - /* Remove the trailing newline. */ - if (strchr(time_string, '\n')) - *strchr(time_string, '\n') = 0; - /* Display the last login time. Host if displayed if known. */ - if (strcmp(buf, "") == 0) - printf("Last login: %s\r\n", time_string); - else - printf("Last login: %s from %s\r\n", time_string, buf); - } - /* Print /etc/motd unless a command was specified or printing it was - disabled in server options or login(1) will be used. Note that - some machines appear to print it in /etc/profile or similar. */ - if (command == NULL && options.print_motd && !quiet_login && - !options.use_login) - { - /* Print /etc/motd if it exists. */ - f = fopen("/etc/motd", "r"); - if (f) - { - while (fgets(line, sizeof(line), f)) - fputs(line, stdout); - fclose(f); - } + /* + * If the user has logged in before, display the time of last + * login. However, don't display anything extra if a command + * has been specified (so that ssh can be used to execute + * commands on a remote machine without users knowing they + * are going to another machine). Login(1) will do this for + * us as well, so check if login(1) is used + */ + if (command == NULL && last_login_time != 0 && !quiet_login && + !options.use_login) { + /* Convert the date to a string. */ + time_string = ctime(&last_login_time); + /* Remove the trailing newline. */ + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + /* Display the last login time. Host if displayed + if known. */ + if (strcmp(buf, "") == 0) + printf("Last login: %s\r\n", time_string); + else + printf("Last login: %s from %s\r\n", time_string, buf); + } + /* + * Print /etc/motd unless a command was specified or printing + * it was disabled in server options or login(1) will be + * used. Note that some machines appear to print it in + * /etc/profile or similar. + */ + if (command == NULL && options.print_motd && !quiet_login && + !options.use_login) { + /* Print /etc/motd if it exists. */ + f = fopen("/etc/motd", "r"); + if (f) { + while (fgets(line, sizeof(line), f)) + fputs(line, stdout); + fclose(f); + } + } + /* Do common processing for the child, such as execing the command. */ + do_child(command, pw, term, display, auth_proto, auth_data, ttyname); + /* NOTREACHED */ } - - /* Do common processing for the child, such as execing the command. */ - do_child(command, pw, term, display, auth_proto, auth_data, ttyname); - /*NOTREACHED*/ - } - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); - /* Parent. Close the slave side of the pseudo tty. */ - close(ttyfd); - - /* Create another descriptor of the pty master side for use as the standard - input. We could use the original descriptor, but this simplifies code - in server_loop. The descriptor is bidirectional. */ - fdout = dup(ptyfd); - if (fdout < 0) - packet_disconnect("dup failed: %.100s", strerror(errno)); - - /* Add a cleanup function to clear the utmp entry and record logout time - in case we call fatal() (e.g., the connection gets closed). */ - cleanup_context.pid = pid; - cleanup_context.ttyname = ttyname; - fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context); - - /* Enter interactive session. */ - server_loop(pid, ptyfd, fdout, -1); - /* server_loop has not closed ptyfd and fdout. */ - - /* Cancel the cleanup function. */ - fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context); - - /* Record that the user has logged out. */ - record_logout(pid, ttyname); - - /* Release the pseudo-tty. */ - pty_release(ttyname); - - /* Close the server side of the socket pairs. We must do this after the - pty cleanup, so that another process doesn't get this pty while we're - still cleaning up. */ - close(ptyfd); - close(fdout); + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + + /* + * Create another descriptor of the pty master side for use as the + * standard input. We could use the original descriptor, but this + * simplifies code in server_loop. The descriptor is bidirectional. + */ + fdout = dup(ptyfd); + if (fdout < 0) + packet_disconnect("dup failed: %.100s", strerror(errno)); + + /* + * Add a cleanup function to clear the utmp entry and record logout + * time in case we call fatal() (e.g., the connection gets closed). + */ + cleanup_context.pid = pid; + cleanup_context.ttyname = ttyname; + fatal_add_cleanup(pty_cleanup_proc, (void *) &cleanup_context); + + /* Enter interactive session. */ + server_loop(pid, ptyfd, fdout, -1); + /* server_loop has not closed ptyfd and fdout. */ + + /* Cancel the cleanup function. */ + fatal_remove_cleanup(pty_cleanup_proc, (void *) &cleanup_context); + + /* Record that the user has logged out. */ + record_logout(pid, ttyname); + + /* Release the pseudo-tty. */ + pty_release(ttyname); + + /* + * Close the server side of the socket pairs. We must do this after + * the pty cleanup, so that another process doesn't get this pty + * while we're still cleaning up. + */ + close(ptyfd); + close(fdout); } -/* Sets the value of the given variable in the environment. If the variable - already exists, its value is overriden. */ - -void child_set_env(char ***envp, unsigned int *envsizep, const char *name, - const char *value) +/* + * Sets the value of the given variable in the environment. If the variable + * already exists, its value is overriden. + */ +void +child_set_env(char ***envp, unsigned int *envsizep, const char *name, + const char *value) { - unsigned int i, namelen; - char **env; - - /* Find the slot where the value should be stored. If the variable already - exists, we reuse the slot; otherwise we append a new slot at the end - of the array, expanding if necessary. */ - env = *envp; - namelen = strlen(name); - for (i = 0; env[i]; i++) - if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') - break; - if (env[i]) - { - /* Name already exists. Reuse the slot. */ - xfree(env[i]); - } - else - { - /* New variable. Expand the array if necessary. */ - if (i >= (*envsizep) - 1) - { - (*envsizep) += 50; - env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); + unsigned int i, namelen; + char **env; + + /* + * Find the slot where the value should be stored. If the variable + * already exists, we reuse the slot; otherwise we append a new slot + * at the end of the array, expanding if necessary. + */ + env = *envp; + namelen = strlen(name); + for (i = 0; env[i]; i++) + if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') + break; + if (env[i]) { + /* Reuse the slot. */ + xfree(env[i]); + } else { + /* New variable. Expand if necessary. */ + if (i >= (*envsizep) - 1) { + (*envsizep) += 50; + env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); + } + /* Need to set the NULL pointer at end of array beyond the new slot. */ + env[i + 1] = NULL; } - /* Need to set the NULL pointer at end of array beyond the new - slot. */ - env[i + 1] = NULL; - } - - /* Allocate space and format the variable in the appropriate slot. */ - env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); - snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); + /* Allocate space and format the variable in the appropriate slot. */ + env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); + snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } -/* Reads environment variables from the given file and adds/overrides them - into the environment. If the file does not exist, this does nothing. - Otherwise, it must consist of empty lines, comments (line starts with '#') - and assignments of the form name=value. No other forms are allowed. */ - -void read_environment_file(char ***env, unsigned int *envsize, - const char *filename) +/* + * Reads environment variables from the given file and adds/overrides them + * into the environment. If the file does not exist, this does nothing. + * Otherwise, it must consist of empty lines, comments (line starts with '#') + * and assignments of the form name=value. No other forms are allowed. + */ +void +read_environment_file(char ***env, unsigned int *envsize, + const char *filename) { - FILE *f; - char buf[4096]; - char *cp, *value; - - /* Open the environment file. */ - f = fopen(filename, "r"); - if (!f) - return; /* Not found. */ - - /* Process each line. */ - while (fgets(buf, sizeof(buf), f)) - { - /* Skip leading whitespace. */ - for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Ignore empty and comment lines. */ - if (!*cp || *cp == '#' || *cp == '\n') - continue; - - /* Remove newline. */ - if (strchr(cp, '\n')) - *strchr(cp, '\n') = '\0'; - - /* Find the equals sign. Its lack indicates badly formatted line. */ - value = strchr(cp, '='); - if (value == NULL) - { - fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); - continue; + FILE *f; + char buf[4096]; + char *cp, *value; + + f = fopen(filename, "r"); + if (!f) + return; + + while (fgets(buf, sizeof(buf), f)) { + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') + continue; + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; + value = strchr(cp, '='); + if (value == NULL) { + fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); + continue; + } + /* Replace the equals sign by nul, and advance value to the value string. */ + *value = '\0'; + value++; + child_set_env(env, envsize, cp, value); } - - /* Replace the equals sign by nul, and advance value to the value - string. */ - *value = '\0'; - value++; - - /* Set the value in environment. */ - child_set_env(env, envsize, cp, value); - } - - fclose(f); + fclose(f); } -/* Performs common processing for the child, such as setting up the - environment, closing extra file descriptors, setting the user and group - ids, and executing the command or shell. */ - -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) +/* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group + * ids, and executing the command or shell. + */ +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) { - const char *shell, *cp = NULL; - char buf[256]; - FILE *f; - unsigned int envsize, i; - char **env; - extern char **environ; - struct stat st; - char *argv[10]; + const char *shell, *cp = NULL; + char buf[256]; + FILE *f; + unsigned int envsize, i; + char **env; + extern char **environ; + struct stat st; + char *argv[10]; #ifndef HAVE_LIBPAM /* pam_nologin handles this */ - /* Check /etc/nologin. */ - f = fopen("/etc/nologin", "r"); - if (f) - { /* /etc/nologin exists. Print its contents and exit. */ - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - if (pw->pw_uid != 0) - exit(254); - } + /* Check /etc/nologin. */ + f = fopen("/etc/nologin", "r"); + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + if (pw->pw_uid != 0) + exit(254); + } #endif /* HAVE_LIBPAM */ #ifdef HAVE_SETLOGIN - /* Set login name in the kernel. */ - if (setlogin(pw->pw_name) < 0) - error("setlogin failed: %s", strerror(errno)); + /* 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" switch, - so we let login(1) to this for us. */ - if(!options.use_login) { - if (getuid() == 0 || geteuid() == 0) - { - if (setgid(pw->pw_gid) < 0) - { - perror("setgid"); - exit(1); - } - /* Initialize the group list. */ - if (initgroups(pw->pw_name, pw->pw_gid) < 0) - { - perror("initgroups"); - exit(1); - } - endgrent(); - - /* Permanently switch to the desired uid. */ - permanently_set_uid(pw->pw_uid); - } - - if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) - fatal("Failed to set uids to %d.", (int)pw->pw_uid); - } - - /* Get the shell from the password data. An empty shell field is legal, - and means /bin/sh. */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + /* Set uid, gid, and groups. */ + /* Login(1) does this as well, and it needs uid 0 for the "-h" + switch, so we let login(1) to this for us. */ + if (!options.use_login) { + if (getuid() == 0 || geteuid() == 0) { + if (setgid(pw->pw_gid) < 0) { + perror("setgid"); + exit(1); + } + /* Initialize the group list. */ + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + perror("initgroups"); + exit(1); + } + endgrent(); + + /* Permanently switch to the desired uid. */ + permanently_set_uid(pw->pw_uid); + } + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %d.", (int) pw->pw_uid); + } + /* + * Get the shell from the password data. An empty shell field is + * legal, and means /bin/sh. + */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; #ifdef AFS - /* Try to get AFS tokens for the local cell. */ - if (k_hasafs()) { - char cell[64]; - - if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) - krb_afslog(cell, 0); - - krb_afslog(0, 0); - } -#endif /* AFS */ - - /* Initialize the environment. In the first part we allocate space for - all environment variables. */ - envsize = 100; - env = xmalloc(envsize * sizeof(char *)); - env[0] = NULL; - - if(!options.use_login) { - /* Set basic environment. */ - child_set_env(&env, &envsize, "USER", pw->pw_name); - child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); - child_set_env(&env, &envsize, "HOME", pw->pw_dir); - child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); - - snprintf(buf, sizeof buf, "%.200s/%.50s", - _PATH_MAILDIR, pw->pw_name); - child_set_env(&env, &envsize, "MAIL", buf); - - /* Normal systems set SHELL by default. */ - child_set_env(&env, &envsize, "SHELL", shell); - } - - /* Let it inherit timezone if we have one. */ - if (getenv("TZ")) - child_set_env(&env, &envsize, "TZ", getenv("TZ")); - - /* Set custom environment options from RSA authentication. */ - while (custom_environment) - { - struct envstring *ce = custom_environment; - char *s = ce->s; - int i; - for (i = 0; s[i] != '=' && s[i]; i++) - ; - if (s[i] == '=') - { - s[i] = 0; - child_set_env(&env, &envsize, s, s + i + 1); + /* Try to get AFS tokens for the local cell. */ + if (k_hasafs()) { + char cell[64]; + + if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) + krb_afslog(cell, 0); + + krb_afslog(0, 0); } - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); - } +#endif /* AFS */ + + /* Initialize the environment. */ + envsize = 100; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; + + if (!options.use_login) { + /* Set basic environment. */ + child_set_env(&env, &envsize, "USER", pw->pw_name); + child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); + child_set_env(&env, &envsize, "HOME", pw->pw_dir); + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); - /* Set SSH_CLIENT. */ - snprintf(buf, sizeof buf, "%.50s %d %d", - get_remote_ipaddr(), get_remote_port(), options.port); - child_set_env(&env, &envsize, "SSH_CLIENT", buf); + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); - /* Set SSH_TTY if we have a pty. */ - if (ttyname) - child_set_env(&env, &envsize, "SSH_TTY", ttyname); + /* Normal systems set SHELL by default. */ + child_set_env(&env, &envsize, "SHELL", shell); + } + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + + /* Set custom environment options from RSA authentication. */ + while (custom_environment) { + struct envstring *ce = custom_environment; + char *s = ce->s; + int i; + for (i = 0; s[i] != '=' && s[i]; i++); + if (s[i] == '=') { + s[i] = 0; + child_set_env(&env, &envsize, s, s + i + 1); + } + custom_environment = ce->next; + xfree(ce->s); + xfree(ce); + } - /* Set TERM if we have a pty. */ - if (term) - child_set_env(&env, &envsize, "TERM", term); + snprintf(buf, sizeof buf, "%.50s %d %d", + get_remote_ipaddr(), get_remote_port(), options.port); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); - /* Set DISPLAY if we have one. */ - if (display) - child_set_env(&env, &envsize, "DISPLAY", display); + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); + if (term) + child_set_env(&env, &envsize, "TERM", term); + if (display) + child_set_env(&env, &envsize, "DISPLAY", display); #ifdef KRB4 - { - extern char *ticket; - - if (ticket) - child_set_env(&env, &envsize, "KRBTKFILE", ticket); - } + { + extern char *ticket; + + if (ticket) + child_set_env(&env, &envsize, "KRBTKFILE", ticket); + } #endif /* KRB4 */ #ifdef HAVE_LIBPAM - /* Pull in any environment variables that may have been set by PAM. */ - { - char *equal_sign, var_name[256], var_val[256]; - long this_var; - char **pam_env = pam_getenvlist((pam_handle_t *)pamh); - for(this_var = 0; pam_env && pam_env[this_var]; this_var++) - { - if(strlen(pam_env[this_var]) < (sizeof(var_name) - 1)) - if((equal_sign = strstr(pam_env[this_var], "=")) != NULL) - { - memset(var_name, 0, sizeof(var_name)); - memset(var_val, 0, sizeof(var_val)); - strncpy(var_name, pam_env[this_var], - equal_sign - pam_env[this_var]); - strcpy(var_val, equal_sign + 1); - child_set_env(&env, &envsize, var_name, var_val); - } - } - } + /* 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 */ - /* Set XAUTHORITY to always be a local file. */ - if (xauthfile) - child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); - - /* Set variable for forwarded authentication connection, if we have one. */ - if (auth_get_socket_name() != NULL) - child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_get_socket_name()); - - /* Read $HOME/.ssh/environment. */ - if(!options.use_login) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); - read_environment_file(&env, &envsize, buf); - } - - /* If debugging, dump the environment to stderr. */ - if (debug_flag) - { - fprintf(stderr, "Environment:\n"); - for (i = 0; env[i]; i++) - fprintf(stderr, " %.200s\n", env[i]); - } - - /* Close the connection descriptors; note that this is the child, and the - server will still have the socket open, and it is important that we - do not shutdown it. Note that the descriptors cannot be closed before - building the environment, as we call get_remote_ipaddr there. */ - 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()); - } - /* Close all descriptors related to channels. They will still remain - open in the parent. */ - channel_close_all(); - - /* Close any extra file descriptors. Note that there may still be - 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 - hanging around in clients. Note that we want to do this after - initgroups, because at least on Solaris 2.3 it leaves file descriptors - open. */ - for (i = 3; i < 64; i++) - close(i); - - /* Change current directory to the user\'s home directory. */ - if (chdir(pw->pw_dir) < 0) - fprintf(stderr, "Could not chdir to home directory %s: %s\n", - pw->pw_dir, strerror(errno)); - - /* Must take new environment into use so that .ssh/rc, /etc/sshrc and - xauth are run in the proper environment. */ - environ = env; - - /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first - in this order). */ - if(!options.use_login) { - if (stat(SSH_USER_RC, &st) >= 0) - { - if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); - - f = popen("/bin/sh " SSH_USER_RC, "w"); - if (f) - { - if (auth_proto != NULL && auth_data != NULL) - fprintf(f, "%s %s\n", auth_proto, auth_data); - pclose(f); - } - else - fprintf(stderr, "Could not run %s\n", SSH_USER_RC); - } - else - if (stat(SSH_SYSTEM_RC, &st) >= 0) - { - if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); - - f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); - if (f) - { - if (auth_proto != NULL && auth_data != NULL) - fprintf(f, "%s %s\n", auth_proto, auth_data); - pclose(f); - } - else - fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); - } + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); + + if (auth_get_socket_name() != NULL) + child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, + auth_get_socket_name()); + + /* read $HOME/.ssh/environment. */ + if (!options.use_login) { + snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); + read_environment_file(&env, &envsize, buf); + } + if (debug_flag) { + /* dump the environment */ + fprintf(stderr, "Environment:\n"); + for (i = 0; env[i]; i++) + fprintf(stderr, " %.200s\n", env[i]); + } + /* + * Close the connection descriptors; note that this is the child, and + * the server will still have the socket open, and it is important + * that we do not shutdown it. Note that the descriptors cannot be + * closed before building the environment, as we call + * get_remote_ipaddr there. + */ + 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()); + } + /* + * Close all descriptors related to channels. They will still remain + * open in the parent. + */ + /* XXX better use close-on-exec? -markus */ + channel_close_all(); + + /* + * Close any extra file descriptors. Note that there may still be + * 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 + * hanging around in clients. Note that we want to do this after + * initgroups, because at least on Solaris 2.3 it leaves file + * descriptors open. + */ + for (i = 3; i < 64; i++) + close(i); + + /* Change current directory to the user\'s home directory. */ + if (chdir(pw->pw_dir) < 0) + fprintf(stderr, "Could not chdir to home directory %s: %s\n", + pw->pw_dir, strerror(errno)); + + /* + * Must take new environment into use so that .ssh/rc, /etc/sshrc and + * xauth are run in the proper environment. + */ + environ = env; + + /* + * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first + * in this order). + */ + if (!options.use_login) { + if (stat(SSH_USER_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); + + f = popen("/bin/sh " SSH_USER_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_USER_RC); + } else if (stat(SSH_SYSTEM_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); + + f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); + } #ifdef XAUTH_PATH - else - { - /* Add authority data to .Xauthority if appropriate. */ - if (auth_proto != NULL && auth_data != NULL) - { - if (debug_flag) - fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", - XAUTH_PATH, display, auth_proto, auth_data); - - f = popen(XAUTH_PATH " -q -", "w"); - if (f) - { - fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); - fclose(f); - } - else - fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); - } - } + else { + /* Add authority data to .Xauthority if appropriate. */ + if (auth_proto != NULL && auth_data != NULL) { + if (debug_flag) + fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", + XAUTH_PATH, display, auth_proto, auth_data); + + f = popen(XAUTH_PATH " -q -", "w"); + if (f) { + fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); + fclose(f); + } else + fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); + } + } #endif /* XAUTH_PATH */ - /* Get the last component of the shell name. */ - cp = strrchr(shell, '/'); - if (cp) - cp++; - else - cp = shell; - } - - /* If we have no command, execute the shell. In this case, the shell name - to be passed in argv[0] is preceded by '-' to indicate that this is - a login shell. */ - if (!command) - { - if(!options.use_login) { - char buf[256]; - - /* Check for mail if we have a tty and it was enabled in server options. */ - if (ttyname && options.check_mail) { - char *mailbox; - struct stat mailstat; - mailbox = getenv("MAIL"); - if(mailbox != NULL) { - if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) { - printf("No mail.\n"); - } else if(mailstat.st_mtime < mailstat.st_atime) { - printf("You have mail.\n"); - } else { - printf("You have new mail.\n"); - } - } - } - /* Start the shell. Set initial character to '-'. */ - buf[0] = '-'; - strncpy(buf + 1, cp, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = 0; - /* Execute the shell. */ - argv[0] = buf; - argv[1] = NULL; - execve(shell, argv, env); - /* Executing the shell failed. */ - perror(shell); - exit(1); - - } else { - /* Launch login(1). */ - - execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL); - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); - } - } - - /* Execute the command using the user's shell. This uses the -c option - to execute the command. */ - argv[0] = (char *)cp; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = NULL; - execve(shell, argv, env); - perror(shell); - exit(1); + /* Get the last component of the shell name. */ + cp = strrchr(shell, '/'); + if (cp) + cp++; + else + cp = shell; + } + /* + * If we have no command, execute the shell. In this case, the shell + * name to be passed in argv[0] is preceded by '-' to indicate that + * this is a login shell. + */ + if (!command) { + if (!options.use_login) { + char buf[256]; + + /* + * Check for mail if we have a tty and it was enabled + * in server options. + */ + if (ttyname && options.check_mail) { + char *mailbox; + struct stat mailstat; + mailbox = getenv("MAIL"); + if (mailbox != NULL) { + if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) + printf("No mail.\n"); + else if (mailstat.st_mtime < mailstat.st_atime) + printf("You have mail.\n"); + else + printf("You have new mail.\n"); + } + } + /* Start the shell. Set initial character to '-'. */ + buf[0] = '-'; + strncpy(buf + 1, cp, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = 0; + + /* Execute the shell. */ + argv[0] = buf; + argv[1] = NULL; + execve(shell, argv, env); + + /* Executing the shell failed. */ + perror(shell); + exit(1); + + } else { + /* Launch login(1). */ + + execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), + "-p", "-f", "--", pw->pw_name, NULL); + + /* Login couldn't be executed, die. */ + + perror("login"); + exit(1); + } + } + /* + * Execute the command using the user's shell. This uses the -c + * option to execute the command. + */ + argv[0] = (char *) cp; + argv[1] = "-c"; + argv[2] = (char *) command; + argv[3] = NULL; + execve(shell, argv, env); + perror(shell); + exit(1); }