]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
nfsdcld: add support for dropping capabilities
authorJeff Layton <jlayton@redhat.com>
Wed, 9 May 2012 17:25:34 +0000 (13:25 -0400)
committerSteve Dickson <steved@redhat.com>
Wed, 9 May 2012 17:25:34 +0000 (13:25 -0400)
As a long running daemon, we need to be security-conscious with nfsdcld,
so let's prune what it can do down to nearly nothing.

We want the daemon to run as root so that it has access to open and
reopen the rpc_pipefs pipe, but we don't actually need any of the
superuser caps that come with it. Have it drop all capabilities early
on. We don't need any of them as long as the fsuid continues to be 0.

Once we do that though, check to ensure that the db dir is actually
usable by root w/o CAP_DAC_OVERRIDE. Do an access() check on it and
throw a warning if it's not. Hopefully that will assist users in
debugging if they get the ownership of the DB dir wrong.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/nfsdcld/Makefile.am
utils/nfsdcld/nfsdcld.c
utils/nfsdcld/nfsdcld.man
utils/nfsdcld/sqlite.c

index f320dffe07381f4e66054526e7ec2c7f292cd579..073a71bc0dd6976929e565c2647a02ae773c0839 100644 (file)
@@ -8,7 +8,7 @@ sbin_PROGRAMS   = nfsdcld
 
 nfsdcld_SOURCES = nfsdcld.c sqlite.c
 
-nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE)
+nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
 
 MAINTAINERCLEANFILES = Makefile.in
 
index c9f0fb98e02183ddc39d7d119715e1197266912b..e7af4e330c85bcd5e5f2166b3805c67479f1705f 100644 (file)
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/inotify.h>
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
 
 #include "xlog.h"
 #include "nfslib.h"
 
 #define DEFAULT_CLD_PATH       PIPEFS_DIR "/nfsd/cld"
 
+#ifndef CLD_DEFAULT_STORAGEDIR
+#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld"
+#endif
+
 #define UPCALL_VERSION         1
 
 /* private data structures */
@@ -79,6 +87,47 @@ usage(char *progname)
        printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname);
 }
 
+static int
+cld_set_caps(void)
+{
+       int ret = 0;
+#ifdef HAVE_SYS_CAPABILITY_H
+       unsigned long i;
+       cap_t caps;
+
+       if (getuid() != 0) {
+               xlog(L_ERROR, "Not running as root. Daemon won't be able to "
+                             "open the pipe after dropping capabilities!");
+               return -EINVAL;
+       }
+
+       /* prune the bounding set to nothing */
+       for (i = 0; i <= CAP_LAST_CAP; ++i) {
+               ret = prctl(PR_CAPBSET_DROP, i);
+               if (ret) {
+                       xlog(L_ERROR, "Unable to prune capability %lu from "
+                                     "bounding set: %m", i);
+                       return -errno;
+               }
+       }
+
+       /* get a blank capset */
+       caps = cap_init();
+       if (caps == NULL) {
+               xlog(L_ERROR, "Unable to get blank capability set: %m");
+               return -errno;
+       }
+
+       /* reset the process capabilities */
+       if (cap_set_proc(caps) != 0) {
+               xlog(L_ERROR, "Unable to set process capabilities: %m");
+               ret = -errno;
+       }
+       cap_free(caps);
+#endif
+       return ret;
+}
+
 #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
 
 static int
@@ -453,7 +502,7 @@ main(int argc, char **argv)
        int rc = 0;
        bool foreground = false;
        char *progname;
-       char *storagedir = NULL;
+       char *storagedir = CLD_DEFAULT_STORAGEDIR;
        struct cld_client clnt;
 
        memset(&clnt, 0, sizeof(clnt));
@@ -502,6 +551,37 @@ main(int argc, char **argv)
                }
        }
 
+       /* drop all capabilities */
+       rc = cld_set_caps();
+       if (rc)
+               goto out;
+
+       /*
+        * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
+        * If it isn't then give the user a warning but proceed as if
+        * everything is OK. If the DB has already been created, then
+        * everything might still work. If it doesn't exist at all, then
+        * assume that the maindb init will be able to create it. Fail on
+        * anything else.
+        */
+       if (access(storagedir, W_OK) == -1) {
+               switch (errno) {
+               case EACCES:
+                       xlog(L_WARNING, "Storage directory %s is not writable. "
+                                       "Should be owned by root and writable "
+                                       "by owner!", storagedir);
+                       break;
+               case ENOENT:
+                       /* ignore and assume that we can create dir as root */
+                       break;
+               default:
+                       xlog(L_ERROR, "Unexpected error when checking access "
+                                     "on %s: %m", storagedir);
+                       rc = -errno;
+                       goto out;
+               }
+       }
+
        /* set up storage db */
        rc = sqlite_maindb_init(storagedir);
        if (rc) {
index bad5f34d8e4f8763ce49aaa462e4c69cf27070c6..9ddaf64c951b793d9a49a80a7c6187c5d1e7b0cf 100644 (file)
@@ -160,7 +160,7 @@ Runs the daemon in the foreground and prints all output to stderr
 Location of the \*(L"cld\*(R" upcall pipe. The default value is
 \&\fI/var/lib/nfs/rpc_pipefs/nfsd/cld\fR. If the pipe does not exist when the
 daemon starts then it will wait for it to be created.
-.IP "\fB\-s\fR \fIstoragedir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
+.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
 .IX Item "-s storagedir, --storagedir=storage_dir"
 Directory where stable storage information should be kept. The default
 value is \fI/var/lib/nfs/nfsdcld\fR.
@@ -175,6 +175,11 @@ This daemon requires a kernel that supports the nfsdcld upcall. If the
 kernel does not support the new upcall, or is using the legacy client
 name tracking code then it will not create the pipe that nfsdcld uses to
 talk to the kernel.
+.PP
+This daemon should be run as root, as the pipe that it uses to communicate
+with the kernel is only accessable by root. The daemon however does drop all
+superuser capabilities after starting. Because of this, the \fIstoragedir\fR
+should be owned by root, and be readable and writable by owner.
 .SH "AUTHORS"
 .IX Header "AUTHORS"
 The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>.
index 9e357741fee7deef3b83ed4bbed50389322622fe..bb2519de804d727cc5ae79299b5aa73dac89a7c0 100644 (file)
 
 #define CLD_SQLITE_SCHEMA_VERSION 1
 
-#ifndef CLD_SQLITE_TOPDIR
-#define CLD_SQLITE_TOPDIR NFS_STATEDIR "/nfsdcld"
-#endif
-
 /* in milliseconds */
 #define CLD_SQLITE_BUSY_TIMEOUT 10000
 
@@ -112,7 +108,7 @@ sqlite_maindb_init(char *topdir)
        char *err = NULL;
        sqlite3_stmt *stmt = NULL;
 
-       sqlite_topdir = topdir ? topdir : CLD_SQLITE_TOPDIR;
+       sqlite_topdir = topdir;
 
        ret = mkdir_if_not_exist(sqlite_topdir);
        if (ret)