summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2004-06-15 10:34:08 +1000
committerDamien Miller <djm@mindrot.org>2004-06-15 10:34:08 +1000
commit0e220dbfbcc9fe252e8f1f4890dbfa415aad35db (patch)
treeb78bab0c628cd5bdb0ec95340f533a1be2fae75f
parent05202ffe214115afa24bf6e7a6d8c8457e6759bb (diff)
- djm@cvs.openbsd.org 2004/06/13 15:03:02
[channels.c channels.h clientloop.c clientloop.h includes.h readconf.c] [readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5] implement session multiplexing in the client (the server has supported this since 2.0); ok markus@
-rw-r--r--ChangeLog7
-rw-r--r--channels.c76
-rw-r--r--channels.h7
-rw-r--r--clientloop.c327
-rw-r--r--clientloop.h4
-rw-r--r--defines.h5
-rw-r--r--includes.h3
-rw-r--r--readconf.c18
-rw-r--r--readconf.h5
-rw-r--r--scp.14
-rw-r--r--sftp.14
-rw-r--r--ssh-rand-helper.c6
-rw-r--r--ssh.126
-rw-r--r--ssh.c276
-rw-r--r--ssh_config.524
15 files changed, 650 insertions, 142 deletions
diff --git a/ChangeLog b/ChangeLog
index 3edf2d19b..36aeb85bb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,11 @@
24 [ssh.1 ssh_config.5 sshd_config.5] 24 [ssh.1 ssh_config.5 sshd_config.5]
25 List supported ciphers in man pages, tidy up ssh -c; 25 List supported ciphers in man pages, tidy up ssh -c;
26 "looks fine" jmc@, ok markus@ 26 "looks fine" jmc@, ok markus@
27 - djm@cvs.openbsd.org 2004/06/13 15:03:02
28 [channels.c channels.h clientloop.c clientloop.h includes.h readconf.c]
29 [readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5]
30 implement session multiplexing in the client (the server has supported
31 this since 2.0); ok markus@
27 32
2820040603 3320040603
29 - (dtucker) [auth-pam.c] Don't use pam_* namespace for sshd's PAM functions. 34 - (dtucker) [auth-pam.c] Don't use pam_* namespace for sshd's PAM functions.
@@ -1208,4 +1213,4 @@
1208 - (djm) Trim deprecated options from INSTALL. Mention UsePAM 1213 - (djm) Trim deprecated options from INSTALL. Mention UsePAM
1209 - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu 1214 - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
1210 1215
1211$Id: ChangeLog,v 1.3381 2004/06/15 00:30:39 djm Exp $ 1216$Id: ChangeLog,v 1.3382 2004/06/15 00:34:08 djm Exp $
diff --git a/channels.c b/channels.c
index 437befa34..1fb1092c8 100644
--- a/channels.c
+++ b/channels.c
@@ -39,7 +39,7 @@
39 */ 39 */
40 40
41#include "includes.h" 41#include "includes.h"
42RCSID("$OpenBSD: channels.c,v 1.203 2004/05/26 23:02:39 markus Exp $"); 42RCSID("$OpenBSD: channels.c,v 1.204 2004/06/13 15:03:02 djm Exp $");
43 43
44#include "ssh.h" 44#include "ssh.h"
45#include "ssh1.h" 45#include "ssh1.h"
@@ -172,6 +172,7 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd,
172 c->rfd = rfd; 172 c->rfd = rfd;
173 c->wfd = wfd; 173 c->wfd = wfd;
174 c->sock = (rfd == wfd) ? rfd : -1; 174 c->sock = (rfd == wfd) ? rfd : -1;
175 c->ctl_fd = -1; /* XXX: set elsewhere */
175 c->efd = efd; 176 c->efd = efd;
176 c->extended_usage = extusage; 177 c->extended_usage = extusage;
177 178
@@ -263,6 +264,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
263 c->single_connection = 0; 264 c->single_connection = 0;
264 c->detach_user = NULL; 265 c->detach_user = NULL;
265 c->confirm = NULL; 266 c->confirm = NULL;
267 c->confirm_ctx = NULL;
266 c->input_filter = NULL; 268 c->input_filter = NULL;
267 debug("channel %d: new [%s]", found, remote_name); 269 debug("channel %d: new [%s]", found, remote_name);
268 return c; 270 return c;
@@ -304,10 +306,11 @@ channel_close_fd(int *fdp)
304static void 306static void
305channel_close_fds(Channel *c) 307channel_close_fds(Channel *c)
306{ 308{
307 debug3("channel %d: close_fds r %d w %d e %d", 309 debug3("channel %d: close_fds r %d w %d e %d c %d",
308 c->self, c->rfd, c->wfd, c->efd); 310 c->self, c->rfd, c->wfd, c->efd, c->ctl_fd);
309 311
310 channel_close_fd(&c->sock); 312 channel_close_fd(&c->sock);
313 channel_close_fd(&c->ctl_fd);
311 channel_close_fd(&c->rfd); 314 channel_close_fd(&c->rfd);
312 channel_close_fd(&c->wfd); 315 channel_close_fd(&c->wfd);
313 channel_close_fd(&c->efd); 316 channel_close_fd(&c->efd);
@@ -333,6 +336,8 @@ channel_free(Channel *c)
333 336
334 if (c->sock != -1) 337 if (c->sock != -1)
335 shutdown(c->sock, SHUT_RDWR); 338 shutdown(c->sock, SHUT_RDWR);
339 if (c->ctl_fd != -1)
340 shutdown(c->ctl_fd, SHUT_RDWR);
336 channel_close_fds(c); 341 channel_close_fds(c);
337 buffer_free(&c->input); 342 buffer_free(&c->input);
338 buffer_free(&c->output); 343 buffer_free(&c->output);
@@ -550,12 +555,13 @@ channel_open_message(void)
550 case SSH_CHANNEL_X11_OPEN: 555 case SSH_CHANNEL_X11_OPEN:
551 case SSH_CHANNEL_INPUT_DRAINING: 556 case SSH_CHANNEL_INPUT_DRAINING:
552 case SSH_CHANNEL_OUTPUT_DRAINING: 557 case SSH_CHANNEL_OUTPUT_DRAINING:
553 snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", 558 snprintf(buf, sizeof buf,
559 " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n",
554 c->self, c->remote_name, 560 c->self, c->remote_name,
555 c->type, c->remote_id, 561 c->type, c->remote_id,
556 c->istate, buffer_len(&c->input), 562 c->istate, buffer_len(&c->input),
557 c->ostate, buffer_len(&c->output), 563 c->ostate, buffer_len(&c->output),
558 c->rfd, c->wfd); 564 c->rfd, c->wfd, c->ctl_fd);
559 buffer_append(&buffer, buf, strlen(buf)); 565 buffer_append(&buffer, buf, strlen(buf));
560 continue; 566 continue;
561 default: 567 default:
@@ -596,14 +602,14 @@ channel_request_start(int id, char *service, int wantconfirm)
596 logit("channel_request_start: %d: unknown channel id", id); 602 logit("channel_request_start: %d: unknown channel id", id);
597 return; 603 return;
598 } 604 }
599 debug2("channel %d: request %s", id, service) ; 605 debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
600 packet_start(SSH2_MSG_CHANNEL_REQUEST); 606 packet_start(SSH2_MSG_CHANNEL_REQUEST);
601 packet_put_int(c->remote_id); 607 packet_put_int(c->remote_id);
602 packet_put_cstring(service); 608 packet_put_cstring(service);
603 packet_put_char(wantconfirm); 609 packet_put_char(wantconfirm);
604} 610}
605void 611void
606channel_register_confirm(int id, channel_callback_fn *fn) 612channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
607{ 613{
608 Channel *c = channel_lookup(id); 614 Channel *c = channel_lookup(id);
609 615
@@ -612,6 +618,7 @@ channel_register_confirm(int id, channel_callback_fn *fn)
612 return; 618 return;
613 } 619 }
614 c->confirm = fn; 620 c->confirm = fn;
621 c->confirm_ctx = ctx;
615} 622}
616void 623void
617channel_register_cleanup(int id, channel_callback_fn *fn) 624channel_register_cleanup(int id, channel_callback_fn *fn)
@@ -729,6 +736,10 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
729 buffer_len(&c->extended) < c->remote_window) 736 buffer_len(&c->extended) < c->remote_window)
730 FD_SET(c->efd, readset); 737 FD_SET(c->efd, readset);
731 } 738 }
739 /* XXX: What about efd? races? */
740 if (compat20 && c->ctl_fd != -1 &&
741 c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN)
742 FD_SET(c->ctl_fd, readset);
732} 743}
733 744
734static void 745static void
@@ -1482,6 +1493,33 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
1482 return 1; 1493 return 1;
1483} 1494}
1484static int 1495static int
1496channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
1497{
1498 char buf[16];
1499 int len;
1500
1501 /* Monitor control fd to detect if the slave client exits */
1502 if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
1503 len = read(c->ctl_fd, buf, sizeof(buf));
1504 if (len < 0 && (errno == EINTR || errno == EAGAIN))
1505 return 1;
1506 if (len <= 0) {
1507 debug2("channel %d: ctl read<=0", c->self);
1508 if (c->type != SSH_CHANNEL_OPEN) {
1509 debug2("channel %d: not open", c->self);
1510 chan_mark_dead(c);
1511 return -1;
1512 } else {
1513 chan_read_failed(c);
1514 chan_write_failed(c);
1515 }
1516 return -1;
1517 } else
1518 fatal("%s: unexpected data on ctl fd", __func__);
1519 }
1520 return 1;
1521}
1522static int
1485channel_check_window(Channel *c) 1523channel_check_window(Channel *c)
1486{ 1524{
1487 if (c->type == SSH_CHANNEL_OPEN && 1525 if (c->type == SSH_CHANNEL_OPEN &&
@@ -1511,6 +1549,7 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
1511 if (!compat20) 1549 if (!compat20)
1512 return; 1550 return;
1513 channel_handle_efd(c, readset, writeset); 1551 channel_handle_efd(c, readset, writeset);
1552 channel_handle_ctl(c, readset, writeset);
1514 channel_check_window(c); 1553 channel_check_window(c);
1515} 1554}
1516 1555
@@ -2011,7 +2050,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
2011 c->remote_maxpacket = packet_get_int(); 2050 c->remote_maxpacket = packet_get_int();
2012 if (c->confirm) { 2051 if (c->confirm) {
2013 debug2("callback start"); 2052 debug2("callback start");
2014 c->confirm(c->self, NULL); 2053 c->confirm(c->self, c->confirm_ctx);
2015 debug2("callback done"); 2054 debug2("callback done");
2016 } 2055 }
2017 debug2("channel %d: open confirm rwindow %u rmax %u", c->self, 2056 debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
@@ -2531,6 +2570,27 @@ channel_connect_to(const char *host, u_short port)
2531 return connect_to(host, port); 2570 return connect_to(host, port);
2532} 2571}
2533 2572
2573void
2574channel_send_window_changes(void)
2575{
2576 int i;
2577 struct winsize ws;
2578
2579 for (i = 0; i < channels_alloc; i++) {
2580 if (channels[i] == NULL ||
2581 channels[i]->type != SSH_CHANNEL_OPEN)
2582 continue;
2583 if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
2584 continue;
2585 channel_request_start(i, "window-change", 0);
2586 packet_put_int(ws.ws_col);
2587 packet_put_int(ws.ws_row);
2588 packet_put_int(ws.ws_xpixel);
2589 packet_put_int(ws.ws_ypixel);
2590 packet_send();
2591 }
2592}
2593
2534/* -- X11 forwarding */ 2594/* -- X11 forwarding */
2535 2595
2536/* 2596/*
diff --git a/channels.h b/channels.h
index 0a49c55ea..41f3cedd3 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: channels.h,v 1.72 2004/05/21 11:33:11 djm Exp $ */ 1/* $OpenBSD: channels.h,v 1.73 2004/06/13 15:03:02 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -76,6 +76,7 @@ struct Channel {
76 int wfd; /* write fd */ 76 int wfd; /* write fd */
77 int efd; /* extended fd */ 77 int efd; /* extended fd */
78 int sock; /* sock fd */ 78 int sock; /* sock fd */
79 int ctl_fd; /* control fd (client sharing) */
79 int isatty; /* rfd is a tty */ 80 int isatty; /* rfd is a tty */
80 int wfd_isatty; /* wfd is a tty */ 81 int wfd_isatty; /* wfd is a tty */
81 int force_drain; /* force close on iEOF */ 82 int force_drain; /* force close on iEOF */
@@ -105,6 +106,7 @@ struct Channel {
105 /* callback */ 106 /* callback */
106 channel_callback_fn *confirm; 107 channel_callback_fn *confirm;
107 channel_callback_fn *detach_user; 108 channel_callback_fn *detach_user;
109 void *confirm_ctx;
108 110
109 /* filter */ 111 /* filter */
110 channel_filter_fn *input_filter; 112 channel_filter_fn *input_filter;
@@ -161,10 +163,11 @@ void channel_stop_listening(void);
161void channel_send_open(int); 163void channel_send_open(int);
162void channel_request_start(int, char *, int); 164void channel_request_start(int, char *, int);
163void channel_register_cleanup(int, channel_callback_fn *); 165void channel_register_cleanup(int, channel_callback_fn *);
164void channel_register_confirm(int, channel_callback_fn *); 166void channel_register_confirm(int, channel_callback_fn *, void *);
165void channel_register_filter(int, channel_filter_fn *); 167void channel_register_filter(int, channel_filter_fn *);
166void channel_cancel_cleanup(int); 168void channel_cancel_cleanup(int);
167int channel_close_fd(int *); 169int channel_close_fd(int *);
170void channel_send_window_changes(void);
168 171
169/* protocol handler */ 172/* protocol handler */
170 173
diff --git a/clientloop.c b/clientloop.c
index 31e604180..6401588a9 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -59,7 +59,7 @@
59 */ 59 */
60 60
61#include "includes.h" 61#include "includes.h"
62RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $"); 62RCSID("$OpenBSD: clientloop.c,v 1.123 2004/06/13 15:03:02 djm Exp $");
63 63
64#include "ssh.h" 64#include "ssh.h"
65#include "ssh1.h" 65#include "ssh1.h"
@@ -81,6 +81,9 @@ RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $");
81#include "atomicio.h" 81#include "atomicio.h"
82#include "sshpty.h" 82#include "sshpty.h"
83#include "misc.h" 83#include "misc.h"
84#include "monitor_fdpass.h"
85#include "match.h"
86#include "msg.h"
84 87
85/* import options */ 88/* import options */
86extern Options options; 89extern Options options;
@@ -91,6 +94,9 @@ extern int stdin_null_flag;
91/* Flag indicating that no shell has been requested */ 94/* Flag indicating that no shell has been requested */
92extern int no_shell_flag; 95extern int no_shell_flag;
93 96
97/* Control socket */
98extern int control_fd;
99
94/* 100/*
95 * Name of the host we are connecting to. This is the name given on the 101 * Name of the host we are connecting to. This is the name given on the
96 * command line, or the HostName specified for the user-supplied name in a 102 * command line, or the HostName specified for the user-supplied name in a
@@ -131,9 +137,19 @@ static int server_alive_timeouts = 0;
131static void client_init_dispatch(void); 137static void client_init_dispatch(void);
132int session_ident = -1; 138int session_ident = -1;
133 139
140struct confirm_ctx {
141 int want_tty;
142 int want_subsys;
143 Buffer cmd;
144 char *term;
145 struct termios tio;
146};
147
134/*XXX*/ 148/*XXX*/
135extern Kex *xxx_kex; 149extern Kex *xxx_kex;
136 150
151void ssh_process_session2_setup(int, int, int, Buffer *);
152
137/* Restores stdin to blocking mode. */ 153/* Restores stdin to blocking mode. */
138 154
139static void 155static void
@@ -291,19 +307,13 @@ client_check_window_change(void)
291 /** XXX race */ 307 /** XXX race */
292 received_window_change_signal = 0; 308 received_window_change_signal = 0;
293 309
294 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
295 return;
296
297 debug2("client_check_window_change: changed"); 310 debug2("client_check_window_change: changed");
298 311
299 if (compat20) { 312 if (compat20) {
300 channel_request_start(session_ident, "window-change", 0); 313 channel_send_window_changes();
301 packet_put_int(ws.ws_col);
302 packet_put_int(ws.ws_row);
303 packet_put_int(ws.ws_xpixel);
304 packet_put_int(ws.ws_ypixel);
305 packet_send();
306 } else { 314 } else {
315 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
316 return;
307 packet_start(SSH_CMSG_WINDOW_SIZE); 317 packet_start(SSH_CMSG_WINDOW_SIZE);
308 packet_put_int(ws.ws_row); 318 packet_put_int(ws.ws_row);
309 packet_put_int(ws.ws_col); 319 packet_put_int(ws.ws_col);
@@ -335,7 +345,6 @@ server_alive_check(void)
335 * Waits until the client can do something (some data becomes available on 345 * Waits until the client can do something (some data becomes available on
336 * one of the file descriptors). 346 * one of the file descriptors).
337 */ 347 */
338
339static void 348static void
340client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 349client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
341 int *maxfdp, int *nallocp, int rekeying) 350 int *maxfdp, int *nallocp, int rekeying)
@@ -381,6 +390,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
381 if (packet_have_data_to_write()) 390 if (packet_have_data_to_write())
382 FD_SET(connection_out, *writesetp); 391 FD_SET(connection_out, *writesetp);
383 392
393 if (control_fd != -1)
394 FD_SET(control_fd, *readsetp);
395
384 /* 396 /*
385 * Wait for something to happen. This will suspend the process until 397 * Wait for something to happen. This will suspend the process until
386 * some selected descriptor can be read, written, or has some other 398 * some selected descriptor can be read, written, or has some other
@@ -500,6 +512,176 @@ client_process_net_input(fd_set * readset)
500} 512}
501 513
502static void 514static void
515client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
516{
517 int id;
518 Channel *c;
519
520 id = packet_get_int();
521 packet_check_eom();
522
523 if ((c = channel_lookup(id)) == NULL) {
524 error("%s: no channel for id %d", __func__, id);
525 return;
526 }
527
528 if (type == SSH2_MSG_CHANNEL_SUCCESS)
529 debug2("Request suceeded on channel %d", id);
530 else if (type == SSH2_MSG_CHANNEL_FAILURE) {
531 error("Request failed on channel %d", id);
532 channel_free(c);
533 }
534}
535
536static void
537client_extra_session2_setup(int id, void *arg)
538{
539 struct confirm_ctx *cctx = arg;
540 Channel *c;
541
542 if (cctx == NULL)
543 fatal("%s: cctx == NULL", __func__);
544 if ((c = channel_lookup(id)) == NULL)
545 fatal("%s: no channel for id %d", __func__, id);
546
547 client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
548 cctx->term, &cctx->tio, c->rfd, &cctx->cmd,
549 client_subsystem_reply);
550
551 c->confirm_ctx = NULL;
552 buffer_free(&cctx->cmd);
553 free(cctx->term);
554 free(cctx);
555}
556
557static void
558client_process_control(fd_set * readset)
559{
560 Buffer m;
561 Channel *c;
562 int client_fd, new_fd[3], ver;
563 socklen_t addrlen;
564 struct sockaddr_storage addr;
565 struct confirm_ctx *cctx;
566 char *cmd;
567 u_int len;
568 uid_t euid;
569 gid_t egid;
570
571 /*
572 * Accept connection on control socket
573 */
574 if (control_fd == -1 || !FD_ISSET(control_fd, readset))
575 return;
576
577 memset(&addr, 0, sizeof(addr));
578 addrlen = sizeof(addr);
579 if ((client_fd = accept(control_fd,
580 (struct sockaddr*)&addr, &addrlen)) == -1) {
581 error("%s accept: %s", __func__, strerror(errno));
582 return;
583 }
584
585 if (getpeereid(client_fd, &euid, &egid) < 0) {
586 error("%s getpeereid failed: %s", __func__, strerror(errno));
587 close(client_fd);
588 return;
589 }
590 if ((euid != 0) && (getuid() != euid)) {
591 error("control mode uid mismatch: peer euid %u != uid %u",
592 (u_int) euid, (u_int) getuid());
593 close(client_fd);
594 return;
595 }
596 /* XXX: implement use of ssh-askpass to confirm additional channels */
597
598 unset_nonblock(client_fd);
599
600 buffer_init(&m);
601
602 buffer_put_int(&m, getpid());
603 if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
604 error("%s: client msg_send failed", __func__);
605 close(client_fd);
606 return;
607 }
608 buffer_clear(&m);
609
610 if (ssh_msg_recv(client_fd, &m) == -1) {
611 error("%s: client msg_recv failed", __func__);
612 close(client_fd);
613 return;
614 }
615
616 if ((ver = buffer_get_char(&m)) != 0) {
617 error("%s: wrong client version %d", __func__, ver);
618 buffer_free(&m);
619 close(client_fd);
620 return;
621 }
622
623 cctx = xmalloc(sizeof(*cctx));
624 memset(cctx, 0, sizeof(*cctx));
625
626 cctx->want_tty = buffer_get_int(&m);
627 cctx->want_subsys = buffer_get_int(&m);
628 cctx->term = buffer_get_string(&m, &len);
629
630 cmd = buffer_get_string(&m, &len);
631 buffer_init(&cctx->cmd);
632 buffer_append(&cctx->cmd, cmd, strlen(cmd));
633
634 debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
635 cctx->want_tty, cctx->want_subsys, cmd);
636
637 /* Gather fds from client */
638 new_fd[0] = mm_receive_fd(client_fd);
639 new_fd[1] = mm_receive_fd(client_fd);
640 new_fd[2] = mm_receive_fd(client_fd);
641
642 debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
643 new_fd[0], new_fd[1], new_fd[2]);
644
645 /* Try to pick up ttymodes from client before it goes raw */
646 if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
647 error("%s: tcgetattr: %s", __func__, strerror(errno));
648
649 buffer_clear(&m);
650 if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
651 error("%s: client msg_send failed", __func__);
652 close(client_fd);
653 close(new_fd[0]);
654 close(new_fd[1]);
655 close(new_fd[2]);
656 return;
657 }
658 buffer_free(&m);
659
660 /* enable nonblocking unless tty */
661 if (!isatty(new_fd[0]))
662 set_nonblock(new_fd[0]);
663 if (!isatty(new_fd[1]))
664 set_nonblock(new_fd[1]);
665 if (!isatty(new_fd[2]))
666 set_nonblock(new_fd[2]);
667
668 set_nonblock(client_fd);
669
670 c = channel_new("session", SSH_CHANNEL_OPENING,
671 new_fd[0], new_fd[1], new_fd[2],
672 CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT,
673 CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
674
675 /* XXX */
676 c->ctl_fd = client_fd;
677
678 debug3("%s: channel_new: %d", __func__, c->self);
679
680 channel_send_open(c->self);
681 channel_register_confirm(c->self, client_extra_session2_setup, cctx);
682}
683
684static void
503process_cmdline(void) 685process_cmdline(void)
504{ 686{
505 void (*handler)(int); 687 void (*handler)(int);
@@ -901,9 +1083,6 @@ simple_escape_filter(Channel *c, char *buf, int len)
901static void 1083static void
902client_channel_closed(int id, void *arg) 1084client_channel_closed(int id, void *arg)
903{ 1085{
904 if (id != session_ident)
905 error("client_channel_closed: id %d != session_ident %d",
906 id, session_ident);
907 channel_cancel_cleanup(id); 1086 channel_cancel_cleanup(id);
908 session_closed = 1; 1087 session_closed = 1;
909 leave_raw_mode(); 1088 leave_raw_mode();
@@ -937,6 +1116,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
937 connection_in = packet_get_connection_in(); 1116 connection_in = packet_get_connection_in();
938 connection_out = packet_get_connection_out(); 1117 connection_out = packet_get_connection_out();
939 max_fd = MAX(connection_in, connection_out); 1118 max_fd = MAX(connection_in, connection_out);
1119 if (control_fd != -1)
1120 max_fd = MAX(max_fd, control_fd);
940 1121
941 if (!compat20) { 1122 if (!compat20) {
942 /* enable nonblocking unless tty */ 1123 /* enable nonblocking unless tty */
@@ -1054,6 +1235,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1054 /* Buffer input from the connection. */ 1235 /* Buffer input from the connection. */
1055 client_process_net_input(readset); 1236 client_process_net_input(readset);
1056 1237
1238 /* Accept control connections. */
1239 client_process_control(readset);
1240
1057 if (quit_pending) 1241 if (quit_pending)
1058 break; 1242 break;
1059 1243
@@ -1385,7 +1569,7 @@ static void
1385client_input_channel_req(int type, u_int32_t seq, void *ctxt) 1569client_input_channel_req(int type, u_int32_t seq, void *ctxt)
1386{ 1570{
1387 Channel *c = NULL; 1571 Channel *c = NULL;
1388 int id, reply, success = 0; 1572 int exitval, id, reply, success = 0;
1389 char *rtype; 1573 char *rtype;
1390 1574
1391 id = packet_get_int(); 1575 id = packet_get_int();
@@ -1395,18 +1579,21 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
1395 debug("client_input_channel_req: channel %d rtype %s reply %d", 1579 debug("client_input_channel_req: channel %d rtype %s reply %d",
1396 id, rtype, reply); 1580 id, rtype, reply);
1397 1581
1398 if (session_ident == -1) {
1399 error("client_input_channel_req: no channel %d", session_ident);
1400 } else if (id != session_ident) {
1401 error("client_input_channel_req: channel %d: wrong channel: %d",
1402 session_ident, id);
1403 }
1404 c = channel_lookup(id); 1582 c = channel_lookup(id);
1405 if (c == NULL) { 1583 if (c == NULL) {
1406 error("client_input_channel_req: channel %d: unknown channel", id); 1584 error("client_input_channel_req: channel %d: unknown channel", id);
1407 } else if (strcmp(rtype, "exit-status") == 0) { 1585 } else if (strcmp(rtype, "exit-status") == 0) {
1408 success = 1; 1586 exitval = packet_get_int();
1409 exit_status = packet_get_int(); 1587 if (id == session_ident) {
1588 success = 1;
1589 exit_status = exitval;
1590 } else if (c->ctl_fd == -1) {
1591 error("client_input_channel_req: unexpected channel %d",
1592 session_ident);
1593 } else {
1594 atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
1595 success = 1;
1596 }
1410 packet_check_eom(); 1597 packet_check_eom();
1411 } 1598 }
1412 if (reply) { 1599 if (reply) {
@@ -1437,6 +1624,98 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt)
1437 xfree(rtype); 1624 xfree(rtype);
1438} 1625}
1439 1626
1627void
1628client_session2_setup(int id, int want_tty, int want_subsystem,
1629 const char *term, struct termios *tiop, int in_fd, Buffer *cmd,
1630 dispatch_fn *subsys_repl)
1631{
1632 int len;
1633
1634 debug2("%s: id %d", __func__, id);
1635
1636 if (want_tty) {
1637 struct winsize ws;
1638 struct termios tio;
1639
1640 /* Store window size in the packet. */
1641 if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
1642 memset(&ws, 0, sizeof(ws));
1643
1644 channel_request_start(id, "pty-req", 0);
1645 packet_put_cstring(term != NULL ? term : "");
1646 packet_put_int(ws.ws_col);
1647 packet_put_int(ws.ws_row);
1648 packet_put_int(ws.ws_xpixel);
1649 packet_put_int(ws.ws_ypixel);
1650 tio = get_saved_tio();
1651 tty_make_modes(-1, tiop != NULL ? tiop : &tio);
1652 packet_send();
1653 /* XXX wait for reply */
1654 }
1655
1656 /* Transfer any environment variables from client to server */
1657 if (options.num_send_env != 0) {
1658 int i, j, matched;
1659 extern char **environ;
1660 char *name, *val;
1661
1662 debug("Sending environment.");
1663 for (i = 0; environ && environ[i] != NULL; i++) {
1664 /* Split */
1665 name = xstrdup(environ[i]);
1666 if ((val = strchr(name, '=')) == NULL) {
1667 free(name);
1668 continue;
1669 }
1670 *val++ = '\0';
1671
1672 matched = 0;
1673 for (j = 0; j < options.num_send_env; j++) {
1674 if (match_pattern(name, options.send_env[j])) {
1675 matched = 1;
1676 break;
1677 }
1678 }
1679 if (!matched) {
1680 debug3("Ignored env %s", name);
1681 free(name);
1682 continue;
1683 }
1684
1685 debug("Sending env %s = %s", name, val);
1686 channel_request_start(id, "env", 0);
1687 packet_put_cstring(name);
1688 packet_put_cstring(val);
1689 packet_send();
1690 free(name);
1691 }
1692 }
1693
1694 len = buffer_len(cmd);
1695 if (len > 0) {
1696 if (len > 900)
1697 len = 900;
1698 if (want_subsystem) {
1699 debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
1700 channel_request_start(id, "subsystem", subsys_repl != NULL);
1701 if (subsys_repl != NULL) {
1702 /* register callback for reply */
1703 /* XXX we assume that client_loop has already been called */
1704 dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
1705 dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
1706 }
1707 } else {
1708 debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
1709 channel_request_start(id, "exec", 0);
1710 }
1711 packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
1712 packet_send();
1713 } else {
1714 channel_request_start(id, "shell", 0);
1715 packet_send();
1716 }
1717}
1718
1440static void 1719static void
1441client_init_dispatch_20(void) 1720client_init_dispatch_20(void)
1442{ 1721{
@@ -1503,5 +1782,7 @@ cleanup_exit(int i)
1503{ 1782{
1504 leave_raw_mode(); 1783 leave_raw_mode();
1505 leave_non_blocking(); 1784 leave_non_blocking();
1785 if (options.control_path != NULL && control_fd != -1)
1786 unlink(options.control_path);
1506 _exit(i); 1787 _exit(i);
1507} 1788}
diff --git a/clientloop.h b/clientloop.h
index 56af06bc1..f1e13ac3a 100644
--- a/clientloop.h
+++ b/clientloop.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: clientloop.h,v 1.8 2003/12/16 15:49:51 markus Exp $ */ 1/* $OpenBSD: clientloop.h,v 1.9 2004/06/13 15:03:02 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -38,3 +38,5 @@
38/* Client side main loop for the interactive session. */ 38/* Client side main loop for the interactive session. */
39int client_loop(int, int, int); 39int client_loop(int, int, int);
40void client_global_request_reply_fwd(int, u_int32_t, void *); 40void client_global_request_reply_fwd(int, u_int32_t, void *);
41void client_session2_setup(int, int, int, const char *, struct termios *,
42 int, Buffer *, dispatch_fn *);
diff --git a/defines.h b/defines.h
index 9b72afecb..73a45fe44 100644
--- a/defines.h
+++ b/defines.h
@@ -25,7 +25,7 @@
25#ifndef _DEFINES_H 25#ifndef _DEFINES_H
26#define _DEFINES_H 26#define _DEFINES_H
27 27
28/* $Id: defines.h,v 1.115 2004/04/14 07:24:30 dtucker Exp $ */ 28/* $Id: defines.h,v 1.116 2004/06/15 00:34:08 djm Exp $ */
29 29
30 30
31/* Constants */ 31/* Constants */
@@ -462,6 +462,9 @@ struct winsize {
462 (struct cmsghdr *)NULL) 462 (struct cmsghdr *)NULL)
463#endif /* CMSG_FIRSTHDR */ 463#endif /* CMSG_FIRSTHDR */
464 464
465#ifndef offsetof
466# define offsetof(type, member) ((size_t) &((type *)0)->member)
467#endif
465 468
466/* Function replacement / compatibility hacks */ 469/* Function replacement / compatibility hacks */
467 470
diff --git a/includes.h b/includes.h
index ca943c7e6..99b70502c 100644
--- a/includes.h
+++ b/includes.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: includes.h,v 1.17 2002/01/26 16:44:22 stevesk Exp $ */ 1/* $OpenBSD: includes.h,v 1.18 2004/06/13 15:03:02 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -33,6 +33,7 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg }
33#include <grp.h> 33#include <grp.h>
34#include <time.h> 34#include <time.h>
35#include <dirent.h> 35#include <dirent.h>
36#include <stddef.h>
36 37
37#ifdef HAVE_LIMITS_H 38#ifdef HAVE_LIMITS_H
38# include <limits.h> /* For PATH_MAX */ 39# include <limits.h> /* For PATH_MAX */
diff --git a/readconf.c b/readconf.c
index 5aa371ed9..2b1d7cc46 100644
--- a/readconf.c
+++ b/readconf.c
@@ -12,7 +12,7 @@
12 */ 12 */
13 13
14#include "includes.h" 14#include "includes.h"
15RCSID("$OpenBSD: readconf.c,v 1.131 2004/05/27 00:50:13 dtucker Exp $"); 15RCSID("$OpenBSD: readconf.c,v 1.132 2004/06/13 15:03:02 djm Exp $");
16 16
17#include "ssh.h" 17#include "ssh.h"
18#include "xmalloc.h" 18#include "xmalloc.h"
@@ -106,7 +106,7 @@ typedef enum {
106 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 106 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
107 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 107 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
108 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 108 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
109 oSendEnv, 109 oSendEnv, oControlPath, oControlMaster,
110 oDeprecated, oUnsupported 110 oDeprecated, oUnsupported
111} OpCodes; 111} OpCodes;
112 112
@@ -195,6 +195,8 @@ static struct {
195 { "serveraliveinterval", oServerAliveInterval }, 195 { "serveraliveinterval", oServerAliveInterval },
196 { "serveralivecountmax", oServerAliveCountMax }, 196 { "serveralivecountmax", oServerAliveCountMax },
197 { "sendenv", oSendEnv }, 197 { "sendenv", oSendEnv },
198 { "controlpath", oControlPath },
199 { "controlmaster", oControlMaster },
198 { NULL, oBadOption } 200 { NULL, oBadOption }
199}; 201};
200 202
@@ -764,6 +766,14 @@ parse_int:
764 } 766 }
765 break; 767 break;
766 768
769 case oControlPath:
770 charptr = &options->control_path;
771 goto parse_string;
772
773 case oControlMaster:
774 intptr = &options->control_master;
775 goto parse_flag;
776
767 case oDeprecated: 777 case oDeprecated:
768 debug("%s line %d: Deprecated option \"%s\"", 778 debug("%s line %d: Deprecated option \"%s\"",
769 filename, linenum, keyword); 779 filename, linenum, keyword);
@@ -905,6 +915,8 @@ initialize_options(Options * options)
905 options->server_alive_interval = -1; 915 options->server_alive_interval = -1;
906 options->server_alive_count_max = -1; 916 options->server_alive_count_max = -1;
907 options->num_send_env = 0; 917 options->num_send_env = 0;
918 options->control_path = NULL;
919 options->control_master = -1;
908} 920}
909 921
910/* 922/*
@@ -1025,6 +1037,8 @@ fill_default_options(Options * options)
1025 options->server_alive_interval = 0; 1037 options->server_alive_interval = 0;
1026 if (options->server_alive_count_max == -1) 1038 if (options->server_alive_count_max == -1)
1027 options->server_alive_count_max = 3; 1039 options->server_alive_count_max = 3;
1040 if (options->control_master == -1)
1041 options->control_master = 0;
1028 /* options->proxy_command should not be set by default */ 1042 /* options->proxy_command should not be set by default */
1029 /* options->user will be set in the main program if appropriate */ 1043 /* options->user will be set in the main program if appropriate */
1030 /* options->hostname will be set in the main program if appropriate */ 1044 /* options->hostname will be set in the main program if appropriate */
diff --git a/readconf.h b/readconf.h
index 668055943..5e504bece 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.62 2004/04/27 09:46:37 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.63 2004/06/13 15:03:02 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -108,6 +108,9 @@ typedef struct {
108 108
109 int num_send_env; 109 int num_send_env;
110 char *send_env[MAX_SEND_ENV]; 110 char *send_env[MAX_SEND_ENV];
111
112 char *control_path;
113 int control_master;
111} Options; 114} Options;
112 115
113 116
diff --git a/scp.1 b/scp.1
index 202ebaadb..f346b2ae9 100644
--- a/scp.1
+++ b/scp.1
@@ -9,7 +9,7 @@
9.\" 9.\"
10.\" Created: Sun May 7 00:14:37 1995 ylo 10.\" Created: Sun May 7 00:14:37 1995 ylo
11.\" 11.\"
12.\" $OpenBSD: scp.1,v 1.35 2004/05/04 18:36:07 jmc Exp $ 12.\" $OpenBSD: scp.1,v 1.36 2004/06/13 15:03:02 djm Exp $
13.\" 13.\"
14.Dd September 25, 1999 14.Dd September 25, 1999
15.Dt SCP 1 15.Dt SCP 1
@@ -128,6 +128,8 @@ For full details of the options listed below, and their possible values, see
128.It CompressionLevel 128.It CompressionLevel
129.It ConnectionAttempts 129.It ConnectionAttempts
130.It ConnectTimeout 130.It ConnectTimeout
131.It ControlMaster
132.It ControlPath
131.It GlobalKnownHostsFile 133.It GlobalKnownHostsFile
132.It GSSAPIAuthentication 134.It GSSAPIAuthentication
133.It GSSAPIDelegateCredentials 135.It GSSAPIDelegateCredentials
diff --git a/sftp.1 b/sftp.1
index 795a0342f..7f0ef1121 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.54 2004/05/02 23:02:17 dtucker Exp $ 1.\" $OpenBSD: sftp.1,v 1.55 2004/06/13 15:03:02 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -154,6 +154,8 @@ For full details of the options listed below, and their possible values, see
154.It CompressionLevel 154.It CompressionLevel
155.It ConnectionAttempts 155.It ConnectionAttempts
156.It ConnectTimeout 156.It ConnectTimeout
157.It ControlMaster
158.It ControlPath
157.It GlobalKnownHostsFile 159.It GlobalKnownHostsFile
158.It GSSAPIAuthentication 160.It GSSAPIAuthentication
159.It GSSAPIDelegateCredentials 161.It GSSAPIDelegateCredentials
diff --git a/ssh-rand-helper.c b/ssh-rand-helper.c
index 8a320a71e..471e7295b 100644
--- a/ssh-rand-helper.c
+++ b/ssh-rand-helper.c
@@ -39,7 +39,7 @@
39#include "pathnames.h" 39#include "pathnames.h"
40#include "log.h" 40#include "log.h"
41 41
42RCSID("$Id: ssh-rand-helper.c,v 1.16 2003/11/21 12:56:47 djm Exp $"); 42RCSID("$Id: ssh-rand-helper.c,v 1.17 2004/06/15 00:34:08 djm Exp $");
43 43
44/* Number of bytes we write out */ 44/* Number of bytes we write out */
45#define OUTPUT_SEED_SIZE 48 45#define OUTPUT_SEED_SIZE 48
@@ -69,10 +69,6 @@ extern char *__progname;
69char *__progname; 69char *__progname;
70#endif 70#endif
71 71
72#ifndef offsetof
73# define offsetof(type, member) ((size_t) &((type *)0)->member)
74#endif
75
76#define WHITESPACE " \t\n" 72#define WHITESPACE " \t\n"
77 73
78#ifndef RUSAGE_SELF 74#ifndef RUSAGE_SELF
diff --git a/ssh.1 b/ssh.1
index 6cef0851d..b70102be5 100644
--- a/ssh.1
+++ b/ssh.1
@@ -34,7 +34,7 @@
34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36.\" 36.\"
37.\" $OpenBSD: ssh.1,v 1.189 2004/06/13 14:01:42 dtucker Exp $ 37.\" $OpenBSD: ssh.1,v 1.190 2004/06/13 15:03:02 djm Exp $
38.Dd September 25, 1999 38.Dd September 25, 1999
39.Dt SSH 1 39.Dt SSH 1
40.Os 40.Os
@@ -43,7 +43,7 @@
43.Nd OpenSSH SSH client (remote login program) 43.Nd OpenSSH SSH client (remote login program)
44.Sh SYNOPSIS 44.Sh SYNOPSIS
45.Nm ssh 45.Nm ssh
46.Op Fl 1246AaCfgkNnqsTtVvXxY 46.Op Fl 1246AaCfgkMNnqSsTtVvXxY
47.Op Fl b Ar bind_address 47.Op Fl b Ar bind_address
48.Op Fl c Ar cipher_spec 48.Op Fl c Ar cipher_spec
49.Op Fl D Ar port 49.Op Fl D Ar port
@@ -605,6 +605,17 @@ be specified in order of preference.
605See the 605See the
606.Cm MACs 606.Cm MACs
607keyword for more information. 607keyword for more information.
608.It Fl M
609Places the
610.Nm
611client into
612.Dq master
613mode for connection sharing.
614Refer to the description of
615.Cm ControlMaster
616in
617.Xr ssh_config 5
618for details.
608.It Fl N 619.It Fl N
609Do not execute a remote command. 620Do not execute a remote command.
610This is useful for just forwarding ports 621This is useful for just forwarding ports
@@ -649,6 +660,8 @@ For full details of the options listed below, and their possible values, see
649.It CompressionLevel 660.It CompressionLevel
650.It ConnectionAttempts 661.It ConnectionAttempts
651.It ConnectTimeout 662.It ConnectTimeout
663.It ControlMaster
664.It ControlPath
652.It DynamicForward 665.It DynamicForward
653.It EscapeChar 666.It EscapeChar
654.It ForwardAgent 667.It ForwardAgent
@@ -724,6 +737,15 @@ IPv6 addresses can be specified with an alternative syntax:
724.Ar hostport . 737.Ar hostport .
725.Xc 738.Xc
726.Sm on 739.Sm on
740.It Fl S
741Places the
742.Nm
743client into slave mode for connection sharing.
744Refer to the description of
745.Cm ControlMaster
746in
747.Xr ssh_config 5
748for details.
727.It Fl s 749.It Fl s
728May be used to request invocation of a subsystem on the remote system. 750May be used to request invocation of a subsystem on the remote system.
729Subsystems are a feature of the SSH2 protocol which facilitate the use 751Subsystems are a feature of the SSH2 protocol which facilitate the use
diff --git a/ssh.c b/ssh.c
index 3c21fa37d..1c6ec8b6a 100644
--- a/ssh.c
+++ b/ssh.c
@@ -40,7 +40,7 @@
40 */ 40 */
41 41
42#include "includes.h" 42#include "includes.h"
43RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $"); 43RCSID("$OpenBSD: ssh.c,v 1.214 2004/06/13 15:03:02 djm Exp $");
44 44
45#include <openssl/evp.h> 45#include <openssl/evp.h>
46#include <openssl/err.h> 46#include <openssl/err.h>
@@ -53,21 +53,24 @@ RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $");
53#include "xmalloc.h" 53#include "xmalloc.h"
54#include "packet.h" 54#include "packet.h"
55#include "buffer.h" 55#include "buffer.h"
56#include "bufaux.h"
56#include "channels.h" 57#include "channels.h"
57#include "key.h" 58#include "key.h"
58#include "authfd.h" 59#include "authfd.h"
59#include "authfile.h" 60#include "authfile.h"
60#include "pathnames.h" 61#include "pathnames.h"
62#include "dispatch.h"
61#include "clientloop.h" 63#include "clientloop.h"
62#include "log.h" 64#include "log.h"
63#include "readconf.h" 65#include "readconf.h"
64#include "sshconnect.h" 66#include "sshconnect.h"
65#include "dispatch.h"
66#include "misc.h" 67#include "misc.h"
67#include "kex.h" 68#include "kex.h"
68#include "mac.h" 69#include "mac.h"
69#include "sshpty.h" 70#include "sshpty.h"
70#include "match.h" 71#include "match.h"
72#include "msg.h"
73#include "monitor_fdpass.h"
71 74
72#ifdef SMARTCARD 75#ifdef SMARTCARD
73#include "scard.h" 76#include "scard.h"
@@ -141,6 +144,13 @@ static int client_global_request_id = 0;
141/* pid of proxycommand child process */ 144/* pid of proxycommand child process */
142pid_t proxy_command_pid = 0; 145pid_t proxy_command_pid = 0;
143 146
147/* fd to control socket */
148int control_fd = -1;
149
150/* Only used in control client mode */
151volatile sig_atomic_t control_client_terminate = 0;
152u_int control_server_pid = 0;
153
144/* Prints a help message to the user. This function never returns. */ 154/* Prints a help message to the user. This function never returns. */
145 155
146static void 156static void
@@ -158,6 +168,7 @@ usage(void)
158static int ssh_session(void); 168static int ssh_session(void);
159static int ssh_session2(void); 169static int ssh_session2(void);
160static void load_public_identity_files(void); 170static void load_public_identity_files(void);
171static void control_client(const char *path);
161 172
162/* 173/*
163 * Main program for the ssh client. 174 * Main program for the ssh client.
@@ -228,7 +239,7 @@ main(int ac, char **av)
228 239
229again: 240again:
230 while ((opt = getopt(ac, av, 241 while ((opt = getopt(ac, av,
231 "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVXY")) != -1) { 242 "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNPR:S:TVXY")) != -1) {
232 switch (opt) { 243 switch (opt) {
233 case '1': 244 case '1':
234 options.protocol = SSH_PROTO_1; 245 options.protocol = SSH_PROTO_1;
@@ -364,6 +375,9 @@ again:
364 exit(1); 375 exit(1);
365 } 376 }
366 break; 377 break;
378 case 'M':
379 options.control_master = 1;
380 break;
367 case 'p': 381 case 'p':
368 options.port = a2port(optarg); 382 options.port = a2port(optarg);
369 if (options.port == 0) { 383 if (options.port == 0) {
@@ -432,6 +446,13 @@ again:
432 case 's': 446 case 's':
433 subsystem_flag = 1; 447 subsystem_flag = 1;
434 break; 448 break;
449 case 'S':
450 if (options.control_path != NULL)
451 free(options.control_path);
452 options.control_path = xstrdup(optarg);
453 if (options.control_master == -1)
454 options.control_master = 0;
455 break;
435 case 'b': 456 case 'b':
436 options.bind_address = optarg; 457 options.bind_address = optarg;
437 break; 458 break;
@@ -566,6 +587,13 @@ again:
566 strcmp(options.proxy_command, "none") == 0) 587 strcmp(options.proxy_command, "none") == 0)
567 options.proxy_command = NULL; 588 options.proxy_command = NULL;
568 589
590 if (options.control_path != NULL) {
591 options.control_path = tilde_expand_filename(
592 options.control_path, original_real_uid);
593 }
594 if (options.control_path != NULL && options.control_master == 0)
595 control_client(options.control_path); /* This doesn't return */
596
569 /* Open a connection to the remote host. */ 597 /* Open a connection to the remote host. */
570 if (ssh_connect(host, &hostaddr, options.port, 598 if (ssh_connect(host, &hostaddr, options.port,
571 options.address_family, options.connection_attempts, 599 options.address_family, options.connection_attempts,
@@ -678,6 +706,9 @@ again:
678 exit_status = compat20 ? ssh_session2() : ssh_session(); 706 exit_status = compat20 ? ssh_session2() : ssh_session();
679 packet_close(); 707 packet_close();
680 708
709 if (options.control_path != NULL && control_fd != -1)
710 unlink(options.control_path);
711
681 /* 712 /*
682 * Send SIGHUP to proxy command if used. We don't wait() in 713 * Send SIGHUP to proxy command if used. We don't wait() in
683 * case it hangs and instead rely on init to reap the child 714 * case it hangs and instead rely on init to reap the child
@@ -974,7 +1005,7 @@ ssh_session(void)
974} 1005}
975 1006
976static void 1007static void
977client_subsystem_reply(int type, u_int32_t seq, void *ctxt) 1008ssh_subsystem_reply(int type, u_int32_t seq, void *ctxt)
978{ 1009{
979 int id, len; 1010 int id, len;
980 1011
@@ -1006,40 +1037,50 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt)
1006 options.remote_forwards[i].port); 1037 options.remote_forwards[i].port);
1007} 1038}
1008 1039
1009/* request pty/x11/agent/tcpfwd/shell for channel */
1010static void 1040static void
1011ssh_session2_setup(int id, void *arg) 1041ssh_control_listener(void)
1012{ 1042{
1013 int len; 1043 struct sockaddr_un addr;
1014 int interactive = 0; 1044 mode_t old_umask;
1015 struct termios tio; 1045
1046 if (options.control_path == NULL || options.control_master != 1)
1047 return;
1016 1048
1017 debug2("ssh_session2_setup: id %d", id); 1049 memset(&addr, '\0', sizeof(addr));
1050 addr.sun_family = AF_UNIX;
1051 addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
1052 strlen(options.control_path) + 1;
1018 1053
1019 if (tty_flag) { 1054 if (strlcpy(addr.sun_path, options.control_path,
1020 struct winsize ws; 1055 sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
1021 char *cp; 1056 fatal("ControlPath too long");
1022 cp = getenv("TERM");
1023 if (!cp)
1024 cp = "";
1025 /* Store window size in the packet. */
1026 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
1027 memset(&ws, 0, sizeof(ws));
1028 1057
1029 channel_request_start(id, "pty-req", 0); 1058 if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1030 packet_put_cstring(cp); 1059 fatal("%s socket(): %s\n", __func__, strerror(errno));
1031 packet_put_int(ws.ws_col); 1060
1032 packet_put_int(ws.ws_row); 1061 old_umask = umask(0177);
1033 packet_put_int(ws.ws_xpixel); 1062 if (bind(control_fd, (struct sockaddr*)&addr, addr.sun_len) == -1) {
1034 packet_put_int(ws.ws_ypixel); 1063 control_fd = -1;
1035 tio = get_saved_tio(); 1064 if (errno == EINVAL)
1036 tty_make_modes(/*ignored*/ 0, &tio); 1065 fatal("ControlSocket %s already exists",
1037 packet_send(); 1066 options.control_path);
1038 interactive = 1; 1067 else
1039 /* XXX wait for reply */ 1068 fatal("%s bind(): %s\n", __func__, strerror(errno));
1040 } 1069 }
1041 if (options.forward_x11 && 1070 umask(old_umask);
1042 getenv("DISPLAY") != NULL) { 1071
1072 if (listen(control_fd, 64) == -1)
1073 fatal("%s listen(): %s\n", __func__, strerror(errno));
1074
1075 set_nonblock(control_fd);
1076}
1077
1078/* request pty/x11/agent/tcpfwd/shell for channel */
1079static void
1080ssh_session2_setup(int id, void *arg)
1081{
1082 int interactive = tty_flag;
1083 if (options.forward_x11 && getenv("DISPLAY") != NULL) {
1043 char *proto, *data; 1084 char *proto, *data;
1044 /* Get reasonable local authentication information. */ 1085 /* Get reasonable local authentication information. */
1045 x11_get_proto(&proto, &data); 1086 x11_get_proto(&proto, &data);
@@ -1057,65 +1098,8 @@ ssh_session2_setup(int id, void *arg)
1057 packet_send(); 1098 packet_send();
1058 } 1099 }
1059 1100
1060 /* Transfer any environment variables from client to server */ 1101 client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"),
1061 if (options.num_send_env != 0) { 1102 NULL, fileno(stdin), &command, &ssh_subsystem_reply);
1062 int i, j, matched;
1063 extern char **environ;
1064 char *name, *val;
1065
1066 debug("Sending environment.");
1067 for (i = 0; environ && environ[i] != NULL; i++) {
1068 /* Split */
1069 name = xstrdup(environ[i]);
1070 if ((val = strchr(name, '=')) == NULL) {
1071 free(name);
1072 continue;
1073 }
1074 *val++ = '\0';
1075
1076 matched = 0;
1077 for (j = 0; j < options.num_send_env; j++) {
1078 if (match_pattern(name, options.send_env[j])) {
1079 matched = 1;
1080 break;
1081 }
1082 }
1083 if (!matched) {
1084 debug3("Ignored env %s", name);
1085 free(name);
1086 continue;
1087 }
1088
1089 debug("Sending env %s = %s", name, val);
1090 channel_request_start(id, "env", 0);
1091 packet_put_cstring(name);
1092 packet_put_cstring(val);
1093 packet_send();
1094 free(name);
1095 }
1096 }
1097
1098 len = buffer_len(&command);
1099 if (len > 0) {
1100 if (len > 900)
1101 len = 900;
1102 if (subsystem_flag) {
1103 debug("Sending subsystem: %.*s", len, (u_char *)buffer_ptr(&command));
1104 channel_request_start(id, "subsystem", /*want reply*/ 1);
1105 /* register callback for reply */
1106 /* XXX we assume that client_loop has already been called */
1107 dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &client_subsystem_reply);
1108 dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &client_subsystem_reply);
1109 } else {
1110 debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command));
1111 channel_request_start(id, "exec", 0);
1112 }
1113 packet_put_string(buffer_ptr(&command), buffer_len(&command));
1114 packet_send();
1115 } else {
1116 channel_request_start(id, "shell", 0);
1117 packet_send();
1118 }
1119 1103
1120 packet_set_interactive(interactive); 1104 packet_set_interactive(interactive);
1121} 1105}
@@ -1161,7 +1145,7 @@ ssh_session2_open(void)
1161 1145
1162 channel_send_open(c->self); 1146 channel_send_open(c->self);
1163 if (!no_shell_flag) 1147 if (!no_shell_flag)
1164 channel_register_confirm(c->self, ssh_session2_setup); 1148 channel_register_confirm(c->self, ssh_session2_setup, NULL);
1165 1149
1166 return c->self; 1150 return c->self;
1167} 1151}
@@ -1173,6 +1157,7 @@ ssh_session2(void)
1173 1157
1174 /* XXX should be pre-session */ 1158 /* XXX should be pre-session */
1175 ssh_init_forwarding(); 1159 ssh_init_forwarding();
1160 ssh_control_listener();
1176 1161
1177 if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) 1162 if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
1178 id = ssh_session2_open(); 1163 id = ssh_session2_open();
@@ -1226,3 +1211,110 @@ load_public_identity_files(void)
1226 options.identity_keys[i] = public; 1211 options.identity_keys[i] = public;
1227 } 1212 }
1228} 1213}
1214
1215static void
1216control_client_sighandler(int signo)
1217{
1218 control_client_terminate = signo;
1219}
1220
1221static void
1222control_client_sigrelay(int signo)
1223{
1224 if (control_server_pid > 1)
1225 kill(control_server_pid, signo);
1226}
1227
1228static void
1229control_client(const char *path)
1230{
1231 struct sockaddr_un addr;
1232 int r, sock, exitval;
1233 Buffer m;
1234 char *cp;
1235
1236 memset(&addr, '\0', sizeof(addr));
1237 addr.sun_family = AF_UNIX;
1238 addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
1239 strlen(path) + 1;
1240
1241 if (strlcpy(addr.sun_path, path,
1242 sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
1243 fatal("ControlPath too long");
1244
1245 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1246 fatal("%s socket(): %s", __func__, strerror(errno));
1247
1248 if (connect(sock, (struct sockaddr*)&addr, addr.sun_len) == -1)
1249 fatal("Couldn't connect to %s: %s", path, strerror(errno));
1250
1251 if ((cp = getenv("TERM")) == NULL)
1252 cp = "";
1253
1254 signal(SIGINT, control_client_sighandler);
1255 signal(SIGTERM, control_client_sighandler);
1256 signal(SIGWINCH, control_client_sigrelay);
1257
1258 buffer_init(&m);
1259
1260 /* Get PID of controlee */
1261 if (ssh_msg_recv(sock, &m) == -1)
1262 fatal("%s: msg_recv", __func__);
1263 if (buffer_get_char(&m) != 0)
1264 fatal("%s: wrong version", __func__);
1265 control_server_pid = buffer_get_int(&m);
1266
1267 /* XXX: env passing */
1268
1269 buffer_clear(&m);
1270 buffer_put_int(&m, tty_flag);
1271 buffer_put_int(&m, subsystem_flag);
1272 buffer_put_cstring(&m, cp);
1273
1274 buffer_append(&command, "\0", 1);
1275 buffer_put_cstring(&m, buffer_ptr(&command));
1276
1277 if (ssh_msg_send(sock, /* version */0, &m) == -1)
1278 fatal("%s: msg_send", __func__);
1279
1280 mm_send_fd(sock, STDIN_FILENO);
1281 mm_send_fd(sock, STDOUT_FILENO);
1282 mm_send_fd(sock, STDERR_FILENO);
1283
1284 /* Wait for reply, so master has a chance to gather ttymodes */
1285 buffer_clear(&m);
1286 if (ssh_msg_recv(sock, &m) == -1)
1287 fatal("%s: msg_recv", __func__);
1288 if (buffer_get_char(&m) != 0)
1289 fatal("%s: master returned error", __func__);
1290 buffer_free(&m);
1291
1292 if (tty_flag)
1293 enter_raw_mode();
1294
1295 /* Stick around until the controlee closes the client_fd */
1296 exitval = 0;
1297 for (;!control_client_terminate;) {
1298 r = read(sock, &exitval, sizeof(exitval));
1299 if (r == 0) {
1300 debug2("Received EOF from master");
1301 break;
1302 }
1303 if (r > 0)
1304 debug2("Received exit status from master %d", exitval);
1305 if (r == -1 && errno != EINTR)
1306 fatal("%s: read %s", __func__, strerror(errno));
1307 }
1308
1309 if (control_client_terminate)
1310 debug2("Exiting on signal %d", control_client_terminate);
1311
1312 close(sock);
1313
1314 leave_raw_mode();
1315
1316 if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
1317 fprintf(stderr, "Connection to master closed.\r\n");
1318
1319 exit(exitval);
1320}
diff --git a/ssh_config.5 b/ssh_config.5
index 46d3012c8..bab11d313 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -34,7 +34,7 @@
34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36.\" 36.\"
37.\" $OpenBSD: ssh_config.5,v 1.35 2004/06/13 14:01:42 dtucker Exp $ 37.\" $OpenBSD: ssh_config.5,v 1.36 2004/06/13 15:03:02 djm Exp $
38.Dd September 25, 1999 38.Dd September 25, 1999
39.Dt SSH_CONFIG 5 39.Dt SSH_CONFIG 5
40.Os 40.Os
@@ -256,6 +256,28 @@ will act as a SOCKS server.
256Multiple forwardings may be specified, and 256Multiple forwardings may be specified, and
257additional forwardings can be given on the command line. 257additional forwardings can be given on the command line.
258Only the superuser can forward privileged ports. 258Only the superuser can forward privileged ports.
259.It Cm ControlMaster
260Enables the sharing of multiple sessions over a single network connection.
261When set to
262.Dq yes
263.Nm ssh
264will listen for connections on a control socket specified using the
265.Cm ControlPath
266argument.
267Additional sessions can connect to this socket using the same
268.Cm ControlPath
269with
270.Cm ControlMaster
271set to
272.Dq no
273(the default.)
274These sessions will reuse the master instance's network connection rather
275than initiating new ones.
276.It Cm ControlPath
277Specify a the path to the control socket used for connection sharing.
278See
279.Cm ControlMaster
280above.
259.It Cm EnableSSHKeysign 281.It Cm EnableSSHKeysign
260Setting this option to 282Setting this option to
261.Dq yes 283.Dq yes