summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@thewalter.net>2010-10-28 16:35:36 +0000
committerStef Walter <stef@thewalter.net>2011-01-23 15:47:45 -0600
commit57ccf44dca2905aa85d852177ffa4ad28ccfb9da (patch)
treef803dc2d99803a6ddde6be59f32cbb8b4cec9af3
parentd806a9d9e19ae13ed8dbf4f26dce3c2afdde7c6a (diff)
Transparent proxying without running as root.
Using linux capabilitiy CAP_NET_ADMIN
-rw-r--r--common/smtppass.c92
-rw-r--r--configure.in7
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 <linux/netfilter_ipv4.h>
#endif
+#ifdef HAVE_LIBCAP
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#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