summaryrefslogtreecommitdiff
path: root/ninfod
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2012-11-14 20:15:21 +0900
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2012-11-14 20:15:21 +0900
commit145fa992c30ed37f314109fd66de6acd9a6eb2ce (patch)
treefa2643ed43879a5c3e9bd126d28c3d7690c7c1b7 /ninfod
parent65e6faf32c43d082a12242417101b98f0e1e81c0 (diff)
downloadiputils-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.in3
-rwxr-xr-xninfod/configure12
-rw-r--r--ninfod/configure.in1
-rw-r--r--ninfod/ninfod.c133
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);