Snort mailing list archives
[RFC][DAQ] nfq: add support for unprivileged operation
From: Florian Westphal <fwestphal () astaro com>
Date: Tue, 3 Aug 2010 18:11:53 +0200
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 ------------------------------------------------------------------------------ The Palm PDK Hot Apps Program offers developers who use the Plug-In Development Kit to bring their C/C++ apps to Palm for a share of $1 Million in cash or HP Products. Visit us here for more details: http://p.sf.net/sfu/dev2dev-palm _______________________________________________ Snort-devel mailing list Snort-devel () lists sourceforge net https://lists.sourceforge.net/lists/listinfo/snort-devel
Current thread:
- [RFC][DAQ] nfq: add support for unprivileged operation Florian Westphal (Aug 03)
- Re: [RFC][DAQ] nfq: add support for unprivileged operation Michael Altizer (Aug 03)