[ovs-dev,2/8] lib/daemon: support --user option for all OVS daemon
diff mbox

Message ID 1442969477-11026-2-git-send-email-azhou@nicira.com
State Accepted
Headers show

Commit Message

Andy Zhou Sept. 23, 2015, 12:51 a.m. UTC
OVS daemons can now support --user option to run as a non-root
user with less privileges.

See the manpage patch for more descriptions.

Signed-off-by: Andy Zhou <azhou@nicira.com>

----

v3->v4: calling daemon_become_new_user() from daemonize_start(), so the
        API works will for most daemons.  In case a daemon processe
        needs to switch user prior to calling daemonize_start(), it
        daemon_become_new_user() can be called directly.

        Added check in daemonize_start() to make sure --user option has
        been executed if specified.

        On linux, libcap-ng is used exclusively for executing the --user
        option.

        On Windows, --user option will cause the process to die with
        an error message.

        All daemons now accepts --user option.
---
 NEWS                                      |   1 +
 lib/daemon-unix.c                         | 361 +++++++++++++++++++++++++++++-
 lib/daemon-windows.c                      |  14 +-
 lib/daemon.c                              |   2 +-
 lib/daemon.h                              |  33 ++-
 lib/daemon.man                            |  15 ++
 ovn/controller-vtep/ovn-controller-vtep.c |   2 +-
 ovn/controller/ovn-controller.c           |   2 +-
 ovn/northd/ovn-northd.c                   |   2 +-
 ovsdb/ovsdb-client.c                      |   5 +-
 ovsdb/ovsdb-server.c                      |   3 +-
 tests/test-jsonrpc.c                      |   2 +-
 tests/test-netflow.c                      |   4 +-
 tests/test-sflow.c                        |   2 +-
 utilities/ovs-ofctl.c                     |   4 +-
 utilities/ovs-testcontroller.c            |   6 +-
 vswitchd/ovs-vswitchd.c                   |   8 +-
 17 files changed, 438 insertions(+), 28 deletions(-)

Comments

Ben Pfaff Sept. 30, 2015, 12:19 a.m. UTC | #1
On Tue, Sep 22, 2015 at 05:51:11PM -0700, Andy Zhou wrote:
> OVS daemons can now support --user option to run as a non-root
> user with less privileges.
> 
> See the manpage patch for more descriptions.
> 
> Signed-off-by: Andy Zhou <azhou@nicira.com>

...

> +void
> +dameon_become_new_user(bool access_datapath OVS_UNUSED)

s/dameon/daemon/ above.

> +.IP
> +On Windows, this option is not currently supported. For security reasons,
> +Specifying this option will cause the daemon process not to start.

s/Specifying/specifying/ above.

There's a change to the comment on read_pidfile() that looks like a
typo.

I'm not really sure why the caller of daemon_become_new_user() needs to
check the OS type to decide on 'access_datapath' and then
daemon_become_new_user() verifies that it's correct for the OS.  I would
guess that it could just be hardcoded for the caller (e.g. always "true"
for ovs-vswitchd) and then daemon_become_new_user() only needs to do
anything special if it's running on Linux.

Acked-by: Ben Pfaff <blp@nicira.com>
Andy Zhou Oct. 1, 2015, 3:50 a.m. UTC | #2
On Tue, Sep 29, 2015 at 5:19 PM, Ben Pfaff <blp@nicira.com> wrote:
> On Tue, Sep 22, 2015 at 05:51:11PM -0700, Andy Zhou wrote:
>> OVS daemons can now support --user option to run as a non-root
>> user with less privileges.
>>
>> See the manpage patch for more descriptions.
>>
>> Signed-off-by: Andy Zhou <azhou@nicira.com>
>
> ...
>
>> +void
>> +dameon_become_new_user(bool access_datapath OVS_UNUSED)
>
> s/dameon/daemon/ above.
>
>> +.IP
>> +On Windows, this option is not currently supported. For security reasons,
>> +Specifying this option will cause the daemon process not to start.
>
> s/Specifying/specifying/ above.
>
> There's a change to the comment on read_pidfile() that looks like a
> typo.
>
> I'm not really sure why the caller of daemon_become_new_user() needs to
> check the OS type to decide on 'access_datapath' and then
> daemon_become_new_user() verifies that it's correct for the OS.  I would
> guess that it could just be hardcoded for the caller (e.g. always "true"
> for ovs-vswitchd) and then daemon_become_new_user() only needs to do
> anything special if it's running on Linux.
>
> Acked-by: Ben Pfaff <blp@nicira.com>
Thanks. Applied to master with the changes suggested.
>
Russell Bryant Oct. 1, 2015, 3:21 p.m. UTC | #3
On 09/22/2015 08:51 PM, Andy Zhou wrote:
> OVS daemons can now support --user option to run as a non-root
> user with less privileges.
> 
> See the manpage patch for more descriptions.
> 
> Signed-off-by: Andy Zhou <azhou@nicira.com>

FYI, this patch series seems to break the build for me.

With clang:

> lib/daemon-unix.c:858:13: error: implicit declaration of function 'daemon_become_new_user_linux' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
>             daemon_become_new_user_linux(access_datapath);
>             ^
> lib/daemon-unix.c:858:13: note: did you mean 'daemon_become_new_user_unix'?
> lib/daemon-unix.c:785:1: note: 'daemon_become_new_user_unix' declared here
> daemon_become_new_user_unix(void)
> ^

With gcc:

> lib/daemon-unix.c: In function 'daemon_become_new_user__':
> lib/daemon-unix.c:858:13: error: implicit declaration of function 'daemon_become_new_user_linux' [-Werror=implicit-function-declaration]
>              daemon_become_new_user_linux(access_datapath);
>              ^

Patch
diff mbox

diff --git a/NEWS b/NEWS
index ca22c8e..ee4dc2a 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,7 @@  Post-v2.4.0
      targets to run a new system testsuite.  These tests can be run inside
      a Vagrant box.  See INSTALL.md for details
    - Dropped support for GRE64 tunnel.
+   - Added --user option to all daemons
 
 
 v2.4.0 - 20 Aug 2015
diff --git a/lib/daemon-unix.c b/lib/daemon-unix.c
index eb95521..9efe43a 100644
--- a/lib/daemon-unix.c
+++ b/lib/daemon-unix.c
@@ -19,6 +19,8 @@ 
 #include "daemon-private.h"
 #include <errno.h>
 #include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,6 +28,9 @@ 
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#if HAVE_LIBCAPNG
+#include <cap-ng.h>
+#endif
 #include "command-line.h"
 #include "fatal-signal.h"
 #include "dirs.h"
@@ -39,6 +44,18 @@ 
 
 VLOG_DEFINE_THIS_MODULE(daemon_unix);
 
+#ifdef __linux__
+#define LINUX 1
+#else
+#define LINUX 0
+#endif
+
+#if HAVE_LIBCAPNG
+#define LIBCAPNG 1
+#else
+#define LIBCAPNG 0
+#endif
+
 /* --detach: Should we run in the background? */
 bool detach;                    /* Was --detach specified? */
 static bool detached;           /* Have we already detached? */
@@ -64,6 +81,15 @@  static int daemonize_fd = -1;
  * it dies due to an error signal? */
 static bool monitor;
 
+/* --user: Only root can use this option. Switch to new uid:gid after
+ * initially running as root.  */
+static bool switch_user = false;
+static bool non_root_user = false;
+static uid_t uid;
+static gid_t gid;
+static char *user = NULL;
+static void daemon_become_new_user__(bool access_datapath);
+
 static void check_already_running(void);
 static int lock_pidfile(FILE *, int command);
 static pid_t fork_and_clean_up(void);
@@ -409,11 +435,21 @@  monitor_daemon(pid_t daemon_pid)
  * daemon_complete()) or that it failed to start up (by exiting with a nonzero
  * exit code). */
 void
-daemonize_start(void)
+daemonize_start(bool access_datapath)
 {
     assert_single_threaded();
     daemonize_fd = -1;
 
+    if (switch_user) {
+        daemon_become_new_user__(access_datapath);
+        switch_user = false;
+    }
+
+    /* If --user is specified, make sure user switch has completed by now.  */
+    if (non_root_user) {
+        ovs_assert(geteuid() && getuid());
+    }
+
     if (detach) {
         pid_t pid;
 
@@ -645,7 +681,7 @@  error:
 }
 
 /* Opens and reads a PID from 'pidfile'.  Returns the positive PID if
- * successful, otherwise a negative errno value. */
+ *successful, otherwise a negative errno value. */
 pid_t
 read_pidfile(const char *pidfile)
 {
@@ -684,3 +720,324 @@  should_service_stop(void)
 {
     return false;
 }
+
+
+static bool
+gid_matches(gid_t expected, gid_t value)
+{
+    return expected == -1 || expected == value;
+}
+
+static bool
+gid_verify(gid_t real, gid_t effective, gid_t saved)
+{
+    gid_t r, e, s;
+
+    return (getresgid(&r, &e, &s) == 0 &&
+            gid_matches(real, r) &&
+            gid_matches(effective, e) &&
+            gid_matches(saved, s));
+}
+
+static void
+daemon_switch_group(gid_t real, gid_t effective,
+                    gid_t saved)
+{
+    if ((setresgid(real, effective, saved) == -1) ||
+        !gid_verify(real, effective, saved)) {
+        VLOG_FATAL("%s: fail to switch group to gid as %d, aborting",
+                   pidfile, gid);
+    }
+}
+
+static bool
+uid_matches(uid_t expected, uid_t value)
+{
+    return expected == -1 || expected == value;
+}
+
+static bool
+uid_verify(const uid_t real, const uid_t effective, const uid_t saved)
+{
+    uid_t r, e, s;
+
+    return (getresuid(&r, &e, &s) == 0 &&
+            uid_matches(real, r) &&
+            uid_matches(effective, e) &&
+            uid_matches(saved, s));
+}
+
+static void
+daemon_switch_user(const uid_t real, const uid_t effective, const uid_t saved,
+                   const char *user)
+{
+    if ((setresuid(real, effective, saved) == -1) ||
+        !uid_verify(real, effective, saved)) {
+        VLOG_FATAL("%s: fail to switch user to %s, aborting",
+                   pidfile, user);
+    }
+}
+
+/* Use portable Unix APIs to switch uid:gid, when datapath
+ * access is not required.  On Linux systems, all capabilities
+ * will be dropped.  */
+static void
+daemon_become_new_user_unix(void)
+{
+    /* "Setuid Demystified" by Hao Chen, etc outlines some caveats of
+     * around unix system call setuid() and friends. This implementation
+     * mostly follow the advice given by the paper.  The paper is
+     * published in 2002, so things could have changed.  */
+
+    /* Change both real and effective uid and gid will permanently
+     * drop the process' privilege.  "Setuid Demystified" suggested
+     * that calling getuid() after each setuid() call to verify they
+     * are actually set, because checking return code alone is not
+     * sufficient.  */
+    daemon_switch_group(gid, gid, gid);
+    if (user && initgroups(user, gid) == -1) {
+        VLOG_FATAL("%s: fail to add supplementary group gid %d, "
+                   "aborting", pidfile, gid);
+    }
+    daemon_switch_user(uid, uid, uid, user);
+}
+
+/* Linux specific implementation of daemon_become_new_user()
+ * using libcap-ng.   */
+#if defined __linux__ &&  HAVE_LIBCAPNG
+static void
+daemon_become_new_user_linux(bool access_datapath)
+{
+    int ret;
+
+    ret = capng_get_caps_process();
+
+    if (!ret) {
+        if (capng_have_capabilities(CAPNG_SELECT_CAPS) > CAPNG_NONE) {
+            unsigned int cap = CAP_IPC_LOCK | CAP_NET_BIND_SERVICE;
+
+            capng_clear(CAPNG_SELECT_BOTH);
+            if (access_datapath) {
+                cap |= CAP_NET_ADMIN | CAP_NET_RAW;
+            }
+            ret = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
+                               cap);
+        } else {
+            ret = -1;
+        }
+    }
+
+    if (!ret) {
+        /* CAPNG_INIT_SUPP_GRP will be a better choice than
+         * CAPNG_DROP_SUPP_GRP. However this enum value is only defined
+         * with libcap-ng higher than version 0.7.4, which is not wildly
+         * available on many Linux distributions yet. Taking a more
+         * conservative approach to make sure OVS behaves consistently.
+         *
+         * XXX We may change this for future OVS releases.
+         */
+        ret = capng_change_id(uid, gid, CAPNG_DROP_SUPP_GRP
+                              | CAPNG_CLEAR_BOUNDING);
+    }
+
+    if (ret) {
+        VLOG_FATAL("%s: libcap-ng fail to switch to user and group "
+                   "%d:%d, aborting", pidfile, uid, gid);
+    }
+}
+#endif
+
+static void
+daemon_become_new_user__(bool access_datapath)
+{
+    if (LINUX) {
+        if (LIBCAPNG) {
+            daemon_become_new_user_linux(access_datapath);
+        } else {
+            VLOG_FATAL("%s: fail to downgrade user using libcap-ng. "
+                       "(libcap-ng is not configured at compile time), "
+                       "aborting.", pidfile);
+        }
+    } else {
+        ovs_assert(!access_datapath);
+        daemon_become_new_user_unix();
+    }
+}
+
+/* Noramlly, user switch is embedded within daemonize_start().
+ * However, there in case the user switch needs to be done
+ * before daemonize_start(), the following API can be used.  */
+void
+daemon_become_new_user(bool access_datapath)
+{
+    assert_single_threaded();
+    if (switch_user) {
+        daemon_become_new_user__(access_datapath);
+
+        /* Make sure daemonize_start() will not switch
+         * user again. */
+        switch_user = false;
+    }
+}
+
+/* Return the maximun suggested buffer size for both getpwname_r()
+ * and getgrnam_r().
+ *
+ * This size may still not be big enough. In case getpwname_r()
+ * and friends return ERANGE, a larger buffer should be supplied to
+ * retry. (The man page did not specify the max size to stop at, we
+ * will keep trying with doubling the buffer size for each round until
+ * the size wraps around size_t.  */
+static size_t
+get_sysconf_buffer_size(void)
+{
+    size_t bufsize, pwd_bs = 0, grp_bs = 0;
+    const size_t default_bufsize = 1024;
+
+    errno = 0;
+    if ((pwd_bs = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
+        if (errno) {
+            VLOG_FATAL("%s: Read initial passwordd struct size "
+                       "failed (%s), aborting. ", pidfile,
+                       ovs_strerror(errno));
+        }
+    }
+
+    if ((grp_bs = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
+        if (errno) {
+            VLOG_FATAL("%s: Read initial group struct size "
+                       "failed (%s), aborting. ", pidfile,
+                       ovs_strerror(errno));
+        }
+    }
+
+    bufsize = MAX(pwd_bs, grp_bs);
+    return bufsize ? bufsize : default_bufsize;
+}
+
+/* Try to double the size of '*buf', return true
+ * if successful, and '*sizep' will be updated with
+ * the new size. Otherwise, return false.  */
+static bool
+enlarge_buffer(char **buf, size_t *sizep)
+{
+    size_t newsize = *sizep * 2;
+
+    if (newsize > *sizep) {
+        *buf = xrealloc(*buf, newsize);
+        *sizep = newsize;
+        return true;
+    }
+
+    return false;
+}
+
+/* Parse and sanity check user_spec.
+ *
+ * If successful, set global variables 'uid' and 'gid'
+ * with the parsed results. Global variable 'user'
+ * will be pointing to a string that stores the name
+ * of the user to be switched into.
+ *
+ * Also set 'switch_to_new_user' to true, The actual
+ * user switching is done as soon as daemonize_start()
+ * is called. I/O access before calling daemonize_start()
+ * will still be with root's credential.  */
+void
+daemon_set_new_user(const char *user_spec)
+{
+    char *pos = strchr(user_spec, ':');
+    size_t init_bufsize, bufsize;
+
+    init_bufsize = get_sysconf_buffer_size();
+    uid = getuid();
+    gid = getgid();
+
+    if (geteuid() || uid) {
+        VLOG_FATAL("%s: only root can use --user option", pidfile);
+    }
+
+    user_spec += strspn(user_spec, " \t\r\n");
+    size_t len = pos ? pos - user_spec : strlen(user_spec);
+    char *buf;
+    struct passwd pwd, *res;
+    int e;
+
+    bufsize = init_bufsize;
+    buf = xmalloc(bufsize);
+    if (len) {
+        user = xmemdup0(user_spec, len);
+
+        while ((e = getpwnam_r(user, &pwd, buf, bufsize, &res)) == ERANGE) {
+            if (!enlarge_buffer(&buf, &bufsize)) {
+                break;
+            }
+        }
+
+        if (e != 0) {
+            VLOG_FATAL("%s: Failed to retrive user %s's uid (%s), aborting.",
+                       pidfile, user, ovs_strerror(e));
+        }
+    } else {
+        /* User name is not specified, use current user.  */
+        while ((e = getpwuid_r(uid, &pwd, buf, bufsize, &res)) == ERANGE) {
+            if (!enlarge_buffer(&buf, &bufsize)) {
+                break;
+            }
+        }
+
+        if (e != 0) {
+            VLOG_FATAL("%s: Failed to retrive current user's name "
+                       "(%s), aborting.", pidfile, ovs_strerror(e));
+        }
+        user = xstrdup(pwd.pw_name);
+    }
+
+    uid = pwd.pw_uid;
+    gid = pwd.pw_gid;
+    free(buf);
+
+    if (pos) {
+        char *grpstr = pos + 1;
+        grpstr += strspn(grpstr, " \t\r\n");
+
+        if (*grpstr) {
+            struct group grp, *res;
+
+            bufsize = init_bufsize;
+            buf = xmalloc(bufsize);
+            while ((e = getgrnam_r(grpstr, &grp, buf, bufsize, &res))
+                         == ERANGE) {
+                if (!enlarge_buffer(&buf, &bufsize)) {
+                    break;
+                }
+            }
+
+            if (e) {
+                VLOG_FATAL("%s: Failed to get group entry for %s, "
+                           "(%s), aborting.", pidfile, grpstr,
+                           ovs_strerror(e));
+            }
+
+            if (gid != grp.gr_gid) {
+                char **mem;
+
+                for (mem = grp.gr_mem; *mem; ++mem) {
+                    if (!strcmp(*mem, user)) {
+                        break;
+                    }
+                }
+
+                if (!*mem) {
+                    VLOG_FATAL("%s: Invalid --user option %s (user %s is "
+                               "not in group %s), aborting.", pidfile,
+                               user_spec, user, grpstr);
+                }
+                gid = grp.gr_gid;
+            }
+            free(buf);
+        }
+    }
+
+    switch_user = non_root_user = true;
+}
diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c
index 04e1f1a..fa6b230 100644
--- a/lib/daemon-windows.c
+++ b/lib/daemon-windows.c
@@ -446,7 +446,8 @@  make_pidfile(void)
     /* Don't close the pidfile till the process exits. */
 }
 
-void daemonize_start(void)
+void
+daemonize_start(bool access_datapath OVS_UNUSED)
 {
     if (pidfile) {
         make_pidfile();
@@ -473,6 +474,11 @@  daemonize_complete(void)
     service_complete();
 }
 
+void
+dameon_become_new_user(bool access_datapath OVS_UNUSED)
+{
+}
+
 /* Returns the file name that would be used for a pidfile if 'name' were
  * provided to set_pidfile().  The caller must free the returned string. */
 char *
@@ -484,3 +490,9 @@  make_pidfile_name(const char *name)
         return xasprintf("%s/%s.pid", ovs_rundir(), program_name);
     }
 }
+
+void
+daemon_set_new_user(const char *user_spec OVS_UNUSED)
+{
+    VLOG_FATAL("--user options is not currently supported.");
+}
diff --git a/lib/daemon.c b/lib/daemon.c
index 4ac32b8..b8313d4 100644
--- a/lib/daemon.c
+++ b/lib/daemon.c
@@ -41,7 +41,7 @@  get_detach(void)
 void
 daemonize(void)
 {
-    daemonize_start();
+    daemonize_start(false);
     daemonize_complete();
 }
 
diff --git a/lib/daemon.h b/lib/daemon.h
index 959341d..cb663ca 100644
--- a/lib/daemon.h
+++ b/lib/daemon.h
@@ -42,14 +42,16 @@ 
     OPT_NO_CHDIR,                               \
     OPT_OVERWRITE_PIDFILE,                      \
     OPT_PIDFILE,                                \
-    OPT_MONITOR
+    OPT_MONITOR,                                \
+    OPT_USER_GROUP
 
-#define DAEMON_LONG_OPTIONS                                             \
-        {"detach",            no_argument, NULL, OPT_DETACH},           \
-        {"no-chdir",          no_argument, NULL, OPT_NO_CHDIR},         \
-        {"pidfile",           optional_argument, NULL, OPT_PIDFILE},    \
+#define DAEMON_LONG_OPTIONS                                              \
+        {"detach",            no_argument, NULL, OPT_DETACH},            \
+        {"no-chdir",          no_argument, NULL, OPT_NO_CHDIR},          \
+        {"pidfile",           optional_argument, NULL, OPT_PIDFILE},     \
         {"overwrite-pidfile", no_argument, NULL, OPT_OVERWRITE_PIDFILE}, \
-        {"monitor",           no_argument, NULL, OPT_MONITOR}
+        {"monitor",           no_argument, NULL, OPT_MONITOR},           \
+        {"user",              required_argument, NULL, OPT_USER_GROUP}
 
 #define DAEMON_OPTION_HANDLERS                  \
         case OPT_DETACH:                        \
@@ -70,6 +72,10 @@ 
                                                 \
         case OPT_MONITOR:                       \
             daemon_set_monitor();               \
+            break;                              \
+                                                \
+        case OPT_USER_GROUP:                    \
+            daemon_set_new_user(optarg);        \
             break;
 
 void set_detach(void);
@@ -84,7 +90,8 @@  pid_t read_pidfile(const char *name);
     OPT_PIDFILE,                               \
     OPT_PIPE_HANDLE,                           \
     OPT_SERVICE,                               \
-    OPT_SERVICE_MONITOR
+    OPT_SERVICE_MONITOR                        \
+    OPT_USER_GROUP                             \
 
 #define DAEMON_LONG_OPTIONS                                               \
         {"detach",             no_argument, NULL, OPT_DETACH},            \
@@ -92,7 +99,8 @@  pid_t read_pidfile(const char *name);
         {"pidfile",            optional_argument, NULL, OPT_PIDFILE},     \
         {"pipe-handle",        required_argument, NULL, OPT_PIPE_HANDLE}, \
         {"service",            no_argument, NULL, OPT_SERVICE},           \
-        {"service-monitor",    no_argument, NULL, OPT_SERVICE_MONITOR}
+        {"service-monitor",    no_argument, NULL, OPT_SERVICE_MONITOR}    \
+        {"user",               required_argument, NULL, OPT_USER_GROUP}
 
 #define DAEMON_OPTION_HANDLERS                  \
         case OPT_DETACH:                        \
@@ -113,7 +121,10 @@  pid_t read_pidfile(const char *name);
             break;                              \
                                                 \
         case OPT_SERVICE_MONITOR:               \
-            break;
+            break;                              \
+                                                \
+        case OPT_USER_GROUP:                    \
+            daemon_set_new_user(optarg);        \
 
 void control_handler(DWORD request);
 void set_pipe_handle(const char *pipe_handle);
@@ -122,8 +133,10 @@  void set_pipe_handle(const char *pipe_handle);
 bool get_detach(void);
 void daemon_save_fd(int fd);
 void daemonize(void);
-void daemonize_start(void);
+void daemonize_start(bool access_datapath);
 void daemonize_complete(void);
+void daemon_set_new_user(const char * user_spec);
+void daemon_become_new_user(bool access_datapath);
 void daemon_usage(void);
 void service_start(int *argcp, char **argvp[]);
 void service_stop(void);
diff --git a/lib/daemon.man b/lib/daemon.man
index 4ab9823..fedc8a0 100644
--- a/lib/daemon.man
+++ b/lib/daemon.man
@@ -50,3 +50,18 @@  core dumps into the current working directory and the root directory
 is not a good directory to use.
 .IP
 This option has no effect when \fB\-\-detach\fR is not specified.
+.
+.TP
+\fB\-\-user\fR
+Causes \fB\*(PN\fR to run as a non root user specified in "user:group", thus
+dropping all root privileges. Short forms "user" and ":group" are also
+allowed, with current user or group are assumed respectively. Only daemons
+started by the root user accepts this argument.
+.IP
+On Linux, daemons will be granted CAP_IPC_LOCK and CAP_NET_BIND_SERVICES
+before dropping root privileges. Daemons interact with datapath,
+such as ovs-vswitchd, will be granted two additional capabilities, namely
+CAP_NET_ADMIN and CAP_NET_RAW.
+.IP
+On Windows, this option is not currently supported. For security reasons,
+Specifying this option will cause the daemon process not to start.
diff --git a/ovn/controller-vtep/ovn-controller-vtep.c b/ovn/controller-vtep/ovn-controller-vtep.c
index b54b29d..da6eeee 100644
--- a/ovn/controller-vtep/ovn-controller-vtep.c
+++ b/ovn/controller-vtep/ovn-controller-vtep.c
@@ -63,7 +63,7 @@  main(int argc, char *argv[])
     parse_options(argc, argv);
     fatal_ignore_sigpipe();
 
-    daemonize_start();
+    daemonize_start(false);
 
     retval = unixctl_server_create(NULL, &unixctl);
     if (retval) {
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 8c858bb..f1bc524 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -386,7 +386,7 @@  main(int argc, char *argv[])
     parse_options(argc, argv);
     fatal_ignore_sigpipe();
 
-    daemonize_start();
+    daemonize_start(false);
 
     retval = unixctl_server_create(NULL, &unixctl);
     if (retval) {
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 0f57e58..e698907 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1167,7 +1167,7 @@  main(int argc, char *argv[])
     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
     parse_options(argc, argv);
 
-    daemonize_start();
+    daemonize_start(false);
 
     retval = unixctl_server_create(NULL, &unixctl);
     if (retval) {
diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
index 575cf91..b59388f 100644
--- a/ovsdb/ovsdb-client.c
+++ b/ovsdb/ovsdb-client.c
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -91,6 +91,7 @@  main(int argc, char *argv[])
     parse_options(argc, argv);
     fatal_ignore_sigpipe();
 
+    daemon_become_new_user(false);
     if (optind >= argc) {
         ovs_fatal(0, "missing command name; use --help for help");
     }
@@ -791,7 +792,7 @@  do_monitor(struct jsonrpc *rpc, const char *database,
     size_t n_mts, allocated_mts;
 
     daemon_save_fd(STDOUT_FILENO);
-    daemonize_start();
+    daemonize_start(false);
     if (get_detach()) {
         int error;
 
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index 4088d85..0c34820 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -221,6 +221,7 @@  main(int argc, char *argv[])
     process_init();
 
     parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
+    daemon_become_new_user(false);
 
     /* Create and initialize 'config_tmpfile' as a temporary file to hold
      * ovsdb-server's most basic configuration, and then save our initial
@@ -248,7 +249,7 @@  main(int argc, char *argv[])
 
     save_config__(config_tmpfile, &remotes, &db_filenames);
 
-    daemonize_start();
+    daemonize_start(false);
 
     /* Load the saved config. */
     load_config(config_tmpfile, &remotes, &db_filenames);
diff --git a/tests/test-jsonrpc.c b/tests/test-jsonrpc.c
index f66dc65..feac0b0 100644
--- a/tests/test-jsonrpc.c
+++ b/tests/test-jsonrpc.c
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/tests/test-netflow.c b/tests/test-netflow.c
index 631d7a2..2abc57f 100644
--- a/tests/test-netflow.c
+++ b/tests/test-netflow.c
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -195,7 +195,7 @@  test_netflow_main(int argc, char *argv[])
     }
 
     daemon_save_fd(STDOUT_FILENO);
-    daemonize_start();
+    daemonize_start(false);
 
     error = unixctl_server_create(NULL, &server);
     if (error) {
diff --git a/tests/test-sflow.c b/tests/test-sflow.c
index 6f90ebf..08591bf 100644
--- a/tests/test-sflow.c
+++ b/tests/test-sflow.c
@@ -683,7 +683,7 @@  test_sflow_main(int argc, char *argv[])
     }
 
     daemon_save_fd(STDOUT_FILENO);
-    daemonize_start();
+    daemonize_start(false);
 
     error = unixctl_server_create(NULL, &server);
     if (error) {
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 75e84e2..0c315c1 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -130,6 +130,8 @@  main(int argc, char *argv[])
     fatal_ignore_sigpipe();
     ctx.argc = argc - optind;
     ctx.argv = argv + optind;
+
+    daemon_become_new_user(false);
     ovs_cmdl_run_command(&ctx, get_all_commands());
     return 0;
 }
@@ -1611,7 +1613,7 @@  monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests)
     int error;
 
     daemon_save_fd(STDERR_FILENO);
-    daemonize_start();
+    daemonize_start(false);
     error = unixctl_server_create(unixctl_path, &server);
     if (error) {
         ovs_fatal(error, "failed to create unixctl server");
diff --git a/utilities/ovs-testcontroller.c b/utilities/ovs-testcontroller.c
index 3d59adb..60cc32f 100644
--- a/utilities/ovs-testcontroller.c
+++ b/utilities/ovs-testcontroller.c
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -108,6 +108,8 @@  main(int argc, char *argv[])
     parse_options(argc, argv);
     fatal_ignore_sigpipe();
 
+    daemon_become_new_user(false);
+
     if (argc - optind < 1) {
         ovs_fatal(0, "at least one vconn argument required; "
                   "use --help for usage");
@@ -145,7 +147,7 @@  main(int argc, char *argv[])
         ovs_fatal(0, "no active or passive switch connections");
     }
 
-    daemonize_start();
+    daemonize_start(false);
 
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c
index 4fe479e..0ccf632 100644
--- a/vswitchd/ovs-vswitchd.c
+++ b/vswitchd/ovs-vswitchd.c
@@ -69,6 +69,7 @@  main(int argc, char *argv[])
     char *remote;
     bool exiting;
     int retval;
+    bool access_datapath;
 
     set_program_name(argv[0]);
     retval = dpdk_init(argc,argv);
@@ -85,7 +86,12 @@  main(int argc, char *argv[])
     fatal_ignore_sigpipe();
     ovsrec_init();
 
-    daemonize_start();
+#ifdef __linux__
+    access_datapath = true;
+#else
+    access_datapath = false;
+#endif
+    daemonize_start(access_datapath);
 
     if (want_mlockall) {
 #ifdef HAVE_MLOCKALL