Hi everybody!

OpenVPN already has support for dropping privileges and confining
itself to a directory *after* startup (thanks to calls like setgid,
setuid and chroot) which makes for much better management than if you
had to respectively start OpenVPN unprivileged and add the privileges
it (only) needs during startup, or if you chroot'ed OpenVPN before
startup and had to copy the necessary files in the confinement
directory.

The very same problem occurs with SELinux policy enforcement: one can
apply a policy before startup (such as the ready-to-use one provided
by 
http://oss.tresys.com/projects/refpolicy/browser/trunk/policy/modules/services/openvpn.te)
but it is waaaay too complicated, just because many rights have to be
granted to OpenVPN during its initialization... or one can apply a
very simple SELinux policy to OpenVPN when the only need left is
basically network I/O operations.

The only downside to this is that, just like with setuid() and
chroot(), OpenVPN has to call setcon() itself, which is why I am
submitting this patch :-)

Please note that, while this patch does indeed bring a new dependency
to libselinux in OpenVPN on Linux:
* the feature will of course only be added if detected by ./configure
* libselinux is so common now that even /bin/ls is linked against it
on most Linux systems
so OpenVPN should get SELinux support quite transparently ;-)


Best regards,

-- 
Sebastien Raveau
Information Warfare Consultant
http://blog.sebastien.raveau.name/
diff -urN -X generated openvpn-2.0.9.orig/configure.ac openvpn-2.0.9/configure.ac
--- openvpn-2.0.9.orig/configure.ac	2006-10-01 14:18:15.000000000 +0200
+++ openvpn-2.0.9/configure.ac	2009-06-22 22:42:30.000000000 +0200
@@ -95,6 +95,12 @@
    [DEBUG="yes"]
 )
 
+AC_ARG_ENABLE(selinux,
+   [  --disable-selinux       Disable SELinux support],
+   [SELINUX="$enableval"],
+   [SELINUX="yes"]
+)
+
 AC_ARG_ENABLE(small,
    [  --enable-small          Enable smaller executable size (disable OCC, usage message, and verb 4 parm list)],
    [SMALL="$enableval"],
@@ -568,6 +574,23 @@
    fi
 fi
 
+dnl
+dnl check for SELinux library and headers
+dnl
+if test "$SELINUX" = "yes"; then
+   AC_CHECKING([for libselinux Library and Header files])
+   AC_CHECK_HEADER(selinux/selinux.h,
+      [AC_CHECK_LIB(selinux, setcon,
+         [
+            OPENVPN_ADD_LIBS(-lselinux)
+            AC_DEFINE(HAVE_SETCON, 1, [SELinux support])
+         ],
+            [AC_MSG_RESULT([SELinux library not found.])]
+         )],
+      [AC_MSG_RESULT([SELinux headers not found.])]
+   )
+fi
+
 dnl enable multi-client mode
 if test "$MULTI" = "yes"; then
    AC_DEFINE(ENABLE_CLIENT_SERVER, 1, [Enable client/server capability])
diff -urN -X generated openvpn-2.0.9.orig/init.c openvpn-2.0.9/init.c
--- openvpn-2.0.9.orig/init.c	2006-04-05 08:42:32.000000000 +0200
+++ openvpn-2.0.9/init.c	2009-06-22 22:42:30.000000000 +0200
@@ -405,6 +405,17 @@
 	{
 	  msg (M_INFO, "NOTE: UID/GID downgrade %s", why_not);
 	}
+
+      /* set SELinux context; doing it after chroot requires /proc
+         to be mounted in the chroot (which is annoying indeed but
+         doing it before requires more complex SELinux policies */
+      if (c->options.selinux_context)
+        {
+          if (no_delay)
+            do_setcon(c->options.selinux_context);
+          else
+            msg (M_INFO, "NOTE: setcon %s", why_not);
+        }
     }
 }
 
@@ -1563,8 +1574,8 @@
   if (o->ping_send_timeout && !o->ping_rec_timeout)
     msg (M_WARN, "WARNING: --ping should normally be used with --ping-restart or --ping-exit");
 
-  if ((o->username || o->groupname || o->chroot_dir) && (!o->persist_tun || !o->persist_key))
-    msg (M_WARN, "WARNING: you are using user/group/chroot without persist-key/persist-tun -- this may cause restarts to fail");
+  if ((o->username || o->groupname || o->chroot_dir || o->selinux_context) && (!o->persist_tun || !o->persist_key))
+    msg (M_WARN, "WARNING: you are using user/group/chroot/setcon without persist-key/persist-tun -- this may cause restarts to fail");
 
 #if P2MP
   if (o->pull && o->ifconfig_local && c->first_time)
diff -urN -X generated openvpn-2.0.9.orig/misc.c openvpn-2.0.9/misc.c
--- openvpn-2.0.9.orig/misc.c	2005-11-05 08:04:22.000000000 +0100
+++ openvpn-2.0.9/misc.c	2009-06-22 23:00:36.000000000 +0200
@@ -62,6 +62,25 @@
     }
 }
 
+/* Apply SELinux context in order to restrict what
+   OpenVPN can do to _only_ what it is supposed to
+   do after initialization is complete (basically
+   network I/O operations) */
+void
+do_setcon (const char *context)
+{
+  if (context)
+    {
+#ifdef HAVE_SETCON
+      if (-1 == setcon (context))
+        msg (M_ERR, "setcon to '%s' failed; is /proc accessible?", context);
+      msg (M_INFO, "setcon to '%s' succeeded", context);
+#else
+      msg (M_FATAL, "Sorry but I can't set SELinux context to '%s' because this operating system doesn't appear to support the setcon() system call", context);
+#endif
+    }
+}
+
 /* Get/Set UID of process */
 
 bool
diff -urN -X generated openvpn-2.0.9.orig/openvpn.8 openvpn-2.0.9/openvpn.8
--- openvpn-2.0.9.orig/openvpn.8	2005-11-03 02:16:43.000000000 +0100
+++ openvpn-2.0.9/openvpn.8	2009-06-22 22:42:30.000000000 +0200
@@ -232,6 +232,7 @@
 [\ \fB\-\-server\-bridge\fR\ \fIgateway\ netmask\ pool\-start\-IP\ pool\-end\-IP\fR\ ]
 [\ \fB\-\-server\fR\ \fInetwork\ netmask\fR\ ]
 [\ \fB\-\-service\fR\ \fIexit\-event\ [0|1]\fR\ ]
+[\ \fB\-\-setcon\fR\ \fIcontext\fR\ ]
 [\ \fB\-\-setenv\fR\ \fIname\ value\fR\ ]
 [\ \fB\-\-shaper\fR\ \fIn\fR\ ]
 [\ \fB\-\-show\-adapters\fR\ ]
@@ -1759,6 +1760,39 @@
 are executed after the chroot operation.
 .\"*********************************************************
 .TP
+.B --setcon context
+Apply SELinux
+.B context
+after initialization. This
+essentially provides the ability to restrict OpenVPN's
+rights to only network I/O operations, thanks to
+SELinux. This goes further than
+.B --user
+and
+.B --chroot
+in that those two, while being great security features,
+unfortunately do not protect against privilege escalation
+by exploitation of a vulnerable system call. You can of
+course combine all three, but please note that since
+setcon requires access to /proc you will have to provide
+it inside the chroot directory (e.g. with mount --bind).
+
+Since the setcon operation is delayed until after
+initialization, OpenVPN can be restricted to just
+network-related system calls, whereas by applying the
+context before startup (such as the OpenVPN one provided
+in the SELinux Reference Policies) you will have to
+allow many things required only during initialization.
+
+Like with chroot, complications can result when scripts
+or restarts are executed after the setcon operation,
+which is why you should really consider using the
+.B --persist-key
+and
+.B --persist-tun
+options.
+.\"*********************************************************
+.TP
 .B --daemon [progname]
 Become a daemon after all initialization functions are completed.
 This option will cause all message and error output to
diff -urN -X generated openvpn-2.0.9.orig/options.c openvpn-2.0.9/options.c
--- openvpn-2.0.9.orig/options.c	2005-12-13 00:50:43.000000000 +0100
+++ openvpn-2.0.9/options.c	2009-06-22 22:42:30.000000000 +0200
@@ -220,6 +220,7 @@
   "                  caused by --ping-restart or SIGUSR1\n"
   "--user user     : Set UID to user after initialization.\n"
   "--group group   : Set GID to group after initialization.\n"
+  "--setcon context: Apply this SELinux context after initialization.\n"
   "--chroot dir    : Chroot to this directory after initialization.\n"
   "--cd dir        : Change to this directory before initialization.\n"
   "--daemon [name] : Become a daemon after initialization.\n"
@@ -1016,6 +1017,7 @@
   SHOW_STR (groupname);
   SHOW_STR (chroot_dir);
   SHOW_STR (cd_dir);
+  SHOW_STR (selinux_context);
   SHOW_STR (writepid);
   SHOW_STR (up_script);
   SHOW_STR (down_script);
@@ -2919,6 +2921,12 @@
 	}
       options->cd_dir = p[1];
     }
+  else if (streq (p[0], "setcon") && p[1])
+    {
+      ++i;
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->selinux_context = p[1];
+    }
   else if (streq (p[0], "writepid") && p[1])
     {
       ++i;
diff -urN -X generated openvpn-2.0.9.orig/options.h openvpn-2.0.9/options.h
--- openvpn-2.0.9.orig/options.h	2005-11-01 12:06:11.000000000 +0100
+++ openvpn-2.0.9/options.h	2009-06-22 22:42:30.000000000 +0200
@@ -192,6 +192,7 @@
   const char *groupname;
   const char *chroot_dir;
   const char *cd_dir;
+  const char *selinux_context;
   const char *writepid;
   const char *up_script;
   const char *down_script;

Reply via email to