From 57ccf44dca2905aa85d852177ffa4ad28ccfb9da Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 28 Oct 2010 16:35:36 +0000 Subject: Transparent proxying without running as root. Using linux capabilitiy CAP_NET_ADMIN --- common/smtppass.c | 92 ++++++++++++++++++++++++++++++++++++++----------------- configure.in | 7 +++++ 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/common/smtppass.c b/common/smtppass.c index 2cddf41..7b668b6 100644 --- a/common/smtppass.c +++ b/common/smtppass.c @@ -68,6 +68,11 @@ #include #endif +#ifdef HAVE_LIBCAP +#include +#include +#endif + #include "compat.h" #include "sock_any.h" #include "stringx.h" @@ -388,40 +393,71 @@ static void on_quit(int signal) static void drop_privileges() { - char* t; - struct passwd* pw; - uid_t uid; - - if(g_state.user) - { - if(geteuid() != 0) - { - sp_messagex(NULL, LOG_WARNING, "must be started as root to switch to user: %s", g_state.user); - return; - } + char* t; + struct passwd* pw; + uid_t uid; +#ifdef HAVE_LIBCAP + cap_t caps; + cap_value_t value; +#endif - uid = strtol(g_state.user, &t, 10); - if(!t[0]) /* successful parse */ - pw = getpwuid(uid); - else /* must be a name */ - pw = getpwnam(g_state.user); + if(g_state.user) + { + if(geteuid() != 0) + { + sp_messagex(NULL, LOG_WARNING, "must be started as root to switch to user: %s", + g_state.user); + return; + } - if(pw == NULL) - errx(1, "couldn't look up user: %s", g_state.user); +#ifdef HAVE_LIBCAP + /* + * Tell kernel not clear capabilities when dropping root. + * We drop them manually below, while keeping a small set. + */ + if(prctl(PR_SET_KEEPCAPS, 1) < 0) + sp_message (NULL, LOG_WARNING, "couldn't keep capabilities when dropping privileges"); +#endif - if(setgid(pw->pw_gid) == -1 || - setuid(pw->pw_uid) == -1) - err(1, "unable to switch to user: %s (uid %d, gid %d)", g_state.user, pw->pw_uid, pw->pw_gid); + uid = strtol(g_state.user, &t, 10); + if(!t[0]) /* successful parse */ + pw = getpwuid(uid); + else /* must be a name */ + pw = getpwnam(g_state.user); + + if(pw == NULL) + errx(1, "couldn't look up user: %s", g_state.user); + + if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + err(1, "unable to switch to user: %s (uid %d, gid %d)", + g_state.user, pw->pw_uid, pw->pw_gid); + +#ifdef HAVE_LIBCAP + /* + * No capabilities have been dropped yet. So we drop them here + * keeping only those we need for our proxying. + */ + caps = cap_init(); + if (caps == NULL) + err(1, "unable to init capabilities"); + value = CAP_NET_ADMIN; + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &value, CAP_SET) < 0 || + cap_set_flag(caps, CAP_PERMITTED, 1, &value, CAP_SET) < 0 || + cap_set_proc(caps) < 0) + err(1, "couldn't set capabilities when switching user"); + cap_free (caps); +#endif - /* A paranoia check */ - if(setreuid(-1, 0) == 0) - err(1, "unable to completely drop privileges"); + /* A paranoia check */ + if(setreuid(-1, 0) == 0) + err(1, "unable to completely drop privileges"); - sp_messagex(NULL, LOG_DEBUG, "switched to user %s (uid %d, gid %d)", g_state.user, pw->pw_uid, pw->pw_gid); - } + sp_messagex(NULL, LOG_DEBUG, "switched to user %s (uid %d, gid %d)", g_state.user, pw->pw_uid, pw->pw_gid); + } - if(geteuid() == 0) - sp_messagex(NULL, LOG_WARNING, "running as root is NOT recommended"); + if(geteuid() == 0) + sp_messagex(NULL, LOG_WARNING, "running as root is NOT recommended"); } diff --git a/configure.in b/configure.in index 321c0a3..48ca230 100644 --- a/configure.in +++ b/configure.in @@ -108,6 +108,13 @@ AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower getaddrinfo AC_CHECK_FUNCS([strlwr strlcat strlcpy strncat strncpy strcasestr setenv daemon]) AC_CHECK_FUNCS([getline getdelim]) +# libcap2 +AC_CHECK_LIB([cap], [cap_get_proc], have_libcap="yes", have_libcap="no") +if test $have_libcap = yes; then + AC_DEFINE(HAVE_LIBCAP, 1, [Have libcap2 package, libcap library]) + LIBS="$LIBS -lcap" +fi + # Have to resolve this for the path below if test "${prefix}" = "NONE"; then prefix=$ac_default_prefix -- cgit v1.2.3