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 99dbdaf7..5ea4afca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -77,6 +77,21 @@
shouldn't happen in compliant implementations, but it could be
abused to leak memory.
ok markus@ (as part of a larger diff)
+ - djm@cvs.openbsd.org 2008/05/08 12:21:16
+ [monitor.c monitor_wrap.c session.h servconf.c servconf.h session.c]
+ [sshd_config sshd_config.5]
+ Make the maximum number of sessions run-time controllable via
+ a sshd_config MaxSessions knob. This is useful for disabling
+ login/shell/subsystem access while leaving port-forwarding working
+ (MaxSessions 0), disabling connection multiplexing (MaxSessions 1) or
+ simply increasing the number of allows multiplexed sessions.
+ Because some bozos are sure to configure MaxSessions in excess of the
+ number of available file descriptors in sshd (which, at peak, might be
+ as many as 9*MaxSessions), audit sshd to ensure that it doesn't leak fds
+ on error paths, and make it fail gracefully on out-of-fd conditions -
+ sending channel errors instead of than exiting with fatal().
+ bz#1090; MaxSessions config bits and manpage from junyer AT gmail.com
+ ok markus@
20080403
- (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile-
@@ -3937,4 +3952,4 @@
OpenServer 6 and add osr5bigcrypt support so when someone migrates
passwords between UnixWare and OpenServer they will still work. OK dtucker@
-$Id: ChangeLog,v 1.4922 2008/05/19 05:28:35 djm Exp $
+$Id: ChangeLog,v 1.4923 2008/05/19 05:34:50 djm Exp $
diff --git a/monitor.c b/monitor.c
index 04f6924b..f872edbb 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.95 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.96 2008/05/08 12:21:16 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -1273,7 +1273,7 @@ mm_session_close(Session *s)
debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd);
session_pty_cleanup2(s);
}
- s->used = 0;
+ session_unused(s->self);
}
int
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 72fd5c83..e65fb127 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.61 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.62 2008/05/08 12:21:16 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -666,7 +666,20 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
{
Buffer m;
char *p, *msg;
- int success = 0;
+ int success = 0, tmp1 = -1, tmp2 = -1;
+
+ /* Kludge: ensure there are fds free to receive the pty/tty */
+ if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 ||
+ (tmp2 = dup(pmonitor->m_recvfd)) == -1) {
+ error("%s: cannot allocate fds for pty", __func__);
+ if (tmp1 > 0)
+ close(tmp1);
+ if (tmp2 > 0)
+ close(tmp2);
+ return 0;
+ }
+ close(tmp1);
+ close(tmp2);
buffer_init(&m);
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m);
@@ -711,8 +724,9 @@ mm_session_pty_cleanup2(Session *s)
buffer_free(&m);
/* closed dup'ed master */
- if (close(s->ptymaster) < 0)
- error("close(s->ptymaster): %s", strerror(errno));
+ if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+ error("close(s->ptymaster/%d): %s",
+ s->ptymaster, strerror(errno));
/* unlink pty from session */
s->ttyfd = -1;
diff --git a/servconf.c b/servconf.c
index b8a968aa..94dff1fd 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.c,v 1.179 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.180 2008/05/08 12:21:16 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -114,6 +114,7 @@ initialize_server_options(ServerOptions *options)
options->max_startups_rate = -1;
options->max_startups = -1;
options->max_authtries = -1;
+ options->max_sessions = -1;
options->banner = NULL;
options->use_dns = -1;
options->client_alive_interval = -1;
@@ -237,6 +238,8 @@ fill_default_server_options(ServerOptions *options)
options->max_startups_begin = options->max_startups;
if (options->max_authtries == -1)
options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
+ if (options->max_sessions == -1)
+ options->max_sessions = DEFAULT_SESSIONS_MAX;
if (options->use_dns == -1)
options->use_dns = 1;
if (options->client_alive_interval == -1)
@@ -291,7 +294,7 @@ typedef enum {
sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
- sMaxStartups, sMaxAuthTries,
+ sMaxStartups, sMaxAuthTries, sMaxSessions,
sBanner, sUseDNS, sHostbasedAuthentication,
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
@@ -395,6 +398,7 @@ static struct {
{ "subsystem", sSubsystem, SSHCFG_GLOBAL },
{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
{ "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL },
+ { "maxsessions", sMaxSessions, SSHCFG_ALL },
{ "banner", sBanner, SSHCFG_ALL },
{ "usedns", sUseDNS, SSHCFG_GLOBAL },
{ "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
@@ -695,7 +699,7 @@ process_server_config_line(ServerOptions *options, char *line,
case sServerKeyBits:
intptr = &options->server_key_bits;
-parse_int:
+ parse_int:
arg = strdelim(&cp);
if (!arg || *arg == '\0')
fatal("%s line %d: missing integer value.",
@@ -707,7 +711,7 @@ parse_int:
case sLoginGraceTime:
intptr = &options->login_grace_time;
-parse_time:
+ parse_time:
arg = strdelim(&cp);
if (!arg || *arg == '\0')
fatal("%s line %d: missing time value.",
@@ -776,7 +780,7 @@ parse_time:
fatal("%s line %d: too many host keys specified (max %d).",
filename, linenum, MAX_HOSTKEYS);
charptr = &options->host_key_files[*intptr];
-parse_filename:
+ parse_filename:
arg = strdelim(&cp);
if (!arg || *arg == '\0')
fatal("%s line %d: missing file name.",
@@ -819,7 +823,7 @@ parse_filename:
case sIgnoreRhosts:
intptr = &options->ignore_rhosts;
-parse_flag:
+ parse_flag:
arg = strdelim(&cp);
if (!arg || *arg == '\0')
fatal("%s line %d: missing yes/no argument.",
@@ -1155,6 +1159,10 @@ parse_flag:
intptr = &options->max_authtries;
goto parse_int;
+ case sMaxSessions:
+ intptr = &options->max_sessions;
+ goto parse_int;
+
case sBanner:
charptr = &options->banner;
goto parse_filename;
@@ -1382,6 +1390,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
M_CP_INTOPT(x11_display_offset);
M_CP_INTOPT(x11_forwarding);
M_CP_INTOPT(x11_use_localhost);
+ M_CP_INTOPT(max_sessions);
M_CP_STROPT(banner);
if (preauth)
diff --git a/servconf.h b/servconf.h
index aaf87cd1..819a028c 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.83 2008/05/07 05:49:37 pyr Exp $ */
+/* $OpenBSD: servconf.h,v 1.84 2008/05/08 12:21:16 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -35,6 +35,7 @@
#define PERMIT_YES 3
#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */
+#define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */
/* Magic name for internal sftp-server */
#define INTERNAL_SFTP_NAME "internal-sftp"
@@ -123,6 +124,7 @@ typedef struct {
int max_startups_rate;
int max_startups;
int max_authtries;
+ int max_sessions;
char *banner; /* SSH-2 banner message */
int use_dns;
int client_alive_interval; /*
diff --git a/session.c b/session.c
index ca04a453..c8ed2523 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.236 2008/05/08 12:02:23 djm Exp $ */
+/* $OpenBSD: session.c,v 1.237 2008/05/08 12:21:16 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -102,9 +102,9 @@ void session_set_fds(Session *, int, int, int);
void session_pty_cleanup(Session *);
void session_proctitle(Session *);
int session_setup_x11fwd(Session *);
-void do_exec_pty(Session *, const char *);
-void do_exec_no_pty(Session *, const char *);
-void do_exec(Session *, const char *);
+int do_exec_pty(Session *, const char *);
+int do_exec_no_pty(Session *, const char *);
+int do_exec(Session *, const char *);
void do_login(Session *, const char *);
#ifdef LOGIN_NEEDS_UTMPX
static void do_pre_login(Session *s);
@@ -132,8 +132,9 @@ extern Buffer loginmsg;
const char *original_command = NULL;
/* data */
-#define MAX_SESSIONS 20
-Session sessions[MAX_SESSIONS];
+static int sessions_first_unused = -1;
+static int sessions_nalloc = 0;
+static Session *sessions = NULL;
#define SUBSYSTEM_NONE 0
#define SUBSYSTEM_EXT 1
@@ -167,7 +168,7 @@ static int
auth_input_request_forwarding(struct passwd * pw)
{
Channel *nc;
- int sock;
+ int sock = -1;
struct sockaddr_un sunaddr;
if (auth_sock_name != NULL) {
@@ -179,43 +180,48 @@ auth_input_request_forwarding(struct passwd * pw)
temporarily_use_uid(pw);
/* Allocate a buffer for the socket name, and format the name. */
- auth_sock_name = xmalloc(MAXPATHLEN);
- auth_sock_dir = xmalloc(MAXPATHLEN);
- strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
+ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
/* Create private directory for socket */
if (mkdtemp(auth_sock_dir) == NULL) {
packet_send_debug("Agent forwarding disabled: "
"mkdtemp() failed: %.100s", strerror(errno));
restore_uid();
- xfree(auth_sock_name);
xfree(auth_sock_dir);
- auth_sock_name = NULL;
auth_sock_dir = NULL;
- return 0;
+ goto authsock_err;
}
- snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld",
- auth_sock_dir, (long) getpid());
+
+ xasprintf(&auth_sock_name, "%s/agent.%ld",
+ auth_sock_dir, (long) getpid());
/* Create the socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- packet_disconnect("socket: %.100s", strerror(errno));
+ if (sock < 0) {
+ error("socket: %.100s", strerror(errno));
+ restore_uid();
+ goto authsock_err;
+ }
/* Bind it to the name. */
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_UNIX;
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path));
- if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
- packet_disconnect("bind: %.100s", strerror(errno));
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
+ error("bind: %.100s", strerror(errno));
+ restore_uid();
+ goto authsock_err;
+ }
/* Restore the privileged uid. */
restore_uid();
/* Start listening on the socket. */
- if (listen(sock, SSH_LISTEN_BACKLOG) < 0)
- packet_disconnect("listen: %.100s", strerror(errno));
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
+ error("listen: %.100s", strerror(errno));
+ goto authsock_err;
+ }
/* Allocate a channel for the authentication agent socket. */
nc = channel_new("auth socket",
@@ -224,6 +230,19 @@ auth_input_request_forwarding(struct passwd * pw)
0, "auth socket", 1);
strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
return 1;
+
+ authsock_err:
+ if (auth_sock_name != NULL)
+ xfree(auth_sock_name);
+ if (auth_sock_dir != NULL) {
+ rmdir(auth_sock_dir);
+ xfree(auth_sock_dir);
+ }
+ if (sock != -1)
+ close(sock);
+ auth_sock_name = NULL;
+ auth_sock_dir = NULL;
+ return 0;
}
static void
@@ -373,10 +392,14 @@ do_authenticated1(Authctxt *authctxt)
if (type == SSH_CMSG_EXEC_CMD) {
command = packet_get_string(&dlen);
debug("Exec command '%.500s'", command);
- do_exec(s, command);
+ if (do_exec(s, command) != 0)
+ packet_disconnect(
+ "command execution failed");
xfree(command);
} else {
- do_exec(s, NULL);
+ if (do_exec(s, NULL) != 0)
+ packet_disconnect(
+ "shell execution failed");
}
packet_check_eom();
session_close(s);
@@ -401,41 +424,84 @@ do_authenticated1(Authctxt *authctxt)
}
}
+#define USE_PIPES
/*
* This is called to fork and execute a command when we have no tty. This
* will call do_child from the child, and server_loop from the parent after
* setting up file descriptors and such.
*/
-void
+int
do_exec_no_pty(Session *s, const char *command)
{
pid_t pid;
#ifdef USE_PIPES
int pin[2], pout[2], perr[2];
+
/* Allocate pipes for communicating with the program. */
- if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
- packet_disconnect("Could not create pipes: %.100s",
- strerror(errno));
-#else /* USE_PIPES */
+ if (pipe(pin) < 0) {
+ error("%s: pipe in: %.100s", __func__, strerror(errno));
+ return -1;
+ }
+ if (pipe(pout) < 0) {
+ error("%s: pipe out: %.100s", __func__, strerror(errno));
+ close(pin[0]);
+ close(pin[1]);
+ return -1;
+ }
+ if (pipe(perr) < 0) {
+ error("%s: pipe err: %.100s", __func__, strerror(errno));
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ return -1;
+ }
+#else
int inout[2], err[2];
+
/* Uses socket pairs to communicate with the program. */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
- socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
- packet_disconnect("Could not create socket pairs: %.100s",
- strerror(errno));
-#endif /* USE_PIPES */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) {
+ error("%s: socketpair #1: %.100s", __func__, strerror(errno));
+ return -1;
+ }
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) {
+ error("%s: socketpair #2: %.100s", __func__, strerror(errno));
+ close(inout[0]);
+ close(inout[1]);
+ return -1;
+ }
+#endif
+
if (s == NULL)
fatal("do_exec_no_pty: no session");
session_proctitle(s);
/* Fork the child. */
- if ((pid = fork()) == 0) {
+ switch ((pid = fork())) {
+ case -1:
+ error("%s: fork: %.100s", __func__, strerror(errno));
+#ifdef USE_PIPES
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ close(perr[0]);
+ close(perr[1]);
+#else
+ close(inout[0]);
+ close(inout[1]);
+ close(err[0]);
+ close(err[1]);
+#endif
+ return -1;
+ case 0:
is_child = 1;
/* Child. Reinitialize the log since the pid has changed. */
- log_init(__progname, options.log_level, options.log_facility, log_stderr);
+ log_init(__progname, options.log_level,
+ options.log_facility, log_stderr);
/*
* Create a new session and process group since the 4.4BSD
@@ -465,7 +531,7 @@ do_exec_no_pty(Session *s, const char *command)
if (dup2(perr[1], 2) < 0)
perror("dup2 stderr");
close(perr[1]);
-#else /* USE_PIPES */
+#else
/*
* Redirect stdin, stdout, and stderr. Stdin and stdout will
* use the same socket, as some programs (particularly rdist)
@@ -475,11 +541,14 @@ do_exec_no_pty(Session *s, const char *command)
close(err[1]);
if (dup2(inout[0], 0) < 0) /* stdin */
perror("dup2 stdin");
- if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */
+ if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */
perror("dup2 stdout");
+ close(inout[0]);
if (dup2(err[0], 2) < 0) /* stderr */
perror("dup2 stderr");
-#endif /* USE_PIPES */
+ close(err[0]);
+#endif
+
#ifdef _UNICOS
cray_init_job(s->pw); /* set up cray jid and tmpdir */
@@ -488,7 +557,10 @@ do_exec_no_pty(Session *s, const char *command)
/* Do processing for the child (exec command etc). */
do_child(s, command);
/* NOTREACHED */
+ default:
+ break;
}
+
#ifdef _UNICOS
signal(WJSIGNAL, cray_job_termination_handler);
#endif /* _UNICOS */
@@ -496,11 +568,18 @@ do_exec_no_pty(Session *s, const char *command)
if (is_winnt)
cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
#endif
- if (pid < 0)
- packet_disconnect("fork failed: %.100s", strerror(errno));
+
s->pid = pid;
/* Set interactive/non-interactive mode. */
packet_set_interactive(s->display != NULL);
+
+ /*
+ * Clear loginmsg, since it's the child's responsibility to display
+ * it to the user, otherwise multiple sessions may accumulate
+ * multiple copies of the login messages.
+ */
+ buffer_clear(&loginmsg);
+
#ifdef USE_PIPES
/* We are the parent. Close the child sides of the pipes. */
close(pin[0]);
@@ -518,29 +597,26 @@ do_exec_no_pty(Session *s, const char *command)
server_loop(pid, pin[1], pout[0], perr[0]);
/* server_loop has closed pin[1], pout[0], and perr[0]. */
}
-#else /* USE_PIPES */
+#else
/* We are the parent. Close the child sides of the socket pairs. */
close(inout[0]);
close(err[0]);
/*
- * Clear loginmsg, since it's the child's responsibility to display
- * it to the user, otherwise multiple sessions may accumulate
- * multiple copies of the login messages.
- */
- buffer_clear(&loginmsg);
-
- /*
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
*/
if (compat20) {
- session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]);
+ session_set_fds(s, inout[1], inout[1],
+ s->is_subsystem ? -1 : err[1]);
+ if (s->is_subsystem)
+ close(err[1]);
} else {
server_loop(pid, inout[1], inout[1], err[1]);
/* server_loop has closed inout[1] and err[1]. */
}
-#endif /* USE_PIPES */
+#endif
+ return 0;
}
/*
@@ -549,7 +625,7 @@ do_exec_no_pty(Session *s, const char *command)
* setting up file descriptors, controlling tty, updating wtmp, utmp,
* lastlog, and other such operations.
*/
-void
+int
do_exec_pty(Session *s, const char *command)
{
int fdout, ptyfd, ttyfd, ptymaster;
@@ -560,12 +636,46 @@ do_exec_pty(Session *s, const char *command)
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
+ /*
+ * Create another descriptor of the pty master side for use as the
+ * standard input. We could use the original descriptor, but this
+ * simplifies code in server_loop. The descriptor is bidirectional.
+ * Do this before forking (and cleanup in the child) so as to
+ * detect and gracefully fail out-of-fd conditions.
+ */
+ if ((fdout = dup(ptyfd)) < 0) {
+ error("%s: dup #1: %s", __func__, strerror(errno));
+ close(ttyfd);
+ close(ptyfd);
+ return -1;
+ }
+ /* we keep a reference to the pty master */
+ if ((ptymaster = dup(ptyfd)) < 0) {
+ error("%s: dup #2: %s", __func__, strerror(errno));
+ close(ttyfd);
+ close(ptyfd);
+ close(fdout);
+ return -1;
+ }
+
/* Fork the child. */
- if ((pid = fork()) == 0) {
+ switch ((pid = fork())) {
+ case -1:
+ error("%s: fork: %.100s", __func__, strerror(errno));
+ close(fdout);
+ close(ptymaster);
+ close(ttyfd);
+ close(ptyfd);
+ return -1;
+ case 0:
is_child = 1;
+ close(fdout);
+ close(ptymaster);
+
/* Child. Reinitialize the log because the pid has changed. */
- log_init(__progname, options.log_level, options.log_facility, log_stderr);
+ log_init(__progname, options.log_level,
+ options.log_facility, log_stderr);
/* Close the master side of the pseudo tty. */
close(ptyfd);
@@ -596,11 +706,16 @@ do_exec_pty(Session *s, const char *command)
do_pre_login(s);
# endif
#endif
-
- /* Do common processing for the child, such as execing the command. */
- do_child(s, command);
- /* NOTREACHED */
+ /*
+ * Do common processing for the child, such as execing
+ * the command.
+ */
+ do_child(s, command);
+ /* NOTREACHED */
+ default:
+ break;
}
+
#ifdef _UNICOS
signal(WJSIGNAL, cray_job_termination_handler);
#endif /* _UNICOS */
@@ -608,29 +723,14 @@ do_exec_pty(Session *s, const char *command)
if (is_winnt)
cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
#endif
- if (pid < 0)
- packet_disconnect("fork failed: %.100s", strerror(errno));
+
s->pid = pid;
/* Parent. Close the slave side of the pseudo tty. */
close(ttyfd);
- /*
- * Create another descriptor of the pty master side for use as the
- * standard input. We could use the original descriptor, but this
- * simplifies code in server_loop. The descriptor is bidirectional.
- */
- fdout = dup(ptyfd);
- if (fdout < 0)
- packet_disconnect("dup #1 failed: %.100s", strerror(errno));
-
- /* we keep a reference to the pty master */
- ptymaster = dup(ptyfd);
- if (ptymaster < 0)
- packet_disconnect("dup #2 failed: %.100s", strerror(errno));
- s->ptymaster = ptymaster;
-
/* Enter interactive session. */
+ s->ptymaster = ptymaster;
packet_set_interactive(1);
if (compat20) {
session_set_fds(s, ptyfd, fdout, -1);
@@ -638,6 +738,7 @@ do_exec_pty(Session *s, const char *command)
server_loop(pid, ptyfd, fdout, -1);
/* server_loop _has_ closed ptyfd and fdout. */
}
+ return 0;
}
#ifdef LOGIN_NEEDS_UTMPX
@@ -672,9 +773,11 @@ do_pre_login(Session *s)
* This is called to fork and execute a command. If another command is
* to be forced, execute that instead.
*/
-void
+int
do_exec(Session *s, const char *command)
{
+ int ret;
+
if (options.adm_forced_command) {
original_command = command;
command = options.adm_forced_command;
@@ -705,9 +808,9 @@ do_exec(Session *s, const char *command)
}
#endif
if (s->ttyfd != -1)
- do_exec_pty(s, command);
+ ret = do_exec_pty(s, command);
else
- do_exec_no_pty(s, command);
+ ret = do_exec_no_pty(s, command);
original_command = NULL;
@@ -717,6 +820,8 @@ do_exec(Session *s, const char *command)
* multiple copies of the login messages.
*/
buffer_clear(&loginmsg);
+
+ return ret;
}
/* administrative, login(1)-like work */
@@ -1740,43 +1845,79 @@ do_child(Session *s, const char *command)
exit(1);
}
+void
+session_unused(int id)
+{
+ debug3("%s: session id %d unused", __func__, id);
+ if (id >= options.max_sessions ||
+ id >= sessions_nalloc) {
+ fatal("%s: insane session id %d (max %d nalloc %d)",
+ __func__, id, options.max_sessions, sessions_nalloc);
+ }
+ bzero(&sessions[id], sizeof(*sessions));
+ sessions[id].self = id;
+ sessions[id].used = 0;
+ sessions[id].chanid = -1;
+ sessions[id].ptyfd = -1;
+ sessions[id].ttyfd = -1;
+ sessions[id].ptymaster = -1;
+ sessions[id].x11_chanids = NULL;
+ sessions[id].next_unused = sessions_first_unused;
+ sessions_first_unused = id;
+}
+
Session *
session_new(void)
{
- int i;
- static int did_init = 0;
- if (!did_init) {
- debug("session_new: init");
- for (i = 0; i < MAX_SESSIONS; i++) {
- sessions[i].used = 0;
+ Session *s, *tmp;
+
+ if (sessions_first_unused == -1) {
+ if (sessions_nalloc >= options.max_sessions)
+ return NULL;
+ debug2("%s: allocate (allocated %d max %d)",
+ __func__, sessions_nalloc, options.max_sessions);
+ tmp = xrealloc(sessions, sessions_nalloc + 1,
+ sizeof(*sessions));
+ if (tmp == NULL) {
+ error("%s: cannot allocate %d sessions",
+ __func__, sessions_nalloc + 1);
+ return NULL;
}
- did_init = 1;
+ sessions = tmp;
+ session_unused(sessions_nalloc++);
}
- for (i = 0; i < MAX_SESSIONS; i++) {
- Session *s = &sessions[i];
- if (! s->used) {
- memset(s, 0, sizeof(*s));
- s->chanid = -1;
- s->ptyfd = -1;
- s->ttyfd = -1;
- s->used = 1;
- s->self = i;
- s->x11_chanids = NULL;
- debug("session_new: session %d", i);
- return s;
- }
+
+ if (sessions_first_unused >= sessions_nalloc ||
+ sessions_first_unused < 0) {
+ fatal("%s: insane first_unused %d max %d nalloc %d",
+ __func__, sessions_first_unused, options.max_sessions,
+ sessions_nalloc);
}
- return NULL;
+
+ s = &sessions[sessions_first_unused];
+ if (s->used) {
+ fatal("%s: session %d already used",
+ __func__, sessions_first_unused);
+ }
+ sessions_first_unused = s->next_unused;
+ s->used = 1;
+ s->next_unused = -1;
+ debug("session_new: session %d", s->self);
+
+ return s;
}
static void
session_dump(void)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
- debug("dump: used %d session %d %p channel %d pid %ld",
+
+ debug("dump: used %d next_unused %d session %d %p "
+ "channel %d pid %ld",
s->used,
+ s->next_unused,
s->self,
s,
s->chanid,
@@ -1806,7 +1947,7 @@ Session *
session_by_tty(char *tty)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
debug("session_by_tty: session %d tty %s", i, tty);
@@ -1822,10 +1963,11 @@ static Session *
session_by_channel(int id)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->chanid == id) {
- debug("session_by_channel: session %d channel %d", i, id);
+ debug("session_by_channel: session %d channel %d",
+ i, id);
return s;
}
}
@@ -1839,7 +1981,7 @@ session_by_x11_channel(int id)
{
int i, j;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->x11_chanids == NULL || !s->used)
@@ -1862,7 +2004,7 @@ session_by_pid(pid_t pid)
{
int i;
debug("session_by_pid: pid %ld", (long)pid);
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->pid == pid)
return s;
@@ -1918,7 +2060,8 @@ session_pty_req(Session *s)
/* Allocate a pty and open it. */
debug("Allocating pty.");
- if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) {
+ if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
+ sizeof(s->tty)))) {
if (s->term)
xfree(s->term);
s->term = NULL;
@@ -1971,8 +2114,7 @@ session_subsystem_req(Session *s)
s->is_subsystem = SUBSYSTEM_EXT;
}
debug("subsystem: exec() %s", cmd);
- do_exec(s, cmd);
- success = 1;
+ success = do_exec(s, cmd) == 0;
break;
}
}
@@ -2015,19 +2157,19 @@ static int
session_shell_req(Session *s)
{
packet_check_eom();
- do_exec(s, NULL);
- return 1;
+ return do_exec(s, NULL) == 0;
}
static int
session_exec_req(Session *s)
{
- u_int len;
+ u_int len, success;
+
char *command = packet_get_string(&len);
packet_check_eom();
- do_exec(s, command);
+ success = do_exec(s, command) == 0;
xfree(command);
- return 1;
+ return success;
}
static int
@@ -2037,8 +2179,7 @@ session_break_req(Session *s)
packet_get_int(); /* ignored */
packet_check_eom();
- if (s->ttyfd == -1 ||
- tcsendbreak(s->ttyfd, 0) < 0)
+ if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0)
return 0;
return 1;
}
@@ -2185,8 +2326,9 @@ session_pty_cleanup2(Session *s)
* the pty cleanup, so that another process doesn't get this pty
* while we're still cleaning up.
*/
- if (close(s->ptymaster) < 0)
- error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno));
+ if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+ error("close(s->ptymaster/%d): %s",
+ s->ptymaster, strerror(errno));
/* unlink pty from session */
s->ttyfd = -1;
@@ -2346,7 +2488,6 @@ session_close(Session *s)
xfree(s->auth_data);
if (s->auth_proto)
xfree(s->auth_proto);
- s->used = 0;
if (s->env != NULL) {
for (i = 0; i < s->num_env; i++) {
xfree(s->env[i].name);
@@ -2355,6 +2496,7 @@ session_close(Session *s)
xfree(s->env);
}
session_proctitle(s);
+ session_unused(s->self);
}
void
@@ -2418,7 +2560,7 @@ void
session_destroy_all(void (*closefunc)(Session *))
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used) {
if (closefunc != NULL)
@@ -2437,7 +2579,7 @@ session_tty_list(void)
char *cp;
buf[0] = '\0';
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1) {
diff --git a/session.h b/session.h
index ee9338e4..cbb8e3a3 100644
--- a/session.h
+++ b/session.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.h,v 1.29 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: session.h,v 1.30 2008/05/08 12:21:16 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -31,6 +31,7 @@ typedef struct Session Session;
struct Session {
int used;
int self;
+ int next_unused;
struct passwd *pw;
Authctxt *authctxt;
pid_t pid;
@@ -65,6 +66,7 @@ void do_authenticated(Authctxt *);
void do_cleanup(Authctxt *);
int session_open(Authctxt *, int);
+void session_unused(int);
int session_input_channel_req(Channel *, const char *);
void session_close_by_pid(pid_t, int);
void session_close_by_channel(int, void *);
diff --git a/sshd_config b/sshd_config
index 1f97a9dc..c5ee7c8a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,4 +1,4 @@
-# $OpenBSD: sshd_config,v 1.78 2008/05/07 06:43:35 pyr Exp $
+# $OpenBSD: sshd_config,v 1.79 2008/05/08 12:21:16 djm Exp $
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
@@ -41,6 +41,7 @@ Protocol 2
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
+#MaxSessions 10
#RSAAuthentication yes
#PubkeyAuthentication yes
diff --git a/sshd_config.5 b/sshd_config.5
index 99b5621e..0d8c140b 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,8 +34,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: sshd_config.5,v 1.89 2008/05/07 08:00:14 jmc Exp $
-.Dd $Mdocdate: May 19 2008 $
+.\" $OpenBSD: sshd_config.5,v 1.90 2008/05/08 12:21:16 djm Exp $
+.Dd $Mdocdate: May 8 2008 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -594,6 +594,9 @@ connection.
Once the number of failures reaches half this value,
additional failures are logged.
The default is 6.
+.It Cm MaxSessions
+Specifies the maximum number of open sessions permitted per network connection.
+The default is 10.
.It Cm MaxStartups
Specifies the maximum number of concurrent unauthenticated connections to the
SSH daemon.