diff options
Diffstat (limited to 'session.c')
-rw-r--r-- | session.c | 581 |
1 files changed, 409 insertions, 172 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: session.c,v 1.221 2007/01/21 01:41:54 stevesk Exp $ */ | 1 | /* $OpenBSD: session.c,v 1.241 2008/06/16 13:22:53 dtucker Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -59,6 +59,7 @@ | |||
59 | #include <string.h> | 59 | #include <string.h> |
60 | #include <unistd.h> | 60 | #include <unistd.h> |
61 | 61 | ||
62 | #include "openbsd-compat/sys-queue.h" | ||
62 | #include "xmalloc.h" | 63 | #include "xmalloc.h" |
63 | #include "ssh.h" | 64 | #include "ssh.h" |
64 | #include "ssh1.h" | 65 | #include "ssh1.h" |
@@ -84,9 +85,11 @@ | |||
84 | #include "sshlogin.h" | 85 | #include "sshlogin.h" |
85 | #include "serverloop.h" | 86 | #include "serverloop.h" |
86 | #include "canohost.h" | 87 | #include "canohost.h" |
88 | #include "misc.h" | ||
87 | #include "session.h" | 89 | #include "session.h" |
88 | #include "kex.h" | 90 | #include "kex.h" |
89 | #include "monitor_wrap.h" | 91 | #include "monitor_wrap.h" |
92 | #include "sftp.h" | ||
90 | 93 | ||
91 | #if defined(KRB5) && defined(USE_AFS) | 94 | #if defined(KRB5) && defined(USE_AFS) |
92 | #include <kafs.h> | 95 | #include <kafs.h> |
@@ -95,13 +98,13 @@ | |||
95 | /* func */ | 98 | /* func */ |
96 | 99 | ||
97 | Session *session_new(void); | 100 | Session *session_new(void); |
98 | void session_set_fds(Session *, int, int, int); | 101 | void session_set_fds(Session *, int, int, int, int); |
99 | void session_pty_cleanup(Session *); | 102 | void session_pty_cleanup(Session *); |
100 | void session_proctitle(Session *); | 103 | void session_proctitle(Session *); |
101 | int session_setup_x11fwd(Session *); | 104 | int session_setup_x11fwd(Session *); |
102 | void do_exec_pty(Session *, const char *); | 105 | int do_exec_pty(Session *, const char *); |
103 | void do_exec_no_pty(Session *, const char *); | 106 | int do_exec_no_pty(Session *, const char *); |
104 | void do_exec(Session *, const char *); | 107 | int do_exec(Session *, const char *); |
105 | void do_login(Session *, const char *); | 108 | void do_login(Session *, const char *); |
106 | #ifdef LOGIN_NEEDS_UTMPX | 109 | #ifdef LOGIN_NEEDS_UTMPX |
107 | static void do_pre_login(Session *s); | 110 | static void do_pre_login(Session *s); |
@@ -129,8 +132,13 @@ extern Buffer loginmsg; | |||
129 | const char *original_command = NULL; | 132 | const char *original_command = NULL; |
130 | 133 | ||
131 | /* data */ | 134 | /* data */ |
132 | #define MAX_SESSIONS 10 | 135 | static int sessions_first_unused = -1; |
133 | Session sessions[MAX_SESSIONS]; | 136 | static int sessions_nalloc = 0; |
137 | static Session *sessions = NULL; | ||
138 | |||
139 | #define SUBSYSTEM_NONE 0 | ||
140 | #define SUBSYSTEM_EXT 1 | ||
141 | #define SUBSYSTEM_INT_SFTP 2 | ||
134 | 142 | ||
135 | #ifdef HAVE_LOGIN_CAP | 143 | #ifdef HAVE_LOGIN_CAP |
136 | login_cap_t *lc; | 144 | login_cap_t *lc; |
@@ -160,7 +168,7 @@ static int | |||
160 | auth_input_request_forwarding(struct passwd * pw) | 168 | auth_input_request_forwarding(struct passwd * pw) |
161 | { | 169 | { |
162 | Channel *nc; | 170 | Channel *nc; |
163 | int sock; | 171 | int sock = -1; |
164 | struct sockaddr_un sunaddr; | 172 | struct sockaddr_un sunaddr; |
165 | 173 | ||
166 | if (auth_sock_name != NULL) { | 174 | if (auth_sock_name != NULL) { |
@@ -172,43 +180,48 @@ auth_input_request_forwarding(struct passwd * pw) | |||
172 | temporarily_use_uid(pw); | 180 | temporarily_use_uid(pw); |
173 | 181 | ||
174 | /* Allocate a buffer for the socket name, and format the name. */ | 182 | /* Allocate a buffer for the socket name, and format the name. */ |
175 | auth_sock_name = xmalloc(MAXPATHLEN); | 183 | auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); |
176 | auth_sock_dir = xmalloc(MAXPATHLEN); | ||
177 | strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); | ||
178 | 184 | ||
179 | /* Create private directory for socket */ | 185 | /* Create private directory for socket */ |
180 | if (mkdtemp(auth_sock_dir) == NULL) { | 186 | if (mkdtemp(auth_sock_dir) == NULL) { |
181 | packet_send_debug("Agent forwarding disabled: " | 187 | packet_send_debug("Agent forwarding disabled: " |
182 | "mkdtemp() failed: %.100s", strerror(errno)); | 188 | "mkdtemp() failed: %.100s", strerror(errno)); |
183 | restore_uid(); | 189 | restore_uid(); |
184 | xfree(auth_sock_name); | ||
185 | xfree(auth_sock_dir); | 190 | xfree(auth_sock_dir); |
186 | auth_sock_name = NULL; | ||
187 | auth_sock_dir = NULL; | 191 | auth_sock_dir = NULL; |
188 | return 0; | 192 | goto authsock_err; |
189 | } | 193 | } |
190 | snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", | 194 | |
191 | auth_sock_dir, (long) getpid()); | 195 | xasprintf(&auth_sock_name, "%s/agent.%ld", |
196 | auth_sock_dir, (long) getpid()); | ||
192 | 197 | ||
193 | /* Create the socket. */ | 198 | /* Create the socket. */ |
194 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 199 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
195 | if (sock < 0) | 200 | if (sock < 0) { |
196 | packet_disconnect("socket: %.100s", strerror(errno)); | 201 | error("socket: %.100s", strerror(errno)); |
202 | restore_uid(); | ||
203 | goto authsock_err; | ||
204 | } | ||
197 | 205 | ||
198 | /* Bind it to the name. */ | 206 | /* Bind it to the name. */ |
199 | memset(&sunaddr, 0, sizeof(sunaddr)); | 207 | memset(&sunaddr, 0, sizeof(sunaddr)); |
200 | sunaddr.sun_family = AF_UNIX; | 208 | sunaddr.sun_family = AF_UNIX; |
201 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); | 209 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
202 | 210 | ||
203 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | 211 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { |
204 | packet_disconnect("bind: %.100s", strerror(errno)); | 212 | error("bind: %.100s", strerror(errno)); |
213 | restore_uid(); | ||
214 | goto authsock_err; | ||
215 | } | ||
205 | 216 | ||
206 | /* Restore the privileged uid. */ | 217 | /* Restore the privileged uid. */ |
207 | restore_uid(); | 218 | restore_uid(); |
208 | 219 | ||
209 | /* Start listening on the socket. */ | 220 | /* Start listening on the socket. */ |
210 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) | 221 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { |
211 | packet_disconnect("listen: %.100s", strerror(errno)); | 222 | error("listen: %.100s", strerror(errno)); |
223 | goto authsock_err; | ||
224 | } | ||
212 | 225 | ||
213 | /* Allocate a channel for the authentication agent socket. */ | 226 | /* Allocate a channel for the authentication agent socket. */ |
214 | nc = channel_new("auth socket", | 227 | nc = channel_new("auth socket", |
@@ -217,6 +230,19 @@ auth_input_request_forwarding(struct passwd * pw) | |||
217 | 0, "auth socket", 1); | 230 | 0, "auth socket", 1); |
218 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); | 231 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
219 | return 1; | 232 | return 1; |
233 | |||
234 | authsock_err: | ||
235 | if (auth_sock_name != NULL) | ||
236 | xfree(auth_sock_name); | ||
237 | if (auth_sock_dir != NULL) { | ||
238 | rmdir(auth_sock_dir); | ||
239 | xfree(auth_sock_dir); | ||
240 | } | ||
241 | if (sock != -1) | ||
242 | close(sock); | ||
243 | auth_sock_name = NULL; | ||
244 | auth_sock_dir = NULL; | ||
245 | return 0; | ||
220 | } | 246 | } |
221 | 247 | ||
222 | static void | 248 | static void |
@@ -329,7 +355,8 @@ do_authenticated1(Authctxt *authctxt) | |||
329 | break; | 355 | break; |
330 | 356 | ||
331 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: | 357 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: |
332 | if (no_agent_forwarding_flag || compat13) { | 358 | if (!options.allow_agent_forwarding || |
359 | no_agent_forwarding_flag || compat13) { | ||
333 | debug("Authentication agent forwarding not permitted for this authentication."); | 360 | debug("Authentication agent forwarding not permitted for this authentication."); |
334 | break; | 361 | break; |
335 | } | 362 | } |
@@ -365,10 +392,14 @@ do_authenticated1(Authctxt *authctxt) | |||
365 | if (type == SSH_CMSG_EXEC_CMD) { | 392 | if (type == SSH_CMSG_EXEC_CMD) { |
366 | command = packet_get_string(&dlen); | 393 | command = packet_get_string(&dlen); |
367 | debug("Exec command '%.500s'", command); | 394 | debug("Exec command '%.500s'", command); |
368 | do_exec(s, command); | 395 | if (do_exec(s, command) != 0) |
396 | packet_disconnect( | ||
397 | "command execution failed"); | ||
369 | xfree(command); | 398 | xfree(command); |
370 | } else { | 399 | } else { |
371 | do_exec(s, NULL); | 400 | if (do_exec(s, NULL) != 0) |
401 | packet_disconnect( | ||
402 | "shell execution failed"); | ||
372 | } | 403 | } |
373 | packet_check_eom(); | 404 | packet_check_eom(); |
374 | session_close(s); | 405 | session_close(s); |
@@ -393,46 +424,84 @@ do_authenticated1(Authctxt *authctxt) | |||
393 | } | 424 | } |
394 | } | 425 | } |
395 | 426 | ||
427 | #define USE_PIPES | ||
396 | /* | 428 | /* |
397 | * This is called to fork and execute a command when we have no tty. This | 429 | * This is called to fork and execute a command when we have no tty. This |
398 | * will call do_child from the child, and server_loop from the parent after | 430 | * will call do_child from the child, and server_loop from the parent after |
399 | * setting up file descriptors and such. | 431 | * setting up file descriptors and such. |
400 | */ | 432 | */ |
401 | void | 433 | int |
402 | do_exec_no_pty(Session *s, const char *command) | 434 | do_exec_no_pty(Session *s, const char *command) |
403 | { | 435 | { |
404 | pid_t pid; | 436 | pid_t pid; |
405 | 437 | ||
406 | #ifdef USE_PIPES | 438 | #ifdef USE_PIPES |
407 | int pin[2], pout[2], perr[2]; | 439 | int pin[2], pout[2], perr[2]; |
440 | |||
408 | /* Allocate pipes for communicating with the program. */ | 441 | /* Allocate pipes for communicating with the program. */ |
409 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) | 442 | if (pipe(pin) < 0) { |
410 | packet_disconnect("Could not create pipes: %.100s", | 443 | error("%s: pipe in: %.100s", __func__, strerror(errno)); |
411 | strerror(errno)); | 444 | return -1; |
412 | #else /* USE_PIPES */ | 445 | } |
446 | if (pipe(pout) < 0) { | ||
447 | error("%s: pipe out: %.100s", __func__, strerror(errno)); | ||
448 | close(pin[0]); | ||
449 | close(pin[1]); | ||
450 | return -1; | ||
451 | } | ||
452 | if (pipe(perr) < 0) { | ||
453 | error("%s: pipe err: %.100s", __func__, strerror(errno)); | ||
454 | close(pin[0]); | ||
455 | close(pin[1]); | ||
456 | close(pout[0]); | ||
457 | close(pout[1]); | ||
458 | return -1; | ||
459 | } | ||
460 | #else | ||
413 | int inout[2], err[2]; | 461 | int inout[2], err[2]; |
462 | |||
414 | /* Uses socket pairs to communicate with the program. */ | 463 | /* Uses socket pairs to communicate with the program. */ |
415 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || | 464 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { |
416 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) | 465 | error("%s: socketpair #1: %.100s", __func__, strerror(errno)); |
417 | packet_disconnect("Could not create socket pairs: %.100s", | 466 | return -1; |
418 | strerror(errno)); | 467 | } |
419 | #endif /* USE_PIPES */ | 468 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { |
469 | error("%s: socketpair #2: %.100s", __func__, strerror(errno)); | ||
470 | close(inout[0]); | ||
471 | close(inout[1]); | ||
472 | return -1; | ||
473 | } | ||
474 | #endif | ||
475 | |||
420 | if (s == NULL) | 476 | if (s == NULL) |
421 | fatal("do_exec_no_pty: no session"); | 477 | fatal("do_exec_no_pty: no session"); |
422 | 478 | ||
423 | session_proctitle(s); | 479 | session_proctitle(s); |
424 | 480 | ||
425 | #if defined(USE_PAM) | ||
426 | if (options.use_pam && !use_privsep) | ||
427 | do_pam_setcred(1); | ||
428 | #endif /* USE_PAM */ | ||
429 | |||
430 | /* Fork the child. */ | 481 | /* Fork the child. */ |
431 | if ((pid = fork()) == 0) { | 482 | switch ((pid = fork())) { |
483 | case -1: | ||
484 | error("%s: fork: %.100s", __func__, strerror(errno)); | ||
485 | #ifdef USE_PIPES | ||
486 | close(pin[0]); | ||
487 | close(pin[1]); | ||
488 | close(pout[0]); | ||
489 | close(pout[1]); | ||
490 | close(perr[0]); | ||
491 | close(perr[1]); | ||
492 | #else | ||
493 | close(inout[0]); | ||
494 | close(inout[1]); | ||
495 | close(err[0]); | ||
496 | close(err[1]); | ||
497 | #endif | ||
498 | return -1; | ||
499 | case 0: | ||
432 | is_child = 1; | 500 | is_child = 1; |
433 | 501 | ||
434 | /* Child. Reinitialize the log since the pid has changed. */ | 502 | /* Child. Reinitialize the log since the pid has changed. */ |
435 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 503 | log_init(__progname, options.log_level, |
504 | options.log_facility, log_stderr); | ||
436 | 505 | ||
437 | /* | 506 | /* |
438 | * Create a new session and process group since the 4.4BSD | 507 | * Create a new session and process group since the 4.4BSD |
@@ -462,7 +531,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
462 | if (dup2(perr[1], 2) < 0) | 531 | if (dup2(perr[1], 2) < 0) |
463 | perror("dup2 stderr"); | 532 | perror("dup2 stderr"); |
464 | close(perr[1]); | 533 | close(perr[1]); |
465 | #else /* USE_PIPES */ | 534 | #else |
466 | /* | 535 | /* |
467 | * Redirect stdin, stdout, and stderr. Stdin and stdout will | 536 | * Redirect stdin, stdout, and stderr. Stdin and stdout will |
468 | * use the same socket, as some programs (particularly rdist) | 537 | * use the same socket, as some programs (particularly rdist) |
@@ -472,11 +541,14 @@ do_exec_no_pty(Session *s, const char *command) | |||
472 | close(err[1]); | 541 | close(err[1]); |
473 | if (dup2(inout[0], 0) < 0) /* stdin */ | 542 | if (dup2(inout[0], 0) < 0) /* stdin */ |
474 | perror("dup2 stdin"); | 543 | perror("dup2 stdin"); |
475 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ | 544 | if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ |
476 | perror("dup2 stdout"); | 545 | perror("dup2 stdout"); |
546 | close(inout[0]); | ||
477 | if (dup2(err[0], 2) < 0) /* stderr */ | 547 | if (dup2(err[0], 2) < 0) /* stderr */ |
478 | perror("dup2 stderr"); | 548 | perror("dup2 stderr"); |
479 | #endif /* USE_PIPES */ | 549 | close(err[0]); |
550 | #endif | ||
551 | |||
480 | 552 | ||
481 | #ifdef _UNICOS | 553 | #ifdef _UNICOS |
482 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ | 554 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ |
@@ -485,7 +557,10 @@ do_exec_no_pty(Session *s, const char *command) | |||
485 | /* Do processing for the child (exec command etc). */ | 557 | /* Do processing for the child (exec command etc). */ |
486 | do_child(s, command); | 558 | do_child(s, command); |
487 | /* NOTREACHED */ | 559 | /* NOTREACHED */ |
560 | default: | ||
561 | break; | ||
488 | } | 562 | } |
563 | |||
489 | #ifdef _UNICOS | 564 | #ifdef _UNICOS |
490 | signal(WJSIGNAL, cray_job_termination_handler); | 565 | signal(WJSIGNAL, cray_job_termination_handler); |
491 | #endif /* _UNICOS */ | 566 | #endif /* _UNICOS */ |
@@ -493,11 +568,18 @@ do_exec_no_pty(Session *s, const char *command) | |||
493 | if (is_winnt) | 568 | if (is_winnt) |
494 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 569 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
495 | #endif | 570 | #endif |
496 | if (pid < 0) | 571 | |
497 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
498 | s->pid = pid; | 572 | s->pid = pid; |
499 | /* Set interactive/non-interactive mode. */ | 573 | /* Set interactive/non-interactive mode. */ |
500 | packet_set_interactive(s->display != NULL); | 574 | packet_set_interactive(s->display != NULL); |
575 | |||
576 | /* | ||
577 | * Clear loginmsg, since it's the child's responsibility to display | ||
578 | * it to the user, otherwise multiple sessions may accumulate | ||
579 | * multiple copies of the login messages. | ||
580 | */ | ||
581 | buffer_clear(&loginmsg); | ||
582 | |||
501 | #ifdef USE_PIPES | 583 | #ifdef USE_PIPES |
502 | /* We are the parent. Close the child sides of the pipes. */ | 584 | /* We are the parent. Close the child sides of the pipes. */ |
503 | close(pin[0]); | 585 | close(pin[0]); |
@@ -509,35 +591,32 @@ do_exec_no_pty(Session *s, const char *command) | |||
509 | close(perr[0]); | 591 | close(perr[0]); |
510 | perr[0] = -1; | 592 | perr[0] = -1; |
511 | } | 593 | } |
512 | session_set_fds(s, pin[1], pout[0], perr[0]); | 594 | session_set_fds(s, pin[1], pout[0], perr[0], 0); |
513 | } else { | 595 | } else { |
514 | /* Enter the interactive session. */ | 596 | /* Enter the interactive session. */ |
515 | server_loop(pid, pin[1], pout[0], perr[0]); | 597 | server_loop(pid, pin[1], pout[0], perr[0]); |
516 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ | 598 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ |
517 | } | 599 | } |
518 | #else /* USE_PIPES */ | 600 | #else |
519 | /* We are the parent. Close the child sides of the socket pairs. */ | 601 | /* We are the parent. Close the child sides of the socket pairs. */ |
520 | close(inout[0]); | 602 | close(inout[0]); |
521 | close(err[0]); | 603 | close(err[0]); |
522 | 604 | ||
523 | /* | 605 | /* |
524 | * Clear loginmsg, since it's the child's responsibility to display | ||
525 | * it to the user, otherwise multiple sessions may accumulate | ||
526 | * multiple copies of the login messages. | ||
527 | */ | ||
528 | buffer_clear(&loginmsg); | ||
529 | |||
530 | /* | ||
531 | * Enter the interactive session. Note: server_loop must be able to | 606 | * Enter the interactive session. Note: server_loop must be able to |
532 | * handle the case that fdin and fdout are the same. | 607 | * handle the case that fdin and fdout are the same. |
533 | */ | 608 | */ |
534 | if (compat20) { | 609 | if (compat20) { |
535 | session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); | 610 | session_set_fds(s, inout[1], inout[1], |
611 | s->is_subsystem ? -1 : err[1], 0); | ||
612 | if (s->is_subsystem) | ||
613 | close(err[1]); | ||
536 | } else { | 614 | } else { |
537 | server_loop(pid, inout[1], inout[1], err[1]); | 615 | server_loop(pid, inout[1], inout[1], err[1]); |
538 | /* server_loop has closed inout[1] and err[1]. */ | 616 | /* server_loop has closed inout[1] and err[1]. */ |
539 | } | 617 | } |
540 | #endif /* USE_PIPES */ | 618 | #endif |
619 | return 0; | ||
541 | } | 620 | } |
542 | 621 | ||
543 | /* | 622 | /* |
@@ -546,7 +625,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
546 | * setting up file descriptors, controlling tty, updating wtmp, utmp, | 625 | * setting up file descriptors, controlling tty, updating wtmp, utmp, |
547 | * lastlog, and other such operations. | 626 | * lastlog, and other such operations. |
548 | */ | 627 | */ |
549 | void | 628 | int |
550 | do_exec_pty(Session *s, const char *command) | 629 | do_exec_pty(Session *s, const char *command) |
551 | { | 630 | { |
552 | int fdout, ptyfd, ttyfd, ptymaster; | 631 | int fdout, ptyfd, ttyfd, ptymaster; |
@@ -557,20 +636,46 @@ do_exec_pty(Session *s, const char *command) | |||
557 | ptyfd = s->ptyfd; | 636 | ptyfd = s->ptyfd; |
558 | ttyfd = s->ttyfd; | 637 | ttyfd = s->ttyfd; |
559 | 638 | ||
560 | #if defined(USE_PAM) | 639 | /* |
561 | if (options.use_pam) { | 640 | * Create another descriptor of the pty master side for use as the |
562 | do_pam_set_tty(s->tty); | 641 | * standard input. We could use the original descriptor, but this |
563 | if (!use_privsep) | 642 | * simplifies code in server_loop. The descriptor is bidirectional. |
564 | do_pam_setcred(1); | 643 | * Do this before forking (and cleanup in the child) so as to |
644 | * detect and gracefully fail out-of-fd conditions. | ||
645 | */ | ||
646 | if ((fdout = dup(ptyfd)) < 0) { | ||
647 | error("%s: dup #1: %s", __func__, strerror(errno)); | ||
648 | close(ttyfd); | ||
649 | close(ptyfd); | ||
650 | return -1; | ||
651 | } | ||
652 | /* we keep a reference to the pty master */ | ||
653 | if ((ptymaster = dup(ptyfd)) < 0) { | ||
654 | error("%s: dup #2: %s", __func__, strerror(errno)); | ||
655 | close(ttyfd); | ||
656 | close(ptyfd); | ||
657 | close(fdout); | ||
658 | return -1; | ||
565 | } | 659 | } |
566 | #endif | ||
567 | 660 | ||
568 | /* Fork the child. */ | 661 | /* Fork the child. */ |
569 | if ((pid = fork()) == 0) { | 662 | switch ((pid = fork())) { |
663 | case -1: | ||
664 | error("%s: fork: %.100s", __func__, strerror(errno)); | ||
665 | close(fdout); | ||
666 | close(ptymaster); | ||
667 | close(ttyfd); | ||
668 | close(ptyfd); | ||
669 | return -1; | ||
670 | case 0: | ||
570 | is_child = 1; | 671 | is_child = 1; |
571 | 672 | ||
673 | close(fdout); | ||
674 | close(ptymaster); | ||
675 | |||
572 | /* Child. Reinitialize the log because the pid has changed. */ | 676 | /* Child. Reinitialize the log because the pid has changed. */ |
573 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 677 | log_init(__progname, options.log_level, |
678 | options.log_facility, log_stderr); | ||
574 | /* Close the master side of the pseudo tty. */ | 679 | /* Close the master side of the pseudo tty. */ |
575 | close(ptyfd); | 680 | close(ptyfd); |
576 | 681 | ||
@@ -601,11 +706,16 @@ do_exec_pty(Session *s, const char *command) | |||
601 | do_pre_login(s); | 706 | do_pre_login(s); |
602 | # endif | 707 | # endif |
603 | #endif | 708 | #endif |
604 | 709 | /* | |
605 | /* Do common processing for the child, such as execing the command. */ | 710 | * Do common processing for the child, such as execing |
606 | do_child(s, command); | 711 | * the command. |
607 | /* NOTREACHED */ | 712 | */ |
713 | do_child(s, command); | ||
714 | /* NOTREACHED */ | ||
715 | default: | ||
716 | break; | ||
608 | } | 717 | } |
718 | |||
609 | #ifdef _UNICOS | 719 | #ifdef _UNICOS |
610 | signal(WJSIGNAL, cray_job_termination_handler); | 720 | signal(WJSIGNAL, cray_job_termination_handler); |
611 | #endif /* _UNICOS */ | 721 | #endif /* _UNICOS */ |
@@ -613,36 +723,22 @@ do_exec_pty(Session *s, const char *command) | |||
613 | if (is_winnt) | 723 | if (is_winnt) |
614 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 724 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
615 | #endif | 725 | #endif |
616 | if (pid < 0) | 726 | |
617 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
618 | s->pid = pid; | 727 | s->pid = pid; |
619 | 728 | ||
620 | /* Parent. Close the slave side of the pseudo tty. */ | 729 | /* Parent. Close the slave side of the pseudo tty. */ |
621 | close(ttyfd); | 730 | close(ttyfd); |
622 | 731 | ||
623 | /* | ||
624 | * Create another descriptor of the pty master side for use as the | ||
625 | * standard input. We could use the original descriptor, but this | ||
626 | * simplifies code in server_loop. The descriptor is bidirectional. | ||
627 | */ | ||
628 | fdout = dup(ptyfd); | ||
629 | if (fdout < 0) | ||
630 | packet_disconnect("dup #1 failed: %.100s", strerror(errno)); | ||
631 | |||
632 | /* we keep a reference to the pty master */ | ||
633 | ptymaster = dup(ptyfd); | ||
634 | if (ptymaster < 0) | ||
635 | packet_disconnect("dup #2 failed: %.100s", strerror(errno)); | ||
636 | s->ptymaster = ptymaster; | ||
637 | |||
638 | /* Enter interactive session. */ | 732 | /* Enter interactive session. */ |
733 | s->ptymaster = ptymaster; | ||
639 | packet_set_interactive(1); | 734 | packet_set_interactive(1); |
640 | if (compat20) { | 735 | if (compat20) { |
641 | session_set_fds(s, ptyfd, fdout, -1); | 736 | session_set_fds(s, ptyfd, fdout, -1, 1); |
642 | } else { | 737 | } else { |
643 | server_loop(pid, ptyfd, fdout, -1); | 738 | server_loop(pid, ptyfd, fdout, -1); |
644 | /* server_loop _has_ closed ptyfd and fdout. */ | 739 | /* server_loop _has_ closed ptyfd and fdout. */ |
645 | } | 740 | } |
741 | return 0; | ||
646 | } | 742 | } |
647 | 743 | ||
648 | #ifdef LOGIN_NEEDS_UTMPX | 744 | #ifdef LOGIN_NEEDS_UTMPX |
@@ -677,16 +773,26 @@ do_pre_login(Session *s) | |||
677 | * This is called to fork and execute a command. If another command is | 773 | * This is called to fork and execute a command. If another command is |
678 | * to be forced, execute that instead. | 774 | * to be forced, execute that instead. |
679 | */ | 775 | */ |
680 | void | 776 | int |
681 | do_exec(Session *s, const char *command) | 777 | do_exec(Session *s, const char *command) |
682 | { | 778 | { |
779 | int ret; | ||
780 | |||
683 | if (options.adm_forced_command) { | 781 | if (options.adm_forced_command) { |
684 | original_command = command; | 782 | original_command = command; |
685 | command = options.adm_forced_command; | 783 | command = options.adm_forced_command; |
784 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) | ||
785 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
786 | else if (s->is_subsystem) | ||
787 | s->is_subsystem = SUBSYSTEM_EXT; | ||
686 | debug("Forced command (config) '%.900s'", command); | 788 | debug("Forced command (config) '%.900s'", command); |
687 | } else if (forced_command) { | 789 | } else if (forced_command) { |
688 | original_command = command; | 790 | original_command = command; |
689 | command = forced_command; | 791 | command = forced_command; |
792 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) | ||
793 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
794 | else if (s->is_subsystem) | ||
795 | s->is_subsystem = SUBSYSTEM_EXT; | ||
690 | debug("Forced command (key option) '%.900s'", command); | 796 | debug("Forced command (key option) '%.900s'", command); |
691 | } | 797 | } |
692 | 798 | ||
@@ -701,11 +807,10 @@ do_exec(Session *s, const char *command) | |||
701 | PRIVSEP(audit_run_command(shell)); | 807 | PRIVSEP(audit_run_command(shell)); |
702 | } | 808 | } |
703 | #endif | 809 | #endif |
704 | |||
705 | if (s->ttyfd != -1) | 810 | if (s->ttyfd != -1) |
706 | do_exec_pty(s, command); | 811 | ret = do_exec_pty(s, command); |
707 | else | 812 | else |
708 | do_exec_no_pty(s, command); | 813 | ret = do_exec_no_pty(s, command); |
709 | 814 | ||
710 | original_command = NULL; | 815 | original_command = NULL; |
711 | 816 | ||
@@ -715,6 +820,8 @@ do_exec(Session *s, const char *command) | |||
715 | * multiple copies of the login messages. | 820 | * multiple copies of the login messages. |
716 | */ | 821 | */ |
717 | buffer_clear(&loginmsg); | 822 | buffer_clear(&loginmsg); |
823 | |||
824 | return ret; | ||
718 | } | 825 | } |
719 | 826 | ||
720 | /* administrative, login(1)-like work */ | 827 | /* administrative, login(1)-like work */ |
@@ -897,8 +1004,9 @@ read_environment_file(char ***env, u_int *envsize, | |||
897 | ; | 1004 | ; |
898 | if (!*cp || *cp == '#' || *cp == '\n') | 1005 | if (!*cp || *cp == '#' || *cp == '\n') |
899 | continue; | 1006 | continue; |
900 | if (strchr(cp, '\n')) | 1007 | |
901 | *strchr(cp, '\n') = '\0'; | 1008 | cp[strcspn(cp, "\n")] = '\0'; |
1009 | |||
902 | value = strchr(cp, '='); | 1010 | value = strchr(cp, '='); |
903 | if (value == NULL) { | 1011 | if (value == NULL) { |
904 | fprintf(stderr, "Bad line %u in %.100s\n", lineno, | 1012 | fprintf(stderr, "Bad line %u in %.100s\n", lineno, |
@@ -1201,8 +1309,9 @@ do_rc_files(Session *s, const char *shell) | |||
1201 | do_xauth = | 1309 | do_xauth = |
1202 | s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; | 1310 | s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; |
1203 | 1311 | ||
1204 | /* ignore _PATH_SSH_USER_RC for subsystems */ | 1312 | /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ |
1205 | if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { | 1313 | if (!s->is_subsystem && options.adm_forced_command == NULL && |
1314 | !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { | ||
1206 | snprintf(cmd, sizeof cmd, "%s -c '%s %s'", | 1315 | snprintf(cmd, sizeof cmd, "%s -c '%s %s'", |
1207 | shell, _PATH_BSHELL, _PATH_SSH_USER_RC); | 1316 | shell, _PATH_BSHELL, _PATH_SSH_USER_RC); |
1208 | if (debug_flag) | 1317 | if (debug_flag) |
@@ -1283,10 +1392,72 @@ do_nologin(struct passwd *pw) | |||
1283 | } | 1392 | } |
1284 | } | 1393 | } |
1285 | 1394 | ||
1395 | /* | ||
1396 | * Chroot into a directory after checking it for safety: all path components | ||
1397 | * must be root-owned directories with strict permissions. | ||
1398 | */ | ||
1399 | static void | ||
1400 | safely_chroot(const char *path, uid_t uid) | ||
1401 | { | ||
1402 | const char *cp; | ||
1403 | char component[MAXPATHLEN]; | ||
1404 | struct stat st; | ||
1405 | |||
1406 | if (*path != '/') | ||
1407 | fatal("chroot path does not begin at root"); | ||
1408 | if (strlen(path) >= sizeof(component)) | ||
1409 | fatal("chroot path too long"); | ||
1410 | |||
1411 | /* | ||
1412 | * Descend the path, checking that each component is a | ||
1413 | * root-owned directory with strict permissions. | ||
1414 | */ | ||
1415 | for (cp = path; cp != NULL;) { | ||
1416 | if ((cp = strchr(cp, '/')) == NULL) | ||
1417 | strlcpy(component, path, sizeof(component)); | ||
1418 | else { | ||
1419 | cp++; | ||
1420 | memcpy(component, path, cp - path); | ||
1421 | component[cp - path] = '\0'; | ||
1422 | } | ||
1423 | |||
1424 | debug3("%s: checking '%s'", __func__, component); | ||
1425 | |||
1426 | if (stat(component, &st) != 0) | ||
1427 | fatal("%s: stat(\"%s\"): %s", __func__, | ||
1428 | component, strerror(errno)); | ||
1429 | if (st.st_uid != 0 || (st.st_mode & 022) != 0) | ||
1430 | fatal("bad ownership or modes for chroot " | ||
1431 | "directory %s\"%s\"", | ||
1432 | cp == NULL ? "" : "component ", component); | ||
1433 | if (!S_ISDIR(st.st_mode)) | ||
1434 | fatal("chroot path %s\"%s\" is not a directory", | ||
1435 | cp == NULL ? "" : "component ", component); | ||
1436 | |||
1437 | } | ||
1438 | |||
1439 | if (chdir(path) == -1) | ||
1440 | fatal("Unable to chdir to chroot path \"%s\": " | ||
1441 | "%s", path, strerror(errno)); | ||
1442 | if (chroot(path) == -1) | ||
1443 | fatal("chroot(\"%s\"): %s", path, strerror(errno)); | ||
1444 | if (chdir("/") == -1) | ||
1445 | fatal("%s: chdir(/) after chroot: %s", | ||
1446 | __func__, strerror(errno)); | ||
1447 | verbose("Changed root directory to \"%s\"", path); | ||
1448 | } | ||
1449 | |||
1286 | /* Set login name, uid, gid, and groups. */ | 1450 | /* Set login name, uid, gid, and groups. */ |
1287 | void | 1451 | void |
1288 | do_setusercontext(struct passwd *pw) | 1452 | do_setusercontext(struct passwd *pw) |
1289 | { | 1453 | { |
1454 | char *chroot_path, *tmp; | ||
1455 | |||
1456 | #ifdef WITH_SELINUX | ||
1457 | /* Cache selinux status for later use */ | ||
1458 | (void)ssh_selinux_enabled(); | ||
1459 | #endif | ||
1460 | |||
1290 | #ifndef HAVE_CYGWIN | 1461 | #ifndef HAVE_CYGWIN |
1291 | if (getuid() == 0 || geteuid() == 0) | 1462 | if (getuid() == 0 || geteuid() == 0) |
1292 | #endif /* HAVE_CYGWIN */ | 1463 | #endif /* HAVE_CYGWIN */ |
@@ -1300,21 +1471,13 @@ do_setusercontext(struct passwd *pw) | |||
1300 | # ifdef __bsdi__ | 1471 | # ifdef __bsdi__ |
1301 | setpgid(0, 0); | 1472 | setpgid(0, 0); |
1302 | # endif | 1473 | # endif |
1303 | #ifdef GSSAPI | ||
1304 | if (options.gss_authentication) { | ||
1305 | temporarily_use_uid(pw); | ||
1306 | ssh_gssapi_storecreds(); | ||
1307 | restore_uid(); | ||
1308 | } | ||
1309 | #endif | ||
1310 | # ifdef USE_PAM | 1474 | # ifdef USE_PAM |
1311 | if (options.use_pam) { | 1475 | if (options.use_pam) { |
1312 | do_pam_session(); | ||
1313 | do_pam_setcred(use_privsep); | 1476 | do_pam_setcred(use_privsep); |
1314 | } | 1477 | } |
1315 | # endif /* USE_PAM */ | 1478 | # endif /* USE_PAM */ |
1316 | if (setusercontext(lc, pw, pw->pw_uid, | 1479 | if (setusercontext(lc, pw, pw->pw_uid, |
1317 | (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { | 1480 | (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { |
1318 | perror("unable to set user context"); | 1481 | perror("unable to set user context"); |
1319 | exit(1); | 1482 | exit(1); |
1320 | } | 1483 | } |
@@ -1337,13 +1500,6 @@ do_setusercontext(struct passwd *pw) | |||
1337 | exit(1); | 1500 | exit(1); |
1338 | } | 1501 | } |
1339 | endgrent(); | 1502 | endgrent(); |
1340 | #ifdef GSSAPI | ||
1341 | if (options.gss_authentication) { | ||
1342 | temporarily_use_uid(pw); | ||
1343 | ssh_gssapi_storecreds(); | ||
1344 | restore_uid(); | ||
1345 | } | ||
1346 | #endif | ||
1347 | # ifdef USE_PAM | 1503 | # ifdef USE_PAM |
1348 | /* | 1504 | /* |
1349 | * PAM credentials may take the form of supplementary groups. | 1505 | * PAM credentials may take the form of supplementary groups. |
@@ -1351,21 +1507,39 @@ do_setusercontext(struct passwd *pw) | |||
1351 | * Reestablish them here. | 1507 | * Reestablish them here. |
1352 | */ | 1508 | */ |
1353 | if (options.use_pam) { | 1509 | if (options.use_pam) { |
1354 | do_pam_session(); | ||
1355 | do_pam_setcred(use_privsep); | 1510 | do_pam_setcred(use_privsep); |
1356 | } | 1511 | } |
1357 | # endif /* USE_PAM */ | 1512 | # endif /* USE_PAM */ |
1358 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) | 1513 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) |
1359 | irix_setusercontext(pw); | 1514 | irix_setusercontext(pw); |
1360 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ | 1515 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ |
1361 | # ifdef _AIX | 1516 | # ifdef _AIX |
1362 | aix_usrinfo(pw); | 1517 | aix_usrinfo(pw); |
1363 | # endif /* _AIX */ | 1518 | # endif /* _AIX */ |
1364 | #ifdef USE_LIBIAF | 1519 | # ifdef USE_LIBIAF |
1365 | if (set_id(pw->pw_name) != 0) { | 1520 | if (set_id(pw->pw_name) != 0) { |
1366 | exit(1); | 1521 | exit(1); |
1367 | } | 1522 | } |
1368 | #endif /* USE_LIBIAF */ | 1523 | # endif /* USE_LIBIAF */ |
1524 | #endif | ||
1525 | |||
1526 | if (options.chroot_directory != NULL && | ||
1527 | strcasecmp(options.chroot_directory, "none") != 0) { | ||
1528 | tmp = tilde_expand_filename(options.chroot_directory, | ||
1529 | pw->pw_uid); | ||
1530 | chroot_path = percent_expand(tmp, "h", pw->pw_dir, | ||
1531 | "u", pw->pw_name, (char *)NULL); | ||
1532 | safely_chroot(chroot_path, pw->pw_uid); | ||
1533 | free(tmp); | ||
1534 | free(chroot_path); | ||
1535 | } | ||
1536 | |||
1537 | #ifdef HAVE_LOGIN_CAP | ||
1538 | if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { | ||
1539 | perror("unable to set user context (setuser)"); | ||
1540 | exit(1); | ||
1541 | } | ||
1542 | #else | ||
1369 | /* Permanently switch to the desired uid. */ | 1543 | /* Permanently switch to the desired uid. */ |
1370 | permanently_set_uid(pw); | 1544 | permanently_set_uid(pw); |
1371 | #endif | 1545 | #endif |
@@ -1464,14 +1638,16 @@ child_close_fds(void) | |||
1464 | * environment, closing extra file descriptors, setting the user and group | 1638 | * environment, closing extra file descriptors, setting the user and group |
1465 | * ids, and executing the command or shell. | 1639 | * ids, and executing the command or shell. |
1466 | */ | 1640 | */ |
1641 | #define ARGV_MAX 10 | ||
1467 | void | 1642 | void |
1468 | do_child(Session *s, const char *command) | 1643 | do_child(Session *s, const char *command) |
1469 | { | 1644 | { |
1470 | extern char **environ; | 1645 | extern char **environ; |
1471 | char **env; | 1646 | char **env; |
1472 | char *argv[10]; | 1647 | char *argv[ARGV_MAX]; |
1473 | const char *shell, *shell0, *hostname = NULL; | 1648 | const char *shell, *shell0, *hostname = NULL; |
1474 | struct passwd *pw = s->pw; | 1649 | struct passwd *pw = s->pw; |
1650 | int r = 0; | ||
1475 | 1651 | ||
1476 | /* remove hostkey from the child's memory */ | 1652 | /* remove hostkey from the child's memory */ |
1477 | destroy_sensitive_data(); | 1653 | destroy_sensitive_data(); |
@@ -1587,20 +1763,42 @@ do_child(Session *s, const char *command) | |||
1587 | 1763 | ||
1588 | /* Change current directory to the user's home directory. */ | 1764 | /* Change current directory to the user's home directory. */ |
1589 | if (chdir(pw->pw_dir) < 0) { | 1765 | if (chdir(pw->pw_dir) < 0) { |
1590 | fprintf(stderr, "Could not chdir to home directory %s: %s\n", | 1766 | /* Suppress missing homedir warning for chroot case */ |
1591 | pw->pw_dir, strerror(errno)); | ||
1592 | #ifdef HAVE_LOGIN_CAP | 1767 | #ifdef HAVE_LOGIN_CAP |
1593 | if (login_getcapbool(lc, "requirehome", 0)) | 1768 | r = login_getcapbool(lc, "requirehome", 0); |
1594 | exit(1); | ||
1595 | #endif | 1769 | #endif |
1770 | if (r || options.chroot_directory == NULL) | ||
1771 | fprintf(stderr, "Could not chdir to home " | ||
1772 | "directory %s: %s\n", pw->pw_dir, | ||
1773 | strerror(errno)); | ||
1774 | if (r) | ||
1775 | exit(1); | ||
1596 | } | 1776 | } |
1597 | 1777 | ||
1778 | closefrom(STDERR_FILENO + 1); | ||
1779 | |||
1598 | if (!options.use_login) | 1780 | if (!options.use_login) |
1599 | do_rc_files(s, shell); | 1781 | do_rc_files(s, shell); |
1600 | 1782 | ||
1601 | /* restore SIGPIPE for child */ | 1783 | /* restore SIGPIPE for child */ |
1602 | signal(SIGPIPE, SIG_DFL); | 1784 | signal(SIGPIPE, SIG_DFL); |
1603 | 1785 | ||
1786 | if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { | ||
1787 | extern int optind, optreset; | ||
1788 | int i; | ||
1789 | char *p, *args; | ||
1790 | |||
1791 | setproctitle("%s@internal-sftp-server", s->pw->pw_name); | ||
1792 | args = strdup(command ? command : "sftp-server"); | ||
1793 | for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) | ||
1794 | if (i < ARGV_MAX - 1) | ||
1795 | argv[i++] = p; | ||
1796 | argv[i] = NULL; | ||
1797 | optind = optreset = 1; | ||
1798 | __progname = argv[0]; | ||
1799 | exit(sftp_server_main(i, argv, s->pw)); | ||
1800 | } | ||
1801 | |||
1604 | if (options.use_login) { | 1802 | if (options.use_login) { |
1605 | launch_login(pw, hostname); | 1803 | launch_login(pw, hostname); |
1606 | /* NEVERREACHED */ | 1804 | /* NEVERREACHED */ |
@@ -1652,43 +1850,79 @@ do_child(Session *s, const char *command) | |||
1652 | exit(1); | 1850 | exit(1); |
1653 | } | 1851 | } |
1654 | 1852 | ||
1853 | void | ||
1854 | session_unused(int id) | ||
1855 | { | ||
1856 | debug3("%s: session id %d unused", __func__, id); | ||
1857 | if (id >= options.max_sessions || | ||
1858 | id >= sessions_nalloc) { | ||
1859 | fatal("%s: insane session id %d (max %d nalloc %d)", | ||
1860 | __func__, id, options.max_sessions, sessions_nalloc); | ||
1861 | } | ||
1862 | bzero(&sessions[id], sizeof(*sessions)); | ||
1863 | sessions[id].self = id; | ||
1864 | sessions[id].used = 0; | ||
1865 | sessions[id].chanid = -1; | ||
1866 | sessions[id].ptyfd = -1; | ||
1867 | sessions[id].ttyfd = -1; | ||
1868 | sessions[id].ptymaster = -1; | ||
1869 | sessions[id].x11_chanids = NULL; | ||
1870 | sessions[id].next_unused = sessions_first_unused; | ||
1871 | sessions_first_unused = id; | ||
1872 | } | ||
1873 | |||
1655 | Session * | 1874 | Session * |
1656 | session_new(void) | 1875 | session_new(void) |
1657 | { | 1876 | { |
1658 | int i; | 1877 | Session *s, *tmp; |
1659 | static int did_init = 0; | 1878 | |
1660 | if (!did_init) { | 1879 | if (sessions_first_unused == -1) { |
1661 | debug("session_new: init"); | 1880 | if (sessions_nalloc >= options.max_sessions) |
1662 | for (i = 0; i < MAX_SESSIONS; i++) { | 1881 | return NULL; |
1663 | sessions[i].used = 0; | 1882 | debug2("%s: allocate (allocated %d max %d)", |
1883 | __func__, sessions_nalloc, options.max_sessions); | ||
1884 | tmp = xrealloc(sessions, sessions_nalloc + 1, | ||
1885 | sizeof(*sessions)); | ||
1886 | if (tmp == NULL) { | ||
1887 | error("%s: cannot allocate %d sessions", | ||
1888 | __func__, sessions_nalloc + 1); | ||
1889 | return NULL; | ||
1664 | } | 1890 | } |
1665 | did_init = 1; | 1891 | sessions = tmp; |
1892 | session_unused(sessions_nalloc++); | ||
1666 | } | 1893 | } |
1667 | for (i = 0; i < MAX_SESSIONS; i++) { | 1894 | |
1668 | Session *s = &sessions[i]; | 1895 | if (sessions_first_unused >= sessions_nalloc || |
1669 | if (! s->used) { | 1896 | sessions_first_unused < 0) { |
1670 | memset(s, 0, sizeof(*s)); | 1897 | fatal("%s: insane first_unused %d max %d nalloc %d", |
1671 | s->chanid = -1; | 1898 | __func__, sessions_first_unused, options.max_sessions, |
1672 | s->ptyfd = -1; | 1899 | sessions_nalloc); |
1673 | s->ttyfd = -1; | ||
1674 | s->used = 1; | ||
1675 | s->self = i; | ||
1676 | s->x11_chanids = NULL; | ||
1677 | debug("session_new: session %d", i); | ||
1678 | return s; | ||
1679 | } | ||
1680 | } | 1900 | } |
1681 | return NULL; | 1901 | |
1902 | s = &sessions[sessions_first_unused]; | ||
1903 | if (s->used) { | ||
1904 | fatal("%s: session %d already used", | ||
1905 | __func__, sessions_first_unused); | ||
1906 | } | ||
1907 | sessions_first_unused = s->next_unused; | ||
1908 | s->used = 1; | ||
1909 | s->next_unused = -1; | ||
1910 | debug("session_new: session %d", s->self); | ||
1911 | |||
1912 | return s; | ||
1682 | } | 1913 | } |
1683 | 1914 | ||
1684 | static void | 1915 | static void |
1685 | session_dump(void) | 1916 | session_dump(void) |
1686 | { | 1917 | { |
1687 | int i; | 1918 | int i; |
1688 | for (i = 0; i < MAX_SESSIONS; i++) { | 1919 | for (i = 0; i < sessions_nalloc; i++) { |
1689 | Session *s = &sessions[i]; | 1920 | Session *s = &sessions[i]; |
1690 | debug("dump: used %d session %d %p channel %d pid %ld", | 1921 | |
1922 | debug("dump: used %d next_unused %d session %d %p " | ||
1923 | "channel %d pid %ld", | ||
1691 | s->used, | 1924 | s->used, |
1925 | s->next_unused, | ||
1692 | s->self, | 1926 | s->self, |
1693 | s, | 1927 | s, |
1694 | s->chanid, | 1928 | s->chanid, |
@@ -1718,7 +1952,7 @@ Session * | |||
1718 | session_by_tty(char *tty) | 1952 | session_by_tty(char *tty) |
1719 | { | 1953 | { |
1720 | int i; | 1954 | int i; |
1721 | for (i = 0; i < MAX_SESSIONS; i++) { | 1955 | for (i = 0; i < sessions_nalloc; i++) { |
1722 | Session *s = &sessions[i]; | 1956 | Session *s = &sessions[i]; |
1723 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { | 1957 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { |
1724 | debug("session_by_tty: session %d tty %s", i, tty); | 1958 | debug("session_by_tty: session %d tty %s", i, tty); |
@@ -1734,10 +1968,11 @@ static Session * | |||
1734 | session_by_channel(int id) | 1968 | session_by_channel(int id) |
1735 | { | 1969 | { |
1736 | int i; | 1970 | int i; |
1737 | for (i = 0; i < MAX_SESSIONS; i++) { | 1971 | for (i = 0; i < sessions_nalloc; i++) { |
1738 | Session *s = &sessions[i]; | 1972 | Session *s = &sessions[i]; |
1739 | if (s->used && s->chanid == id) { | 1973 | if (s->used && s->chanid == id) { |
1740 | debug("session_by_channel: session %d channel %d", i, id); | 1974 | debug("session_by_channel: session %d channel %d", |
1975 | i, id); | ||
1741 | return s; | 1976 | return s; |
1742 | } | 1977 | } |
1743 | } | 1978 | } |
@@ -1751,7 +1986,7 @@ session_by_x11_channel(int id) | |||
1751 | { | 1986 | { |
1752 | int i, j; | 1987 | int i, j; |
1753 | 1988 | ||
1754 | for (i = 0; i < MAX_SESSIONS; i++) { | 1989 | for (i = 0; i < sessions_nalloc; i++) { |
1755 | Session *s = &sessions[i]; | 1990 | Session *s = &sessions[i]; |
1756 | 1991 | ||
1757 | if (s->x11_chanids == NULL || !s->used) | 1992 | if (s->x11_chanids == NULL || !s->used) |
@@ -1774,7 +2009,7 @@ session_by_pid(pid_t pid) | |||
1774 | { | 2009 | { |
1775 | int i; | 2010 | int i; |
1776 | debug("session_by_pid: pid %ld", (long)pid); | 2011 | debug("session_by_pid: pid %ld", (long)pid); |
1777 | for (i = 0; i < MAX_SESSIONS; i++) { | 2012 | for (i = 0; i < sessions_nalloc; i++) { |
1778 | Session *s = &sessions[i]; | 2013 | Session *s = &sessions[i]; |
1779 | if (s->used && s->pid == pid) | 2014 | if (s->used && s->pid == pid) |
1780 | return s; | 2015 | return s; |
@@ -1830,7 +2065,8 @@ session_pty_req(Session *s) | |||
1830 | 2065 | ||
1831 | /* Allocate a pty and open it. */ | 2066 | /* Allocate a pty and open it. */ |
1832 | debug("Allocating pty."); | 2067 | debug("Allocating pty."); |
1833 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { | 2068 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, |
2069 | sizeof(s->tty)))) { | ||
1834 | if (s->term) | 2070 | if (s->term) |
1835 | xfree(s->term); | 2071 | xfree(s->term); |
1836 | s->term = NULL; | 2072 | s->term = NULL; |
@@ -1873,15 +2109,17 @@ session_subsystem_req(Session *s) | |||
1873 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { | 2109 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { |
1874 | prog = options.subsystem_command[i]; | 2110 | prog = options.subsystem_command[i]; |
1875 | cmd = options.subsystem_args[i]; | 2111 | cmd = options.subsystem_args[i]; |
1876 | if (stat(prog, &st) < 0) { | 2112 | if (!strcmp(INTERNAL_SFTP_NAME, prog)) { |
2113 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
2114 | } else if (stat(prog, &st) < 0) { | ||
1877 | error("subsystem: cannot stat %s: %s", prog, | 2115 | error("subsystem: cannot stat %s: %s", prog, |
1878 | strerror(errno)); | 2116 | strerror(errno)); |
1879 | break; | 2117 | break; |
2118 | } else { | ||
2119 | s->is_subsystem = SUBSYSTEM_EXT; | ||
1880 | } | 2120 | } |
1881 | debug("subsystem: exec() %s", cmd); | 2121 | debug("subsystem: exec() %s", cmd); |
1882 | s->is_subsystem = 1; | 2122 | success = do_exec(s, cmd) == 0; |
1883 | do_exec(s, cmd); | ||
1884 | success = 1; | ||
1885 | break; | 2123 | break; |
1886 | } | 2124 | } |
1887 | } | 2125 | } |
@@ -1924,19 +2162,19 @@ static int | |||
1924 | session_shell_req(Session *s) | 2162 | session_shell_req(Session *s) |
1925 | { | 2163 | { |
1926 | packet_check_eom(); | 2164 | packet_check_eom(); |
1927 | do_exec(s, NULL); | 2165 | return do_exec(s, NULL) == 0; |
1928 | return 1; | ||
1929 | } | 2166 | } |
1930 | 2167 | ||
1931 | static int | 2168 | static int |
1932 | session_exec_req(Session *s) | 2169 | session_exec_req(Session *s) |
1933 | { | 2170 | { |
1934 | u_int len; | 2171 | u_int len, success; |
2172 | |||
1935 | char *command = packet_get_string(&len); | 2173 | char *command = packet_get_string(&len); |
1936 | packet_check_eom(); | 2174 | packet_check_eom(); |
1937 | do_exec(s, command); | 2175 | success = do_exec(s, command) == 0; |
1938 | xfree(command); | 2176 | xfree(command); |
1939 | return 1; | 2177 | return success; |
1940 | } | 2178 | } |
1941 | 2179 | ||
1942 | static int | 2180 | static int |
@@ -1946,8 +2184,7 @@ session_break_req(Session *s) | |||
1946 | packet_get_int(); /* ignored */ | 2184 | packet_get_int(); /* ignored */ |
1947 | packet_check_eom(); | 2185 | packet_check_eom(); |
1948 | 2186 | ||
1949 | if (s->ttyfd == -1 || | 2187 | if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) |
1950 | tcsendbreak(s->ttyfd, 0) < 0) | ||
1951 | return 0; | 2188 | return 0; |
1952 | return 1; | 2189 | return 1; |
1953 | } | 2190 | } |
@@ -1992,7 +2229,7 @@ session_auth_agent_req(Session *s) | |||
1992 | { | 2229 | { |
1993 | static int called = 0; | 2230 | static int called = 0; |
1994 | packet_check_eom(); | 2231 | packet_check_eom(); |
1995 | if (no_agent_forwarding_flag) { | 2232 | if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { |
1996 | debug("session_auth_agent_req: no_agent_forwarding_flag"); | 2233 | debug("session_auth_agent_req: no_agent_forwarding_flag"); |
1997 | return 0; | 2234 | return 0; |
1998 | } | 2235 | } |
@@ -2048,7 +2285,7 @@ session_input_channel_req(Channel *c, const char *rtype) | |||
2048 | } | 2285 | } |
2049 | 2286 | ||
2050 | void | 2287 | void |
2051 | session_set_fds(Session *s, int fdin, int fdout, int fderr) | 2288 | session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty) |
2052 | { | 2289 | { |
2053 | if (!compat20) | 2290 | if (!compat20) |
2054 | fatal("session_set_fds: called for proto != 2.0"); | 2291 | fatal("session_set_fds: called for proto != 2.0"); |
@@ -2061,8 +2298,7 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr) | |||
2061 | channel_set_fds(s->chanid, | 2298 | channel_set_fds(s->chanid, |
2062 | fdout, fdin, fderr, | 2299 | fdout, fdin, fderr, |
2063 | fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, | 2300 | fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, |
2064 | 1, | 2301 | 1, is_tty, CHAN_SES_WINDOW_DEFAULT); |
2065 | CHAN_SES_WINDOW_DEFAULT); | ||
2066 | } | 2302 | } |
2067 | 2303 | ||
2068 | /* | 2304 | /* |
@@ -2094,8 +2330,9 @@ session_pty_cleanup2(Session *s) | |||
2094 | * the pty cleanup, so that another process doesn't get this pty | 2330 | * the pty cleanup, so that another process doesn't get this pty |
2095 | * while we're still cleaning up. | 2331 | * while we're still cleaning up. |
2096 | */ | 2332 | */ |
2097 | if (close(s->ptymaster) < 0) | 2333 | if (s->ptymaster != -1 && close(s->ptymaster) < 0) |
2098 | error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); | 2334 | error("close(s->ptymaster/%d): %s", |
2335 | s->ptymaster, strerror(errno)); | ||
2099 | 2336 | ||
2100 | /* unlink pty from session */ | 2337 | /* unlink pty from session */ |
2101 | s->ttyfd = -1; | 2338 | s->ttyfd = -1; |
@@ -2203,7 +2440,7 @@ session_exit_message(Session *s, int status) | |||
2203 | channel_request_start(s->chanid, "exit-signal", 0); | 2440 | channel_request_start(s->chanid, "exit-signal", 0); |
2204 | packet_put_cstring(sig2name(WTERMSIG(status))); | 2441 | packet_put_cstring(sig2name(WTERMSIG(status))); |
2205 | #ifdef WCOREDUMP | 2442 | #ifdef WCOREDUMP |
2206 | packet_put_char(WCOREDUMP(status)); | 2443 | packet_put_char(WCOREDUMP(status)? 1 : 0); |
2207 | #else /* WCOREDUMP */ | 2444 | #else /* WCOREDUMP */ |
2208 | packet_put_char(0); | 2445 | packet_put_char(0); |
2209 | #endif /* WCOREDUMP */ | 2446 | #endif /* WCOREDUMP */ |
@@ -2255,7 +2492,6 @@ session_close(Session *s) | |||
2255 | xfree(s->auth_data); | 2492 | xfree(s->auth_data); |
2256 | if (s->auth_proto) | 2493 | if (s->auth_proto) |
2257 | xfree(s->auth_proto); | 2494 | xfree(s->auth_proto); |
2258 | s->used = 0; | ||
2259 | if (s->env != NULL) { | 2495 | if (s->env != NULL) { |
2260 | for (i = 0; i < s->num_env; i++) { | 2496 | for (i = 0; i < s->num_env; i++) { |
2261 | xfree(s->env[i].name); | 2497 | xfree(s->env[i].name); |
@@ -2264,6 +2500,7 @@ session_close(Session *s) | |||
2264 | xfree(s->env); | 2500 | xfree(s->env); |
2265 | } | 2501 | } |
2266 | session_proctitle(s); | 2502 | session_proctitle(s); |
2503 | session_unused(s->self); | ||
2267 | } | 2504 | } |
2268 | 2505 | ||
2269 | void | 2506 | void |
@@ -2327,7 +2564,7 @@ void | |||
2327 | session_destroy_all(void (*closefunc)(Session *)) | 2564 | session_destroy_all(void (*closefunc)(Session *)) |
2328 | { | 2565 | { |
2329 | int i; | 2566 | int i; |
2330 | for (i = 0; i < MAX_SESSIONS; i++) { | 2567 | for (i = 0; i < sessions_nalloc; i++) { |
2331 | Session *s = &sessions[i]; | 2568 | Session *s = &sessions[i]; |
2332 | if (s->used) { | 2569 | if (s->used) { |
2333 | if (closefunc != NULL) | 2570 | if (closefunc != NULL) |
@@ -2346,7 +2583,7 @@ session_tty_list(void) | |||
2346 | char *cp; | 2583 | char *cp; |
2347 | 2584 | ||
2348 | buf[0] = '\0'; | 2585 | buf[0] = '\0'; |
2349 | for (i = 0; i < MAX_SESSIONS; i++) { | 2586 | for (i = 0; i < sessions_nalloc; i++) { |
2350 | Session *s = &sessions[i]; | 2587 | Session *s = &sessions[i]; |
2351 | if (s->used && s->ttyfd != -1) { | 2588 | if (s->used && s->ttyfd != -1) { |
2352 | 2589 | ||