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: }