X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=support%2Fnsm%2Ffile.c;h=5dd52c1e2640fb2effd07004c8902700811aa775;hp=8796705918545b95007628930999ded4eaae8379;hb=5c3e26650a54af7826a2b4c6759b56681a350c37;hpb=7f98c14d38badedd30d2d4a6b1d15e913967bf87 diff --git a/support/nsm/file.c b/support/nsm/file.c index 8796705..5dd52c1 100644 --- a/support/nsm/file.c +++ b/support/nsm/file.c @@ -67,6 +67,10 @@ #endif #include +#ifdef HAVE_SYS_CAPABILITY_H +#include +#endif +#include #include #include @@ -90,14 +94,6 @@ #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" @@ -122,7 +118,7 @@ exact_error_check(const ssize_t len, const size_t buflen) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_record_pathname(const char *directory, const char *hostname) { @@ -170,7 +166,7 @@ nsm_make_record_pathname(const char *directory, const char *hostname) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_pathname(const char *directory) { @@ -200,7 +196,7 @@ nsm_make_pathname(const char *directory) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_temp_pathname(const char *pathname) { @@ -335,6 +331,36 @@ nsm_is_default_parentdir(void) return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0; } +/* + * Clear all capabilities but CAP_NET_BIND_SERVICE. This permits + * callers to acquire privileged source ports, but all other root + * capabilities are disallowed. + * + * Returns true if successful, or false if some error occurred. + */ +static _Bool +nsm_clear_capabilities(void) +{ +#ifdef HAVE_SYS_CAPABILITY_H + cap_t caps; + + caps = cap_from_text("cap_net_bind_service=ep"); + if (caps == NULL) { + xlog(L_ERROR, "Failed to allocate capability: %m"); + return false; + } + + if (cap_set_proc(caps) == -1) { + xlog(L_ERROR, "Failed to set capability flags: %m"); + (void)cap_free(caps); + return false; + } + + (void)cap_free(caps); +#endif + return true; +} + /** * nsm_drop_privileges - drop root privileges * @pidfd: file descriptor of a pid file @@ -361,18 +387,18 @@ 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 (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 @@ -382,6 +408,14 @@ nsm_drop_privileges(const int pidfd) if (fchown(pidfd, st.st_uid, st.st_gid) == -1) xlog_warn("Failed to change owner of pidfile: %m"); + /* + * Don't clear capabilities when dropping root. + */ + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { + xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m"); + return false; + } + if (setgroups(0, NULL) == -1) { xlog(L_ERROR, "Failed to drop supplementary groups: %m"); return false; @@ -399,7 +433,8 @@ nsm_drop_privileges(const int pidfd) } xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid); - return true; + + return nsm_clear_capabilities(); } /** @@ -525,9 +560,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; @@ -537,6 +571,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); @@ -591,7 +639,7 @@ nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen) /* * Returns the length in bytes of the created record. */ -__attribute_noinline__ +__attribute__((__noinline__)) static size_t nsm_create_monitor_record(char *buf, const size_t buflen, const struct sockaddr *sap, const struct mon *m) @@ -741,7 +789,7 @@ out: return result; } -__attribute_noinline__ +__attribute__((__noinline__)) static _Bool nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m) { @@ -803,7 +851,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. */ @@ -820,10 +868,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) { @@ -870,8 +923,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;