]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - support/nsm/file.c
Merge branch 'sid'
[nfs-utils.git] / support / nsm / file.c
index 4714bbf50f2d7bedf9a662887c20c29705a145da..4711c2cd79134f7f1c036c5cda8298aeb53af9a6 100644 (file)
 
 #define NSM_KERNEL_STATE_FILE  "/proc/sys/fs/nfs/nsm_local_state"
 
-/*
- * Some distributions place statd's files in a subdirectory
- */
-#define NSM_PATH_EXTENSION
-/* #define NSM_PATH_EXTENSION  "/statd" */
-
-#define NSM_DEFAULT_STATEDIR           NFS_STATEDIR NSM_PATH_EXTENSION
-
 static char nsm_base_dirname[PATH_MAX] = NSM_DEFAULT_STATEDIR;
 
 #define NSM_MONITOR_DIR        "sm"
@@ -346,10 +338,10 @@ nsm_is_default_parentdir(void)
  *
  * Returns true if successful, or false if some error occurred.
  */
+#ifdef HAVE_SYS_CAPABILITY_H
 static _Bool
 nsm_clear_capabilities(void)
 {
-#ifdef HAVE_SYS_CAPABILITY_H
        cap_t caps;
 
        caps = cap_from_text("cap_net_bind_service=ep");
@@ -365,10 +357,60 @@ nsm_clear_capabilities(void)
        }
 
        (void)cap_free(caps);
-#endif
        return true;
 }
 
+#define CAP_BOUND_PROCFILE "/proc/sys/kernel/cap-bound"
+static _Bool
+prune_bounding_set(void)
+{
+#ifdef PR_CAPBSET_DROP
+       int ret;
+       unsigned long i;
+       struct stat st;
+
+       /*
+        * Prior to kernel 2.6.25, the capabilities bounding set was a global
+        * value. Check to see if /proc/sys/kernel/cap-bound exists and don't
+        * bother to clear the bounding set if it does.
+        */
+       ret = stat(CAP_BOUND_PROCFILE, &st);
+       if (!ret) {
+               xlog(L_WARNING, "%s exists. Not attempting to clear "
+                               "capabilities bounding set.",
+                               CAP_BOUND_PROCFILE);
+               return true;
+       } else if (errno != ENOENT) {
+               /* Warn, but attempt to clear the bounding set anyway. */
+               xlog(L_WARNING, "Unable to stat %s: %m", CAP_BOUND_PROCFILE);
+       }
+
+       /* prune the bounding set to nothing */
+       for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >=0 ; ++i) {
+               ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+               if (ret) {
+                       xlog(L_ERROR, "Unable to prune capability %lu from "
+                                     "bounding set: %m", i);
+                       return false;
+               }
+       }
+#endif /* PR_CAPBSET_DROP */
+       return true;
+}
+#else /* !HAVE_SYS_CAPABILITY_H */
+static _Bool
+nsm_clear_capabilities(void)
+{
+       return true;
+}
+
+static _Bool
+prune_bounding_set(void)
+{
+       return true;
+}
+#endif /* HAVE_SYS_CAPABILITY_H */
+
 /**
  * nsm_drop_privileges - drop root privileges
  * @pidfd: file descriptor of a pid file
@@ -395,18 +437,21 @@ nsm_drop_privileges(const int pidfd)
                return false;
        }
 
-       if (st.st_uid == 0) {
-               xlog_warn("Running as root.  "
-                       "chown %s to choose different user", nsm_base_dirname);
-               return true;
-       }
-
        if (chdir(nsm_base_dirname) == -1) {
                xlog(L_ERROR, "Failed to change working directory to %s: %m",
                                nsm_base_dirname);
                return false;
        }
 
+       if (!prune_bounding_set())
+               return false;
+
+       if (st.st_uid == 0) {
+               xlog_warn("Running as root.  "
+                       "chown %s to choose different user", nsm_base_dirname);
+               return true;
+       }
+
        /*
         * If the pidfile happens to reside on NFS, dropping privileges
         * will probably cause us to lose access, even though we are
@@ -421,7 +466,7 @@ nsm_drop_privileges(const int pidfd)
         */
         if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
                 xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m");
-               return 0;
+               return false;
        }
 
        if (setgroups(0, NULL) == -1) {
@@ -568,9 +613,8 @@ nsm_retire_monitored_hosts(void)
 
        while ((de = readdir(dir)) != NULL) {
                char *src, *dst;
+               struct stat stb;
 
-               if (de->d_type != (unsigned char)DT_REG)
-                       continue;
                if (de->d_name[0] == '.')
                        continue;
 
@@ -580,6 +624,20 @@ nsm_retire_monitored_hosts(void)
                        continue;
                }
 
+               /* NB: not all file systems fill in d_type correctly */
+               if (lstat(src, &stb) == -1) {
+                       xlog_warn("Bad monitor file %s, skipping: %m",
+                                       de->d_name);
+                       free(src);
+                       continue;
+               }
+               if (!S_ISREG(stb.st_mode)) {
+                       xlog(D_GENERAL, "Skipping non-regular file %s",
+                                       de->d_name);
+                       free(src);
+                       continue;
+               }
+
                dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name);
                if (dst == NULL) {
                        free(src);
@@ -846,7 +904,7 @@ nsm_read_line(const char *hostname, const time_t timestamp, char *line,
 }
 
 /*
- * Given a filename, reads data from a file under NSM_MONITOR_DIR
+ * Given a filename, reads data from a file under "directory"
  * and invokes @func so caller can populate their in-core
  * database with this data.
  */
@@ -863,10 +921,15 @@ nsm_load_host(const char *directory, const char *filename, nsm_populate_t func)
        if (path == NULL)
                goto out_err;
 
-       if (stat(path, &stb) == -1) {
+       if (lstat(path, &stb) == -1) {
                xlog(L_ERROR, "Failed to stat %s: %m", path);
                goto out_freepath;
        }
+       if (!S_ISREG(stb.st_mode)) {
+               xlog(D_GENERAL, "Skipping non-regular file %s",
+                               path);
+               goto out_freepath;
+       }
 
        f = fopen(path, "r");
        if (f == NULL) {
@@ -913,8 +976,6 @@ nsm_load_dir(const char *directory, nsm_populate_t func)
        }
 
        while ((de = readdir(dir)) != NULL) {
-               if (de->d_type != (unsigned char)DT_REG)
-                       continue;
                if (de->d_name[0] == '.')
                        continue;