[Snort-devel] [RFC][DAQ] nfq: add support for unprivileged operation

Florian Westphal fwestphal at ...2119...
Tue Aug 3 12:11:53 EDT 2010


Disclaimer:

This patch should not be applied.

It seems better to me to follow Will Metcalfs libcap-ng pointer
than to use the "old" libcap (which is what this patch uses).

Also, newer kernels (and libcap-ng) would allow use of
uid 0 without the ability to regain a full privilege set
via execve().

Anyway, maybe it is of some help.
Note that without the setuid() threads spawned before retain
their capability set, so its not easy to do without it.

=============================
its possible to use nfqueue with a non-zero user id as long as
the calling thread has CAP_NET_ADMIN privileges.

Add "--daq-var uid=<id>" to have the nfq backend switch uid while
keep CAP_NET_ADMIN privileges.

Its currently not possible to have snort do the setuid, as we need
to follow this sequence:

1. prctl(PR_SET_KEEPCAPS, 1)
2. setuid()
3. set capabilities to CAP_NET_ADMIN (discarding all others)

1 has to be called before snort will call setuid/setgid, this can be
done e.g. in the daq init hook.

However, snort may invoke pthread_create (depends on configuration)
before it calls into the daq module again (DAQ_Start), so we have something like
this:

1. prctl (daq init)
2. setuid (snort)
3. pthread_create (snort -- e.g. for the reload thread)
4. set capabilities to CAP_NET_ADMIN (discarding all others)

as capabilites are per-thread attributes, all spawned threads
still run with the full privilege set.

This would have to be solved either by creating other DAQ hooks
(e.g. DAQ_pre_setuid/DAQ_post_setuid) or by guaranteeing that threads
are only created after e.g. DAQ_start.

---
 configure.ac               |   12 ++++++-
 os-daq-modules/Makefile.am |    4 ++
 os-daq-modules/daq_nfq.c   |   79 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 1 deletions(-)

diff --git a/configure.ac b/configure.ac
index 42a3eeb..b7c08f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -131,6 +131,7 @@ fi
 AM_CONDITIONAL([BUILD_IPQ_MODULE], [test "$enable_ipq_module" = yes])
 
 # NFQ Module
+AH_TEMPLATE([HAVE_LIBCAP], [define if libcap is available])
 AC_ARG_ENABLE(nfq-module,
               AC_HELP_STRING([--disable-nfq-module],[don't build the bundled NFQ module]),
               [enable_nfq_module="$enableval"], [enable_nfq_module=yes])
@@ -140,8 +141,17 @@ if test "$enable_nfq_module" = yes; then
                     [
                         #include <netinet/in.h>
                     ])
+    AC_CHECK_LIB([cap], [cap_init], [enable_nfq_module_unpriv=yes
+		AC_DEFINE(HAVE_LIBCAP, 1) ])
+
 fi
 AM_CONDITIONAL([BUILD_NFQ_MODULE], [test "$enable_nfq_module" = yes])
+AM_CONDITIONAL([BUILD_NFQ_MODULE_UNPRIV], [test "$enable_nfq_module_unpriv" = yes ])
+if test "$enable_nfq_module" = yes && test "$enable_nfq_module_unpriv" = yes; then
+    enable_nfq_module_unpriv=", unprivileged"
+else
+    enable_nfq_module_unpriv=""
+fi
 
 # PCAP Module
 AC_ARG_ENABLE(pcap-module,
@@ -188,7 +198,7 @@ if test "$disable_bundled_modules" = no; then
     echo "Build Dump DAQ module...... : $enable_dump_module"
     echo "Build IPFW DAQ module...... : $enable_ipfw_module"
     echo "Build IPQ DAQ module....... : $enable_ipq_module"
-    echo "Build NFQ DAQ module....... : $enable_nfq_module"
+    echo "Build NFQ DAQ module....... : $enable_nfq_module$enable_nfq_module_unpriv"
     echo "Build PCAP DAQ module...... : $enable_pcap_module"
 else
     echo "Building of bundled DAQ modules disabled."
diff --git a/os-daq-modules/Makefile.am b/os-daq-modules/Makefile.am
index 974841e..924ad66 100644
--- a/os-daq-modules/Makefile.am
+++ b/os-daq-modules/Makefile.am
@@ -83,6 +83,10 @@ if BUILD_NFQ_MODULE
     libdaq_static_modules_la_SOURCES += daq_nfq.c
     libdaq_static_modules_la_CFLAGS += -DBUILD_NFQ_MODULE
     STATIC_LIBS += -lnfnetlink -lnetfilter_queue -ldnet -lsfbpf
+if BUILD_NFQ_MODULE_UNPRIV
+    STATIC_LIBS += -lcap
+    daq_nfq_la_LIBADD += -lcap
+endif
 endif
 
 INCLUDES = -I$(top_srcdir)/api -I$(top_srcdir)/sfbpf
diff --git a/os-daq-modules/daq_nfq.c b/os-daq-modules/daq_nfq.c
index b278306..77b8edd 100644
--- a/os-daq-modules/daq_nfq.c
+++ b/os-daq-modules/daq_nfq.c
@@ -41,6 +41,13 @@
 
 #define DAQ_MOD_VERSION  1
 
+#ifdef HAVE_LIBCAP
+# include <unistd.h>
+# include <sys/capability.h>
+# include <sys/prctl.h>
+# include <sys/types.h>
+#endif
+
 #define DAQ_NAME "nfq"
 #define DAQ_TYPE (DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_INLINE_CAPABLE | \
                   DAQ_TYPE_MULTI_INSTANCE | DAQ_TYPE_NO_UNPRIV)
@@ -72,6 +79,9 @@ typedef struct
     char error[DAQ_ERRBUF_SIZE];
     DAQ_State state;
     DAQ_Stats_t stats;
+
+    uid_t id_user;
+    gid_t id_group;
 } NfqImpl;
 
 static void nfq_daq_shutdown(void* handle);
@@ -102,6 +112,53 @@ static int nfq_daq_get_protos (const char* s)
     return 0;
 }
 
+/*
+ * drop all capabilites (except CAP_NET_ADMIN).
+ */
+static int nfq_set_uidgid(NfqImpl *impl)
+{
+    int ret = DAQ_ERROR;
+#ifdef HAVE_LIBCAP
+    static const cap_value_t capval[] = {
+        CAP_NET_ADMIN,
+    };
+    static const size_t len = 1;
+
+    cap_t caps = cap_init();
+    if (!caps)
+       return -1;
+
+    cap_set_flag(caps, CAP_EFFECTIVE, len, capval, CAP_SET);
+    cap_set_flag(caps, CAP_PERMITTED, len, capval, CAP_SET);
+
+    if (prctl(PR_SET_KEEPCAPS, 1) == 0 &&
+        setgid(impl->id_group) == 0 &&
+        setuid(impl->id_user) == 0 &&
+        cap_set_proc(caps) == 0 &&
+        prctl(PR_SET_KEEPCAPS, 0) == 0)
+            ret = DAQ_SUCCESS;
+
+    cap_free(caps);
+#endif
+    return ret;
+}
+
+#ifdef HAVE_LIBCAP
+static int nfq_parse_uidgid(DAQ_Dict *entry, char *errBuf, size_t errMax, long *res)
+{
+    char *end = entry->value;
+    long int id = (int)strtol(entry->value, &end, 0);
+
+    if ( *end || id <= 0 ) {
+        snprintf(errBuf, errMax, "%s: bad %s (%s)\n",
+                    __FUNCTION__, entry->key, entry->value);
+        return DAQ_ERROR;
+    }
+    *res = id;
+    return DAQ_SUCCESS;
+}
+#endif
+
 static int nfq_daq_get_setup (
     NfqImpl* impl, const DAQ_Config_t* cfg, char* errBuf, size_t errMax)
 {
@@ -110,6 +167,8 @@ static int nfq_daq_get_setup (
     impl->protos = 0x1;
     impl->qid = DEFAULT_Q;
     impl->qlen = 0;
+    impl->id_user = 0;
+    impl->id_group = 0;
 
     for ( entry = cfg->values; entry; entry = entry->next)
     {
@@ -166,6 +225,22 @@ static int nfq_daq_get_setup (
                 return DAQ_ERROR;
             }
         }
+#ifdef HAVE_LIBCAP
+        else if ( !strcmp(entry->key, "uid") )
+        {
+            long int id;
+            if (nfq_parse_uidgid(entry, errBuf, errMax, &id))
+		return DAQ_ERROR;
+            impl->id_user = (uid_t) id;
+        }
+        else if ( !strcmp(entry->key, "gid") )
+        {
+            long int gid;
+            if (nfq_parse_uidgid(entry, errBuf, errMax, &gid))
+		return DAQ_ERROR;
+            impl->id_group = (gid_t) gid;
+        }
+#endif
         else
         {
             snprintf(errBuf, errMax,
@@ -558,6 +633,10 @@ static int nfq_daq_set_filter (void* handle, const char* filter)
 static int nfq_daq_start (void* handle)
 {
     NfqImpl* impl = (NfqImpl*)handle;
+
+    if (impl->id_user && nfq_set_uidgid(impl))
+        return DAQ_ERROR;
+
     impl->state = DAQ_STATE_STARTED;
     return DAQ_SUCCESS;
 }
-- 
1.7.1





More information about the Snort-devel mailing list