X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/1d1ffb876ba515d13d488979fc6c752c1289aebb..a22aff1fb16cbb68775742f7b60c5bfa3f72c903:/session.c diff --git a/session.c b/session.c index c490f087..4b58131a 100644 --- a/session.c +++ b/session.c @@ -1,21 +1,45 @@ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved - */ -/* + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * * SSH2 support by Markus Friedl. * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.12 2000/05/03 18:03:07 markus Exp $"); +RCSID("$OpenBSD: session.c,v 1.42 2000/10/27 07:32:18 markus Exp $"); #include "xmalloc.h" #include "ssh.h" #include "pty.h" #include "packet.h" #include "buffer.h" -#include "cipher.h" #include "mpaux.h" #include "servconf.h" #include "uidswap.h" @@ -26,6 +50,41 @@ RCSID("$OpenBSD: session.c,v 1.12 2000/05/03 18:03:07 markus Exp $"); #include "bufaux.h" #include "ssh2.h" #include "auth.h" +#include "auth-options.h" + +#ifdef WITH_IRIX_PROJECT +#include +#endif /* WITH_IRIX_PROJECT */ + +#if defined(HAVE_USERSEC_H) +#include +#endif + +#ifdef HAVE_OSF_SIA +# include +# include +#endif + +#ifdef HAVE_CYGWIN +#include +#include +#define is_winnt (GetVersion() < 0x80000000) +#endif + +/* AIX limits */ +#if defined(HAVE_GETUSERATTR) && !defined(S_UFSIZE_HARD) && defined(S_UFSIZE) +# define S_UFSIZE_HARD S_UFSIZE "_hard" +# define S_UCPU_HARD S_UCPU "_hard" +# define S_UDATA_HARD S_UDATA "_hard" +# define S_USTACK_HARD S_USTACK "_hard" +# define S_URSS_HARD S_URSS "_hard" +# define S_UCORE_HARD S_UCORE "_hard" +# define S_UNOFILE_HARD S_UNOFILE "_hard" +#endif + +#ifdef HAVE_LOGIN_CAP +#include +#endif /* types */ @@ -60,6 +119,7 @@ void session_pty_cleanup(Session *s); void session_proctitle(Session *s); void do_exec_pty(Session *s, const char *command, struct passwd * pw); void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); +void do_login(Session *s, const char *command); void do_child(const char *command, struct passwd * pw, const char *term, @@ -76,25 +136,28 @@ static const char *__progname = "sshd"; extern int log_stderr; extern int debug_flag; +extern unsigned int utmp_len; + +extern int startup_pipe; /* Local Xauthority file. */ static char *xauthfile; +/* original command from peer. */ +char *original_command = NULL; + /* data */ #define MAX_SESSIONS 10 Session sessions[MAX_SESSIONS]; -/* 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; - -/* RSA authentication "command=" option. */ -char *forced_command = NULL; +#ifdef WITH_AIXAUTHENTICATE +/* AIX's lastlogin message, set in auth1.c */ +char *aixloginmsg; +#endif /* WITH_AIXAUTHENTICATE */ -/* RSA authentication "environment=" options. */ -struct envstring *custom_environment = NULL; +#ifdef HAVE_LOGIN_CAP +static login_cap_t *lc; +#endif /* * Remove local Xauthority file. @@ -148,7 +211,7 @@ void do_authenticated(struct passwd * pw) { Session *s; - int type; + int type, fd; int compression_level = 0, enable_compression_after_reply = 0; int have_pty = 0; char *command; @@ -161,6 +224,10 @@ do_authenticated(struct passwd * pw) * authentication. */ alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } /* * Inform the channel mechanism that we are the server side and that @@ -169,12 +236,19 @@ do_authenticated(struct passwd * pw) * by the client telling us, so we can equally well trust the client * not to request anything bogus.) */ - if (!no_port_forwarding_flag) + if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); s = session_new(); s->pw = pw; +#if defined(HAVE_LOGIN_CAP) && defined(HAVE_PW_CLASS_IN_PASSWD) + if ((lc = login_getclass(pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#endif + /* * We stay in this loop until the client requests to execute a shell * or a command. @@ -253,7 +327,10 @@ do_authenticated(struct passwd * pw) packet_send_debug("X11 forwarding disabled in server configuration file."); break; } -#ifdef XAUTH_PATH + if (!options.xauth_location) { + packet_send_debug("No xauth program; cannot forward with spoofing."); + break; + } if (no_x11_forwarding_flag) { packet_send_debug("X11 forwarding not permitted for this authentication."); break; @@ -289,15 +366,13 @@ do_authenticated(struct passwd * pw) break; } strlcat(xauthfile, "/cookies", MAXPATHLEN); - open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); restore_uid(); fatal_add_cleanup(xauthfile_cleanup_proc, NULL); success = 1; break; -#else /* XAUTH_PATH */ - packet_send_debug("No xauth program; cannot forward with spoofing."); - break; -#endif /* XAUTH_PATH */ case SSH_CMSG_AGENT_REQUEST_FORWARDING: if (no_agent_forwarding_flag || compat13) { @@ -305,8 +380,7 @@ do_authenticated(struct passwd * pw) break; } debug("Received authentication agent forwarding request."); - auth_input_request_forwarding(pw); - success = 1; + success = auth_input_request_forwarding(pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: @@ -314,6 +388,10 @@ do_authenticated(struct passwd * pw) debug("Port forwarding not permitted for this authentication."); break; } + if (!options.allow_tcp_forwarding) { + debug("Port forwarding not permitted."); + break; + } debug("Received TCP/IP port forwarding request."); channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); success = 1; @@ -339,6 +417,7 @@ do_authenticated(struct passwd * pw) packet_integrity_check(plen, 0, type); } if (forced_command != NULL) { + original_command = command; command = forced_command; debug("Forced command '%.500s'", forced_command); } @@ -400,6 +479,8 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) if (s == NULL) fatal("do_exec_no_pty: no session"); + signal(SIGPIPE, SIG_DFL); + session_proctitle(s); #ifdef USE_PAM @@ -459,6 +540,10 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL); /* NOTREACHED */ } +#ifdef HAVE_CYGWIN + if (is_winnt) + cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); +#endif if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; @@ -502,35 +587,14 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) void do_exec_pty(Session *s, const char *command, struct passwd * pw) { - FILE *f; - char buf[100], *time_string; - char line[256]; - const char *hostname; int fdout, ptyfd, ttyfd, ptymaster; - int quiet_login; pid_t pid; - socklen_t fromlen; - struct sockaddr_storage from; - struct stat st; - time_t last_login_time; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; - /* 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)); - } - #ifdef USE_PAM do_pam_session(pw->pw_name, s->tty); do_pam_setcred(); @@ -538,10 +602,7 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) /* Fork the child. */ if ((pid = fork()) == 0) { - pid = getpid(); - - /* Child. Reinitialize the log because the pid has - changed. */ + /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ @@ -565,76 +626,19 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); -/* XXXX ? move to do_child() ??*/ - /* - * 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_connection_is_on_socket()) { - 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, s->tty, pw->pw_name, pw->pw_uid, hostname, - (struct sockaddr *)&from); - - /* Check if .hushlogin exists. */ - snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); - quiet_login = stat(line, &st) >= 0; + /* record login, etc. similar to login(1) */ + if (!(options.use_login && command == NULL)) + do_login(s, command); -#ifdef USE_PAM - if (!quiet_login) - print_pam_messages(); -#endif /* USE_PAM */ - - /* - * 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, s->term, s->display, s->auth_proto, s->auth_data, s->tty); + do_child(command, pw, s->term, s->display, s->auth_proto, + s->auth_data, s->tty); /* NOTREACHED */ } +#ifdef HAVE_CYGWIN + if (is_winnt) + cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); +#endif if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; @@ -667,6 +671,110 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) } } +const char * +get_remote_name_or_ip(void) +{ + static const char *remote = ""; + if (utmp_len > 0) + remote = get_canonical_hostname(); + if (utmp_len == 0 || strlen(remote) > utmp_len) + remote = get_remote_ipaddr(); + return remote; +} + +/* administrative, login(1)-like work */ +void +do_login(Session *s, const char *command) +{ + FILE *f; + char *time_string; + char buf[256]; + char hostname[MAXHOSTNAMELEN]; + socklen_t fromlen; + struct sockaddr_storage from; + struct stat st; + time_t last_login_time; + struct passwd * pw = s->pw; + pid_t pid = getpid(); + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + if (packet_connection_is_on_socket()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + + /* Get the time and hostname when the user last logged in. */ + hostname[0] = '\0'; + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + hostname, sizeof(hostname)); + + /* Record that there was a login on that tty from the remote host. */ + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, + get_remote_name_or_ip(), (struct sockaddr *)&from); + +#ifdef USE_PAM + /* + * If password change is needed, do it now. + * This needs to occur before the ~/.hushlogin check. + */ + if (pam_password_change_required()) { + print_pam_messages(); + do_pam_chauthtok(); + } +#endif + + /* Done if .hushlogin exists or a command given. */ + if (command != NULL) + return; + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) +#else + if (stat(buf, &st) >= 0) +#endif + return; + +#ifdef USE_PAM + if (!pam_password_change_required()) + print_pam_messages(); +#endif /* USE_PAM */ +#ifdef WITH_AIXAUTHENTICATE + if (aixloginmsg && *aixloginmsg) + printf("%s\n", aixloginmsg); +#endif /* WITH_AIXAUTHENTICATE */ + + if (last_login_time != 0) { + time_string = ctime(&last_login_time); + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + if (strcmp(hostname, "") == 0) + printf("Last login: %s\r\n", time_string); + else + printf("Last login: %s from %s\r\n", time_string, hostname); + } + if (options.print_motd) { +#ifdef HAVE_LOGIN_CAP + f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", + "/etc/motd"), "r"); +#else + f = fopen("/etc/motd", "r"); +#endif + if (f) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stdout); + fclose(f); + } + } +} + /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. @@ -736,7 +844,10 @@ read_environment_file(char ***env, unsigned int *envsize, fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); continue; } - /* Replace the equals sign by nul, and advance value to the value string. */ + /* + * Replace the equals sign by nul, and advance value to + * the value string. + */ *value = '\0'; value++; child_set_env(env, envsize, cp, value); @@ -768,7 +879,7 @@ void do_pam_environment(char ***env, int *envsize) strncpy(var_name, pam_env[i], equals - pam_env[i]); strcpy(var_val, equals + 1); - debug("PAM environment: %s=%s", var_name, var_val); + debug3("PAM environment: %s=%s", var_name, var_val); child_set_env(env, envsize, var_name, var_val); } @@ -776,6 +887,111 @@ void do_pam_environment(char ***env, int *envsize) } #endif /* USE_PAM */ + +#ifdef HAVE_CYGWIN +void copy_environment(char ***env, int *envsize) +{ + char *equals, var_name[512], var_val[512]; + int i; + + for(i = 0; environ[i] != NULL; i++) { + if ((equals = strstr(environ[i], "=")) == NULL) + continue; + + if (strlen(environ[i]) < (sizeof(var_name) - 1)) { + memset(var_name, '\0', sizeof(var_name)); + memset(var_val, '\0', sizeof(var_val)); + + strncpy(var_name, environ[i], equals - environ[i]); + strcpy(var_val, equals + 1); + + debug3("Copy environment: %s=%s", var_name, var_val); + + child_set_env(env, envsize, var_name, var_val); + } + } +} +#endif + +#if defined(HAVE_GETUSERATTR) +/* + * AIX-specific login initialisation + */ +void set_limit(char *user, char *soft, char *hard, int resource, int mult) +{ + struct rlimit rlim; + int slim, hlim; + + getrlimit(resource, &rlim); + + slim = 0; + if (getuserattr(user, soft, &slim, SEC_INT) != -1) { + if (slim < 0) { + rlim.rlim_cur = RLIM_INFINITY; + } else if (slim != 0) { + /* See the wackiness below */ + if (rlim.rlim_cur == slim * mult) + slim = 0; + else + rlim.rlim_cur = slim * mult; + } + } + + hlim = 0; + if (getuserattr(user, hard, &hlim, SEC_INT) != -1) { + if (hlim < 0) { + rlim.rlim_max = RLIM_INFINITY; + } else if (hlim != 0) { + rlim.rlim_max = hlim * mult; + } + } + + /* + * XXX For cpu and fsize the soft limit is set to the hard limit + * if the hard limit is left at its default value and the soft limit + * is changed from its default value, either by requesting it + * (slim == 0) or by setting it to the current default. At least + * that's how rlogind does it. If you're confused you're not alone. + * Bug or feature? AIX 4.3.1.2 + */ + if ((!strcmp(soft, "fsize") || !strcmp(soft, "cpu")) + && hlim == 0 && slim != 0) + rlim.rlim_max = rlim.rlim_cur; + /* A specified hard limit limits the soft limit */ + else if (hlim > 0 && rlim.rlim_cur > rlim.rlim_max) + rlim.rlim_cur = rlim.rlim_max; + /* A soft limit can increase a hard limit */ + else if (rlim.rlim_cur > rlim.rlim_max) + rlim.rlim_max = rlim.rlim_cur; + + if (setrlimit(resource, &rlim) != 0) + error("setrlimit(%.10s) failed: %.100s", soft, strerror(errno)); +} + +void set_limits_from_userattr(char *user) +{ + int mask; + char buf[16]; + + set_limit(user, S_UFSIZE, S_UFSIZE_HARD, RLIMIT_FSIZE, 512); + set_limit(user, S_UCPU, S_UCPU_HARD, RLIMIT_CPU, 1); + set_limit(user, S_UDATA, S_UDATA_HARD, RLIMIT_DATA, 512); + set_limit(user, S_USTACK, S_USTACK_HARD, RLIMIT_STACK, 512); + set_limit(user, S_URSS, S_URSS_HARD, RLIMIT_RSS, 512); + set_limit(user, S_UCORE, S_UCORE_HARD, RLIMIT_CORE, 512); +#if defined(S_UNOFILE) + set_limit(user, S_UNOFILE, S_UNOFILE_HARD, RLIMIT_NOFILE, 1); +#endif + + if (getuserattr(user, S_UMASK, &mask, SEC_INT) != -1) { + /* Convert decimal to octal */ + (void) snprintf(buf, sizeof(buf), "%d", mask); + if (sscanf(buf, "%o", &mask) == 1) + umask(mask); + } +} +#endif /* defined(HAVE_GETUSERATTR) */ + /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group @@ -786,36 +1002,80 @@ 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; + const char *shell, *hostname = NULL, *cp = NULL; char buf[256]; - FILE *f; + char cmd[1024]; + FILE *f = NULL; unsigned int envsize, i; char **env; extern char **environ; struct stat st; char *argv[10]; +#ifdef WITH_IRIX_PROJECT + prid_t projid; +#endif /* WITH_IRIX_PROJECT */ + + /* login(1) is only called if we execute the login shell */ + if (options.use_login && command != NULL) + options.use_login = 0; #ifndef USE_PAM /* pam_nologin handles this */ - 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) + if (!options.use_login) { +# ifdef HAVE_LOGIN_CAP + if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, + _PATH_NOLOGIN), "r"); +# else /* HAVE_LOGIN_CAP */ + if (pw->pw_uid) + f = fopen(_PATH_NOLOGIN, "r"); +# endif /* HAVE_LOGIN_CAP */ + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); exit(254); + } } #endif /* USE_PAM */ - /* Set login name in the kernel. */ - if (setlogin(pw->pw_name) < 0) - error("setlogin failed: %s", strerror(errno)); - - /* Set uid, gid, and groups. */ + /* Set login name, 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) { +#ifdef HAVE_OSF_SIA + extern char **saved_argv; + extern int saved_argc; + char *host = get_canonical_hostname (); + + if (sia_become_user(NULL, saved_argc, saved_argv, host, + pw->pw_name, ttyname, 0, NULL, NULL, SIA_BEU_SETLUID) != + SIASUCCESS) { + perror("sia_become_user"); + exit(1); + } + if (setreuid(geteuid(), geteuid()) < 0) { + perror("setreuid"); + exit(1); + } +#else /* HAVE_OSF_SIA */ +#ifdef HAVE_CYGWIN + if (is_winnt) { +#else if (getuid() == 0 || geteuid() == 0) { +#endif +# ifdef HAVE_GETUSERATTR + set_limits_from_userattr(pw->pw_name); +# endif /* HAVE_GETUSERATTR */ +# ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { + perror("unable to set user context"); + exit(1); + } +# else /* HAVE_LOGIN_CAP */ + if (setlogin(pw->pw_name) < 0) + error("setlogin failed: %s", strerror(errno)); if (setgid(pw->pw_gid) < 0) { perror("setgid"); exit(1); @@ -826,18 +1086,42 @@ do_child(const char *command, struct passwd * pw, const char *term, exit(1); } endgrent(); - +# ifdef WITH_IRIX_ARRAY + /* initialize array session */ + if (newarraysess() != 0) + fatal("Failed to set up new array session: %.100s", + strerror(errno)); +# endif /* WITH_IRIX_ARRAY */ +# ifdef WITH_IRIX_PROJECT + /* initialize irix project info */ + if ((projid = getdfltprojuser(pw->pw_name)) == -1) { + debug("Failed to get project id, using projid 0"); + projid = 0; + } + if (setprid(projid)) + fatal("Failed to initialize project %d for %s: %.100s", + (int)projid, pw->pw_name, strerror(errno)); +# endif /* WITH_IRIX_PROJECT */ /* Permanently switch to the desired uid. */ permanently_set_uid(pw->pw_uid); +# endif /* HAVE_LOGIN_CAP */ } +#endif /* HAVE_OSF_SIA */ + +#ifdef HAVE_CYGWIN + if (is_winnt) +#endif if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) - fatal("Failed to set uids to %d.", (int) pw->pw_uid); + fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); } /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; +#ifdef HAVE_LOGIN_CAP + shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); +#endif #ifdef AFS /* Try to get AFS tokens for the local cell. */ @@ -856,12 +1140,33 @@ do_child(const char *command, struct passwd * pw, const char *term, env = xmalloc(envsize * sizeof(char *)); env[0] = NULL; +#ifdef HAVE_CYGWIN + /* + * The Windows environment contains some setting which are + * important for a running system. They must not be dropped. + */ + copy_environment(&env, &envsize); +#endif + 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); +#ifdef HAVE_LOGIN_CAP + (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH); + child_set_env(&env, &envsize, "PATH", getenv("PATH")); +#else /* HAVE_LOGIN_CAP */ +# ifndef HAVE_CYGWIN + /* + * There's no standard path on Windows. The path contains + * important components pointing to the system directories, + * needed for loading shared libraries. So the path better + * remains intact here. + */ child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); +# endif /* HAVE_CYGWIN */ +#endif /* HAVE_LOGIN_CAP */ snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); @@ -898,17 +1203,16 @@ do_child(const char *command, struct passwd * pw, const char *term, child_set_env(&env, &envsize, "TERM", term); if (display) child_set_env(&env, &envsize, "DISPLAY", display); + if (original_command) + child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", + original_command); #ifdef _AIX - { - char *authstate,*krb5cc; - - if ((authstate = getenv("AUTHSTATE")) != NULL) - child_set_env(&env,&envsize,"AUTHSTATE",authstate); - - if ((krb5cc = getenv("KRB5CCNAME")) != NULL) - child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc); - } + if ((cp = getenv("AUTHSTATE")) != NULL) + child_set_env(&env, &envsize, "AUTHSTATE", cp); + if ((cp = getenv("KRB5CCNAME")) != NULL) + child_set_env(&env, &envsize, "KRB5CCNAME", cp); + read_environment_file(&env, &envsize, "/etc/environment"); #endif #ifdef KRB4 @@ -925,8 +1229,6 @@ do_child(const char *command, struct passwd * pw, const char *term, do_pam_environment(&env, &envsize); #endif /* USE_PAM */ - read_environment_file(&env,&envsize,"/etc/environment"); - if (xauthfile) child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); if (auth_get_socket_name() != NULL) @@ -935,7 +1237,8 @@ do_child(const char *command, struct passwd * pw, const char *term, /* read $HOME/.ssh/environment. */ if (!options.use_login) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); + snprintf(buf, sizeof buf, "%.200s/.ssh/environment", + pw->pw_dir); read_environment_file(&env, &envsize, buf); } if (debug_flag) { @@ -944,6 +1247,9 @@ do_child(const char *command, struct passwd * pw, const char *term, for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } + /* we have to stash the hostname before we close our socket. */ + if (options.use_login) + hostname = get_remote_name_or_ip(); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important @@ -980,9 +1286,14 @@ do_child(const char *command, struct passwd * pw, const char *term, close(i); /* Change current directory to the user\'s home directory. */ - if (chdir(pw->pw_dir) < 0) + if (chdir(pw->pw_dir) < 0) { fprintf(stderr, "Could not chdir to home directory %s: %s\n", pw->pw_dir, strerror(errno)); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "requirehome", 0)) + exit(1); +#endif + } /* * Must take new environment into use so that .ssh/rc, /etc/sshrc and @@ -997,9 +1308,9 @@ do_child(const char *command, struct passwd * pw, const char *term, if (!options.use_login) { if (stat(SSH_USER_RC, &st) >= 0) { if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); + fprintf(stderr, "Running "_PATH_BSHELL" %s\n", SSH_USER_RC); - f = popen("/bin/sh " SSH_USER_RC, "w"); + f = popen(_PATH_BSHELL " " SSH_USER_RC, "w"); if (f) { if (auth_proto != NULL && auth_data != NULL) fprintf(f, "%s %s\n", auth_proto, auth_data); @@ -1008,34 +1319,51 @@ do_child(const char *command, struct passwd * pw, const char *term, 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); + fprintf(stderr, "Running "_PATH_BSHELL" %s\n", SSH_SYSTEM_RC); - f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); + f = popen(_PATH_BSHELL " " 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 { + } else if (options.xauth_location != NULL) { /* 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"); + char *screen = strchr(display, ':'); + if (debug_flag) { + fprintf(stderr, + "Running %.100s add %.100s %.100s %.100s\n", + options.xauth_location, display, + auth_proto, auth_data); +#ifndef HAVE_CYGWIN /* Unix sockets are not supported */ + if (screen != NULL) + fprintf(stderr, + "Adding %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, auth_data); +#endif + } + snprintf(cmd, sizeof cmd, "%s -q -", + options.xauth_location); + f = popen(cmd, "w"); if (f) { - fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); + fprintf(f, "add %s %s %s\n", display, + auth_proto, auth_data); +#ifndef HAVE_CYGWIN /* Unix sockets are not supported */ + if (screen != NULL) + fprintf(f, "add %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, auth_data); +#endif pclose(f); - } else - fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); + } } } -#endif /* XAUTH_PATH */ - /* Get the last component of the shell name. */ cp = strrchr(shell, '/'); if (cp) @@ -1061,7 +1389,8 @@ do_child(const char *command, struct passwd * pw, const char *term, struct stat mailstat; mailbox = getenv("MAIL"); if (mailbox != NULL) { - if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) + 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"); @@ -1086,8 +1415,8 @@ do_child(const char *command, struct passwd * pw, const char *term, } else { /* Launch login(1). */ - execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), - "-p", "-f", "--", pw->pw_name, NULL); + execl(LOGIN_PROGRAM, "login", "-h", hostname, + "-p", "-f", "--", pw->pw_name, NULL); /* Login couldn't be executed, die. */ @@ -1225,6 +1554,8 @@ session_pty_req(Session *s) unsigned int len; char *term_modes; /* encoded terminal modes */ + if (no_pty_flag) + return 0; if (s->ttyfd != -1) return 0; s->term = packet_get_string(&len); @@ -1272,10 +1603,22 @@ session_subsystem_req(Session *s) unsigned int len; int success = 0; char *subsys = packet_get_string(&len); + int i; packet_done(); log("subsystem request for %s", subsys); + for (i = 0; i < options.num_subsystems; i++) { + if(strcmp(subsys, options.subsystem_name[i]) == 0) { + debug("subsystem: exec() %s", options.subsystem_command[i]); + do_exec_no_pty(s, options.subsystem_command[i], s->pw); + success = 1; + } + } + + if (!success) + log("subsystem request for %s failed, subsystem not found", subsys); + xfree(subsys); return success; } @@ -1283,6 +1626,11 @@ session_subsystem_req(Session *s) int session_x11_req(Session *s) { + int fd; + if (no_x11_forwarding_flag) { + debug("X11 forwarding disabled in user configuration file."); + return 0; + } if (!options.x11_forwarding) { debug("X11 forwarding disabled in server configuration file."); return 0; @@ -1323,12 +1671,49 @@ session_x11_req(Session *s) return 0; } strlcat(xauthfile, "/cookies", MAXPATHLEN); - open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); restore_uid(); fatal_add_cleanup(xauthfile_cleanup_proc, s); return 1; } +int +session_shell_req(Session *s) +{ + /* if forced_command == NULL, the shell is execed */ + char *shell = forced_command; + packet_done(); + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, shell, s->pw); + else + do_exec_pty(s, shell, s->pw); + return 1; +} + +int +session_exec_req(Session *s) +{ + unsigned int len; + char *command = packet_get_string(&len); + packet_done(); + if (forced_command) { + original_command = command; + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, command, s->pw); + else + do_exec_pty(s, command, s->pw); + if (forced_command == NULL) + xfree(command); + return 1; +} + void session_input_channel_req(int id, void *arg) { @@ -1358,23 +1743,9 @@ session_input_channel_req(int id, void *arg) */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { - packet_done(); - s->extended = 1; - if (s->ttyfd == -1) - do_exec_no_pty(s, NULL, s->pw); - else - do_exec_pty(s, NULL, s->pw); - success = 1; + success = session_shell_req(s); } else if (strcmp(rtype, "exec") == 0) { - char *command = packet_get_string(&len); - packet_done(); - s->extended = 1; - if (s->ttyfd == -1) - do_exec_no_pty(s, command, s->pw); - else - do_exec_pty(s, command, s->pw); - xfree(command); - success = 1; + success = session_exec_req(s); } else if (strcmp(rtype, "pty-req") == 0) { success = session_pty_req(s); } else if (strcmp(rtype, "x11-req") == 0) { @@ -1409,7 +1780,8 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr) fatal("no channel for session %d", s->self); channel_set_fds(s->chanid, fdout, fdin, fderr, - fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ); + fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, + 1); } void @@ -1460,7 +1832,11 @@ session_exit_message(Session *s, int status) channel_request_start(s->chanid, "exit-signal", 0); packet_put_int(WTERMSIG(status)); +#ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)); +#else /* WCOREDUMP */ + packet_put_char(0); +#endif /* WCOREDUMP */ packet_put_cstring(""); packet_put_cstring(""); packet_send(); @@ -1477,9 +1853,13 @@ session_exit_message(Session *s, int status) * interested in data we write. * Note that we must not call 'chan_read_failed', since there could * be some more data waiting in the pipe. + * djm - This is no longer true as we have allowed one pass through + * the select loop before killing the connection */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(c); + if (c->istate != CHAN_INPUT_CLOSED) + chan_read_failed(c); s->chanid = -1; } @@ -1541,6 +1921,8 @@ session_close_by_channel(int id, void *arg) session_close(s); } else { /* notify child, delay session cleanup */ + if (s->pid <= 1) + fatal("session_close_by_channel: Unsafe s->pid = %d", s->pid); if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0) error("session_close_by_channel: kill %d: %s", s->pid, strerror(errno)); @@ -1578,11 +1960,26 @@ session_proctitle(Session *s) void do_authenticated2(void) { +#ifdef HAVE_LOGIN_CAP + struct passwd *pw; +#endif + /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } +#if defined(HAVE_LOGIN_CAP) && defined(HAVE_PW_CLASS_IN_PASSWD) + pw = auth_get_user(); + if ((lc = login_getclass(pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#endif server_loop2(); if (xauthfile) xauthfile_cleanup_proc(NULL);