summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog17
-rw-r--r--monitor.c4
-rw-r--r--monitor_wrap.c22
-rw-r--r--servconf.c21
-rw-r--r--servconf.h4
-rw-r--r--session.c382
-rw-r--r--session.h4
-rw-r--r--sshd_config3
-rw-r--r--sshd_config.57
9 files changed, 326 insertions, 138 deletions
diff --git a/ChangeLog b/ChangeLog
index 99dbdaf78..5ea4afcac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -77,6 +77,21 @@
77 shouldn't happen in compliant implementations, but it could be 77 shouldn't happen in compliant implementations, but it could be
78 abused to leak memory. 78 abused to leak memory.
79 ok markus@ (as part of a larger diff) 79 ok markus@ (as part of a larger diff)
80 - djm@cvs.openbsd.org 2008/05/08 12:21:16
81 [monitor.c monitor_wrap.c session.h servconf.c servconf.h session.c]
82 [sshd_config sshd_config.5]
83 Make the maximum number of sessions run-time controllable via
84 a sshd_config MaxSessions knob. This is useful for disabling
85 login/shell/subsystem access while leaving port-forwarding working
86 (MaxSessions 0), disabling connection multiplexing (MaxSessions 1) or
87 simply increasing the number of allows multiplexed sessions.
88 Because some bozos are sure to configure MaxSessions in excess of the
89 number of available file descriptors in sshd (which, at peak, might be
90 as many as 9*MaxSessions), audit sshd to ensure that it doesn't leak fds
91 on error paths, and make it fail gracefully on out-of-fd conditions -
92 sending channel errors instead of than exiting with fatal().
93 bz#1090; MaxSessions config bits and manpage from junyer AT gmail.com
94 ok markus@
80 95
8120080403 9620080403
82 - (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile- 97 - (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile-
@@ -3937,4 +3952,4 @@
3937 OpenServer 6 and add osr5bigcrypt support so when someone migrates 3952 OpenServer 6 and add osr5bigcrypt support so when someone migrates
3938 passwords between UnixWare and OpenServer they will still work. OK dtucker@ 3953 passwords between UnixWare and OpenServer they will still work. OK dtucker@
3939 3954
3940$Id: ChangeLog,v 1.4922 2008/05/19 05:28:35 djm Exp $ 3955$Id: ChangeLog,v 1.4923 2008/05/19 05:34:50 djm Exp $
diff --git a/monitor.c b/monitor.c
index 04f6924b6..f872edbb5 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor.c,v 1.95 2008/05/08 12:02:23 djm Exp $ */ 1/* $OpenBSD: monitor.c,v 1.96 2008/05/08 12:21:16 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -1273,7 +1273,7 @@ mm_session_close(Session *s)
1273 debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); 1273 debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd);
1274 session_pty_cleanup2(s); 1274 session_pty_cleanup2(s);
1275 } 1275 }
1276 s->used = 0; 1276 session_unused(s->self);
1277} 1277}
1278 1278
1279int 1279int
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 72fd5c83c..e65fb1279 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor_wrap.c,v 1.61 2008/05/08 12:02:23 djm Exp $ */ 1/* $OpenBSD: monitor_wrap.c,v 1.62 2008/05/08 12:21:16 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -666,7 +666,20 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
666{ 666{
667 Buffer m; 667 Buffer m;
668 char *p, *msg; 668 char *p, *msg;
669 int success = 0; 669 int success = 0, tmp1 = -1, tmp2 = -1;
670
671 /* Kludge: ensure there are fds free to receive the pty/tty */
672 if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 ||
673 (tmp2 = dup(pmonitor->m_recvfd)) == -1) {
674 error("%s: cannot allocate fds for pty", __func__);
675 if (tmp1 > 0)
676 close(tmp1);
677 if (tmp2 > 0)
678 close(tmp2);
679 return 0;
680 }
681 close(tmp1);
682 close(tmp2);
670 683
671 buffer_init(&m); 684 buffer_init(&m);
672 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m); 685 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m);
@@ -711,8 +724,9 @@ mm_session_pty_cleanup2(Session *s)
711 buffer_free(&m); 724 buffer_free(&m);
712 725
713 /* closed dup'ed master */ 726 /* closed dup'ed master */
714 if (close(s->ptymaster) < 0) 727 if (s->ptymaster != -1 && close(s->ptymaster) < 0)
715 error("close(s->ptymaster): %s", strerror(errno)); 728 error("close(s->ptymaster/%d): %s",
729 s->ptymaster, strerror(errno));
716 730
717 /* unlink pty from session */ 731 /* unlink pty from session */
718 s->ttyfd = -1; 732 s->ttyfd = -1;
diff --git a/servconf.c b/servconf.c
index b8a968aa3..94dff1fd6 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.c,v 1.179 2008/05/08 12:02:23 djm Exp $ */ 1/* $OpenBSD: servconf.c,v 1.180 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
@@ -114,6 +114,7 @@ initialize_server_options(ServerOptions *options)
114 options->max_startups_rate = -1; 114 options->max_startups_rate = -1;
115 options->max_startups = -1; 115 options->max_startups = -1;
116 options->max_authtries = -1; 116 options->max_authtries = -1;
117 options->max_sessions = -1;
117 options->banner = NULL; 118 options->banner = NULL;
118 options->use_dns = -1; 119 options->use_dns = -1;
119 options->client_alive_interval = -1; 120 options->client_alive_interval = -1;
@@ -237,6 +238,8 @@ fill_default_server_options(ServerOptions *options)
237 options->max_startups_begin = options->max_startups; 238 options->max_startups_begin = options->max_startups;
238 if (options->max_authtries == -1) 239 if (options->max_authtries == -1)
239 options->max_authtries = DEFAULT_AUTH_FAIL_MAX; 240 options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
241 if (options->max_sessions == -1)
242 options->max_sessions = DEFAULT_SESSIONS_MAX;
240 if (options->use_dns == -1) 243 if (options->use_dns == -1)
241 options->use_dns = 1; 244 options->use_dns = 1;
242 if (options->client_alive_interval == -1) 245 if (options->client_alive_interval == -1)
@@ -291,7 +294,7 @@ typedef enum {
291 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, 294 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
292 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, 295 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
293 sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, 296 sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
294 sMaxStartups, sMaxAuthTries, 297 sMaxStartups, sMaxAuthTries, sMaxSessions,
295 sBanner, sUseDNS, sHostbasedAuthentication, 298 sBanner, sUseDNS, sHostbasedAuthentication,
296 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 299 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
297 sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, 300 sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
@@ -395,6 +398,7 @@ static struct {
395 { "subsystem", sSubsystem, SSHCFG_GLOBAL }, 398 { "subsystem", sSubsystem, SSHCFG_GLOBAL },
396 { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, 399 { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
397 { "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL }, 400 { "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL },
401 { "maxsessions", sMaxSessions, SSHCFG_ALL },
398 { "banner", sBanner, SSHCFG_ALL }, 402 { "banner", sBanner, SSHCFG_ALL },
399 { "usedns", sUseDNS, SSHCFG_GLOBAL }, 403 { "usedns", sUseDNS, SSHCFG_GLOBAL },
400 { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, 404 { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
@@ -695,7 +699,7 @@ process_server_config_line(ServerOptions *options, char *line,
695 699
696 case sServerKeyBits: 700 case sServerKeyBits:
697 intptr = &options->server_key_bits; 701 intptr = &options->server_key_bits;
698parse_int: 702 parse_int:
699 arg = strdelim(&cp); 703 arg = strdelim(&cp);
700 if (!arg || *arg == '\0') 704 if (!arg || *arg == '\0')
701 fatal("%s line %d: missing integer value.", 705 fatal("%s line %d: missing integer value.",
@@ -707,7 +711,7 @@ parse_int:
707 711
708 case sLoginGraceTime: 712 case sLoginGraceTime:
709 intptr = &options->login_grace_time; 713 intptr = &options->login_grace_time;
710parse_time: 714 parse_time:
711 arg = strdelim(&cp); 715 arg = strdelim(&cp);
712 if (!arg || *arg == '\0') 716 if (!arg || *arg == '\0')
713 fatal("%s line %d: missing time value.", 717 fatal("%s line %d: missing time value.",
@@ -776,7 +780,7 @@ parse_time:
776 fatal("%s line %d: too many host keys specified (max %d).", 780 fatal("%s line %d: too many host keys specified (max %d).",
777 filename, linenum, MAX_HOSTKEYS); 781 filename, linenum, MAX_HOSTKEYS);
778 charptr = &options->host_key_files[*intptr]; 782 charptr = &options->host_key_files[*intptr];
779parse_filename: 783 parse_filename:
780 arg = strdelim(&cp); 784 arg = strdelim(&cp);
781 if (!arg || *arg == '\0') 785 if (!arg || *arg == '\0')
782 fatal("%s line %d: missing file name.", 786 fatal("%s line %d: missing file name.",
@@ -819,7 +823,7 @@ parse_filename:
819 823
820 case sIgnoreRhosts: 824 case sIgnoreRhosts:
821 intptr = &options->ignore_rhosts; 825 intptr = &options->ignore_rhosts;
822parse_flag: 826 parse_flag:
823 arg = strdelim(&cp); 827 arg = strdelim(&cp);
824 if (!arg || *arg == '\0') 828 if (!arg || *arg == '\0')
825 fatal("%s line %d: missing yes/no argument.", 829 fatal("%s line %d: missing yes/no argument.",
@@ -1155,6 +1159,10 @@ parse_flag:
1155 intptr = &options->max_authtries; 1159 intptr = &options->max_authtries;
1156 goto parse_int; 1160 goto parse_int;
1157 1161
1162 case sMaxSessions:
1163 intptr = &options->max_sessions;
1164 goto parse_int;
1165
1158 case sBanner: 1166 case sBanner:
1159 charptr = &options->banner; 1167 charptr = &options->banner;
1160 goto parse_filename; 1168 goto parse_filename;
@@ -1382,6 +1390,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
1382 M_CP_INTOPT(x11_display_offset); 1390 M_CP_INTOPT(x11_display_offset);
1383 M_CP_INTOPT(x11_forwarding); 1391 M_CP_INTOPT(x11_forwarding);
1384 M_CP_INTOPT(x11_use_localhost); 1392 M_CP_INTOPT(x11_use_localhost);
1393 M_CP_INTOPT(max_sessions);
1385 1394
1386 M_CP_STROPT(banner); 1395 M_CP_STROPT(banner);
1387 if (preauth) 1396 if (preauth)
diff --git a/servconf.h b/servconf.h
index aaf87cd18..819a028c8 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.83 2008/05/07 05:49:37 pyr Exp $ */ 1/* $OpenBSD: servconf.h,v 1.84 2008/05/08 12:21:16 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -35,6 +35,7 @@
35#define PERMIT_YES 3 35#define PERMIT_YES 3
36 36
37#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ 37#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */
38#define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */
38 39
39/* Magic name for internal sftp-server */ 40/* Magic name for internal sftp-server */
40#define INTERNAL_SFTP_NAME "internal-sftp" 41#define INTERNAL_SFTP_NAME "internal-sftp"
@@ -123,6 +124,7 @@ typedef struct {
123 int max_startups_rate; 124 int max_startups_rate;
124 int max_startups; 125 int max_startups;
125 int max_authtries; 126 int max_authtries;
127 int max_sessions;
126 char *banner; /* SSH-2 banner message */ 128 char *banner; /* SSH-2 banner message */
127 int use_dns; 129 int use_dns;
128 int client_alive_interval; /* 130 int client_alive_interval; /*
diff --git a/session.c b/session.c
index ca04a4532..c8ed25234 100644
--- a/session.c
+++ b/session.c
@@ -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);
102void session_pty_cleanup(Session *); 102void session_pty_cleanup(Session *);
103void session_proctitle(Session *); 103void session_proctitle(Session *);
104int session_setup_x11fwd(Session *); 104int session_setup_x11fwd(Session *);
105void do_exec_pty(Session *, const char *); 105int do_exec_pty(Session *, const char *);
106void do_exec_no_pty(Session *, const char *); 106int do_exec_no_pty(Session *, const char *);
107void do_exec(Session *, const char *); 107int do_exec(Session *, const char *);
108void do_login(Session *, const char *); 108void do_login(Session *, const char *);
109#ifdef LOGIN_NEEDS_UTMPX 109#ifdef LOGIN_NEEDS_UTMPX
110static void do_pre_login(Session *s); 110static void do_pre_login(Session *s);
@@ -132,8 +132,9 @@ extern Buffer loginmsg;
132const char *original_command = NULL; 132const char *original_command = NULL;
133 133
134/* data */ 134/* data */
135#define MAX_SESSIONS 20 135static int sessions_first_unused = -1;
136Session sessions[MAX_SESSIONS]; 136static int sessions_nalloc = 0;
137static 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
167auth_input_request_forwarding(struct passwd * pw) 168auth_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
229static void 248static 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 */
409void 433int
410do_exec_no_pty(Session *s, const char *command) 434do_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 */
552void 628int
553do_exec_pty(Session *s, const char *command) 629do_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 */
675void 776int
676do_exec(Session *s, const char *command) 777do_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
1848void
1849session_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
1743Session * 1869Session *
1744session_new(void) 1870session_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
1772static void 1910static void
1773session_dump(void) 1911session_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 *
1806session_by_tty(char *tty) 1947session_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 *
1822session_by_channel(int id) 1963session_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
2015session_shell_req(Session *s) 2157session_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
2022static int 2163static int
2023session_exec_req(Session *s) 2164session_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
2033static int 2175static 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
2360void 2502void
@@ -2418,7 +2560,7 @@ void
2418session_destroy_all(void (*closefunc)(Session *)) 2560session_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
diff --git a/session.h b/session.h
index ee9338e4f..cbb8e3a32 100644
--- a/session.h
+++ b/session.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: session.h,v 1.29 2006/08/03 03:34:42 deraadt Exp $ */ 1/* $OpenBSD: session.h,v 1.30 2008/05/08 12:21:16 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -31,6 +31,7 @@ typedef struct Session Session;
31struct Session { 31struct Session {
32 int used; 32 int used;
33 int self; 33 int self;
34 int next_unused;
34 struct passwd *pw; 35 struct passwd *pw;
35 Authctxt *authctxt; 36 Authctxt *authctxt;
36 pid_t pid; 37 pid_t pid;
@@ -65,6 +66,7 @@ void do_authenticated(Authctxt *);
65void do_cleanup(Authctxt *); 66void do_cleanup(Authctxt *);
66 67
67int session_open(Authctxt *, int); 68int session_open(Authctxt *, int);
69void session_unused(int);
68int session_input_channel_req(Channel *, const char *); 70int session_input_channel_req(Channel *, const char *);
69void session_close_by_pid(pid_t, int); 71void session_close_by_pid(pid_t, int);
70void session_close_by_channel(int, void *); 72void session_close_by_channel(int, void *);
diff --git a/sshd_config b/sshd_config
index 1f97a9dcc..c5ee7c8a4 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,4 +1,4 @@
1# $OpenBSD: sshd_config,v 1.78 2008/05/07 06:43:35 pyr Exp $ 1# $OpenBSD: sshd_config,v 1.79 2008/05/08 12:21:16 djm Exp $
2 2
3# This is the sshd server system-wide configuration file. See 3# This is the sshd server system-wide configuration file. See
4# sshd_config(5) for more information. 4# sshd_config(5) for more information.
@@ -41,6 +41,7 @@ Protocol 2
41#PermitRootLogin yes 41#PermitRootLogin yes
42#StrictModes yes 42#StrictModes yes
43#MaxAuthTries 6 43#MaxAuthTries 6
44#MaxSessions 10
44 45
45#RSAAuthentication yes 46#RSAAuthentication yes
46#PubkeyAuthentication yes 47#PubkeyAuthentication yes
diff --git a/sshd_config.5 b/sshd_config.5
index 99b5621e7..0d8c140bf 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,8 +34,8 @@
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: sshd_config.5,v 1.89 2008/05/07 08:00:14 jmc Exp $ 37.\" $OpenBSD: sshd_config.5,v 1.90 2008/05/08 12:21:16 djm Exp $
38.Dd $Mdocdate: May 19 2008 $ 38.Dd $Mdocdate: May 8 2008 $
39.Dt SSHD_CONFIG 5 39.Dt SSHD_CONFIG 5
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -594,6 +594,9 @@ connection.
594Once the number of failures reaches half this value, 594Once the number of failures reaches half this value,
595additional failures are logged. 595additional failures are logged.
596The default is 6. 596The default is 6.
597.It Cm MaxSessions
598Specifies the maximum number of open sessions permitted per network connection.
599The default is 10.
597.It Cm MaxStartups 600.It Cm MaxStartups
598Specifies the maximum number of concurrent unauthenticated connections to the 601Specifies the maximum number of concurrent unauthenticated connections to the
599SSH daemon. 602SSH daemon.