[BACK]Return to session.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/session.c, Revision 1.1

1.1     ! markus      1: /*
        !             2:  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
        !             3:  *                    All rights reserved
        !             4:  */
        !             5:
        !             6: #include "includes.h"
        !             7: RCSID("$OpenBSD: session.c,v 1.26 2000/03/17 16:13:06 markus Exp $");
        !             8:
        !             9: #include "xmalloc.h"
        !            10: #include "ssh.h"
        !            11: #include "pty.h"
        !            12: #include "packet.h"
        !            13: #include "buffer.h"
        !            14: #include "cipher.h"
        !            15: #include "mpaux.h"
        !            16: #include "servconf.h"
        !            17: #include "uidswap.h"
        !            18: #include "compat.h"
        !            19: #include "channels.h"
        !            20: #include "nchan.h"
        !            21:
        !            22: /* types */
        !            23:
        !            24: #define TTYSZ 64
        !            25: typedef struct Session Session;
        !            26: struct Session {
        !            27:        int     used;
        !            28:        int     self;
        !            29:        struct  passwd *pw;
        !            30:        pid_t   pid;
        !            31:        /* tty */
        !            32:        char    *term;
        !            33:        int     ptyfd, ttyfd, ptymaster;
        !            34:        int     row, col, xpixel, ypixel;
        !            35:        char    tty[TTYSZ];
        !            36:        /* X11 */
        !            37:        char    *display;
        !            38:        int     screen;
        !            39:        char    *auth_proto;
        !            40:        char    *auth_data;
        !            41:        /* proto 2 */
        !            42:        int     chanid;
        !            43: };
        !            44:
        !            45: /* func */
        !            46:
        !            47: Session *session_new(void);
        !            48: void   session_set_fds(Session *s, int fdin, int fdout, int fderr);
        !            49: void   session_pty_cleanup(Session *s);
        !            50: void   do_exec_pty(Session *s, const char *command, struct passwd * pw);
        !            51: void   do_exec_no_pty(Session *s, const char *command, struct passwd * pw);
        !            52:
        !            53: void
        !            54: do_child(const char *command, struct passwd * pw, const char *term,
        !            55:     const char *display, const char *auth_proto,
        !            56:     const char *auth_data, const char *ttyname);
        !            57:
        !            58: /* import */
        !            59: extern ServerOptions options;
        !            60: extern char *__progname;
        !            61: extern int log_stderr;
        !            62: extern int debug_flag;
        !            63:
        !            64: /* Local Xauthority file. */
        !            65: static char *xauthfile;
        !            66:
        !            67: /* data */
        !            68: #define MAX_SESSIONS 10
        !            69: Session        sessions[MAX_SESSIONS];
        !            70:
        !            71: /* Flags set in auth-rsa from authorized_keys flags.  These are set in auth-rsa.c. */
        !            72: int no_port_forwarding_flag = 0;
        !            73: int no_agent_forwarding_flag = 0;
        !            74: int no_x11_forwarding_flag = 0;
        !            75: int no_pty_flag = 0;
        !            76:
        !            77: /* RSA authentication "command=" option. */
        !            78: char *forced_command = NULL;
        !            79:
        !            80: /* RSA authentication "environment=" options. */
        !            81: struct envstring *custom_environment = NULL;
        !            82:
        !            83: /*
        !            84:  * Remove local Xauthority file.
        !            85:  */
        !            86: void
        !            87: xauthfile_cleanup_proc(void *ignore)
        !            88: {
        !            89:        debug("xauthfile_cleanup_proc called");
        !            90:
        !            91:        if (xauthfile != NULL) {
        !            92:                char *p;
        !            93:                unlink(xauthfile);
        !            94:                p = strrchr(xauthfile, '/');
        !            95:                if (p != NULL) {
        !            96:                        *p = '\0';
        !            97:                        rmdir(xauthfile);
        !            98:                }
        !            99:                xfree(xauthfile);
        !           100:                xauthfile = NULL;
        !           101:        }
        !           102: }
        !           103:
        !           104: /*
        !           105:  * Function to perform cleanup if we get aborted abnormally (e.g., due to a
        !           106:  * dropped connection).
        !           107:  */
        !           108: void
        !           109: pty_cleanup_proc(void *session)
        !           110: {
        !           111:        Session *s=session;
        !           112:        if (s == NULL)
        !           113:                fatal("pty_cleanup_proc: no session");
        !           114:        debug("pty_cleanup_proc: %s", s->tty);
        !           115:
        !           116:        if (s->pid != 0) {
        !           117:                /* Record that the user has logged out. */
        !           118:                record_logout(s->pid, s->tty);
        !           119:        }
        !           120:
        !           121:        /* Release the pseudo-tty. */
        !           122:        pty_release(s->tty);
        !           123: }
        !           124:
        !           125: /*
        !           126:  * Prepares for an interactive session.  This is called after the user has
        !           127:  * been successfully authenticated.  During this message exchange, pseudo
        !           128:  * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
        !           129:  * are requested, etc.
        !           130:  */
        !           131: void
        !           132: do_authenticated(struct passwd * pw)
        !           133: {
        !           134:        Session *s;
        !           135:        int type;
        !           136:        int compression_level = 0, enable_compression_after_reply = 0;
        !           137:        int have_pty = 0;
        !           138:        char *command;
        !           139:        int n_bytes;
        !           140:        int plen;
        !           141:        unsigned int proto_len, data_len, dlen;
        !           142:
        !           143:        /*
        !           144:         * Cancel the alarm we set to limit the time taken for
        !           145:         * authentication.
        !           146:         */
        !           147:        alarm(0);
        !           148:
        !           149:        /*
        !           150:         * Inform the channel mechanism that we are the server side and that
        !           151:         * the client may request to connect to any port at all. (The user
        !           152:         * could do it anyway, and we wouldn\'t know what is permitted except
        !           153:         * by the client telling us, so we can equally well trust the client
        !           154:         * not to request anything bogus.)
        !           155:         */
        !           156:        if (!no_port_forwarding_flag)
        !           157:                channel_permit_all_opens();
        !           158:
        !           159:        s = session_new();
        !           160:
        !           161:        /*
        !           162:         * We stay in this loop until the client requests to execute a shell
        !           163:         * or a command.
        !           164:         */
        !           165:        for (;;) {
        !           166:                int success = 0;
        !           167:
        !           168:                /* Get a packet from the client. */
        !           169:                type = packet_read(&plen);
        !           170:
        !           171:                /* Process the packet. */
        !           172:                switch (type) {
        !           173:                case SSH_CMSG_REQUEST_COMPRESSION:
        !           174:                        packet_integrity_check(plen, 4, type);
        !           175:                        compression_level = packet_get_int();
        !           176:                        if (compression_level < 1 || compression_level > 9) {
        !           177:                                packet_send_debug("Received illegal compression level %d.",
        !           178:                                     compression_level);
        !           179:                                break;
        !           180:                        }
        !           181:                        /* Enable compression after we have responded with SUCCESS. */
        !           182:                        enable_compression_after_reply = 1;
        !           183:                        success = 1;
        !           184:                        break;
        !           185:
        !           186:                case SSH_CMSG_REQUEST_PTY:
        !           187:                        if (no_pty_flag) {
        !           188:                                debug("Allocating a pty not permitted for this authentication.");
        !           189:                                break;
        !           190:                        }
        !           191:                        if (have_pty)
        !           192:                                packet_disconnect("Protocol error: you already have a pty.");
        !           193:
        !           194:                        debug("Allocating pty.");
        !           195:
        !           196:                        /* Allocate a pty and open it. */
        !           197:                        if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
        !           198:                            sizeof(s->tty))) {
        !           199:                                error("Failed to allocate pty.");
        !           200:                                break;
        !           201:                        }
        !           202:                        fatal_add_cleanup(pty_cleanup_proc, (void *)s);
        !           203:                        pty_setowner(pw, s->tty);
        !           204:
        !           205:                        /* Get TERM from the packet.  Note that the value may be of arbitrary length. */
        !           206:                        s->term = packet_get_string(&dlen);
        !           207:                        packet_integrity_check(dlen, strlen(s->term), type);
        !           208:                        /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
        !           209:                        /* Remaining bytes */
        !           210:                        n_bytes = plen - (4 + dlen + 4 * 4);
        !           211:
        !           212:                        if (strcmp(s->term, "") == 0) {
        !           213:                                xfree(s->term);
        !           214:                                s->term = NULL;
        !           215:                        }
        !           216:                        /* Get window size from the packet. */
        !           217:                        s->row = packet_get_int();
        !           218:                        s->col = packet_get_int();
        !           219:                        s->xpixel = packet_get_int();
        !           220:                        s->ypixel = packet_get_int();
        !           221:                        pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
        !           222:
        !           223:                        /* Get tty modes from the packet. */
        !           224:                        tty_parse_modes(s->ttyfd, &n_bytes);
        !           225:                        packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
        !           226:
        !           227:                        /* Indicate that we now have a pty. */
        !           228:                        success = 1;
        !           229:                        have_pty = 1;
        !           230:                        break;
        !           231:
        !           232:                case SSH_CMSG_X11_REQUEST_FORWARDING:
        !           233:                        if (!options.x11_forwarding) {
        !           234:                                packet_send_debug("X11 forwarding disabled in server configuration file.");
        !           235:                                break;
        !           236:                        }
        !           237: #ifdef XAUTH_PATH
        !           238:                        if (no_x11_forwarding_flag) {
        !           239:                                packet_send_debug("X11 forwarding not permitted for this authentication.");
        !           240:                                break;
        !           241:                        }
        !           242:                        debug("Received request for X11 forwarding with auth spoofing.");
        !           243:                        if (s->display != NULL)
        !           244:                                packet_disconnect("Protocol error: X11 display already set.");
        !           245:
        !           246:                        s->auth_proto = packet_get_string(&proto_len);
        !           247:                        s->auth_data = packet_get_string(&data_len);
        !           248:                        packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
        !           249:
        !           250:                        if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
        !           251:                                s->screen = packet_get_int();
        !           252:                        else
        !           253:                                s->screen = 0;
        !           254:                        s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
        !           255:
        !           256:                        if (s->display == NULL)
        !           257:                                break;
        !           258:
        !           259:                        /* Setup to always have a local .Xauthority. */
        !           260:                        xauthfile = xmalloc(MAXPATHLEN);
        !           261:                        strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
        !           262:                        temporarily_use_uid(pw->pw_uid);
        !           263:                        if (mkdtemp(xauthfile) == NULL) {
        !           264:                                restore_uid();
        !           265:                                error("private X11 dir: mkdtemp %s failed: %s",
        !           266:                                    xauthfile, strerror(errno));
        !           267:                                xfree(xauthfile);
        !           268:                                xauthfile = NULL;
        !           269:                                break;
        !           270:                        }
        !           271:                        strlcat(xauthfile, "/cookies", MAXPATHLEN);
        !           272:                        open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
        !           273:                        restore_uid();
        !           274:                        fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
        !           275:                        success = 1;
        !           276:                        break;
        !           277: #else /* XAUTH_PATH */
        !           278:                        packet_send_debug("No xauth program; cannot forward with spoofing.");
        !           279:                        break;
        !           280: #endif /* XAUTH_PATH */
        !           281:
        !           282:                case SSH_CMSG_AGENT_REQUEST_FORWARDING:
        !           283:                        if (no_agent_forwarding_flag || compat13) {
        !           284:                                debug("Authentication agent forwarding not permitted for this authentication.");
        !           285:                                break;
        !           286:                        }
        !           287:                        debug("Received authentication agent forwarding request.");
        !           288:                        auth_input_request_forwarding(pw);
        !           289:                        success = 1;
        !           290:                        break;
        !           291:
        !           292:                case SSH_CMSG_PORT_FORWARD_REQUEST:
        !           293:                        if (no_port_forwarding_flag) {
        !           294:                                debug("Port forwarding not permitted for this authentication.");
        !           295:                                break;
        !           296:                        }
        !           297:                        debug("Received TCP/IP port forwarding request.");
        !           298:                        channel_input_port_forward_request(pw->pw_uid == 0);
        !           299:                        success = 1;
        !           300:                        break;
        !           301:
        !           302:                case SSH_CMSG_MAX_PACKET_SIZE:
        !           303:                        if (packet_set_maxsize(packet_get_int()) > 0)
        !           304:                                success = 1;
        !           305:                        break;
        !           306:
        !           307:                case SSH_CMSG_EXEC_SHELL:
        !           308:                case SSH_CMSG_EXEC_CMD:
        !           309:                        /* Set interactive/non-interactive mode. */
        !           310:                        packet_set_interactive(have_pty || s->display != NULL,
        !           311:                            options.keepalives);
        !           312:
        !           313:                        if (type == SSH_CMSG_EXEC_CMD) {
        !           314:                                command = packet_get_string(&dlen);
        !           315:                                debug("Exec command '%.500s'", command);
        !           316:                                packet_integrity_check(plen, 4 + dlen, type);
        !           317:                        } else {
        !           318:                                command = NULL;
        !           319:                                packet_integrity_check(plen, 0, type);
        !           320:                        }
        !           321:                        if (forced_command != NULL) {
        !           322:                                command = forced_command;
        !           323:                                debug("Forced command '%.500s'", forced_command);
        !           324:                        }
        !           325:                        if (have_pty)
        !           326:                                do_exec_pty(s, command, pw);
        !           327:                        else
        !           328:                                do_exec_no_pty(s, command, pw);
        !           329:
        !           330:                        if (command != NULL)
        !           331:                                xfree(command);
        !           332:                        /* Cleanup user's local Xauthority file. */
        !           333:                        if (xauthfile)
        !           334:                                xauthfile_cleanup_proc(NULL);
        !           335:                        return;
        !           336:
        !           337:                default:
        !           338:                        /*
        !           339:                         * Any unknown messages in this phase are ignored,
        !           340:                         * and a failure message is returned.
        !           341:                         */
        !           342:                        log("Unknown packet type received after authentication: %d", type);
        !           343:                }
        !           344:                packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
        !           345:                packet_send();
        !           346:                packet_write_wait();
        !           347:
        !           348:                /* Enable compression now that we have replied if appropriate. */
        !           349:                if (enable_compression_after_reply) {
        !           350:                        enable_compression_after_reply = 0;
        !           351:                        packet_start_compression(compression_level);
        !           352:                }
        !           353:        }
        !           354: }
        !           355:
        !           356: /*
        !           357:  * This is called to fork and execute a command when we have no tty.  This
        !           358:  * will call do_child from the child, and server_loop from the parent after
        !           359:  * setting up file descriptors and such.
        !           360:  */
        !           361: void
        !           362: do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
        !           363: {
        !           364:        int pid;
        !           365:
        !           366: #ifdef USE_PIPES
        !           367:        int pin[2], pout[2], perr[2];
        !           368:        /* Allocate pipes for communicating with the program. */
        !           369:        if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
        !           370:                packet_disconnect("Could not create pipes: %.100s",
        !           371:                                  strerror(errno));
        !           372: #else /* USE_PIPES */
        !           373:        int inout[2], err[2];
        !           374:        /* Uses socket pairs to communicate with the program. */
        !           375:        if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
        !           376:            socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
        !           377:                packet_disconnect("Could not create socket pairs: %.100s",
        !           378:                                  strerror(errno));
        !           379: #endif /* USE_PIPES */
        !           380:        if (s == NULL)
        !           381:                fatal("do_exec_no_pty: no session");
        !           382:
        !           383:        setproctitle("%s@notty", pw->pw_name);
        !           384:
        !           385:        /* Fork the child. */
        !           386:        if ((pid = fork()) == 0) {
        !           387:                /* Child.  Reinitialize the log since the pid has changed. */
        !           388:                log_init(__progname, options.log_level, options.log_facility, log_stderr);
        !           389:
        !           390:                /*
        !           391:                 * Create a new session and process group since the 4.4BSD
        !           392:                 * setlogin() affects the entire process group.
        !           393:                 */
        !           394:                if (setsid() < 0)
        !           395:                        error("setsid failed: %.100s", strerror(errno));
        !           396:
        !           397: #ifdef USE_PIPES
        !           398:                /*
        !           399:                 * Redirect stdin.  We close the parent side of the socket
        !           400:                 * pair, and make the child side the standard input.
        !           401:                 */
        !           402:                close(pin[1]);
        !           403:                if (dup2(pin[0], 0) < 0)
        !           404:                        perror("dup2 stdin");
        !           405:                close(pin[0]);
        !           406:
        !           407:                /* Redirect stdout. */
        !           408:                close(pout[0]);
        !           409:                if (dup2(pout[1], 1) < 0)
        !           410:                        perror("dup2 stdout");
        !           411:                close(pout[1]);
        !           412:
        !           413:                /* Redirect stderr. */
        !           414:                close(perr[0]);
        !           415:                if (dup2(perr[1], 2) < 0)
        !           416:                        perror("dup2 stderr");
        !           417:                close(perr[1]);
        !           418: #else /* USE_PIPES */
        !           419:                /*
        !           420:                 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
        !           421:                 * use the same socket, as some programs (particularly rdist)
        !           422:                 * seem to depend on it.
        !           423:                 */
        !           424:                close(inout[1]);
        !           425:                close(err[1]);
        !           426:                if (dup2(inout[0], 0) < 0)      /* stdin */
        !           427:                        perror("dup2 stdin");
        !           428:                if (dup2(inout[0], 1) < 0)      /* stdout.  Note: same socket as stdin. */
        !           429:                        perror("dup2 stdout");
        !           430:                if (dup2(err[0], 2) < 0)        /* stderr */
        !           431:                        perror("dup2 stderr");
        !           432: #endif /* USE_PIPES */
        !           433:
        !           434:                /* Do processing for the child (exec command etc). */
        !           435:                do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL);
        !           436:                /* NOTREACHED */
        !           437:        }
        !           438:        if (pid < 0)
        !           439:                packet_disconnect("fork failed: %.100s", strerror(errno));
        !           440:        s->pid = pid;
        !           441: #ifdef USE_PIPES
        !           442:        /* We are the parent.  Close the child sides of the pipes. */
        !           443:        close(pin[0]);
        !           444:        close(pout[1]);
        !           445:        close(perr[1]);
        !           446:
        !           447:        /* Enter the interactive session. */
        !           448:        server_loop(pid, pin[1], pout[0], perr[0]);
        !           449:        /* server_loop has closed pin[1], pout[1], and perr[1]. */
        !           450: #else /* USE_PIPES */
        !           451:        /* We are the parent.  Close the child sides of the socket pairs. */
        !           452:        close(inout[0]);
        !           453:        close(err[0]);
        !           454:
        !           455:        /*
        !           456:         * Enter the interactive session.  Note: server_loop must be able to
        !           457:         * handle the case that fdin and fdout are the same.
        !           458:         */
        !           459:        server_loop(pid, inout[1], inout[1], err[1]);
        !           460:        /* server_loop has closed inout[1] and err[1]. */
        !           461: #endif /* USE_PIPES */
        !           462: }
        !           463:
        !           464: /*
        !           465:  * This is called to fork and execute a command when we have a tty.  This
        !           466:  * will call do_child from the child, and server_loop from the parent after
        !           467:  * setting up file descriptors, controlling tty, updating wtmp, utmp,
        !           468:  * lastlog, and other such operations.
        !           469:  */
        !           470: void
        !           471: do_exec_pty(Session *s, const char *command, struct passwd * pw)
        !           472: {
        !           473:        FILE *f;
        !           474:        char buf[100], *time_string;
        !           475:        char line[256];
        !           476:        const char *hostname;
        !           477:        int fdout, ptyfd, ttyfd, ptymaster;
        !           478:        int quiet_login;
        !           479:        pid_t pid;
        !           480:        socklen_t fromlen;
        !           481:        struct sockaddr_storage from;
        !           482:        struct stat st;
        !           483:        time_t last_login_time;
        !           484:
        !           485:        if (s == NULL)
        !           486:                fatal("do_exec_pty: no session");
        !           487:        ptyfd = s->ptyfd;
        !           488:        ttyfd = s->ttyfd;
        !           489:
        !           490:        /* Get remote host name. */
        !           491:        hostname = get_canonical_hostname();
        !           492:
        !           493:        /*
        !           494:         * Get the time when the user last logged in.  Buf will be set to
        !           495:         * contain the hostname the last login was from.
        !           496:         */
        !           497:        if (!options.use_login) {
        !           498:                last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
        !           499:                                                      buf, sizeof(buf));
        !           500:        }
        !           501:        setproctitle("%s@%s", pw->pw_name, strrchr(s->tty, '/') + 1);
        !           502:
        !           503:        /* Fork the child. */
        !           504:        if ((pid = fork()) == 0) {
        !           505:                pid = getpid();
        !           506:
        !           507:                /* Child.  Reinitialize the log because the pid has
        !           508:                   changed. */
        !           509:                log_init(__progname, options.log_level, options.log_facility, log_stderr);
        !           510:
        !           511:                /* Close the master side of the pseudo tty. */
        !           512:                close(ptyfd);
        !           513:
        !           514:                /* Make the pseudo tty our controlling tty. */
        !           515:                pty_make_controlling_tty(&ttyfd, s->tty);
        !           516:
        !           517:                /* Redirect stdin from the pseudo tty. */
        !           518:                if (dup2(ttyfd, fileno(stdin)) < 0)
        !           519:                        error("dup2 stdin failed: %.100s", strerror(errno));
        !           520:
        !           521:                /* Redirect stdout to the pseudo tty. */
        !           522:                if (dup2(ttyfd, fileno(stdout)) < 0)
        !           523:                        error("dup2 stdin failed: %.100s", strerror(errno));
        !           524:
        !           525:                /* Redirect stderr to the pseudo tty. */
        !           526:                if (dup2(ttyfd, fileno(stderr)) < 0)
        !           527:                        error("dup2 stdin failed: %.100s", strerror(errno));
        !           528:
        !           529:                /* Close the extra descriptor for the pseudo tty. */
        !           530:                close(ttyfd);
        !           531:
        !           532: ///XXXX ? move to do_child() ??
        !           533:                /*
        !           534:                 * Get IP address of client.  This is needed because we want
        !           535:                 * to record where the user logged in from.  If the
        !           536:                 * connection is not a socket, let the ip address be 0.0.0.0.
        !           537:                 */
        !           538:                memset(&from, 0, sizeof(from));
        !           539:                if (packet_connection_is_on_socket()) {
        !           540:                        fromlen = sizeof(from);
        !           541:                        if (getpeername(packet_get_connection_in(),
        !           542:                             (struct sockaddr *) & from, &fromlen) < 0) {
        !           543:                                debug("getpeername: %.100s", strerror(errno));
        !           544:                                fatal_cleanup();
        !           545:                        }
        !           546:                }
        !           547:                /* Record that there was a login on that terminal. */
        !           548:                record_login(pid, s->tty, pw->pw_name, pw->pw_uid, hostname,
        !           549:                             (struct sockaddr *)&from);
        !           550:
        !           551:                /* Check if .hushlogin exists. */
        !           552:                snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
        !           553:                quiet_login = stat(line, &st) >= 0;
        !           554:
        !           555:                /*
        !           556:                 * If the user has logged in before, display the time of last
        !           557:                 * login. However, don't display anything extra if a command
        !           558:                 * has been specified (so that ssh can be used to execute
        !           559:                 * commands on a remote machine without users knowing they
        !           560:                 * are going to another machine). Login(1) will do this for
        !           561:                 * us as well, so check if login(1) is used
        !           562:                 */
        !           563:                if (command == NULL && last_login_time != 0 && !quiet_login &&
        !           564:                    !options.use_login) {
        !           565:                        /* Convert the date to a string. */
        !           566:                        time_string = ctime(&last_login_time);
        !           567:                        /* Remove the trailing newline. */
        !           568:                        if (strchr(time_string, '\n'))
        !           569:                                *strchr(time_string, '\n') = 0;
        !           570:                        /* Display the last login time.  Host if displayed
        !           571:                           if known. */
        !           572:                        if (strcmp(buf, "") == 0)
        !           573:                                printf("Last login: %s\r\n", time_string);
        !           574:                        else
        !           575:                                printf("Last login: %s from %s\r\n", time_string, buf);
        !           576:                }
        !           577:                /*
        !           578:                 * Print /etc/motd unless a command was specified or printing
        !           579:                 * it was disabled in server options or login(1) will be
        !           580:                 * used.  Note that some machines appear to print it in
        !           581:                 * /etc/profile or similar.
        !           582:                 */
        !           583:                if (command == NULL && options.print_motd && !quiet_login &&
        !           584:                    !options.use_login) {
        !           585:                        /* Print /etc/motd if it exists. */
        !           586:                        f = fopen("/etc/motd", "r");
        !           587:                        if (f) {
        !           588:                                while (fgets(line, sizeof(line), f))
        !           589:                                        fputs(line, stdout);
        !           590:                                fclose(f);
        !           591:                        }
        !           592:                }
        !           593:                /* Do common processing for the child, such as execing the command. */
        !           594:                do_child(command, pw, s->term, s->display, s->auth_proto, s->auth_data, s->tty);
        !           595:                /* NOTREACHED */
        !           596:        }
        !           597:        if (pid < 0)
        !           598:                packet_disconnect("fork failed: %.100s", strerror(errno));
        !           599:        s->pid = pid;
        !           600:
        !           601:        /* Parent.  Close the slave side of the pseudo tty. */
        !           602:        close(ttyfd);
        !           603:
        !           604:        /*
        !           605:         * Create another descriptor of the pty master side for use as the
        !           606:         * standard input.  We could use the original descriptor, but this
        !           607:         * simplifies code in server_loop.  The descriptor is bidirectional.
        !           608:         */
        !           609:        fdout = dup(ptyfd);
        !           610:        if (fdout < 0)
        !           611:                packet_disconnect("dup #1 failed: %.100s", strerror(errno));
        !           612:
        !           613:        /* we keep a reference to the pty master */
        !           614:        ptymaster = dup(ptyfd);
        !           615:        if (ptymaster < 0)
        !           616:                packet_disconnect("dup #2 failed: %.100s", strerror(errno));
        !           617:        s->ptymaster = ptymaster;
        !           618:
        !           619:        /* Enter interactive session. */
        !           620:        server_loop(pid, ptyfd, fdout, -1);
        !           621:        /* server_loop _has_ closed ptyfd and fdout. */
        !           622:        session_pty_cleanup(s);
        !           623: }
        !           624:
        !           625: /*
        !           626:  * Sets the value of the given variable in the environment.  If the variable
        !           627:  * already exists, its value is overriden.
        !           628:  */
        !           629: void
        !           630: child_set_env(char ***envp, unsigned int *envsizep, const char *name,
        !           631:              const char *value)
        !           632: {
        !           633:        unsigned int i, namelen;
        !           634:        char **env;
        !           635:
        !           636:        /*
        !           637:         * Find the slot where the value should be stored.  If the variable
        !           638:         * already exists, we reuse the slot; otherwise we append a new slot
        !           639:         * at the end of the array, expanding if necessary.
        !           640:         */
        !           641:        env = *envp;
        !           642:        namelen = strlen(name);
        !           643:        for (i = 0; env[i]; i++)
        !           644:                if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
        !           645:                        break;
        !           646:        if (env[i]) {
        !           647:                /* Reuse the slot. */
        !           648:                xfree(env[i]);
        !           649:        } else {
        !           650:                /* New variable.  Expand if necessary. */
        !           651:                if (i >= (*envsizep) - 1) {
        !           652:                        (*envsizep) += 50;
        !           653:                        env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
        !           654:                }
        !           655:                /* Need to set the NULL pointer at end of array beyond the new slot. */
        !           656:                env[i + 1] = NULL;
        !           657:        }
        !           658:
        !           659:        /* Allocate space and format the variable in the appropriate slot. */
        !           660:        env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
        !           661:        snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
        !           662: }
        !           663:
        !           664: /*
        !           665:  * Reads environment variables from the given file and adds/overrides them
        !           666:  * into the environment.  If the file does not exist, this does nothing.
        !           667:  * Otherwise, it must consist of empty lines, comments (line starts with '#')
        !           668:  * and assignments of the form name=value.  No other forms are allowed.
        !           669:  */
        !           670: void
        !           671: read_environment_file(char ***env, unsigned int *envsize,
        !           672:                      const char *filename)
        !           673: {
        !           674:        FILE *f;
        !           675:        char buf[4096];
        !           676:        char *cp, *value;
        !           677:
        !           678:        f = fopen(filename, "r");
        !           679:        if (!f)
        !           680:                return;
        !           681:
        !           682:        while (fgets(buf, sizeof(buf), f)) {
        !           683:                for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
        !           684:                        ;
        !           685:                if (!*cp || *cp == '#' || *cp == '\n')
        !           686:                        continue;
        !           687:                if (strchr(cp, '\n'))
        !           688:                        *strchr(cp, '\n') = '\0';
        !           689:                value = strchr(cp, '=');
        !           690:                if (value == NULL) {
        !           691:                        fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
        !           692:                        continue;
        !           693:                }
        !           694:                /* Replace the equals sign by nul, and advance value to the value string. */
        !           695:                *value = '\0';
        !           696:                value++;
        !           697:                child_set_env(env, envsize, cp, value);
        !           698:        }
        !           699:        fclose(f);
        !           700: }
        !           701:
        !           702: /*
        !           703:  * Performs common processing for the child, such as setting up the
        !           704:  * environment, closing extra file descriptors, setting the user and group
        !           705:  * ids, and executing the command or shell.
        !           706:  */
        !           707: void
        !           708: do_child(const char *command, struct passwd * pw, const char *term,
        !           709:         const char *display, const char *auth_proto,
        !           710:         const char *auth_data, const char *ttyname)
        !           711: {
        !           712:        const char *shell, *cp = NULL;
        !           713:        char buf[256];
        !           714:        FILE *f;
        !           715:        unsigned int envsize, i;
        !           716:        char **env;
        !           717:        extern char **environ;
        !           718:        struct stat st;
        !           719:        char *argv[10];
        !           720:
        !           721:        f = fopen("/etc/nologin", "r");
        !           722:        if (f) {
        !           723:                /* /etc/nologin exists.  Print its contents and exit. */
        !           724:                while (fgets(buf, sizeof(buf), f))
        !           725:                        fputs(buf, stderr);
        !           726:                fclose(f);
        !           727:                if (pw->pw_uid != 0)
        !           728:                        exit(254);
        !           729:        }
        !           730:        /* Set login name in the kernel. */
        !           731:        if (setlogin(pw->pw_name) < 0)
        !           732:                error("setlogin failed: %s", strerror(errno));
        !           733:
        !           734:        /* Set uid, gid, and groups. */
        !           735:        /* Login(1) does this as well, and it needs uid 0 for the "-h"
        !           736:           switch, so we let login(1) to this for us. */
        !           737:        if (!options.use_login) {
        !           738:                if (getuid() == 0 || geteuid() == 0) {
        !           739:                        if (setgid(pw->pw_gid) < 0) {
        !           740:                                perror("setgid");
        !           741:                                exit(1);
        !           742:                        }
        !           743:                        /* Initialize the group list. */
        !           744:                        if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
        !           745:                                perror("initgroups");
        !           746:                                exit(1);
        !           747:                        }
        !           748:                        endgrent();
        !           749:
        !           750:                        /* Permanently switch to the desired uid. */
        !           751:                        permanently_set_uid(pw->pw_uid);
        !           752:                }
        !           753:                if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
        !           754:                        fatal("Failed to set uids to %d.", (int) pw->pw_uid);
        !           755:        }
        !           756:        /*
        !           757:         * Get the shell from the password data.  An empty shell field is
        !           758:         * legal, and means /bin/sh.
        !           759:         */
        !           760:        shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
        !           761:
        !           762: #ifdef AFS
        !           763:        /* Try to get AFS tokens for the local cell. */
        !           764:        if (k_hasafs()) {
        !           765:                char cell[64];
        !           766:
        !           767:                if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
        !           768:                        krb_afslog(cell, 0);
        !           769:
        !           770:                krb_afslog(0, 0);
        !           771:        }
        !           772: #endif /* AFS */
        !           773:
        !           774:        /* Initialize the environment. */
        !           775:        envsize = 100;
        !           776:        env = xmalloc(envsize * sizeof(char *));
        !           777:        env[0] = NULL;
        !           778:
        !           779:        if (!options.use_login) {
        !           780:                /* Set basic environment. */
        !           781:                child_set_env(&env, &envsize, "USER", pw->pw_name);
        !           782:                child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
        !           783:                child_set_env(&env, &envsize, "HOME", pw->pw_dir);
        !           784:                child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
        !           785:
        !           786:                snprintf(buf, sizeof buf, "%.200s/%.50s",
        !           787:                         _PATH_MAILDIR, pw->pw_name);
        !           788:                child_set_env(&env, &envsize, "MAIL", buf);
        !           789:
        !           790:                /* Normal systems set SHELL by default. */
        !           791:                child_set_env(&env, &envsize, "SHELL", shell);
        !           792:        }
        !           793:        if (getenv("TZ"))
        !           794:                child_set_env(&env, &envsize, "TZ", getenv("TZ"));
        !           795:
        !           796:        /* Set custom environment options from RSA authentication. */
        !           797:        while (custom_environment) {
        !           798:                struct envstring *ce = custom_environment;
        !           799:                char *s = ce->s;
        !           800:                int i;
        !           801:                for (i = 0; s[i] != '=' && s[i]; i++);
        !           802:                if (s[i] == '=') {
        !           803:                        s[i] = 0;
        !           804:                        child_set_env(&env, &envsize, s, s + i + 1);
        !           805:                }
        !           806:                custom_environment = ce->next;
        !           807:                xfree(ce->s);
        !           808:                xfree(ce);
        !           809:        }
        !           810:
        !           811:        snprintf(buf, sizeof buf, "%.50s %d %d",
        !           812:                 get_remote_ipaddr(), get_remote_port(), get_local_port());
        !           813:        child_set_env(&env, &envsize, "SSH_CLIENT", buf);
        !           814:
        !           815:        if (ttyname)
        !           816:                child_set_env(&env, &envsize, "SSH_TTY", ttyname);
        !           817:        if (term)
        !           818:                child_set_env(&env, &envsize, "TERM", term);
        !           819:        if (display)
        !           820:                child_set_env(&env, &envsize, "DISPLAY", display);
        !           821:
        !           822: #ifdef KRB4
        !           823:        {
        !           824:                extern char *ticket;
        !           825:
        !           826:                if (ticket)
        !           827:                        child_set_env(&env, &envsize, "KRBTKFILE", ticket);
        !           828:        }
        !           829: #endif /* KRB4 */
        !           830:
        !           831:        if (xauthfile)
        !           832:                child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
        !           833:        if (auth_get_socket_name() != NULL)
        !           834:                child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
        !           835:                              auth_get_socket_name());
        !           836:
        !           837:        /* read $HOME/.ssh/environment. */
        !           838:        if (!options.use_login) {
        !           839:                snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
        !           840:                read_environment_file(&env, &envsize, buf);
        !           841:        }
        !           842:        if (debug_flag) {
        !           843:                /* dump the environment */
        !           844:                fprintf(stderr, "Environment:\n");
        !           845:                for (i = 0; env[i]; i++)
        !           846:                        fprintf(stderr, "  %.200s\n", env[i]);
        !           847:        }
        !           848:        /*
        !           849:         * Close the connection descriptors; note that this is the child, and
        !           850:         * the server will still have the socket open, and it is important
        !           851:         * that we do not shutdown it.  Note that the descriptors cannot be
        !           852:         * closed before building the environment, as we call
        !           853:         * get_remote_ipaddr there.
        !           854:         */
        !           855:        if (packet_get_connection_in() == packet_get_connection_out())
        !           856:                close(packet_get_connection_in());
        !           857:        else {
        !           858:                close(packet_get_connection_in());
        !           859:                close(packet_get_connection_out());
        !           860:        }
        !           861:        /*
        !           862:         * Close all descriptors related to channels.  They will still remain
        !           863:         * open in the parent.
        !           864:         */
        !           865:        /* XXX better use close-on-exec? -markus */
        !           866:        channel_close_all();
        !           867:
        !           868:        /*
        !           869:         * Close any extra file descriptors.  Note that there may still be
        !           870:         * descriptors left by system functions.  They will be closed later.
        !           871:         */
        !           872:        endpwent();
        !           873:
        !           874:        /*
        !           875:         * Close any extra open file descriptors so that we don\'t have them
        !           876:         * hanging around in clients.  Note that we want to do this after
        !           877:         * initgroups, because at least on Solaris 2.3 it leaves file
        !           878:         * descriptors open.
        !           879:         */
        !           880:        for (i = 3; i < 64; i++)
        !           881:                close(i);
        !           882:
        !           883:        /* Change current directory to the user\'s home directory. */
        !           884:        if (chdir(pw->pw_dir) < 0)
        !           885:                fprintf(stderr, "Could not chdir to home directory %s: %s\n",
        !           886:                        pw->pw_dir, strerror(errno));
        !           887:
        !           888:        /*
        !           889:         * Must take new environment into use so that .ssh/rc, /etc/sshrc and
        !           890:         * xauth are run in the proper environment.
        !           891:         */
        !           892:        environ = env;
        !           893:
        !           894:        /*
        !           895:         * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
        !           896:         * in this order).
        !           897:         */
        !           898:        if (!options.use_login) {
        !           899:                if (stat(SSH_USER_RC, &st) >= 0) {
        !           900:                        if (debug_flag)
        !           901:                                fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
        !           902:
        !           903:                        f = popen("/bin/sh " SSH_USER_RC, "w");
        !           904:                        if (f) {
        !           905:                                if (auth_proto != NULL && auth_data != NULL)
        !           906:                                        fprintf(f, "%s %s\n", auth_proto, auth_data);
        !           907:                                pclose(f);
        !           908:                        } else
        !           909:                                fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
        !           910:                } else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
        !           911:                        if (debug_flag)
        !           912:                                fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
        !           913:
        !           914:                        f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
        !           915:                        if (f) {
        !           916:                                if (auth_proto != NULL && auth_data != NULL)
        !           917:                                        fprintf(f, "%s %s\n", auth_proto, auth_data);
        !           918:                                pclose(f);
        !           919:                        } else
        !           920:                                fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
        !           921:                }
        !           922: #ifdef XAUTH_PATH
        !           923:                else {
        !           924:                        /* Add authority data to .Xauthority if appropriate. */
        !           925:                        if (auth_proto != NULL && auth_data != NULL) {
        !           926:                                if (debug_flag)
        !           927:                                        fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
        !           928:                                                XAUTH_PATH, display, auth_proto, auth_data);
        !           929:
        !           930:                                f = popen(XAUTH_PATH " -q -", "w");
        !           931:                                if (f) {
        !           932:                                        fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
        !           933:                                        pclose(f);
        !           934:                                } else
        !           935:                                        fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
        !           936:                        }
        !           937:                }
        !           938: #endif /* XAUTH_PATH */
        !           939:
        !           940:                /* Get the last component of the shell name. */
        !           941:                cp = strrchr(shell, '/');
        !           942:                if (cp)
        !           943:                        cp++;
        !           944:                else
        !           945:                        cp = shell;
        !           946:        }
        !           947:        /*
        !           948:         * If we have no command, execute the shell.  In this case, the shell
        !           949:         * name to be passed in argv[0] is preceded by '-' to indicate that
        !           950:         * this is a login shell.
        !           951:         */
        !           952:        if (!command) {
        !           953:                if (!options.use_login) {
        !           954:                        char buf[256];
        !           955:
        !           956:                        /*
        !           957:                         * Check for mail if we have a tty and it was enabled
        !           958:                         * in server options.
        !           959:                         */
        !           960:                        if (ttyname && options.check_mail) {
        !           961:                                char *mailbox;
        !           962:                                struct stat mailstat;
        !           963:                                mailbox = getenv("MAIL");
        !           964:                                if (mailbox != NULL) {
        !           965:                                        if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
        !           966:                                                printf("No mail.\n");
        !           967:                                        else if (mailstat.st_mtime < mailstat.st_atime)
        !           968:                                                printf("You have mail.\n");
        !           969:                                        else
        !           970:                                                printf("You have new mail.\n");
        !           971:                                }
        !           972:                        }
        !           973:                        /* Start the shell.  Set initial character to '-'. */
        !           974:                        buf[0] = '-';
        !           975:                        strncpy(buf + 1, cp, sizeof(buf) - 1);
        !           976:                        buf[sizeof(buf) - 1] = 0;
        !           977:
        !           978:                        /* Execute the shell. */
        !           979:                        argv[0] = buf;
        !           980:                        argv[1] = NULL;
        !           981:                        execve(shell, argv, env);
        !           982:
        !           983:                        /* Executing the shell failed. */
        !           984:                        perror(shell);
        !           985:                        exit(1);
        !           986:
        !           987:                } else {
        !           988:                        /* Launch login(1). */
        !           989:
        !           990:                        execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(),
        !           991:                              "-p", "-f", "--", pw->pw_name, NULL);
        !           992:
        !           993:                        /* Login couldn't be executed, die. */
        !           994:
        !           995:                        perror("login");
        !           996:                        exit(1);
        !           997:                }
        !           998:        }
        !           999:        /*
        !          1000:         * Execute the command using the user's shell.  This uses the -c
        !          1001:         * option to execute the command.
        !          1002:         */
        !          1003:        argv[0] = (char *) cp;
        !          1004:        argv[1] = "-c";
        !          1005:        argv[2] = (char *) command;
        !          1006:        argv[3] = NULL;
        !          1007:        execve(shell, argv, env);
        !          1008:        perror(shell);
        !          1009:        exit(1);
        !          1010: }
        !          1011:
        !          1012: Session *
        !          1013: session_new(void)
        !          1014: {
        !          1015:        int i;
        !          1016:        static int did_init = 0;
        !          1017:        if (!did_init) {
        !          1018:                debug("session_new: init");
        !          1019:                for(i = 0; i < MAX_SESSIONS; i++) {
        !          1020:                        sessions[i].used = 0;
        !          1021:                        sessions[i].self = i;
        !          1022:                }
        !          1023:                did_init = 1;
        !          1024:        }
        !          1025:        for(i = 0; i < MAX_SESSIONS; i++) {
        !          1026:                Session *s = &sessions[i];
        !          1027:                if (! s->used) {
        !          1028:                        s->pid = 0;
        !          1029:                        s->chanid = -1;
        !          1030:                        s->ptyfd = -1;
        !          1031:                        s->ttyfd = -1;
        !          1032:                        s->term = NULL;
        !          1033:                        s->pw = NULL;
        !          1034:                        s->display = NULL;
        !          1035:                        s->screen = 0;
        !          1036:                        s->auth_data = NULL;
        !          1037:                        s->auth_proto = NULL;
        !          1038:                        s->used = 1;
        !          1039:                        debug("session_new: session %d", i);
        !          1040:                        return s;
        !          1041:                }
        !          1042:        }
        !          1043:        return NULL;
        !          1044: }
        !          1045:
        !          1046: void
        !          1047: session_dump(void)
        !          1048: {
        !          1049:        int i;
        !          1050:        for(i = 0; i < MAX_SESSIONS; i++) {
        !          1051:                Session *s = &sessions[i];
        !          1052:                debug("dump: used %d session %d %p channel %d pid %d",
        !          1053:                    s->used,
        !          1054:                    s->self,
        !          1055:                    s,
        !          1056:                    s->chanid,
        !          1057:                    s->pid);
        !          1058:        }
        !          1059: }
        !          1060:
        !          1061: void
        !          1062: session_pty_cleanup(Session *s)
        !          1063: {
        !          1064:        if (s == NULL || s->ttyfd == -1)
        !          1065:                return;
        !          1066:
        !          1067:        debug("session_pty_cleanup: session %i release %s", s->self, s->tty);
        !          1068:
        !          1069:        /* Cancel the cleanup function. */
        !          1070:        fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
        !          1071:
        !          1072:        /* Record that the user has logged out. */
        !          1073:        record_logout(s->pid, s->tty);
        !          1074:
        !          1075:        /* Release the pseudo-tty. */
        !          1076:        pty_release(s->tty);
        !          1077:
        !          1078:        /*
        !          1079:         * Close the server side of the socket pairs.  We must do this after
        !          1080:         * the pty cleanup, so that another process doesn't get this pty
        !          1081:         * while we're still cleaning up.
        !          1082:         */
        !          1083:        if (close(s->ptymaster) < 0)
        !          1084:                error("close(s->ptymaster): %s", strerror(errno));
        !          1085: }