diff options
author | YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> | 2012-11-14 20:15:21 +0900 |
---|---|---|
committer | YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> | 2012-11-14 20:15:21 +0900 |
commit | 145fa992c30ed37f314109fd66de6acd9a6eb2ce (patch) | |
tree | fa2643ed43879a5c3e9bd126d28c3d7690c7c1b7 /ninfod | |
parent | 65e6faf32c43d082a12242417101b98f0e1e81c0 (diff) | |
download | iputils-145fa992c30ed37f314109fd66de6acd9a6eb2ce.tar.gz |
ninfod: Add run as user (-u user) option.
Note that we will not write pid file as root anymore, even if ninfod is
suid-root'ed or if ninfod is run by root (with -u option).
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Diffstat (limited to 'ninfod')
-rw-r--r-- | ninfod/config.h.in | 3 | ||||
-rwxr-xr-x | ninfod/configure | 12 | ||||
-rw-r--r-- | ninfod/configure.in | 1 | ||||
-rw-r--r-- | ninfod/ninfod.c | 133 |
4 files changed, 145 insertions, 4 deletions
diff --git a/ninfod/config.h.in b/ninfod/config.h.in index de22221..4bdb50a 100644 --- a/ninfod/config.h.in +++ b/ninfod/config.h.in @@ -51,6 +51,9 @@ /* Define to 1 if you have the <pthread.h> header file. */ #undef HAVE_PTHREAD_H +/* Define to 1 if you have the <pwd.h> header file. */ +#undef HAVE_PWD_H + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H diff --git a/ninfod/configure b/ninfod/configure index be6e71a..4b69014 100755 --- a/ninfod/configure +++ b/ninfod/configure @@ -3584,6 +3584,18 @@ fi done +for ac_header in pwd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default" +if test "x$ac_cv_header_pwd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PWD_H 1 +_ACEOF + +fi + +done + for ac_header in netinet/in.h do : ac_fn_c_check_header_mongrel "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" diff --git a/ninfod/configure.in b/ninfod/configure.in index d543af6..f33b77d 100644 --- a/ninfod/configure.in +++ b/ninfod/configure.in @@ -80,6 +80,7 @@ AC_CHECK_HEADERS(openssl/md5.h) AC_CHECK_HEADERS(sys/uio.h) AC_CHECK_HEADERS(sys/utsname.h arpa/inet.h netdb.h syslog.h) AC_CHECK_HEADERS(sys/capability.h) +AC_CHECK_HEADERS(pwd.h) AC_CHECK_HEADERS(netinet/in.h) AC_CHECK_HEADERS(netinet/ip6.h netinet/icmp6.h,,,[ #if HAVE_SYS_TYPES_H diff --git a/ninfod/ninfod.c b/ninfod/ninfod.c index 44ab42e..c0f6f93 100644 --- a/ninfod/ninfod.c +++ b/ninfod/ninfod.c @@ -98,6 +98,15 @@ # include <syslog.h> #endif +#if HAVE_PWD_H +# include <pwd.h> +#endif + +#if HAVE_SYS_CAPABILITY_H +# include <sys/prctl.h> +# include <sys/capability.h> +#endif + #include "ninfod.h" #ifndef offsetof @@ -117,6 +126,7 @@ static int opt_d = 0; /* debug */ static int opt_h = 0; /* help */ static char *opt_p = NINFOD_PIDFILE; /* pidfile */ int opt_v = 0; /* verbose */ +static uid_t opt_u; static int ipv6_pktinfo = IPV6_PKTINFO; @@ -413,12 +423,115 @@ static void do_daemonize(void) } /* --------- */ +#ifdef HAVE_LIBCAP +static const cap_value_t caps[] = { CAP_NET_RAW, CAP_SETUID }; +#else +static uid_t euid; +#endif + +static void limit_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap_p; + + cap_p = cap_init(); + if (!cap_p) { + DEBUG(LOG_ERR, "cap_init: %s\n", strerror(errno)); + exit(-1); + } + + /* net_raw + setuid / net_raw */ + if (cap_set_flag(cap_p, CAP_PERMITTED, 2, caps, CAP_SET) < 0 || + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps, CAP_SET) < 0) { + DEBUG(LOG_ERR, "cap_set_flag: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + if (errno != EPERM) + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_free(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_free: %s\n", strerror(errno)); + exit(-1); + } +#else + euid = geteuid(); +#endif +} + +static void drop_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap_p; + + cap_p = cap_init(); + if (!cap_p) { + DEBUG(LOG_ERR, "cap_init: %s\n", strerror(errno)); + exit(-1); + } + + /* setuid / setuid */ + if (cap_set_flag(cap_p, CAP_PERMITTED, 1, caps + 1, CAP_SET) < 0 || + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps + 1, CAP_SET) < 0) { + DEBUG(LOG_ERR, "cap_set_flag: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + exit(-1); + } + + if (seteuid(opt_u ? opt_u : getuid()) < 0) { + DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno)); + exit(-1); + } + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_clear(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_clear: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_set_proc(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno)); + exit(-1); + } + + if (cap_free(cap_p) < 0) { + DEBUG(LOG_ERR, "cap_free: %s\n", strerror(errno)); + exit(-1); + } + +#else + if (setuid(getuid()) < 0) { + DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno)); + exit(-1); + } +#endif +} + +/* --------- */ static void parse_args(int argc, char **argv) { int c; + unsigned long val; + char *ep; /* parse options */ - while ((c = getopt(argc, argv, "dhvp:")) != -1) { + while ((c = getopt(argc, argv, "dhvp:u:")) != -1) { switch(c) { case 'd': /* debug */ opt_d = 1; @@ -429,6 +542,18 @@ static void parse_args(int argc, char **argv) case 'p': opt_p = optarg; break; + case 'u': + val = strtoul(optarg, &ep, 10); + if (!optarg || *ep) { + struct passwd *pw = getpwnam(optarg); + if (!pw) { + DEBUG(LOG_ERR, "No such user: %s", optarg); + exit(1); + } + opt_u = pw->pw_uid; + } else + opt_u = val; + break; case 'h': /* help */ default: opt_h = 1; @@ -455,7 +580,7 @@ static void print_copying(void) { static void print_usage(void) { fprintf(stderr, - "Usage: %s [-d [-p pidfile]] [-h] [-v]\n\n", + "Usage: %s [-d [-p pidfile]] [-u user] [-h] [-v]\n\n", appname ); } @@ -476,6 +601,8 @@ int main (int argc, char **argv) parse_args(argc, argv); + drop_capabilities(); + if (opt_h || opt_v) print_copying(); if (opt_h) { @@ -491,8 +618,6 @@ int main (int argc, char **argv) if (!opt_d) do_daemonize(); - drop_capabilities(); - /* initialize */ if (init_sock(sock) < 0) exit(1); |