=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshd.c,v retrieving revision 1.63 retrieving revision 1.64 diff -u -r1.63 -r1.64 --- src/usr.bin/ssh/sshd.c 1999/11/22 21:52:42 1.63 +++ src/usr.bin/ssh/sshd.c 1999/11/23 22:25:55 1.64 @@ -18,7 +18,7 @@ */ #include "includes.h" -RCSID("$Id: sshd.c,v 1.63 1999/11/22 21:52:42 markus Exp $"); +RCSID("$Id: sshd.c,v 1.64 1999/11/23 22:25:55 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -78,16 +78,18 @@ 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. */ +/* 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]; @@ -97,13 +99,9 @@ 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; +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 @@ -120,17 +118,17 @@ /* 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); @@ -138,85 +136,89 @@ the effect is to reread the configuration file (and to regenerate the server key). */ -void sighup_handler(int sig) +void +sighup_handler(int sig) { - received_sighup = 1; - signal(SIGHUP, sighup_handler); + received_sighup = 1; + signal(SIGHUP, sighup_handler); } -/* Called from the main program after receiving SIGHUP. Restarts the - server. */ +/* Called from the main program after receiving SIGHUP. Restarts the server. */ -void sighup_restart() +void +sighup_restart() { - 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); + 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); } -/* Generic signal handler for terminating signals in the master daemon. +/* 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) +void +sigterm_handler(int sig) { - log("Received signal %d; terminating.", sig); - close(listen_sock); - exit(255); + log("Received signal %d; terminating.", sig); + close(listen_sock); + exit(255); } -/* SIGCHLD handler. This is called whenever a child dies. This will then +/* 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) +void +main_sigchld_handler(int sig) { - int save_errno = errno; - int status; + int save_errno = errno; + int status; - while (waitpid(-1, &status, WNOHANG) > 0) - ; + while (waitpid(-1, &status, WNOHANG) > 0) + ; - signal(SIGCHLD, main_sigchld_handler); - errno = save_errno; + signal(SIGCHLD, main_sigchld_handler); + errno = save_errno; } /* Signal handler for the alarm after the login grace period has expired. */ -void grace_alarm_handler(int sig) +void +grace_alarm_handler(int sig) { - /* Close the connection. */ - packet_close(); - - /* Log error and exit. */ - fatal("Timeout before authentication for %s.", get_remote_ipaddr()); + /* Close the connection. */ + packet_close(); + + /* Log error and exit. */ + fatal("Timeout before authentication for %s.", get_remote_ipaddr()); } /* 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"; + 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"; + case SSH_CMSG_AUTH_KERBEROS: + return "kerberos"; #endif #ifdef SKEY - case SSH_CMSG_AUTH_TIS_RESPONSE: - return "s/key"; + case SSH_CMSG_AUTH_TIS_RESPONSE: + return "s/key"; #endif - } - fatal("get_authname: unknown auth %d: internal error", type); - return NULL; + } + fatal("get_authname: unknown auth %d: internal error", type); + return NULL; } /* Signal handler for the key regeneration alarm. Note that this @@ -224,35 +226,34 @@ 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) +void +key_regeneration_alarm(int sig) { - int save_errno = errno; + 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); + /* 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 (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(); + 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; + 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. */ @@ -260,513 +261,485 @@ 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. */ - const char *remote_ip; - int remote_port; - char *comment; - FILE *f; - struct linger linger; + 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. */ + 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]; + /* 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); + /* 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/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); + /* 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/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); + } } - } - /* 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); - } + /* 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); - /* 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); - /* 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); + } + /* 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); - /* 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); - } + debug("sshd version %.100s", SSH_VERSION); - /* Check that there are no remaining arguments. */ - if (optind < ac) - { - fprintf(stderr, "Extra argument %s.\n", av[optind]); - exit(1); - } + 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); - /* 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); + /* 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(); - 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) - { + /* 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); - /* 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); - /* 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); - } + /* Initialize the random number generator. */ + arc4random_stir(); - /* Do not display messages to stdout in RSA code. */ - rsa_set_verbose(0); + /* Chdir to the root directory so that the current disk can be + unmounted if desired. */ + chdir("/"); - /* 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; + /* 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); + /* 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)); + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); - /* 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)); + 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)); - /* 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); + /* 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)); - /* 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); - } + /* 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); - 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); - } - } + /* 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); + } + } - /* Start listening on the port. */ - log("Server listening on port %d.", options.port); - if (listen(listen_sock, 5) < 0) - fatal("listen: %.100s", strerror(errno)); + 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."); + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); + 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."); - /* 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); + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); - /* 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; - } + /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ + signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGQUIT, sigterm_handler); - /* 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; - } - } + /* Arrange SIGCHLD to be caught. */ + signal(SIGCHLD, main_sigchld_handler); - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %d.", pid); + /* 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; + } + } - /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + /* Parent. Stay in the loop. */ + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %d.", pid); - arc4random_stir(); + /* Mark that the key has been used (it was "given" to the child). */ + key_used = 1; - /* Close the new socket (the child is now taking care of it). */ - close(newsock); + arc4random_stir(); + + /* Close the new socket (the child is now taking care of it). */ + close(newsock); + } } - } - - /* 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); + /* This is the child processing a new connection. */ - /* 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)); + /* 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); - /* Register our connection. This turns encryption off because we do not - have a key. */ - packet_set_connection(sock_in, sock_out); + /* 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)); - remote_port = get_remote_port(); - remote_ip = get_remote_ipaddr(); + /* Register our connection. This turns encryption off because we + do not have a key. */ + packet_set_connection(sock_in, sock_out); - /* Check whether logins are denied from this host. */ + remote_port = get_remote_port(); + remote_ip = get_remote_ipaddr(); + + /* Check whether logins are denied from this host. */ #ifdef LIBWRAP - { - struct request_info req; + { + struct request_info req; - request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); - fromhost(&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); - } - verbose("Connection from %.500s port %d", eval_client(&req), remote_port); - } + if (!hosts_access(&req)) { + close(sock_in); + close(sock_out); + refuse(&req); + } + verbose("Connection from %.500s port %d", eval_client(&req), remote_port); + } #else - /* Log the connection. */ - verbose("Connection from %.500s port %d", remote_ip, 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); + /* 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 (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; - } - - /* 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' 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) - { - 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 for %s: %d vs. %d", - get_remote_ipaddr(), - PROTOCOL_MAJOR, remote_major); - } + 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 (write(sock_out, buf, strlen(buf)) != strlen(buf)) + fatal("Could not write ident string to %s.", get_remote_ipaddr()); - /* 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."); + /* 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 (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 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' 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) { + 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 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."); - /* 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; - } + 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(); - packet_set_nonblocking(); - - /* Handle the connection. */ - do_connection(); + /* 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. */ - verbose("Closing connection to %.100s", remote_ip); - packet_close(); - exit(0); + /* The connection has been terminated. */ + verbose("Closing connection to %.100s", remote_ip); + packet_close(); + exit(0); } /* Process an incoming connection. Protocol version identifiers have already @@ -776,201 +749,196 @@ 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; + 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]); + /* 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; + } - /* 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); + /* 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 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); + /* 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); - /* Put protocol flags. */ - packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); + /* 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); - /* Declare which ciphers we support. */ - packet_put_int(cipher_mask()); + /* Put protocol flags. */ + packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); - /* 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; + /* 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 #ifdef SKEY - if (options.skey_authentication == 1) - auth_mask |= 1 << SSH_AUTH_TIS; + 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); + 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(); + /* 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)); + 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); + /* 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 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."); + /* 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)); + 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 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); + /* 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); + 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); - } + /* 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); + } - 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 for this session. */ + compute_session_id(session_id, check_bytes, + sensitive_data.host_key->n, + sensitive_data.private_key->n); - /* Compute session id for this session. */ - 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); - /* 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]; + /* 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)); + /* Destroy the decrypted integer. It is no longer needed. */ + BN_clear_free(session_key_int); - debug("Received session key; encryption turned on."); + /* Set the session key. From this on all communications will be encrypted. */ + packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); - /* Send an acknowledgement packet. Note that this packet is sent - encrypted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); + /* Destroy our copy of the session key. It is no longer needed. */ + memset(session_key, 0, sizeof(session_key)); - /* Get the name of the user that we wish to log in as. */ - packet_read_expect(&plen, SSH_CMSG_USER); + debug("Received session key; encryption turned on."); - /* Get the user name. */ - { - int ulen; - user = packet_get_string(&ulen); - packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); - } + /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); - /* 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); + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(&plen, SSH_CMSG_USER); - setproctitle("%s", user); - /* Do the authentication. */ - do_authentication(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); } /* Check if the user is allowed to log in via ssh. If user is listed in @@ -981,74 +949,67 @@ 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; + struct group *grp; + int i; - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw) - return 0; + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (!pw) + return 0; - /* XXX Should check for valid login shell */ + /* 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 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 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; + /* 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 @@ -1058,71 +1019,69 @@ 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; - /* 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."); + /* Verify that the user is a valid user. */ + pw = getpwnam(user); + if (!pw || !allowed_user(pw)) + do_fake_authloop(user); - debug("Attempting authentication for %.100s.", 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; - /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && + /* 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); + + /* 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. */ - 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); - } + 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()); - } + /* 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(); - /* The user has been authenticated and accepted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - /* Perform session preparation. */ - do_authenticated(pw); + /* Perform session preparation. */ + do_authenticated(pw); } #define AUTH_FAIL_MAX 6 @@ -1132,255 +1091,243 @@ /* 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 attempt = 0; - unsigned int bits; - BIGNUM *client_host_key_e, *client_host_key_n; - BIGNUM *n; - char *client_user, *password; - char user[1024]; - int plen, dlen, nlen, ulen, elen; - int type = 0; - void (*authlog)(const char *fmt, ...) = verbose; + int attempt = 0; + unsigned int bits; + BIGNUM *client_host_key_e, *client_host_key_n; + BIGNUM *n; + char *client_user, *password; + char user[1024]; + int plen, dlen, nlen, ulen, elen; + int type = 0; + void (*authlog) (const char *fmt,...) = verbose; - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); + /* 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); + 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) - { + /* 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(); */ - 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; + 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(); */ - 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); + 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 (authenticated) { - snprintf(user, sizeof user, " tktuser %s", tkt_user); - xfree(tkt_user); - } - } - break; + 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) - { - 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); - xfree(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); + 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); - 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); + /* Try to authenticate using /etc/hosts.equiv and + .rhosts. */ + authenticated = auth_rhosts(pw, client_user); - snprintf(user, sizeof user, " ruser %s", client_user); - xfree(client_user); - 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); - - /* Try authentication with the password. */ - authenticated = auth_password(pw, password); + snprintf(user, sizeof user, " ruser %s", client_user); + xfree(client_user); + break; - memset(password, 0, strlen(password)); - xfree(password); - 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); + xfree(client_user); + 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); + + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); + + memset(password, 0, strlen(password)); + xfree(password); + break; + #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; + 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; + 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; - } - /* Raise logging level */ - if (authenticated || - attempt == AUTH_FAIL_LOG || - type == SSH_CMSG_AUTH_PASSWORD) - authlog = log; + default: + /* Any unknown messages will be ignored (and + failure returned) during authentication. */ + log("Unknown message during authentication: type %d", type); + break; + } - 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); + /* Raise logging level */ + if (authenticated || + attempt == AUTH_FAIL_LOG || + type == SSH_CMSG_AUTH_PASSWORD) + authlog = log; - if (authenticated) - return; + 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); - if (attempt > AUTH_FAIL_MAX) - packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); + if (authenticated) + return; - /* Send a message indicating that the authentication attempt failed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); + + /* 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, @@ -1388,50 +1335,50 @@ void do_fake_authloop(char *user) { - int attempt = 0; + int attempt = 0; - log("Faking authloop for illegal user %.200s from %.200s port %d", - user, - get_remote_ipaddr(), - get_remote_port()); + 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(); + /* 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); + /* 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 dlen; - char *password, *skeyinfo; - if (options.password_authentication && - options.skey_authentication == 1 && - type == SSH_CMSG_AUTH_PASSWORD && - (password = packet_get_string(&dlen)) != NULL && - dlen == 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; + if (options.password_authentication && + options.skey_authentication == 1 && + type == SSH_CMSG_AUTH_PASSWORD && + (password = packet_get_string(&dlen)) != NULL && + dlen == 5 && + strncasecmp(password, "s/key", 5) == 0 && + (skeyinfo = skey_fake_keyinfo(user)) != NULL) { + /* Send a fake s/key challenge. */ + packet_send_debug(skeyinfo); + } #endif - if (attempt > AUTH_FAIL_MAX) - packet_disconnect(AUTH_FAIL_MSG, user); + 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(); + /* Send failure. This should be indistinguishable from a + failed authentication. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } + /* NOTREACHED */ + abort(); } @@ -1439,13 +1386,13 @@ 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 @@ -1453,390 +1400,372 @@ terminals are allocated, X11, TCP/IP, and authentication agent forwardings are requested, etc. */ -void do_authenticated(struct passwd *pw) +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); + 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; - /* 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(); + /* Cancel the alarm we set to limit the time taken for + authentication. */ + alarm(0); - /* We stay in this loop until the client requests to execute a shell or a - command. */ - while (1) - { - int plen, dlen; + /* 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(); - /* 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; + /* We stay in this loop until the client requests to execute a + shell or a command. */ + while (1) { + int plen, dlen; - 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."); + /* Get a packet from the client. */ + type = packet_read(&plen); - debug("Allocating pty."); + /* 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; - /* Allocate a pty and open it. */ - if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) - { - error("Failed to allocate pty."); - goto fail; - } + 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."); - /* 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; - } + debug("Allocating pty."); - /* 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)); + /* 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; + } - /* Get TERM from the packet. Note that the value may be of arbitrary - length. */ + /* 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)); - 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 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); - /* 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); + if (strcmp(term, "") == 0) + term = NULL; - /* Get tty modes from the packet. */ - tty_parse_modes(ttyfd, &n_bytes); - packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); + /* 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); - /* Indicate that we now have a pty. */ - have_pty = 1; - break; + /* Get tty modes from the packet. */ + tty_parse_modes(ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); - case SSH_CMSG_X11_REQUEST_FORWARDING: - if (!options.x11_forwarding) - { - packet_send_debug("X11 forwarding disabled in server configuration file."); - goto fail; - } + /* 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; + } #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; + 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; + /* 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_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_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_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_SHELL: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); - 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; + 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; - 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_EXEC_CMD: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || display != NULL, + options.keepalives); - 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; - } + 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; - /* The request was successfully processed. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); + 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; + } - /* Enable compression now that we have replied if appropriate. */ - if (enable_compression_after_reply) - { - enable_compression_after_reply = 0; - packet_start_compression(compression_level); - } + /* The request was successfully processed. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); - continue; + /* 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; - 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; + +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; + } } /* 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; +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); - /* 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)); + /* 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)); + #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 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 stderr. */ - close(perr[0]); - if (dup2(perr[1], 2) < 0) - perror("dup2 stderr"); - close(perr[1]); + /* 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]); + /* 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]. */ + /* 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) +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 @@ -1844,203 +1773,194 @@ 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) +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; + 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 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)); - } + /* 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); - setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); + /* Fork the child. */ + if ((pid = fork()) == 0) { + pid = getpid(); - /* 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); - /* 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); - /* Close the master side of the pseudo tty. */ - close(ptyfd); + /* Make the pseudo tty our controlling tty. */ + pty_make_controlling_tty(&ttyfd, ttyname); - /* 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 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 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)); - /* 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); - /* 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); - /* 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(); - } - } + /* Check if .hushlogin exists. */ + snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); + quiet_login = stat(line, &st) >= 0; - /* 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; - - /* 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); + /* 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 */ } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); - /* 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); - } - } + /* 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)); - /* 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); - /* 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. */ - /* 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); - /* Cancel the cleanup function. */ - fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context); + /* Record that the user has logged out. */ + record_logout(pid, ttyname); - /* Record that the user has logged out. */ - record_logout(pid, ttyname); + /* Release the pseudo-tty. */ + pty_release(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); + /* 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) +void +child_set_env(char ***envp, unsigned int *envsizep, const char *name, + const char *value) { - unsigned int i, namelen; - char **env; + 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 *)); + /* 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 *)); + } + /* 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 @@ -2048,374 +1968,351 @@ 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) +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++) - ; + FILE *f; + char buf[4096]; + char *cp, *value; - /* Ignore empty and comment lines. */ - if (!*cp || *cp == '#' || *cp == '\n') - continue; + /* Open the environment file. */ + f = fopen(filename, "r"); + if (!f) + return; - /* Remove newline. */ - if (strchr(cp, '\n')) - *strchr(cp, '\n') = '\0'; + /* Process each line. */ + while (fgets(buf, sizeof(buf), f)) { + /* Skip leading whitespace. */ + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++); - /* 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; - } + /* Ignore empty and comment lines. */ + if (!*cp || *cp == '#' || *cp == '\n') + continue; - /* Replace the equals sign by nul, and advance value to the value - string. */ - *value = '\0'; - value++; + /* Remove newline. */ + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; - /* Set the value in environment. */ - child_set_env(env, envsize, cp, value); - } - - fclose(f); + /* 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; + } + /* 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); } -/* Performs common processing for the child, such as setting up the - environment, closing extra file descriptors, setting the user and group +/* 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) +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]; - /* 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); + } + /* 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)); + /* 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(); - /* 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); - } + /* 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; - /* 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); + /* Try to get AFS tokens for the local cell. */ + if (k_hasafs()) { + char cell[64]; - krb_afslog(0, 0); - } + 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); - } + /* Initialize the environment. In the first part we allocate + space for all environment variables. */ + envsize = 100; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; - /* Let it inherit timezone if we have one. */ - if (getenv("TZ")) - child_set_env(&env, &envsize, "TZ", getenv("TZ")); + 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 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); + 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); } - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); - } + /* Let it inherit timezone if we have one. */ + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); - /* 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); + /* 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 SSH_TTY if we have a pty. */ - if (ttyname) - child_set_env(&env, &envsize, "SSH_TTY", ttyname); + /* 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); - /* Set TERM if we have a pty. */ - if (term) - child_set_env(&env, &envsize, "TERM", term); + /* Set SSH_TTY if we have a pty. */ + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); - /* Set DISPLAY if we have one. */ - if (display) - child_set_env(&env, &envsize, "DISPLAY", display); + /* Set TERM if we have a pty. */ + if (term) + child_set_env(&env, &envsize, "TERM", term); + /* Set DISPLAY if we have one. */ + 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 */ - - /* 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()); + /* Set XAUTHORITY to always be a local file. */ + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); - /* Read $HOME/.ssh/environment. */ - if(!options.use_login) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); - read_environment_file(&env, &envsize, buf); - } + /* 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()); - /* 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]); - } + /* 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 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 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); - /* 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)); - /* 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; - /* 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); - /* 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); - } + 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; - } + /* 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]; - /* 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; - /* 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); + /* Execute the shell. */ + argv[0] = buf; + argv[1] = NULL; + execve(shell, argv, env); - } else { - /* Launch login(1). */ + /* Executing the shell failed. */ + perror(shell); + exit(1); - execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL); + } else { + /* Launch login(1). */ - /* Login couldn't be executed, die. */ + execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), + "-p", "-f", "--", pw->pw_name, NULL); - perror("login"); - exit(1); - } - } + /* Login couldn't be executed, die. */ - /* 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); + 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); }