diff options
Diffstat (limited to 'session.c')
-rw-r--r-- | session.c | 382 |
1 files changed, 262 insertions, 120 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: session.c,v 1.236 2008/05/08 12:02:23 djm Exp $ */ | 1 | /* $OpenBSD: session.c,v 1.237 2008/05/08 12:21:16 djm 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 |
@@ -102,9 +102,9 @@ void session_set_fds(Session *, int, int, int); | |||
102 | void session_pty_cleanup(Session *); | 102 | void session_pty_cleanup(Session *); |
103 | void session_proctitle(Session *); | 103 | void session_proctitle(Session *); |
104 | int session_setup_x11fwd(Session *); | 104 | int session_setup_x11fwd(Session *); |
105 | void do_exec_pty(Session *, const char *); | 105 | int do_exec_pty(Session *, const char *); |
106 | void do_exec_no_pty(Session *, const char *); | 106 | int do_exec_no_pty(Session *, const char *); |
107 | void do_exec(Session *, const char *); | 107 | int do_exec(Session *, const char *); |
108 | void do_login(Session *, const char *); | 108 | void do_login(Session *, const char *); |
109 | #ifdef LOGIN_NEEDS_UTMPX | 109 | #ifdef LOGIN_NEEDS_UTMPX |
110 | static void do_pre_login(Session *s); | 110 | static void do_pre_login(Session *s); |
@@ -132,8 +132,9 @@ extern Buffer loginmsg; | |||
132 | const char *original_command = NULL; | 132 | const char *original_command = NULL; |
133 | 133 | ||
134 | /* data */ | 134 | /* data */ |
135 | #define MAX_SESSIONS 20 | 135 | static int sessions_first_unused = -1; |
136 | Session sessions[MAX_SESSIONS]; | 136 | static int sessions_nalloc = 0; |
137 | static Session *sessions = NULL; | ||
137 | 138 | ||
138 | #define SUBSYSTEM_NONE 0 | 139 | #define SUBSYSTEM_NONE 0 |
139 | #define SUBSYSTEM_EXT 1 | 140 | #define SUBSYSTEM_EXT 1 |
@@ -167,7 +168,7 @@ static int | |||
167 | auth_input_request_forwarding(struct passwd * pw) | 168 | auth_input_request_forwarding(struct passwd * pw) |
168 | { | 169 | { |
169 | Channel *nc; | 170 | Channel *nc; |
170 | int sock; | 171 | int sock = -1; |
171 | struct sockaddr_un sunaddr; | 172 | struct sockaddr_un sunaddr; |
172 | 173 | ||
173 | if (auth_sock_name != NULL) { | 174 | if (auth_sock_name != NULL) { |
@@ -179,43 +180,48 @@ auth_input_request_forwarding(struct passwd * pw) | |||
179 | temporarily_use_uid(pw); | 180 | temporarily_use_uid(pw); |
180 | 181 | ||
181 | /* Allocate a buffer for the socket name, and format the name. */ | 182 | /* Allocate a buffer for the socket name, and format the name. */ |
182 | auth_sock_name = xmalloc(MAXPATHLEN); | 183 | auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); |
183 | auth_sock_dir = xmalloc(MAXPATHLEN); | ||
184 | strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); | ||
185 | 184 | ||
186 | /* Create private directory for socket */ | 185 | /* Create private directory for socket */ |
187 | if (mkdtemp(auth_sock_dir) == NULL) { | 186 | if (mkdtemp(auth_sock_dir) == NULL) { |
188 | packet_send_debug("Agent forwarding disabled: " | 187 | packet_send_debug("Agent forwarding disabled: " |
189 | "mkdtemp() failed: %.100s", strerror(errno)); | 188 | "mkdtemp() failed: %.100s", strerror(errno)); |
190 | restore_uid(); | 189 | restore_uid(); |
191 | xfree(auth_sock_name); | ||
192 | xfree(auth_sock_dir); | 190 | xfree(auth_sock_dir); |
193 | auth_sock_name = NULL; | ||
194 | auth_sock_dir = NULL; | 191 | auth_sock_dir = NULL; |
195 | return 0; | 192 | goto authsock_err; |
196 | } | 193 | } |
197 | snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", | 194 | |
198 | auth_sock_dir, (long) getpid()); | 195 | xasprintf(&auth_sock_name, "%s/agent.%ld", |
196 | auth_sock_dir, (long) getpid()); | ||
199 | 197 | ||
200 | /* Create the socket. */ | 198 | /* Create the socket. */ |
201 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 199 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
202 | if (sock < 0) | 200 | if (sock < 0) { |
203 | packet_disconnect("socket: %.100s", strerror(errno)); | 201 | error("socket: %.100s", strerror(errno)); |
202 | restore_uid(); | ||
203 | goto authsock_err; | ||
204 | } | ||
204 | 205 | ||
205 | /* Bind it to the name. */ | 206 | /* Bind it to the name. */ |
206 | memset(&sunaddr, 0, sizeof(sunaddr)); | 207 | memset(&sunaddr, 0, sizeof(sunaddr)); |
207 | sunaddr.sun_family = AF_UNIX; | 208 | sunaddr.sun_family = AF_UNIX; |
208 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); | 209 | strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
209 | 210 | ||
210 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | 211 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { |
211 | packet_disconnect("bind: %.100s", strerror(errno)); | 212 | error("bind: %.100s", strerror(errno)); |
213 | restore_uid(); | ||
214 | goto authsock_err; | ||
215 | } | ||
212 | 216 | ||
213 | /* Restore the privileged uid. */ | 217 | /* Restore the privileged uid. */ |
214 | restore_uid(); | 218 | restore_uid(); |
215 | 219 | ||
216 | /* Start listening on the socket. */ | 220 | /* Start listening on the socket. */ |
217 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) | 221 | if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { |
218 | packet_disconnect("listen: %.100s", strerror(errno)); | 222 | error("listen: %.100s", strerror(errno)); |
223 | goto authsock_err; | ||
224 | } | ||
219 | 225 | ||
220 | /* Allocate a channel for the authentication agent socket. */ | 226 | /* Allocate a channel for the authentication agent socket. */ |
221 | nc = channel_new("auth socket", | 227 | nc = channel_new("auth socket", |
@@ -224,6 +230,19 @@ auth_input_request_forwarding(struct passwd * pw) | |||
224 | 0, "auth socket", 1); | 230 | 0, "auth socket", 1); |
225 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); | 231 | strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
226 | 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; | ||
227 | } | 246 | } |
228 | 247 | ||
229 | static void | 248 | static void |
@@ -373,10 +392,14 @@ do_authenticated1(Authctxt *authctxt) | |||
373 | if (type == SSH_CMSG_EXEC_CMD) { | 392 | if (type == SSH_CMSG_EXEC_CMD) { |
374 | command = packet_get_string(&dlen); | 393 | command = packet_get_string(&dlen); |
375 | debug("Exec command '%.500s'", command); | 394 | debug("Exec command '%.500s'", command); |
376 | do_exec(s, command); | 395 | if (do_exec(s, command) != 0) |
396 | packet_disconnect( | ||
397 | "command execution failed"); | ||
377 | xfree(command); | 398 | xfree(command); |
378 | } else { | 399 | } else { |
379 | do_exec(s, NULL); | 400 | if (do_exec(s, NULL) != 0) |
401 | packet_disconnect( | ||
402 | "shell execution failed"); | ||
380 | } | 403 | } |
381 | packet_check_eom(); | 404 | packet_check_eom(); |
382 | session_close(s); | 405 | session_close(s); |
@@ -401,41 +424,84 @@ do_authenticated1(Authctxt *authctxt) | |||
401 | } | 424 | } |
402 | } | 425 | } |
403 | 426 | ||
427 | #define USE_PIPES | ||
404 | /* | 428 | /* |
405 | * 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 |
406 | * 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 |
407 | * setting up file descriptors and such. | 431 | * setting up file descriptors and such. |
408 | */ | 432 | */ |
409 | void | 433 | int |
410 | do_exec_no_pty(Session *s, const char *command) | 434 | do_exec_no_pty(Session *s, const char *command) |
411 | { | 435 | { |
412 | pid_t pid; | 436 | pid_t pid; |
413 | 437 | ||
414 | #ifdef USE_PIPES | 438 | #ifdef USE_PIPES |
415 | int pin[2], pout[2], perr[2]; | 439 | int pin[2], pout[2], perr[2]; |
440 | |||
416 | /* Allocate pipes for communicating with the program. */ | 441 | /* Allocate pipes for communicating with the program. */ |
417 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) | 442 | if (pipe(pin) < 0) { |
418 | packet_disconnect("Could not create pipes: %.100s", | 443 | error("%s: pipe in: %.100s", __func__, strerror(errno)); |
419 | strerror(errno)); | 444 | return -1; |
420 | #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 | ||
421 | int inout[2], err[2]; | 461 | int inout[2], err[2]; |
462 | |||
422 | /* Uses socket pairs to communicate with the program. */ | 463 | /* Uses socket pairs to communicate with the program. */ |
423 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || | 464 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { |
424 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) | 465 | error("%s: socketpair #1: %.100s", __func__, strerror(errno)); |
425 | packet_disconnect("Could not create socket pairs: %.100s", | 466 | return -1; |
426 | strerror(errno)); | 467 | } |
427 | #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 | |||
428 | if (s == NULL) | 476 | if (s == NULL) |
429 | fatal("do_exec_no_pty: no session"); | 477 | fatal("do_exec_no_pty: no session"); |
430 | 478 | ||
431 | session_proctitle(s); | 479 | session_proctitle(s); |
432 | 480 | ||
433 | /* Fork the child. */ | 481 | /* Fork the child. */ |
434 | 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: | ||
435 | is_child = 1; | 500 | is_child = 1; |
436 | 501 | ||
437 | /* Child. Reinitialize the log since the pid has changed. */ | 502 | /* Child. Reinitialize the log since the pid has changed. */ |
438 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 503 | log_init(__progname, options.log_level, |
504 | options.log_facility, log_stderr); | ||
439 | 505 | ||
440 | /* | 506 | /* |
441 | * Create a new session and process group since the 4.4BSD | 507 | * Create a new session and process group since the 4.4BSD |
@@ -465,7 +531,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
465 | if (dup2(perr[1], 2) < 0) | 531 | if (dup2(perr[1], 2) < 0) |
466 | perror("dup2 stderr"); | 532 | perror("dup2 stderr"); |
467 | close(perr[1]); | 533 | close(perr[1]); |
468 | #else /* USE_PIPES */ | 534 | #else |
469 | /* | 535 | /* |
470 | * Redirect stdin, stdout, and stderr. Stdin and stdout will | 536 | * Redirect stdin, stdout, and stderr. Stdin and stdout will |
471 | * use the same socket, as some programs (particularly rdist) | 537 | * use the same socket, as some programs (particularly rdist) |
@@ -475,11 +541,14 @@ do_exec_no_pty(Session *s, const char *command) | |||
475 | close(err[1]); | 541 | close(err[1]); |
476 | if (dup2(inout[0], 0) < 0) /* stdin */ | 542 | if (dup2(inout[0], 0) < 0) /* stdin */ |
477 | perror("dup2 stdin"); | 543 | perror("dup2 stdin"); |
478 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ | 544 | if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ |
479 | perror("dup2 stdout"); | 545 | perror("dup2 stdout"); |
546 | close(inout[0]); | ||
480 | if (dup2(err[0], 2) < 0) /* stderr */ | 547 | if (dup2(err[0], 2) < 0) /* stderr */ |
481 | perror("dup2 stderr"); | 548 | perror("dup2 stderr"); |
482 | #endif /* USE_PIPES */ | 549 | close(err[0]); |
550 | #endif | ||
551 | |||
483 | 552 | ||
484 | #ifdef _UNICOS | 553 | #ifdef _UNICOS |
485 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ | 554 | cray_init_job(s->pw); /* set up cray jid and tmpdir */ |
@@ -488,7 +557,10 @@ do_exec_no_pty(Session *s, const char *command) | |||
488 | /* Do processing for the child (exec command etc). */ | 557 | /* Do processing for the child (exec command etc). */ |
489 | do_child(s, command); | 558 | do_child(s, command); |
490 | /* NOTREACHED */ | 559 | /* NOTREACHED */ |
560 | default: | ||
561 | break; | ||
491 | } | 562 | } |
563 | |||
492 | #ifdef _UNICOS | 564 | #ifdef _UNICOS |
493 | signal(WJSIGNAL, cray_job_termination_handler); | 565 | signal(WJSIGNAL, cray_job_termination_handler); |
494 | #endif /* _UNICOS */ | 566 | #endif /* _UNICOS */ |
@@ -496,11 +568,18 @@ do_exec_no_pty(Session *s, const char *command) | |||
496 | if (is_winnt) | 568 | if (is_winnt) |
497 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 569 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
498 | #endif | 570 | #endif |
499 | if (pid < 0) | 571 | |
500 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
501 | s->pid = pid; | 572 | s->pid = pid; |
502 | /* Set interactive/non-interactive mode. */ | 573 | /* Set interactive/non-interactive mode. */ |
503 | 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 | |||
504 | #ifdef USE_PIPES | 583 | #ifdef USE_PIPES |
505 | /* We are the parent. Close the child sides of the pipes. */ | 584 | /* We are the parent. Close the child sides of the pipes. */ |
506 | close(pin[0]); | 585 | close(pin[0]); |
@@ -518,29 +597,26 @@ do_exec_no_pty(Session *s, const char *command) | |||
518 | server_loop(pid, pin[1], pout[0], perr[0]); | 597 | server_loop(pid, pin[1], pout[0], perr[0]); |
519 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ | 598 | /* server_loop has closed pin[1], pout[0], and perr[0]. */ |
520 | } | 599 | } |
521 | #else /* USE_PIPES */ | 600 | #else |
522 | /* 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. */ |
523 | close(inout[0]); | 602 | close(inout[0]); |
524 | close(err[0]); | 603 | close(err[0]); |
525 | 604 | ||
526 | /* | 605 | /* |
527 | * Clear loginmsg, since it's the child's responsibility to display | ||
528 | * it to the user, otherwise multiple sessions may accumulate | ||
529 | * multiple copies of the login messages. | ||
530 | */ | ||
531 | buffer_clear(&loginmsg); | ||
532 | |||
533 | /* | ||
534 | * Enter the interactive session. Note: server_loop must be able to | 606 | * Enter the interactive session. Note: server_loop must be able to |
535 | * handle the case that fdin and fdout are the same. | 607 | * handle the case that fdin and fdout are the same. |
536 | */ | 608 | */ |
537 | if (compat20) { | 609 | if (compat20) { |
538 | 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]); | ||
612 | if (s->is_subsystem) | ||
613 | close(err[1]); | ||
539 | } else { | 614 | } else { |
540 | server_loop(pid, inout[1], inout[1], err[1]); | 615 | server_loop(pid, inout[1], inout[1], err[1]); |
541 | /* server_loop has closed inout[1] and err[1]. */ | 616 | /* server_loop has closed inout[1] and err[1]. */ |
542 | } | 617 | } |
543 | #endif /* USE_PIPES */ | 618 | #endif |
619 | return 0; | ||
544 | } | 620 | } |
545 | 621 | ||
546 | /* | 622 | /* |
@@ -549,7 +625,7 @@ do_exec_no_pty(Session *s, const char *command) | |||
549 | * setting up file descriptors, controlling tty, updating wtmp, utmp, | 625 | * setting up file descriptors, controlling tty, updating wtmp, utmp, |
550 | * lastlog, and other such operations. | 626 | * lastlog, and other such operations. |
551 | */ | 627 | */ |
552 | void | 628 | int |
553 | do_exec_pty(Session *s, const char *command) | 629 | do_exec_pty(Session *s, const char *command) |
554 | { | 630 | { |
555 | int fdout, ptyfd, ttyfd, ptymaster; | 631 | int fdout, ptyfd, ttyfd, ptymaster; |
@@ -560,12 +636,46 @@ do_exec_pty(Session *s, const char *command) | |||
560 | ptyfd = s->ptyfd; | 636 | ptyfd = s->ptyfd; |
561 | ttyfd = s->ttyfd; | 637 | ttyfd = s->ttyfd; |
562 | 638 | ||
639 | /* | ||
640 | * Create another descriptor of the pty master side for use as the | ||
641 | * standard input. We could use the original descriptor, but this | ||
642 | * simplifies code in server_loop. The descriptor is bidirectional. | ||
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; | ||
659 | } | ||
660 | |||
563 | /* Fork the child. */ | 661 | /* Fork the child. */ |
564 | 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: | ||
565 | is_child = 1; | 671 | is_child = 1; |
566 | 672 | ||
673 | close(fdout); | ||
674 | close(ptymaster); | ||
675 | |||
567 | /* Child. Reinitialize the log because the pid has changed. */ | 676 | /* Child. Reinitialize the log because the pid has changed. */ |
568 | log_init(__progname, options.log_level, options.log_facility, log_stderr); | 677 | log_init(__progname, options.log_level, |
678 | options.log_facility, log_stderr); | ||
569 | /* Close the master side of the pseudo tty. */ | 679 | /* Close the master side of the pseudo tty. */ |
570 | close(ptyfd); | 680 | close(ptyfd); |
571 | 681 | ||
@@ -596,11 +706,16 @@ do_exec_pty(Session *s, const char *command) | |||
596 | do_pre_login(s); | 706 | do_pre_login(s); |
597 | # endif | 707 | # endif |
598 | #endif | 708 | #endif |
599 | 709 | /* | |
600 | /* Do common processing for the child, such as execing the command. */ | 710 | * Do common processing for the child, such as execing |
601 | do_child(s, command); | 711 | * the command. |
602 | /* NOTREACHED */ | 712 | */ |
713 | do_child(s, command); | ||
714 | /* NOTREACHED */ | ||
715 | default: | ||
716 | break; | ||
603 | } | 717 | } |
718 | |||
604 | #ifdef _UNICOS | 719 | #ifdef _UNICOS |
605 | signal(WJSIGNAL, cray_job_termination_handler); | 720 | signal(WJSIGNAL, cray_job_termination_handler); |
606 | #endif /* _UNICOS */ | 721 | #endif /* _UNICOS */ |
@@ -608,29 +723,14 @@ do_exec_pty(Session *s, const char *command) | |||
608 | if (is_winnt) | 723 | if (is_winnt) |
609 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); | 724 | cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); |
610 | #endif | 725 | #endif |
611 | if (pid < 0) | 726 | |
612 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
613 | s->pid = pid; | 727 | s->pid = pid; |
614 | 728 | ||
615 | /* Parent. Close the slave side of the pseudo tty. */ | 729 | /* Parent. Close the slave side of the pseudo tty. */ |
616 | close(ttyfd); | 730 | close(ttyfd); |
617 | 731 | ||
618 | /* | ||
619 | * Create another descriptor of the pty master side for use as the | ||
620 | * standard input. We could use the original descriptor, but this | ||
621 | * simplifies code in server_loop. The descriptor is bidirectional. | ||
622 | */ | ||
623 | fdout = dup(ptyfd); | ||
624 | if (fdout < 0) | ||
625 | packet_disconnect("dup #1 failed: %.100s", strerror(errno)); | ||
626 | |||
627 | /* we keep a reference to the pty master */ | ||
628 | ptymaster = dup(ptyfd); | ||
629 | if (ptymaster < 0) | ||
630 | packet_disconnect("dup #2 failed: %.100s", strerror(errno)); | ||
631 | s->ptymaster = ptymaster; | ||
632 | |||
633 | /* Enter interactive session. */ | 732 | /* Enter interactive session. */ |
733 | s->ptymaster = ptymaster; | ||
634 | packet_set_interactive(1); | 734 | packet_set_interactive(1); |
635 | if (compat20) { | 735 | if (compat20) { |
636 | session_set_fds(s, ptyfd, fdout, -1); | 736 | session_set_fds(s, ptyfd, fdout, -1); |
@@ -638,6 +738,7 @@ do_exec_pty(Session *s, const char *command) | |||
638 | server_loop(pid, ptyfd, fdout, -1); | 738 | server_loop(pid, ptyfd, fdout, -1); |
639 | /* server_loop _has_ closed ptyfd and fdout. */ | 739 | /* server_loop _has_ closed ptyfd and fdout. */ |
640 | } | 740 | } |
741 | return 0; | ||
641 | } | 742 | } |
642 | 743 | ||
643 | #ifdef LOGIN_NEEDS_UTMPX | 744 | #ifdef LOGIN_NEEDS_UTMPX |
@@ -672,9 +773,11 @@ do_pre_login(Session *s) | |||
672 | * 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 |
673 | * to be forced, execute that instead. | 774 | * to be forced, execute that instead. |
674 | */ | 775 | */ |
675 | void | 776 | int |
676 | do_exec(Session *s, const char *command) | 777 | do_exec(Session *s, const char *command) |
677 | { | 778 | { |
779 | int ret; | ||
780 | |||
678 | if (options.adm_forced_command) { | 781 | if (options.adm_forced_command) { |
679 | original_command = command; | 782 | original_command = command; |
680 | command = options.adm_forced_command; | 783 | command = options.adm_forced_command; |
@@ -705,9 +808,9 @@ do_exec(Session *s, const char *command) | |||
705 | } | 808 | } |
706 | #endif | 809 | #endif |
707 | if (s->ttyfd != -1) | 810 | if (s->ttyfd != -1) |
708 | do_exec_pty(s, command); | 811 | ret = do_exec_pty(s, command); |
709 | else | 812 | else |
710 | do_exec_no_pty(s, command); | 813 | ret = do_exec_no_pty(s, command); |
711 | 814 | ||
712 | original_command = NULL; | 815 | original_command = NULL; |
713 | 816 | ||
@@ -717,6 +820,8 @@ do_exec(Session *s, const char *command) | |||
717 | * multiple copies of the login messages. | 820 | * multiple copies of the login messages. |
718 | */ | 821 | */ |
719 | buffer_clear(&loginmsg); | 822 | buffer_clear(&loginmsg); |
823 | |||
824 | return ret; | ||
720 | } | 825 | } |
721 | 826 | ||
722 | /* administrative, login(1)-like work */ | 827 | /* administrative, login(1)-like work */ |
@@ -1740,43 +1845,79 @@ do_child(Session *s, const char *command) | |||
1740 | exit(1); | 1845 | exit(1); |
1741 | } | 1846 | } |
1742 | 1847 | ||
1848 | void | ||
1849 | session_unused(int id) | ||
1850 | { | ||
1851 | debug3("%s: session id %d unused", __func__, id); | ||
1852 | if (id >= options.max_sessions || | ||
1853 | id >= sessions_nalloc) { | ||
1854 | fatal("%s: insane session id %d (max %d nalloc %d)", | ||
1855 | __func__, id, options.max_sessions, sessions_nalloc); | ||
1856 | } | ||
1857 | bzero(&sessions[id], sizeof(*sessions)); | ||
1858 | sessions[id].self = id; | ||
1859 | sessions[id].used = 0; | ||
1860 | sessions[id].chanid = -1; | ||
1861 | sessions[id].ptyfd = -1; | ||
1862 | sessions[id].ttyfd = -1; | ||
1863 | sessions[id].ptymaster = -1; | ||
1864 | sessions[id].x11_chanids = NULL; | ||
1865 | sessions[id].next_unused = sessions_first_unused; | ||
1866 | sessions_first_unused = id; | ||
1867 | } | ||
1868 | |||
1743 | Session * | 1869 | Session * |
1744 | session_new(void) | 1870 | session_new(void) |
1745 | { | 1871 | { |
1746 | int i; | 1872 | Session *s, *tmp; |
1747 | static int did_init = 0; | 1873 | |
1748 | if (!did_init) { | 1874 | if (sessions_first_unused == -1) { |
1749 | debug("session_new: init"); | 1875 | if (sessions_nalloc >= options.max_sessions) |
1750 | for (i = 0; i < MAX_SESSIONS; i++) { | 1876 | return NULL; |
1751 | sessions[i].used = 0; | 1877 | debug2("%s: allocate (allocated %d max %d)", |
1878 | __func__, sessions_nalloc, options.max_sessions); | ||
1879 | tmp = xrealloc(sessions, sessions_nalloc + 1, | ||
1880 | sizeof(*sessions)); | ||
1881 | if (tmp == NULL) { | ||
1882 | error("%s: cannot allocate %d sessions", | ||
1883 | __func__, sessions_nalloc + 1); | ||
1884 | return NULL; | ||
1752 | } | 1885 | } |
1753 | did_init = 1; | 1886 | sessions = tmp; |
1887 | session_unused(sessions_nalloc++); | ||
1754 | } | 1888 | } |
1755 | for (i = 0; i < MAX_SESSIONS; i++) { | 1889 | |
1756 | Session *s = &sessions[i]; | 1890 | if (sessions_first_unused >= sessions_nalloc || |
1757 | if (! s->used) { | 1891 | sessions_first_unused < 0) { |
1758 | memset(s, 0, sizeof(*s)); | 1892 | fatal("%s: insane first_unused %d max %d nalloc %d", |
1759 | s->chanid = -1; | 1893 | __func__, sessions_first_unused, options.max_sessions, |
1760 | s->ptyfd = -1; | 1894 | sessions_nalloc); |
1761 | s->ttyfd = -1; | ||
1762 | s->used = 1; | ||
1763 | s->self = i; | ||
1764 | s->x11_chanids = NULL; | ||
1765 | debug("session_new: session %d", i); | ||
1766 | return s; | ||
1767 | } | ||
1768 | } | 1895 | } |
1769 | return NULL; | 1896 | |
1897 | s = &sessions[sessions_first_unused]; | ||
1898 | if (s->used) { | ||
1899 | fatal("%s: session %d already used", | ||
1900 | __func__, sessions_first_unused); | ||
1901 | } | ||
1902 | sessions_first_unused = s->next_unused; | ||
1903 | s->used = 1; | ||
1904 | s->next_unused = -1; | ||
1905 | debug("session_new: session %d", s->self); | ||
1906 | |||
1907 | return s; | ||
1770 | } | 1908 | } |
1771 | 1909 | ||
1772 | static void | 1910 | static void |
1773 | session_dump(void) | 1911 | session_dump(void) |
1774 | { | 1912 | { |
1775 | int i; | 1913 | int i; |
1776 | for (i = 0; i < MAX_SESSIONS; i++) { | 1914 | for (i = 0; i < sessions_nalloc; i++) { |
1777 | Session *s = &sessions[i]; | 1915 | Session *s = &sessions[i]; |
1778 | debug("dump: used %d session %d %p channel %d pid %ld", | 1916 | |
1917 | debug("dump: used %d next_unused %d session %d %p " | ||
1918 | "channel %d pid %ld", | ||
1779 | s->used, | 1919 | s->used, |
1920 | s->next_unused, | ||
1780 | s->self, | 1921 | s->self, |
1781 | s, | 1922 | s, |
1782 | s->chanid, | 1923 | s->chanid, |
@@ -1806,7 +1947,7 @@ Session * | |||
1806 | session_by_tty(char *tty) | 1947 | session_by_tty(char *tty) |
1807 | { | 1948 | { |
1808 | int i; | 1949 | int i; |
1809 | for (i = 0; i < MAX_SESSIONS; i++) { | 1950 | for (i = 0; i < sessions_nalloc; i++) { |
1810 | Session *s = &sessions[i]; | 1951 | Session *s = &sessions[i]; |
1811 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { | 1952 | if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { |
1812 | debug("session_by_tty: session %d tty %s", i, tty); | 1953 | debug("session_by_tty: session %d tty %s", i, tty); |
@@ -1822,10 +1963,11 @@ static Session * | |||
1822 | session_by_channel(int id) | 1963 | session_by_channel(int id) |
1823 | { | 1964 | { |
1824 | int i; | 1965 | int i; |
1825 | for (i = 0; i < MAX_SESSIONS; i++) { | 1966 | for (i = 0; i < sessions_nalloc; i++) { |
1826 | Session *s = &sessions[i]; | 1967 | Session *s = &sessions[i]; |
1827 | if (s->used && s->chanid == id) { | 1968 | if (s->used && s->chanid == id) { |
1828 | debug("session_by_channel: session %d channel %d", i, id); | 1969 | debug("session_by_channel: session %d channel %d", |
1970 | i, id); | ||
1829 | return s; | 1971 | return s; |
1830 | } | 1972 | } |
1831 | } | 1973 | } |
@@ -1839,7 +1981,7 @@ session_by_x11_channel(int id) | |||
1839 | { | 1981 | { |
1840 | int i, j; | 1982 | int i, j; |
1841 | 1983 | ||
1842 | for (i = 0; i < MAX_SESSIONS; i++) { | 1984 | for (i = 0; i < sessions_nalloc; i++) { |
1843 | Session *s = &sessions[i]; | 1985 | Session *s = &sessions[i]; |
1844 | 1986 | ||
1845 | if (s->x11_chanids == NULL || !s->used) | 1987 | if (s->x11_chanids == NULL || !s->used) |
@@ -1862,7 +2004,7 @@ session_by_pid(pid_t pid) | |||
1862 | { | 2004 | { |
1863 | int i; | 2005 | int i; |
1864 | debug("session_by_pid: pid %ld", (long)pid); | 2006 | debug("session_by_pid: pid %ld", (long)pid); |
1865 | for (i = 0; i < MAX_SESSIONS; i++) { | 2007 | for (i = 0; i < sessions_nalloc; i++) { |
1866 | Session *s = &sessions[i]; | 2008 | Session *s = &sessions[i]; |
1867 | if (s->used && s->pid == pid) | 2009 | if (s->used && s->pid == pid) |
1868 | return s; | 2010 | return s; |
@@ -1918,7 +2060,8 @@ session_pty_req(Session *s) | |||
1918 | 2060 | ||
1919 | /* Allocate a pty and open it. */ | 2061 | /* Allocate a pty and open it. */ |
1920 | debug("Allocating pty."); | 2062 | debug("Allocating pty."); |
1921 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { | 2063 | if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, |
2064 | sizeof(s->tty)))) { | ||
1922 | if (s->term) | 2065 | if (s->term) |
1923 | xfree(s->term); | 2066 | xfree(s->term); |
1924 | s->term = NULL; | 2067 | s->term = NULL; |
@@ -1971,8 +2114,7 @@ session_subsystem_req(Session *s) | |||
1971 | s->is_subsystem = SUBSYSTEM_EXT; | 2114 | s->is_subsystem = SUBSYSTEM_EXT; |
1972 | } | 2115 | } |
1973 | debug("subsystem: exec() %s", cmd); | 2116 | debug("subsystem: exec() %s", cmd); |
1974 | do_exec(s, cmd); | 2117 | success = do_exec(s, cmd) == 0; |
1975 | success = 1; | ||
1976 | break; | 2118 | break; |
1977 | } | 2119 | } |
1978 | } | 2120 | } |
@@ -2015,19 +2157,19 @@ static int | |||
2015 | session_shell_req(Session *s) | 2157 | session_shell_req(Session *s) |
2016 | { | 2158 | { |
2017 | packet_check_eom(); | 2159 | packet_check_eom(); |
2018 | do_exec(s, NULL); | 2160 | return do_exec(s, NULL) == 0; |
2019 | return 1; | ||
2020 | } | 2161 | } |
2021 | 2162 | ||
2022 | static int | 2163 | static int |
2023 | session_exec_req(Session *s) | 2164 | session_exec_req(Session *s) |
2024 | { | 2165 | { |
2025 | u_int len; | 2166 | u_int len, success; |
2167 | |||
2026 | char *command = packet_get_string(&len); | 2168 | char *command = packet_get_string(&len); |
2027 | packet_check_eom(); | 2169 | packet_check_eom(); |
2028 | do_exec(s, command); | 2170 | success = do_exec(s, command) == 0; |
2029 | xfree(command); | 2171 | xfree(command); |
2030 | return 1; | 2172 | return success; |
2031 | } | 2173 | } |
2032 | 2174 | ||
2033 | static int | 2175 | static int |
@@ -2037,8 +2179,7 @@ session_break_req(Session *s) | |||
2037 | packet_get_int(); /* ignored */ | 2179 | packet_get_int(); /* ignored */ |
2038 | packet_check_eom(); | 2180 | packet_check_eom(); |
2039 | 2181 | ||
2040 | if (s->ttyfd == -1 || | 2182 | if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) |
2041 | tcsendbreak(s->ttyfd, 0) < 0) | ||
2042 | return 0; | 2183 | return 0; |
2043 | return 1; | 2184 | return 1; |
2044 | } | 2185 | } |
@@ -2185,8 +2326,9 @@ session_pty_cleanup2(Session *s) | |||
2185 | * the pty cleanup, so that another process doesn't get this pty | 2326 | * the pty cleanup, so that another process doesn't get this pty |
2186 | * while we're still cleaning up. | 2327 | * while we're still cleaning up. |
2187 | */ | 2328 | */ |
2188 | if (close(s->ptymaster) < 0) | 2329 | if (s->ptymaster != -1 && close(s->ptymaster) < 0) |
2189 | error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); | 2330 | error("close(s->ptymaster/%d): %s", |
2331 | s->ptymaster, strerror(errno)); | ||
2190 | 2332 | ||
2191 | /* unlink pty from session */ | 2333 | /* unlink pty from session */ |
2192 | s->ttyfd = -1; | 2334 | s->ttyfd = -1; |
@@ -2346,7 +2488,6 @@ session_close(Session *s) | |||
2346 | xfree(s->auth_data); | 2488 | xfree(s->auth_data); |
2347 | if (s->auth_proto) | 2489 | if (s->auth_proto) |
2348 | xfree(s->auth_proto); | 2490 | xfree(s->auth_proto); |
2349 | s->used = 0; | ||
2350 | if (s->env != NULL) { | 2491 | if (s->env != NULL) { |
2351 | for (i = 0; i < s->num_env; i++) { | 2492 | for (i = 0; i < s->num_env; i++) { |
2352 | xfree(s->env[i].name); | 2493 | xfree(s->env[i].name); |
@@ -2355,6 +2496,7 @@ session_close(Session *s) | |||
2355 | xfree(s->env); | 2496 | xfree(s->env); |
2356 | } | 2497 | } |
2357 | session_proctitle(s); | 2498 | session_proctitle(s); |
2499 | session_unused(s->self); | ||
2358 | } | 2500 | } |
2359 | 2501 | ||
2360 | void | 2502 | void |
@@ -2418,7 +2560,7 @@ void | |||
2418 | session_destroy_all(void (*closefunc)(Session *)) | 2560 | session_destroy_all(void (*closefunc)(Session *)) |
2419 | { | 2561 | { |
2420 | int i; | 2562 | int i; |
2421 | for (i = 0; i < MAX_SESSIONS; i++) { | 2563 | for (i = 0; i < sessions_nalloc; i++) { |
2422 | Session *s = &sessions[i]; | 2564 | Session *s = &sessions[i]; |
2423 | if (s->used) { | 2565 | if (s->used) { |
2424 | if (closefunc != NULL) | 2566 | if (closefunc != NULL) |
@@ -2437,7 +2579,7 @@ session_tty_list(void) | |||
2437 | char *cp; | 2579 | char *cp; |
2438 | 2580 | ||
2439 | buf[0] = '\0'; | 2581 | buf[0] = '\0'; |
2440 | for (i = 0; i < MAX_SESSIONS; i++) { | 2582 | for (i = 0; i < sessions_nalloc; i++) { |
2441 | Session *s = &sessions[i]; | 2583 | Session *s = &sessions[i]; |
2442 | if (s->used && s->ttyfd != -1) { | 2584 | if (s->used && s->ttyfd != -1) { |
2443 | 2585 | ||