Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: OpenSSH: Dev

sandbox pre-auth privsep child

 

 

OpenSSH dev RSS feed   Index | Next | Previous | View Threaded


djm at mindrot

Jun 22, 2011, 5:53 AM

Post #1 of 5 (497 views)
Permalink
sandbox pre-auth privsep child

Hi,

This patch (relative to -HEAD) defines an API to allow sandboxing of the
pre-auth privsep child and a couple of sandbox implementations.

The idea here is to heavily restrict what the network-face pre-auth
process can do. This was the original intent behind dropping to a
dedicated uid and chrooting to an empty directory, but even this still
allows a compromised slave process to make new network connections and
try to exploit local kernel attack surface.

Fortunately, now that the logging-via-monitor diff is in, the slave
never needs to open a socket, or any new fd at all - so the set of
syscalls that it runs is very small. This lets us use various OS-level
measures to limit what it can do. This approach is not new - it has been
used by Chris Evans' vsftpd FTP server and, more recently, by Google's
Chrome web browser.

This patch includes three concrete sandbox implementations, a dummy one
for platforms that support nothing else, a weak one that uses
setrlimit(2) and a strong one that uses OpenBSD's systrace(4).

The setrlimit(2) sandbox drops the hard and soft fd, process and
"created file size" limits to zero. This effectively prevents the
slave process from forking or creating new file descriptors (e.g.
sockets). This works well suprisingly well on most platforms at
preventing a compromised slave from, e.g., acting as a proxy into
one's network but it doesn't do much to reduce kernel attack surface.
Credit goes to Darren Tucker for this idea.

The systrace(4) sandbox uses systrace's unsupervised fast-path, which
is basically a list of allowed syscalls. The set of allowed syscalls
here is very narrow, so this is excellent containment that prevents
the slave from doing anything nasty directly, and removes almost all of
the kernel attack surface too. Note that the systrace sandbox uses a
recently-committed extension to systrace (the per-syscall
SYSTR_POLICY_KILL policy) that is, so far, only present in OpenBSD.

I'd like to add specific sandboxes for other platforms too - OS X has
OS-level sandboxing support that looks suitable and shouldn't be too
hard to add. Linux's PR_SET_SECCOMP is too restrictive, but Will
Drewry's patches to extend it to allow a set of permitted syscalls
would make it a great fit. FreeBSD's Capsicum would be another good
target.

In the meantime, I'd appreciate test reports of the rlimit sandbox
at least. It should do no harm on any platform that supports setrlimit().
Note that you will need to set "UsePrivilegeSeparation=sandbox" when
starting sshd with this patch to actually use the sandbox.

-d

Index: Makefile.in
===================================================================
RCS file: /var/cvs/openssh/Makefile.in,v
retrieving revision 1.322
diff -u -p -r1.322 Makefile.in
--- Makefile.in 5 May 2011 03:48:37 -0000 1.322
+++ Makefile.in 22 Jun 2011 09:17:44 -0000
@@ -89,7 +89,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
auth2-gss.o gss-serv.o gss-serv-krb5.o \
loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
sftp-server.o sftp-common.o \
- roaming_common.o roaming_serv.o
+ roaming_common.o roaming_serv.o \
+ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o

MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out
MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5
Index: configure.ac
===================================================================
RCS file: /var/cvs/openssh/configure.ac,v
retrieving revision 1.476
diff -u -p -r1.476 configure.ac
--- configure.ac 3 Jun 2011 02:11:38 -0000 1.476
+++ configure.ac 22 Jun 2011 09:29:47 -0000
@@ -106,6 +106,16 @@ AC_SUBST([LD])
AC_C_INLINE

AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include <limits.h>])
+AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [.
+ #include <sys/types.h>
+ #include <sys/param.h>
+ #include <dev/systrace.h>
+])
+AC_CHECK_DECL([RLIMIT_NPROC],
+ [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [
+ #include <sys/types.h>
+ #include <sys/resource.h>
+])

use_stack_protector=1
AC_ARG_WITH([stackprotect],
@@ -2461,6 +2471,34 @@ AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], [
[non-privileged user for privilege separation])
AC_SUBST([SSH_PRIVSEP_USER])

+# Decide which sandbox style to use
+sandbox_arg=""
+AC_ARG_WITH([sandbox],
+ [ --with-sandbox=style Specify privilege separation sandbox (no, rlimit, systrace)],
+ [.
+ if test "x$withval" = "xyes" ; then
+ sandbox_arg=""
+ else
+ sandbox_arg="$withval"
+ fi
+ ]
+)
+if test "x$sandbox_arg" = "xsystrace" || \
+ ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then
+ SANDBOX_STYLE="systrace"
+ AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)])
+elif test "x$sandbox_arg" = "xrlimit" || \
+ ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" ) ; then
+ SANDBOX_STYLE="rlimit"
+ AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)])
+elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \
+ test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then
+ SANDBOX_STYLE="none"
+ AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing])
+else
+ AC_MSG_ERROR([unsupported -with-sandbox])
+fi
+
# Cheap hack to ensure NEWS-OS libraries are arranged right.
if test ! -z "$SONY" ; then
LIBS="$LIBS -liberty";
@@ -4191,6 +4229,7 @@ echo " IP address in \$DISPLAY hac
echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
echo " BSD Auth support: $BSD_AUTH_MSG"
echo " Random number source: $RAND_MSG"
+echo " Privsep sandbox style: $SANDBOX_STYLE"

echo ""

Index: sandbox-null.c
===================================================================
RCS file: sandbox-null.c
diff -N sandbox-null.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sandbox-null.c 22 Jun 2011 09:17:44 -0000
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2011 Damien Miller <djm [at] mindrot>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef SANDBOX_NULL
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "sandbox.h"
+#include "xmalloc.h"
+
+/* dummy sandbox */
+
+struct ssh_sandbox {
+ int junk;
+};
+
+struct ssh_sandbox *
+ssh_sandbox_init(void)
+{
+ struct ssh_sandbox *box;
+
+ /*
+ * Strictly, we don't need to maintain any state here but we need
+ * to return non-NULL to satisfy the API.
+ */
+ box = xcalloc(1, sizeof(*box));
+ return box;
+}
+
+void
+ssh_sandbox_child(struct ssh_sandbox *box)
+{
+ /* Nothing to do here */
+}
+
+void
+ssh_sandbox_parent_finish(struct ssh_sandbox *box)
+{
+ free(box);
+}
+
+void
+ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
+{
+ /* Nothing to do here */
+}
+
+#endif /* SANDBOX_NULL */
Index: sandbox-rlimit.c
===================================================================
RCS file: sandbox-rlimit.c
diff -N sandbox-rlimit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sandbox-rlimit.c 22 Jun 2011 12:35:14 -0000
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Damien Miller <djm [at] mindrot>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef SANDBOX_RLIMIT
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "sandbox.h"
+#include "xmalloc.h"
+
+/* Minimal sandbox that sets zero nfiles, nprocs and filesize rlimits */
+
+struct ssh_sandbox {
+ pid_t child_pid;
+};
+
+struct ssh_sandbox *
+ssh_sandbox_init(void)
+{
+ struct ssh_sandbox *box;
+
+ /*
+ * Strictly, we don't need to maintain any state here but we need
+ * to return non-NULL to satisfy the API.
+ */
+ debug3("%s: preparing rlimit sandbox", __func__);
+ box = xcalloc(1, sizeof(*box));
+ box->child_pid = 0;
+
+ return box;
+}
+
+void
+ssh_sandbox_child(struct ssh_sandbox *box)
+{
+ struct rlimit rl_zero;
+
+ rl_zero.rlim_cur = rl_zero.rlim_max = 0;
+
+ if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
+ fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s",
+ __func__, strerror(errno));
+ if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1)
+ fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s",
+ __func__, strerror(errno));
+#ifdef HAVE_RLIMIT_NPROC
+ if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
+ fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s",
+ __func__, strerror(errno));
+#endif
+}
+
+void
+ssh_sandbox_parent_finish(struct ssh_sandbox *box)
+{
+ free(box);
+ debug3("%s: finished", __func__);
+}
+
+void
+ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
+{
+ box->child_pid = child_pid;
+}
+
+#endif /* SANDBOX_RLIMIT */
Index: sandbox-systrace.c
===================================================================
RCS file: sandbox-systrace.c
diff -N sandbox-systrace.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sandbox-systrace.c 22 Jun 2011 09:17:44 -0000
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2011 Damien Miller <djm [at] mindrot>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef SANDBOX_SYSTRACE
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+
+#include <dev/systrace.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atomicio.h"
+#include "log.h"
+#include "sandbox.h"
+#include "xmalloc.h"
+
+static const int preauth_policy[] = {
+ SYS___sysctl,
+ SYS_close,
+ SYS_exit,
+ SYS_getpid,
+ SYS_gettimeofday,
+ SYS_madvise,
+ SYS_mmap,
+ SYS_mprotect,
+ SYS_poll,
+ SYS_munmap,
+ SYS_read,
+ SYS_select,
+ SYS_sigprocmask,
+ SYS_write,
+ -1
+};
+
+struct ssh_sandbox {
+ int child_sock;
+ int parent_sock;
+ int systrace_fd;
+ pid_t child_pid;
+ struct systrace_policy policy;
+};
+
+struct ssh_sandbox *
+ssh_sandbox_init(void)
+{
+ struct ssh_sandbox *box;
+ int s[2];
+
+ debug3("%s: preparing systrace sandbox", __func__);
+ box = xcalloc(1, sizeof(*box));
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1)
+ fatal("%s: socketpair: %s", __func__, strerror(errno));
+ box->child_sock = s[0];
+ box->parent_sock = s[1];
+ box->systrace_fd = -1;
+ box->child_pid = 0;
+
+ return box;
+}
+
+void
+ssh_sandbox_child(struct ssh_sandbox *box)
+{
+ char whatever = 0;
+
+ close(box->parent_sock);
+ /* Signal parent that we are ready */
+ debug3("%s: ready", __func__);
+ if (atomicio(vwrite, box->child_sock, &whatever, 1) != 1)
+ fatal("%s: write: %s", __func__, strerror(errno));
+ /* Wait for parent to signal for us to go */
+ if (atomicio(read, box->child_sock, &whatever, 1) != 1)
+ fatal("%s: read: %s", __func__, strerror(errno));
+ debug3("%s: started", __func__);
+ close(box->child_sock);
+}
+
+static void
+ssh_sandbox_parent(struct ssh_sandbox *box, pid_t child_pid,
+ const int *allowed_syscalls)
+{
+ int dev_systrace, i, j, found;
+ char whatever = 0;
+
+ debug3("%s: wait for child %ld", __func__, (long)child_pid);
+ box->child_pid = child_pid;
+ close(box->child_sock);
+ /* Wait for child to signal that it is ready */
+ if (atomicio(read, box->parent_sock, &whatever, 1) != 1)
+ fatal("%s: read: %s", __func__, strerror(errno));
+ debug3("%s: child %ld ready", __func__, (long)child_pid);
+
+ /* Set up systracing of child */
+ if ((dev_systrace = open("/dev/systrace", O_RDONLY)) == -1)
+ fatal("%s: open(\"/dev/systrace\"): %s", __func__,
+ strerror(errno));
+ if (ioctl(dev_systrace, STRIOCCLONE, &box->systrace_fd) == -1)
+ fatal("%s: ioctl(STRIOCCLONE, %d): %s", __func__,
+ dev_systrace, strerror(errno));
+ close(dev_systrace);
+ debug3("%s: systrace attach, fd=%d", __func__, box->systrace_fd);
+ if (ioctl(box->systrace_fd, STRIOCATTACH, &child_pid) == -1)
+ fatal("%s: ioctl(%d, STRIOCATTACH, %d): %s", __func__,
+ box->systrace_fd, child_pid, strerror(errno));
+
+ /* Allocate and assign policy */
+ bzero(&box->policy, sizeof(box->policy));
+ box->policy.strp_op = SYSTR_POLICY_NEW;
+ box->policy.strp_maxents = SYS_MAXSYSCALL;
+ if (ioctl(box->systrace_fd, STRIOCPOLICY, &box->policy) == -1)
+ fatal("%s: ioctl(%d, STRIOCPOLICY (new)): %s", __func__,
+ box->systrace_fd, strerror(errno));
+
+ box->policy.strp_op = SYSTR_POLICY_ASSIGN;
+ box->policy.strp_pid = box->child_pid;
+ if (ioctl(box->systrace_fd, STRIOCPOLICY, &box->policy) == -1)
+ fatal("%s: ioctl(%d, STRIOCPOLICY (assign)): %s",
+ __func__, box->systrace_fd, strerror(errno));
+
+ /* Set per-syscall policy */
+ for (i = 0; i < SYS_MAXSYSCALL; i++) {
+ for (j = found = 0; allowed_syscalls[j] != -1 && !found; j++) {
+ if (allowed_syscalls[j] == i)
+ found = 1;
+ }
+ box->policy.strp_op = SYSTR_POLICY_MODIFY;
+ box->policy.strp_code = i;
+ box->policy.strp_policy = found ?
+ SYSTR_POLICY_PERMIT : SYSTR_POLICY_KILL;
+ if (found)
+ debug3("%s: policy: enable syscall %d", __func__, i);
+ if (ioctl(box->systrace_fd, STRIOCPOLICY,
+ &box->policy) == -1)
+ fatal("%s: ioctl(%d, STRIOCPOLICY (modify)): %s",
+ __func__, box->systrace_fd, strerror(errno));
+ }
+
+ /* Signal the child to start running */
+ debug3("%s: start child %ld", __func__, (long)child_pid);
+ if (atomicio(vwrite, box->parent_sock, &whatever, 1) != 1)
+ fatal("%s: write: %s", __func__, strerror(errno));
+ close(box->parent_sock);
+}
+
+void
+ssh_sandbox_parent_finish(struct ssh_sandbox *box)
+{
+ /* Closing this before the child exits will terminate it */
+ close(box->systrace_fd);
+
+ free(box);
+ debug3("%s: finished", __func__);
+}
+
+void
+ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
+{
+ ssh_sandbox_parent(box, child_pid, preauth_policy);
+}
+
+#endif /* SANDBOX_SYSTRACE */
Index: sandbox.h
===================================================================
RCS file: sandbox.h
diff -N sandbox.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sandbox.h 22 Jun 2011 09:17:44 -0000
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2011 Damien Miller <djm [at] mindrot>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct ssh_sandbox;
+
+struct ssh_sandbox *ssh_sandbox_init(void);
+void ssh_sandbox_child(struct ssh_sandbox *);
+void ssh_sandbox_parent_finish(struct ssh_sandbox *);
+void ssh_sandbox_parent_preauth(struct ssh_sandbox *, pid_t);
Index: servconf.c
===================================================================
RCS file: /var/cvs/openssh/servconf.c,v
retrieving revision 1.217
diff -u -p -r1.217 servconf.c
--- servconf.c 20 Jun 2011 04:43:11 -0000 1.217
+++ servconf.c 22 Jun 2011 09:17:44 -0000
@@ -280,7 +280,7 @@ fill_default_server_options(ServerOption

/* Turn privilege separation on by default */
if (use_privsep == -1)
- use_privsep = 1;
+ use_privsep = PRIVSEP_SANDBOX;

#ifndef HAVE_MMAP
if (use_privsep && options->compression == 1) {
@@ -701,6 +701,12 @@ static const struct multistate multistat
{ "no", 0 },
{ NULL, -1 }
};
+static const struct multistate multistate_privsep[] = {
+ { "sandbox", PRIVSEP_SANDBOX },
+ { "yes", PRIVSEP_ON },
+ { "no", PRIVSEP_OFF },
+ { NULL, -1 }
+};

int
process_server_config_line(ServerOptions *options, char *line,
@@ -1066,7 +1072,8 @@ process_server_config_line(ServerOptions

case sUsePrivilegeSeparation:
intptr = &use_privsep;
- goto parse_flag;
+ multistate_ptr = multistate_privsep;
+ goto parse_multistate;

case sAllowUsers:
while ((arg = strdelim(&cp)) && *arg != '\0') {
@@ -1549,31 +1556,34 @@ parse_server_config(ServerOptions *optio
}

static const char *
-fmt_intarg(ServerOpCodes code, int val)
+fmt_multistate_int(int val, const struct multistate *m)
{
- if (code == sAddressFamily) {
- switch (val) {
- case AF_INET:
- return "inet";
- case AF_INET6:
- return "inet6";
- case AF_UNSPEC:
- return "any";
- default:
- return "UNKNOWN";
- }
- }
- if (code == sPermitRootLogin) {
- switch (val) {
- case PERMIT_NO_PASSWD:
- return "without-password";
- case PERMIT_FORCED_ONLY:
- return "forced-commands-only";
- case PERMIT_YES:
- return "yes";
- }
+ u_int i;
+
+ if (val == -1)
+ return "unset";
+ for (i = 0; m[i].key != NULL; i++) {
+ if (m[i].value == val)
+ return m[i].key;
}
- if (code == sProtocol) {
+ return "UNKNOWN";
+}
+
+static const char *
+fmt_intarg(ServerOpCodes code, int val)
+{
+ switch (code) {
+ case sAddressFamily:
+ return fmt_multistate_int(val, multistate_addressfamily);
+ case sPermitRootLogin:
+ return fmt_multistate_int(val, multistate_permitrootlogin);
+ case sGatewayPorts:
+ return fmt_multistate_int(val, multistate_gatewayports);
+ case sCompression:
+ return fmt_multistate_int(val, multistate_compression);
+ case sUsePrivilegeSeparation:
+ return fmt_multistate_int(val, multistate_privsep);
+ case sProtocol:
switch (val) {
case SSH_PROTO_1:
return "1";
@@ -1584,20 +1594,18 @@ fmt_intarg(ServerOpCodes code, int val)
default:
return "UNKNOWN";
}
+ default:
+ switch (val) {
+ case -1:
+ return "unset";
+ case 0:
+ return "no";
+ case 1:
+ return "yes";
+ default:
+ return "UNKNOWN";
+ }
}
- if (code == sGatewayPorts && val == 2)
- return "clientspecified";
- if (code == sCompression && val == COMP_DELAYED)
- return "delayed";
- switch (val) {
- case -1:
- return "unset";
- case 0:
- return "no";
- case 1:
- return "yes";
- }
- return "UNKNOWN";
}

static const char *
Index: servconf.h
===================================================================
RCS file: /var/cvs/openssh/servconf.h,v
retrieving revision 1.90
diff -u -p -r1.90 servconf.h
--- servconf.h 29 May 2011 11:39:39 -0000 1.90
+++ servconf.h 22 Jun 2011 09:17:44 -0000
@@ -36,6 +36,11 @@
#define PERMIT_NO_PASSWD 2
#define PERMIT_YES 3

+/* use_privsep */
+#define PRIVSEP_OFF 0
+#define PRIVSEP_ON 1
+#define PRIVSEP_SANDBOX 2
+
#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */
#define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */

Index: sshd.c
===================================================================
RCS file: /var/cvs/openssh/sshd.c,v
retrieving revision 1.405
diff -u -p -r1.405 sshd.c
--- sshd.c 20 Jun 2011 04:42:23 -0000 1.405
+++ sshd.c 22 Jun 2011 09:17:44 -0000
@@ -118,6 +118,7 @@
#endif
#include "monitor_wrap.h"
#include "roaming.h"
+#include "sandbox.h"
#include "version.h"

#ifdef LIBWRAP
@@ -624,18 +625,23 @@ privsep_preauth(Authctxt *authctxt)
{
int status;
pid_t pid;
+ struct ssh_sandbox *box = NULL;

/* Set up unprivileged child process to deal with network data */
pmonitor = monitor_init();
/* Store a pointer to the kex for later rekeying */
pmonitor->m_pkex = &xxx_kex;

+ if (use_privsep == PRIVSEP_SANDBOX)
+ box = ssh_sandbox_init();
pid = fork();
if (pid == -1) {
fatal("fork of unprivileged child failed");
} else if (pid != 0) {
debug2("Network child is on pid %ld", (long)pid);

+ if (box != NULL)
+ ssh_sandbox_parent_preauth(box, pid);
pmonitor->m_pid = pid;
monitor_child_preauth(authctxt, pmonitor);

@@ -643,10 +649,21 @@ privsep_preauth(Authctxt *authctxt)
monitor_sync(pmonitor);

/* Wait for the child's exit status */
- while (waitpid(pid, &status, 0) < 0)
+ while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR)
- break;
- return (1);
+ fatal("%s: waitpid: %s", __func__,
+ strerror(errno));
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ fatal("%s: preauth child exited with status %d",
+ __func__, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status))
+ fatal("%s: preauth child terminated by signal %d",
+ __func__, WTERMSIG(status));
+ if (box != NULL)
+ ssh_sandbox_parent_finish(box);
+ return 1;
} else {
/* child */
close(pmonitor->m_sendfd);
@@ -659,8 +676,11 @@ privsep_preauth(Authctxt *authctxt)
if (getuid() == 0 || geteuid() == 0)
privsep_preauth_child();
setproctitle("%s", "[net]");
+ if (box != NULL)
+ ssh_sandbox_child(box);
+
+ return 0;
}
- return (0);
}

static void
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev [at] mindrot
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev


djm at mindrot

Jun 22, 2011, 6:06 AM

Post #2 of 5 (480 views)
Permalink
Re: sandbox pre-auth privsep child [In reply to]

On Wed, 22 Jun 2011, Damien Miller wrote:

> Hi,
>
> This patch (relative to -HEAD) defines an API to allow sandboxing of the
> pre-auth privsep child and a couple of sandbox implementations.

If you want to verify that the sandbox is actually working, you might
want to try this little hack.

Needless to say, don't bother doing this will the null sandbox :)

--- sshd.c.orig 2011-06-22 23:05:21.000000000 +1000
+++ sshd.c 2011-06-22 23:05:28.000000000 +1000
@@ -676,8 +676,14 @@
if (getuid() == 0 || geteuid() == 0)
privsep_preauth_child();
setproctitle("%s", "[net]");
- if (box != NULL)
+ if (box != NULL) {
ssh_sandbox_child(box);
+ if (fork() != -1)
+ fatal("fork() succeeded despite sandbox");
+ if (socket(AF_INET, SOCK_STREAM, 0) != -1)
+ fatal("fork() succeeded despite sandbox");
+ debug("sandbox seems to be working");
+ }

return 0;
}
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev [at] mindrot
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev


dkg at fifthhorseman

Jun 22, 2011, 7:07 AM

Post #3 of 5 (475 views)
Permalink
Re: sandbox pre-auth privsep child [In reply to]

On 06/22/2011 09:06 AM, Damien Miller wrote:
>> This patch (relative to -HEAD) defines an API to allow sandboxing of the
>> pre-auth privsep child and a couple of sandbox implementations.

thanks for doing this, Damien. I like this approach. A quick note on
this test:


> - if (box != NULL)
> + if (box != NULL) {
> ssh_sandbox_child(box);
> + if (fork() != -1)
> + fatal("fork() succeeded despite sandbox");
> + if (socket(AF_INET, SOCK_STREAM, 0) != -1)
> + fatal("fork() succeeded despite sandbox");

i think this error message should mention socket() instead of fork().

Regards,

--dkg
Attachments: signature.asc (1.01 KB)


scott_n at xypro

Jun 22, 2011, 8:20 AM

Post #4 of 5 (479 views)
Permalink
RE: sandbox pre-auth privsep child [In reply to]

> On Wed, 22 Jun 2011, Damien Miller wrote:
>
> > Hi,
> >
> > This patch (relative to -HEAD) defines an API to allow sandboxing of
> the
> > pre-auth privsep child and a couple of sandbox implementations.
>
> If you want to verify that the sandbox is actually working, you might
> want to try this little hack.
>
> Needless to say, don't bother doing this will the null sandbox :)
>
> --- sshd.c.orig 2011-06-22 23:05:21.000000000 +1000
> +++ sshd.c 2011-06-22 23:05:28.000000000 +1000
> @@ -676,8 +676,14 @@
> if (getuid() == 0 || geteuid() == 0)
> privsep_preauth_child();
> setproctitle("%s", "[net]");
> - if (box != NULL)
> + if (box != NULL) {
> ssh_sandbox_child(box);
> + if (fork() != -1)
> + fatal("fork() succeeded despite
sandbox");
> + if (socket(AF_INET, SOCK_STREAM, 0) != -1)
> + fatal("fork() succeeded despite
sandbox");
> + debug("sandbox seems to be working");
> + }
>
> return 0;
> }

The message in the second fatal() call should probably read
"socket() succeeded..." instead of "fork() succeeded..."


_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev [at] mindrot
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev


alex at alex

Jun 22, 2011, 8:21 AM

Post #5 of 5 (475 views)
Permalink
Re: sandbox pre-auth privsep child [In reply to]

--On 22 June 2011 22:53:05 +1000 Damien Miller <djm [at] mindrot> wrote:

> The idea here is to heavily restrict what the network-face pre-auth
> process can do. This was the original intent behind dropping to a
> dedicated uid and chrooting to an empty directory, but even this still
> allows a compromised slave process to make new network connections and
> try to exploit local kernel attack surface

Perhaps not ready for primetime, but at least on Linux have you looked at
CLONE_NEWNET etc.? This generates a child with (essentially) an unconfigured
network stack; the other CLONE_XXX flags may be useful too.

--
Alex Bligh
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev [at] mindrot
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

OpenSSH dev RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.