summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2008-05-08 12:21:17 +0000
committerDamien Miller <djm@cvs.openbsd.org>2008-05-08 12:21:17 +0000
commit213a55b8eabbf7f0692cae325c37e551c983afb6 (patch)
tree1f4e55b27bd1176028684fafa7f815446b1110d7
parent1e325f2a90e53ea6a2c04110feba6e635db362c7 (diff)
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@
-rw-r--r--usr.bin/ssh/Makefile.inc6
-rw-r--r--usr.bin/ssh/monitor.c4
-rw-r--r--usr.bin/ssh/monitor_wrap.c22
-rw-r--r--usr.bin/ssh/servconf.c21
-rw-r--r--usr.bin/ssh/servconf.h4
-rw-r--r--usr.bin/ssh/session.c313
-rw-r--r--usr.bin/ssh/session.h4
-rw-r--r--usr.bin/ssh/sshd_config3
-rw-r--r--usr.bin/ssh/sshd_config.57
9 files changed, 262 insertions, 122 deletions
diff --git a/usr.bin/ssh/Makefile.inc b/usr.bin/ssh/Makefile.inc
index 798165f23a7..34d76c176d3 100644
--- a/usr.bin/ssh/Makefile.inc
+++ b/usr.bin/ssh/Makefile.inc
@@ -1,9 +1,9 @@
-# $OpenBSD: Makefile.inc,v 1.30 2005/06/17 02:45:19 djm Exp $
+# $OpenBSD: Makefile.inc,v 1.31 2008/05/08 12:21:16 djm Exp $
CFLAGS+= -I${.CURDIR}/..
CDIAGFLAGS= -Wall
-#CDIAGFLAGS+= -Werror
+CDIAGFLAGS+= -Werror
CDIAGFLAGS+= -Wpointer-arith
CDIAGFLAGS+= -Wno-uninitialized
CDIAGFLAGS+= -Wstrict-prototypes
@@ -13,7 +13,7 @@ CDIAGFLAGS+= -Wsign-compare
CDIAGFLAGS+= -Wbounded
CDIAGFLAGS+= -Wshadow
-#DEBUG=-g
+DEBUG=-g
#CFLAGS+= -DSMARTCARD
#LDADD+= -lsectok
diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c
index c14ecc614e5..9d07e5cc75f 100644
--- a/usr.bin/ssh/monitor.c
+++ b/usr.bin/ssh/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>
@@ -1000,7 +1000,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/usr.bin/ssh/monitor_wrap.c b/usr.bin/ssh/monitor_wrap.c
index 8f2d13a0813..a3fb07cc957 100644
--- a/usr.bin/ssh/monitor_wrap.c
+++ b/usr.bin/ssh/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>
@@ -654,7 +654,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);
@@ -699,8 +712,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/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c
index ee47a9d9814..78569c4a5b0 100644
--- a/usr.bin/ssh/servconf.c
+++ b/usr.bin/ssh/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
@@ -107,6 +107,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;
@@ -225,6 +226,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)
@@ -266,7 +269,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,
@@ -358,6 +361,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 },
@@ -652,7 +656,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.",
@@ -664,7 +668,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.",
@@ -733,7 +737,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.",
@@ -776,7 +780,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.",
@@ -1112,6 +1116,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;
@@ -1339,6 +1347,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/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h
index fbb1bcff625..b79accb4328 100644
--- a/usr.bin/ssh/servconf.h
+++ b/usr.bin/ssh/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/usr.bin/ssh/session.c b/usr.bin/ssh/session.c
index 48ebc859bcc..0e65e603104 100644
--- a/usr.bin/ssh/session.c
+++ b/usr.bin/ssh/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
@@ -94,9 +94,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 *);
void do_child(Session *, const char *);
void do_motd(void);
@@ -121,8 +121,9 @@ extern Buffer loginmsg;
const char *original_command = NULL;
/* data */
-#define MAX_SESSIONS 10
-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
@@ -154,7 +155,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) {
@@ -166,43 +167,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",
@@ -211,6 +217,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
@@ -360,10 +379,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);
@@ -393,28 +416,44 @@ do_authenticated1(Authctxt *authctxt)
* 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;
-
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));
+ 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;
+ }
+
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));
+ close(inout[0]);
+ close(inout[1]);
+ close(err[0]);
+ close(err[1]);
+ 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
@@ -432,7 +471,7 @@ 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");
if (dup2(err[0], 2) < 0) /* stderr */
perror("dup2 stderr");
@@ -440,9 +479,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;
}
- if (pid < 0)
- packet_disconnect("fork failed: %.100s", strerror(errno));
+
s->pid = pid;
/* Set interactive/non-interactive mode. */
packet_set_interactive(s->display != NULL);
@@ -456,11 +496,15 @@ do_exec_no_pty(Session *s, const char *command)
* 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]. */
}
+ return 0;
}
/*
@@ -469,7 +513,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;
@@ -480,12 +524,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);
@@ -507,33 +585,22 @@ do_exec_pty(Session *s, const char *command)
if (!(options.use_login && command == NULL))
do_login(s, command);
- /* Do common processing for the child, such as execing the command. */
+ /*
+ * Do common processing for the child, such as execing
+ * the command.
+ */
do_child(s, command);
/* NOTREACHED */
+ default:
+ break;
}
- 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);
@@ -541,15 +608,18 @@ do_exec_pty(Session *s, const char *command)
server_loop(pid, ptyfd, fdout, -1);
/* server_loop _has_ closed ptyfd and fdout. */
}
+ return 0;
}
/*
* 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;
@@ -576,9 +646,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;
@@ -588,6 +658,8 @@ do_exec(Session *s, const char *command)
* multiple copies of the login messages.
*/
buffer_clear(&loginmsg);
+
+ return ret;
}
@@ -1293,43 +1365,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,
@@ -1359,7 +1467,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);
@@ -1375,10 +1483,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;
}
}
@@ -1392,7 +1501,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)
@@ -1415,7 +1524,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;
@@ -1471,7 +1580,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;
@@ -1524,8 +1634,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;
}
}
@@ -1568,19 +1677,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
@@ -1590,8 +1699,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;
}
@@ -1738,8 +1846,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;
@@ -1895,7 +2004,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);
@@ -1904,6 +2012,7 @@ session_close(Session *s)
xfree(s->env);
}
session_proctitle(s);
+ session_unused(s->self);
}
void
@@ -1967,7 +2076,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)
@@ -1984,7 +2093,7 @@ session_tty_list(void)
static char buf[1024];
int i;
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) {
if (buf[0] != '\0')
diff --git a/usr.bin/ssh/session.h b/usr.bin/ssh/session.h
index ee9338e4f5e..cbb8e3a32d1 100644
--- a/usr.bin/ssh/session.h
+++ b/usr.bin/ssh/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/usr.bin/ssh/sshd_config b/usr.bin/ssh/sshd_config
index eeca08087a9..8dea49e8bb3 100644
--- a/usr.bin/ssh/sshd_config
+++ b/usr.bin/ssh/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.
@@ -39,6 +39,7 @@ Protocol 2
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
+#MaxSessions 10
#RSAAuthentication yes
#PubkeyAuthentication yes
diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5
index 2e95d02c791..1edd8217a17 100644
--- a/usr.bin/ssh/sshd_config.5
+++ b/usr.bin/ssh/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 7 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.