diff --git a/config.h.in b/config.h.in index 2344e27f65..b74615d42b 100644 --- a/config.h.in +++ b/config.h.in @@ -12,6 +12,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `cap-ng' library (-lcap-ng). */ +#undef HAVE_LIBCAP_NG + /* Define to 1 if you have the `cuda' library (-lcuda). */ #undef HAVE_LIBCUDA diff --git a/configure.in b/configure.in index c8509eea2c..eab96b4527 100644 --- a/configure.in +++ b/configure.in @@ -698,6 +698,42 @@ AC_CHECK_HEADER(pcap.h,,[AC_ERROR(pcap.h not found ...)]) fi fi +# Check for libcap-ng + + AC_ARG_WITH(libcap_ng_includes, + [ --with-libcap_ng-includes=DIR libcap_ng include directory], + [with_libcap-ng_includes="$withval"],[with_libcap_ng_includes=no]) + AC_ARG_WITH(libcap_ng_libraries, + [ --with-libcap_ng-libraries=DIR libcap_ng library directory], + [with_libcap_ng_libraries="$withval"],[with_libcap_ng_libraries="no"]) + + if test "$with_libcap_ng_includes" != "no"; then + CPPFLAGS="${CPPFLAGS} -I${with_libcap_ng_includes}" + fi + + if test "$with_libcap_ng_libraries" != "no"; then + LDFLAGS="${LDFLAGS} -L${with_libcap_ng_libraries}" + fi + + AC_CHECK_HEADER(cap-ng.h,,LIBCAP_NG="no") + if test "$LIBCAP_NG" != "no"; then + LIBCAP_NG="" + AC_CHECK_LIB(cap-ng,capng_clear,,LIBCAP_NG="no") + fi + + if test "$LIBCAP_NG" != "no"; then + CFLAGS="${CFLAGS} -DHAVE_LIBCAP_NG" + fi + + if test "$LIBCAP_NG" = "no"; then + echo + echo " WARNING! libcap-ng library not found, go get it" + echo " from http://people.redhat.com/sgrubb/libcap-ng/" + echo " or check your package manager." + echo + echo " Suricata will be built without support for dropping privs." + echo + fi AC_SUBST(CFLAGS) AC_SUBST(LDFLAGS) diff --git a/src/Makefile.am b/src/Makefile.am index ae94196342..9e6eaa32c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,6 +155,7 @@ util-strlcatu.c \ util-strlcpyu.c \ util-cuda.c util-cuda.h \ util-cuda-handlers.c util-cuda-handlers.h \ +util-privs.c util-privs.h \ tm-modules.c tm-modules.h \ tm-queues.c tm-queues.h \ tm-queuehandlers.c tm-queuehandlers.h \ diff --git a/src/alert-debuglog.c b/src/alert-debuglog.c index b05930677c..09ace7ec5d 100644 --- a/src/alert-debuglog.c +++ b/src/alert-debuglog.c @@ -28,6 +28,7 @@ #include "output.h" #include "alert-debuglog.h" +#include "util-privs.h" #define DEFAULT_LOG_FILENAME "alert-debug.log" @@ -48,6 +49,7 @@ void TmModuleAlertDebugLogRegister (void) { tmm_modules[TMM_ALERTDEBUGLOG].ThreadExitPrintStats = AlertDebugLogExitPrintStats; tmm_modules[TMM_ALERTDEBUGLOG].ThreadDeinit = AlertDebugLogThreadDeinit; tmm_modules[TMM_ALERTDEBUGLOG].RegisterTests = NULL; + tmm_modules[TMM_ALERTDEBUGLOG].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "alert-debug", AlertDebugLogInitCtx); } diff --git a/src/alert-fastlog.c b/src/alert-fastlog.c index f710327384..10c32cc014 100644 --- a/src/alert-fastlog.c +++ b/src/alert-fastlog.c @@ -37,6 +37,7 @@ #include "util-mpm-b2g-cuda.h" #include "util-cuda-handlers.h" +#include "util-privs.h" #define DEFAULT_LOG_FILENAME "fast.log" @@ -59,6 +60,7 @@ void TmModuleAlertFastLogRegister (void) { tmm_modules[TMM_ALERTFASTLOG].ThreadExitPrintStats = AlertFastLogExitPrintStats; tmm_modules[TMM_ALERTFASTLOG].ThreadDeinit = AlertFastLogThreadDeinit; tmm_modules[TMM_ALERTFASTLOG].RegisterTests = AlertFastLogRegisterTests; + tmm_modules[TMM_ALERTFASTLOG].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "fast", AlertFastLogInitCtx); } diff --git a/src/alert-prelude.c b/src/alert-prelude.c index f414d82e6e..054543e0fd 100644 --- a/src/alert-prelude.c +++ b/src/alert-prelude.c @@ -38,6 +38,7 @@ #include "util-error.h" #include "output.h" +#include "util-privs.h" #ifndef PRELUDE /** Handle the case where no PRELUDE support is compiled in. @@ -117,6 +118,7 @@ void TmModuleAlertPreludeRegister (void) { tmm_modules[TMM_ALERTPRELUDE].Func = AlertPrelude; tmm_modules[TMM_ALERTPRELUDE].ThreadDeinit = AlertPreludeThreadDeinit; tmm_modules[TMM_ALERTPRELUDE].RegisterTests = AlertPreludeRegisterTests; + tmm_modules[TMM_ALERTPRELUDE].cap_flags = 0; OutputRegisterModule("AlertPrelude", "alert-prelude", AlertPreludeInitCtx); } diff --git a/src/alert-unified-alert.c b/src/alert-unified-alert.c index 80303870bd..22f20c5b30 100644 --- a/src/alert-unified-alert.c +++ b/src/alert-unified-alert.c @@ -29,6 +29,7 @@ #include "output.h" #include "alert-unified-alert.h" +#include "util-privs.h" #define DEFAULT_LOG_FILENAME "unified.alert" @@ -53,6 +54,7 @@ void TmModuleAlertUnifiedAlertRegister (void) { tmm_modules[TMM_ALERTUNIFIEDALERT].Func = AlertUnifiedAlert; tmm_modules[TMM_ALERTUNIFIEDALERT].ThreadDeinit = AlertUnifiedAlertThreadDeinit; tmm_modules[TMM_ALERTUNIFIEDALERT].RegisterTests = AlertUnifiedAlertRegisterTests; + tmm_modules[TMM_ALERTUNIFIEDALERT].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "unified-alert", AlertUnifiedAlertInitCtx); } diff --git a/src/alert-unified-log.c b/src/alert-unified-log.c index 3d2d6c8599..de4fd0d75f 100644 --- a/src/alert-unified-log.c +++ b/src/alert-unified-log.c @@ -30,6 +30,7 @@ #include "output.h" #include "alert-unified-log.h" +#include "util-privs.h" #define DEFAULT_LOG_FILENAME "unified.log" @@ -54,6 +55,7 @@ void TmModuleAlertUnifiedLogRegister (void) { tmm_modules[TMM_ALERTUNIFIEDLOG].Func = AlertUnifiedLog; tmm_modules[TMM_ALERTUNIFIEDLOG].ThreadDeinit = AlertUnifiedLogThreadDeinit; tmm_modules[TMM_ALERTUNIFIEDLOG].RegisterTests = AlertUnifiedLogRegisterTests; + tmm_modules[TMM_ALERTUNIFIEDLOG].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "unified-log", AlertUnifiedLogInitCtx); diff --git a/src/alert-unified2-alert.c b/src/alert-unified2-alert.c index a88c732913..3e5516a57d 100644 --- a/src/alert-unified2-alert.c +++ b/src/alert-unified2-alert.c @@ -25,6 +25,7 @@ #include "output.h" #include "alert-unified2-alert.h" +#include "util-privs.h" #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 @@ -138,6 +139,7 @@ void TmModuleUnified2AlertRegister (void) { tmm_modules[TMM_ALERTUNIFIED2ALERT].Func = Unified2Alert; tmm_modules[TMM_ALERTUNIFIED2ALERT].ThreadDeinit = Unified2AlertThreadDeinit; tmm_modules[TMM_ALERTUNIFIED2ALERT].RegisterTests = Unified2RegisterTests; + tmm_modules[TMM_ALERTUNIFIED2ALERT].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "unified2-alert", Unified2AlertInitCtx); } diff --git a/src/counters.c b/src/counters.c index 8dccdf4039..934c7331b8 100644 --- a/src/counters.c +++ b/src/counters.c @@ -12,6 +12,7 @@ #include "util-time.h" #include "util-unittest.h" #include "util-debug.h" +#include "util-privs.h" /** \todo Get the default log directory from some global resource. */ #define SC_PERF_DEFAULT_LOG_FILENAME "stats.log" @@ -356,6 +357,11 @@ static void *SCPerfMgmtThread(void *arg) /* Set the thread name */ SCSetThreadName(tv_local->name); + /* Set the threads capability */ + tv_local->cap_flags = 0; + + SCDropCaps(tv_local); + if (sc_perf_op_ctx == NULL) { SCLogError(SC_ERR_PERF_STATS_NOT_INIT, "Perf Counter API not init" "SCPerfInitCounterApi() has to be called first"); @@ -403,6 +409,11 @@ static void *SCPerfWakeupThread(void *arg) /* Set the thread name */ SCSetThreadName(tv_local->name); + /* Set the threads capability */ + tv_local->cap_flags = 0; + + SCDropCaps(tv_local); + if (sc_perf_op_ctx == NULL) { SCLogError(SC_ERR_PERF_STATS_NOT_INIT, "Perf Counter API not init" "SCPerfInitCounterApi() has to be called first"); @@ -1657,10 +1668,8 @@ void SCPerfReleasePCA(SCPerfCounterArray *pca) return; } - /*----------------------------------Unit_Tests--------------------------------*/ - static int SCPerfTestCounterReg01() { SCPerfContext pctx; diff --git a/src/detect-parse.c b/src/detect-parse.c index cb03b60581..9079b7a2b2 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -30,6 +30,8 @@ #include "detect-parse.h" #include "detect-engine-iponly.h" +extern int sc_set_caps; + static pcre *config_pcre = NULL; static pcre *option_pcre = NULL; static pcre_extra *config_pcre_extra = NULL; @@ -506,6 +508,28 @@ int SigParsePort(Signature *s, const char *portstr, char flag) return 0; } +/** \retval 1 valid + * \retval 0 invalid + */ +static int SigParseActionRejectValidate(void) { +#ifdef HAVE_LIBNET11 +#ifdef HAVE_LIBCAP_NG + if (sc_set_caps == TRUE) { + SCLogError(SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG, "Libnet 1.1 is " + "incompatible with POSIX based capabilities with privs dropping. " + "For rejects to work, run as root/super user."); + return 0; + } +#endif +#else /* no libnet 1.1 */ + SCLogError(SC_ERR_LIBNET_REQUIRED_FOR_ACTION, "Libnet 1.1.x is " + "required for action \"%s\" but is not compiled into Suricata", + action); + return 0; +#endif + return 1; +} + /** * \brief Parses the action that has been used by the Signature and allots it * to its Signatue instance. @@ -527,30 +551,26 @@ int SigParseAction(Signature *s, const char *action) { } else if (strcasecmp(action, "pass") == 0) { s->action = ACTION_PASS; return 0; -#ifdef HAVE_LIBNET11 } else if (strcasecmp(action, "reject") == 0) { + if (!(SigParseActionRejectValidate())) + return -1; s->action = ACTION_REJECT; return 0; } else if (strcasecmp(action, "rejectsrc") == 0) { + if (!(SigParseActionRejectValidate())) + return -1; s->action = ACTION_REJECT; return 0; } else if (strcasecmp(action, "rejectdst") == 0) { + if (!(SigParseActionRejectValidate())) + return -1; s->action = ACTION_REJECT_DST; return 0; } else if (strcasecmp(action, "rejectboth") == 0) { + if (!(SigParseActionRejectValidate())) + return -1; s->action = ACTION_REJECT_BOTH; return 0; -#else - } else if (strcasecmp(action, "reject") == 0 || - strcasecmp(action, "rejectsrc") == 0 || - strcasecmp(action, "rejectdst") == 0 || - strcasecmp(action, "rejectboth") == 0) - { - SCLogError(SC_ERR_LIBNET_REQUIRED_FOR_ACTION, "Libnet 1.1.x is " - "required for action \"%s\" but is not compiled into Suricata", - action); - return -1; -#endif /* HAVE_LIBNET11 */ } else { SCLogError(SC_ERR_INVALID_ACTION,"An invalid action \"%s\" was given",action); return -1; @@ -639,7 +659,7 @@ int SigParse(DetectEngineCtx *de_ctx, Signature *s, char *sigstr, uint8_t addrs_ int ret = SigParseBasics(s, sigstr, &basics, addrs_direction); if (ret < 0) { - printf("SigParseBasics failed\n"); + SCLogDebug("SigParseBasics failed"); SCReturnInt(-1); } diff --git a/src/detect.c b/src/detect.c index e1537f24a5..0fa5d420a5 100644 --- a/src/detect.c +++ b/src/detect.c @@ -114,6 +114,7 @@ #include "util-cuda-handlers.h" #include "util-mpm-b2g-cuda.h" #include "util-cuda.h" +#include "util-privs.h" SigMatch *SigMatchAlloc(void); void DetectExitPrintStats(ThreadVars *tv, void *data); @@ -133,6 +134,7 @@ void TmModuleDetectRegister (void) { tmm_modules[TMM_DETECT].ThreadExitPrintStats = DetectExitPrintStats; tmm_modules[TMM_DETECT].ThreadDeinit = DetectThreadDeinit; tmm_modules[TMM_DETECT].RegisterTests = SigRegisterTests; + tmm_modules[TMM_DETECT].cap_flags = 0; } void DetectExitPrintStats(ThreadVars *tv, void *data) { diff --git a/src/flow.c b/src/flow.c index b2aadf2a94..aa00f678cf 100644 --- a/src/flow.c +++ b/src/flow.c @@ -33,6 +33,7 @@ #include "util-byte.h" #include "util-debug.h" +#include "util-privs.h" //#define FLOW_DEFAULT_HASHSIZE 262144 #define FLOW_DEFAULT_HASHSIZE 65536 @@ -651,6 +652,10 @@ void *FlowManagerThread(void *td) SCSetThreadName(th_v->name); SCLogDebug("%s started...", th_v->name); + /* Set the threads capability */ + th_v->cap_flags = 0; + SCDropCaps(th_v); + TmThreadsSetFlag(th_v, THV_INIT_DONE); while (1) { diff --git a/src/log-httplog.c b/src/log-httplog.c index 388d8170d1..a70d7b9b62 100644 --- a/src/log-httplog.c +++ b/src/log-httplog.c @@ -25,6 +25,7 @@ #include "app-layer-htp.h" #include #include "app-layer.h" +#include "util-privs.h" #define DEFAULT_LOG_FILENAME "http.log" @@ -46,6 +47,7 @@ void TmModuleLogHttpLogRegister (void) { tmm_modules[TMM_LOGHTTPLOG].ThreadExitPrintStats = LogHttpLogExitPrintStats; tmm_modules[TMM_LOGHTTPLOG].ThreadDeinit = LogHttpLogThreadDeinit; tmm_modules[TMM_LOGHTTPLOG].RegisterTests = NULL; + tmm_modules[TMM_LOGHTTPLOG].cap_flags = 0; OutputRegisterModule(MODULE_NAME, "http-log", LogHttpLogInitCtx); } diff --git a/src/respond-reject-libnet11.c b/src/respond-reject-libnet11.c index e78dca5455..f1b93340c9 100644 --- a/src/respond-reject-libnet11.c +++ b/src/respond-reject-libnet11.c @@ -31,6 +31,10 @@ #ifdef HAVE_LIBNET11 +/** set to true in main if we're setting caps. We need it here if we're using + * reject rules as libnet 1.1 is not compatible with caps. */ +extern int sc_set_caps; + #include diff --git a/src/respond-reject.c b/src/respond-reject.c index 629d0259c9..57fd70de2f 100644 --- a/src/respond-reject.c +++ b/src/respond-reject.c @@ -24,6 +24,7 @@ #include "respond-reject-libnet11.h" #include "util-debug.h" +#include "util-privs.h" int RejectSendIPv4TCP(ThreadVars *, Packet *, void *); int RejectSendIPv4ICMP(ThreadVars *, Packet *, void *); @@ -37,6 +38,7 @@ void TmModuleRespondRejectRegister (void) { tmm_modules[TMM_RESPONDREJECT].Func = RespondRejectFunc; tmm_modules[TMM_RESPONDREJECT].ThreadDeinit = NULL; tmm_modules[TMM_RESPONDREJECT].RegisterTests = NULL; + tmm_modules[TMM_RESPONDREJECT].cap_flags = 0; /* libnet is not compat with caps */ } TmEcode RespondRejectFunc(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) { diff --git a/src/runmodes.c b/src/runmodes.c index fc4a16229a..df8c4521fd 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -127,6 +127,7 @@ static void SetupOutputs(ThreadVars *tv) { RunModeOutput *output; TAILQ_FOREACH(output, &RunModeOutputs, entries) { + tv->cap_flags |= output->tm_module->cap_flags; TmVarSlotSetFuncAppend(tv, output->tm_module, output->output_ctx); } } diff --git a/src/source-ipfw.c b/src/source-ipfw.c index e3c42bc970..ad1b72d21e 100644 --- a/src/source-ipfw.c +++ b/src/source-ipfw.c @@ -16,6 +16,7 @@ #include "source-ipfw.h" #include "util-debug.h" #include "conf.h" +#include "util-privs.h" #define IPFW_ACCEPT 0 #define IPFW_DROP 1 @@ -40,6 +41,8 @@ void TmModuleReceiveIPFWRegister (void) { tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = NULL; tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVEIPFW].RegisterTests = NULL; + tmm_modules[TMM_RECEIVEIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW | + SC_CAP_NET_BIND_SERVICE | SC_CAP_NET_BROADCAST; /** \todo untested */ } void TmModuleVerdictIPFWRegister (void) { @@ -49,6 +52,8 @@ void TmModuleVerdictIPFWRegister (void) { tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = NULL; tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = NULL; tmm_modules[TMM_VERDICTIPFW].RegisterTests = NULL; + tmm_modules[TMM_VERDICTIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW | + SC_CAP_NET_BIND_SERVICE; /** \todo untested */ } void TmModuleDecodeIPFWRegister (void) { @@ -58,6 +63,7 @@ void TmModuleDecodeIPFWRegister (void) { tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODEIPFW].ThreadDeinit = NULL; tmm_modules[TMM_DECODEIPFW].RegisterTests = NULL; + tmm_modules[TMM_DECODEIPFW].cap_flags = 0; } TmEcode NoIPFWSupportExit(ThreadVars *tv, void *initdata, void **data) { diff --git a/src/source-nfq.c b/src/source-nfq.c index 33514efa35..761ad418e2 100644 --- a/src/source-nfq.c +++ b/src/source-nfq.c @@ -28,6 +28,7 @@ #include "util-debug.h" #include "util-error.h" #include "util-byte.h" +#include "util-privs.h" #ifndef NFQ /** Handle the case where no NFQ support is compiled in. @@ -43,6 +44,7 @@ void TmModuleReceiveNFQRegister (void) { tmm_modules[TMM_RECEIVENFQ].ThreadExitPrintStats = NULL; tmm_modules[TMM_RECEIVENFQ].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVENFQ].RegisterTests = NULL; + tmm_modules[TMM_RECEIVENFQ].cap_flags = SC_CAP_NET_ADMIN; } void TmModuleVerdictNFQRegister (void) { @@ -52,6 +54,7 @@ void TmModuleVerdictNFQRegister (void) { tmm_modules[TMM_VERDICTNFQ].ThreadExitPrintStats = NULL; tmm_modules[TMM_VERDICTNFQ].ThreadDeinit = NULL; tmm_modules[TMM_VERDICTNFQ].RegisterTests = NULL; + tmm_modules[TMM_VERDICTNFQ].cap_flags = SC_CAP_NET_ADMIN; } void TmModuleDecodeNFQRegister (void) { @@ -61,6 +64,7 @@ void TmModuleDecodeNFQRegister (void) { tmm_modules[TMM_DECODENFQ].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODENFQ].ThreadDeinit = NULL; tmm_modules[TMM_DECODENFQ].RegisterTests = NULL; + tmm_modules[TMM_DECODENFQ].cap_flags = 0; } TmEcode NoNFQSupportExit(ThreadVars *tv, void *initdata, void **data) diff --git a/src/source-pcap-file.c b/src/source-pcap-file.c index f439695d2f..73a58199ad 100644 --- a/src/source-pcap-file.c +++ b/src/source-pcap-file.c @@ -25,6 +25,7 @@ #include "util-debug.h" #include "conf.h" #include "util-error.h" +#include "util-privs.h" extern int max_pending_packets; @@ -66,6 +67,7 @@ void TmModuleReceivePcapFileRegister (void) { tmm_modules[TMM_RECEIVEPCAPFILE].ThreadExitPrintStats = ReceivePcapFileThreadExitStats; tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVEPCAPFILE].RegisterTests = NULL; + tmm_modules[TMM_RECEIVEPCAPFILE].cap_flags = 0; } void TmModuleDecodePcapFileRegister (void) { @@ -75,6 +77,7 @@ void TmModuleDecodePcapFileRegister (void) { tmm_modules[TMM_DECODEPCAPFILE].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODEPCAPFILE].ThreadDeinit = NULL; tmm_modules[TMM_DECODEPCAPFILE].RegisterTests = NULL; + tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0; } void PcapFileCallback(char *user, struct pcap_pkthdr *h, u_char *pkt) { diff --git a/src/source-pcap.c b/src/source-pcap.c index 467c3bd4bf..69f3df13cc 100644 --- a/src/source-pcap.c +++ b/src/source-pcap.c @@ -19,6 +19,7 @@ #include "conf.h" #include "util-debug.h" #include "util-error.h" +#include "util-privs.h" extern int max_pending_packets; @@ -63,6 +64,7 @@ void TmModuleReceivePcapRegister (void) { tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitStats; tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVEPCAP].RegisterTests = NULL; + tmm_modules[TMM_RECEIVEPCAP].cap_flags = SC_CAP_NET_RAW; } /** @@ -76,6 +78,7 @@ void TmModuleDecodePcapRegister (void) { tmm_modules[TMM_DECODEPCAP].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODEPCAP].ThreadDeinit = NULL; tmm_modules[TMM_DECODEPCAP].RegisterTests = NULL; + tmm_modules[TMM_DECODEPCAP].cap_flags = 0; } /** diff --git a/src/source-pfring.c b/src/source-pfring.c index 0e9492deda..c92731fbb8 100644 --- a/src/source-pfring.c +++ b/src/source-pfring.c @@ -16,6 +16,7 @@ #include "tm-threads.h" #include "source-pfring.h" #include "util-debug.h" +#include "util-privs.h" TmEcode ReceivePfring(ThreadVars *, Packet *, void *, PacketQueue *); TmEcode ReceivePfringThreadInit(ThreadVars *, void *, void **); @@ -39,6 +40,8 @@ void TmModuleReceivePfringRegister (void) { tmm_modules[TMM_RECEIVEPFRING].ThreadExitPrintStats = NULL; tmm_modules[TMM_RECEIVEPFRING].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVEPFRING].RegisterTests = NULL; + tmm_modules[TMM_RECEIVEPFRING].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW | + SC_CAP_NET_BIND_SERVICE | SC_CAP_NET_BROADCAST; } void TmModuleDecodePfringRegister (void) { @@ -48,6 +51,7 @@ void TmModuleDecodePfringRegister (void) { tmm_modules[TMM_DECODEPFRING].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODEPFRING].ThreadDeinit = NULL; tmm_modules[TMM_DECODEPFRING].RegisterTests = NULL; + tmm_modules[TMM_DECODEPFRING].cap_flags = 0; } /** diff --git a/src/stream-tcp.c b/src/stream-tcp.c index ab07bbb648..493da41052 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -36,6 +36,7 @@ #include "app-layer-parser.h" #include "util-host-os-info.h" +#include "util-privs.h" //#define DEBUG @@ -93,6 +94,7 @@ void TmModuleStreamTcpRegister (void) tmm_modules[TMM_STREAMTCP].ThreadExitPrintStats = StreamTcpExitPrintStats; tmm_modules[TMM_STREAMTCP].ThreadDeinit = StreamTcpThreadDeinit; tmm_modules[TMM_STREAMTCP].RegisterTests = StreamTcpRegisterTests; + tmm_modules[TMM_STREAMTCP].cap_flags = 0; } void StreamTcpIncrMemuse(uint32_t size) { diff --git a/src/suricata.c b/src/suricata.c index 1de76afd0d..4e3e346e09 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -103,6 +103,7 @@ #include "util-cuda-handlers.h" #include "output.h" +#include "util-privs.h" /* * we put this here, because we only use it here in main. @@ -122,12 +123,15 @@ volatile sig_atomic_t sigterm_count = 0; static uint8_t sigflags = 0; -/* Run mode selected */ +/** Run mode selected */ int run_mode = MODE_UNKNOWN; -/* Maximum packets to simultaneously process. */ +/** Maximum packets to simultaneously process. */ intmax_t max_pending_packets; +/** set caps or not */ +int sc_set_caps; + int RunmodeIsUnittests(void) { if (run_mode == MODE_UNITTEST) return 1; @@ -401,10 +405,18 @@ int main(int argc, char **argv) int dump_config = 0; int list_unittests = 0; int daemon = 0; + char *user_name = NULL; + char *group_name = NULL; + uint8_t do_setuid = FALSE; + uint8_t do_setgid = FALSE; + uint32_t userid = 0; + uint32_t groupid = 0; char *log_dir; struct stat buf; + sc_set_caps = FALSE; + #ifdef OS_WIN32 WSADATA wsaData; if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) { @@ -431,6 +443,8 @@ int main(int argc, char **argv) {"pidfile", required_argument, 0, 0}, {"init-errors-fatal", 0, 0, 0}, {"fatal-unittests", 0, 0, 0}, + {"user", required_argument, 0, 0}, + {"group", required_argument, 0, 0}, {NULL, 0, NULL, 0} }; @@ -489,6 +503,26 @@ int main(int argc, char **argv) fprintf(stderr, "ERROR: Unit tests not enabled. Make sure to pass --enable-unittests to configure when building.\n"); exit(EXIT_FAILURE); #endif /* UNITTESTS */ + } + else if(strcmp((long_opts[option_index]).name, "user") == 0) { +#ifndef HAVE_LIBCAP_NG + SCLogError(SC_ERR_LIBCAP_NG_REQUIRED, "libcap-ng is required to" + " drop privileges, but it was not compiled into Suricata."); + exit(EXIT_FAILURE); +#else + user_name = optarg; + do_setuid = TRUE; +#endif /* HAVE_LIBCAP_NG */ + } + else if(strcmp((long_opts[option_index]).name, "group") == 0) { +#ifndef HAVE_LIBCAP_NG + SCLogError(SC_ERR_LIBCAP_NG_REQUIRED, "libcap-ng is required to" + " drop privileges, but it was not compiled into Suricata."); + exit(EXIT_FAILURE); +#else + group_name = optarg; + do_setgid = TRUE; +#endif /* HAVE_LIBCAP_NG */ } break; case 'c': @@ -834,6 +868,24 @@ int main(int argc, char **argv) SignalHandlerSetup(SIGHUP, SignalHandlerSighup); #endif /* OS_WIN32 */ + /* Get the suricata user ID to given user ID */ + if (do_setuid == TRUE) { + if (SCGetUserID(user_name, group_name, &userid, &groupid) != 0) { + SCLogError(SC_ERR_UID_FAILED, "failed in getting user ID"); + exit(EXIT_FAILURE); + } + + sc_set_caps = TRUE; + /* Get the suricata group ID to given group ID */ + } else if (do_setgid == TRUE) { + if (SCGetGroupID(group_name, &groupid) != 0) { + SCLogError(SC_ERR_GID_FAILED, "failed in getting group ID"); + exit(EXIT_FAILURE); + } + + sc_set_caps = TRUE; + } + /* pre allocate packets */ SCLogDebug("preallocating packets... packet size %" PRIuMAX "", (uintmax_t)sizeof(Packet)); int i = 0; @@ -875,7 +927,11 @@ int main(int argc, char **argv) memset(&start_time, 0, sizeof(start_time)); gettimeofday(&start_time, NULL); + SCDropMainThreadCaps(userid, groupid); + RunModeInitializeOutputs(); + + /* run the selected runmode */ if (run_mode == MODE_PCAP_DEV) { //RunModeIdsPcap3(de_ctx, pcap_dev); //RunModeIdsPcap2(de_ctx, pcap_dev); diff --git a/src/threadvars.h b/src/threadvars.h index 8103cb1dc0..9c0bd30e8c 100644 --- a/src/threadvars.h +++ b/src/threadvars.h @@ -66,6 +66,8 @@ typedef struct ThreadVars_ { SCMutex *m; SCCondT *cond; + uint8_t cap_flags; /**< Flags to indicate the capabilities of all the + TmModules resgitered under this thread */ struct ThreadVars_ *next; struct ThreadVars_ *prev; } ThreadVars; diff --git a/src/tm-modules.h b/src/tm-modules.h index 214571f625..1711b786f1 100644 --- a/src/tm-modules.h +++ b/src/tm-modules.h @@ -21,6 +21,9 @@ typedef struct TmModule_ { TmEcode (*Func)(ThreadVars *, Packet *, void *, PacketQueue *); void (*RegisterTests)(void); + + uint8_t cap_flags; /**< Flags to indicate the capability requierment of + the given TmModule */ } TmModule; enum { diff --git a/src/tm-threads.c b/src/tm-threads.c index bbc8dadd12..f0cbe62b48 100644 --- a/src/tm-threads.c +++ b/src/tm-threads.c @@ -19,6 +19,7 @@ #include "util-debug.h" #include #include +#include "util-privs.h" #ifdef OS_FREEBSD #include @@ -131,6 +132,9 @@ void *TmThreadsSlot1NoIn(void *td) { /* Set the thread name */ SCSetThreadName(tv->name); + /* Drop the capabilities for this thread */ + SCDropCaps(tv); + if (tv->thread_setup_flags != 0) TmThreadSetupOptions(tv); @@ -198,6 +202,9 @@ void *TmThreadsSlot1NoOut(void *td) { /* Set the thread name */ SCSetThreadName(tv->name); + /* Drop the capabilities for this thread */ + SCDropCaps(tv); + if (tv->thread_setup_flags != 0) TmThreadSetupOptions(tv); @@ -258,6 +265,9 @@ void *TmThreadsSlot1NoInOut(void *td) { /* Set the thread name */ SCSetThreadName(tv->name); + /* Drop the capabilities for this thread */ + SCDropCaps(tv); + if (tv->thread_setup_flags != 0) TmThreadSetupOptions(tv); @@ -322,6 +332,9 @@ void *TmThreadsSlot1(void *td) { /* Set the thread name */ SCSetThreadName(tv->name); + /* Drop the capabilities for this thread */ + SCDropCaps(tv); + if (tv->thread_setup_flags != 0) TmThreadSetupOptions(tv); @@ -442,6 +455,9 @@ void *TmThreadsSlotVar(void *td) { /* Set the thread name */ SCSetThreadName(tv->name); + /* Drop the capabilities for this thread */ + SCDropCaps(tv); + if (tv->thread_setup_flags != 0) TmThreadSetupOptions(tv); @@ -571,6 +587,7 @@ void Tm1SlotSetFunc(ThreadVars *tv, TmModule *tm, void *data) { s1->s.SlotFunc = tm->Func; s1->s.SlotThreadExitPrintStats = tm->ThreadExitPrintStats; s1->s.SlotThreadDeinit = tm->ThreadDeinit; + tv->cap_flags |= tm->cap_flags; } void TmVarSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data) { @@ -586,6 +603,7 @@ void TmVarSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data) { slot->SlotFunc = tm->Func; slot->SlotThreadExitPrintStats = tm->ThreadExitPrintStats; slot->SlotThreadDeinit = tm->ThreadDeinit; + tv->cap_flags |= tm->cap_flags; if (s->s == NULL) { s->s = slot; diff --git a/src/util-error.c b/src/util-error.c index e71ad4664a..b78a58f57c 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -146,6 +146,11 @@ const char * SCErrorToString(SCError err) CASE_CODE (SC_ERR_PIDFILE_OPEN); CASE_CODE (SC_ERR_PIDFILE_WRITE); CASE_CODE (SC_ERR_PIDFILE_DAEMON); + CASE_CODE (SC_ERR_UID_FAILED); + CASE_CODE (SC_ERR_GID_FAILED); + CASE_CODE (SC_ERR_CHANGING_CAPS_FAILED); + CASE_CODE (SC_ERR_LIBCAP_NG_REQUIRED); + CASE_CODE (SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG); default: return "UNKNOWN_ERROR"; diff --git a/src/util-error.h b/src/util-error.h index 2c6e6a016f..8562630fff 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -162,6 +162,11 @@ typedef enum { SC_ERR_PIDFILE_OPEN, SC_ERR_PIDFILE_WRITE, SC_ERR_PIDFILE_DAEMON, + SC_ERR_UID_FAILED, + SC_ERR_GID_FAILED, + SC_ERR_CHANGING_CAPS_FAILED, + SC_ERR_LIBCAP_NG_REQUIRED, + SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG, } SCError; diff --git a/src/util-privs.c b/src/util-privs.c new file mode 100644 index 0000000000..eb2758cc49 --- /dev/null +++ b/src/util-privs.c @@ -0,0 +1,205 @@ +/* Copyright (c) 2010 Open Infomation Security Foundation */ + +/** + * \file + * \author Gurvinder Singh + * + * \brief File to drop the engine capabilities using libcap-ng by + * Steve Grubb + */ + +#include +#include +#include "util-debug.h" +#include "suricata-common.h" + +#ifdef HAVE_LIBCAP_NG + +#include +#include +#include "threadvars.h" +#include "util-cpu.h" +#include "util-privs.h" + +/** flag indicating if we'll be using caps */ +extern int sc_set_caps; + +/** + * \brief Drop all the previliges of the given thread + */ +void SCDropAllCaps() +{ + capng_clear(CAPNG_SELECT_BOTH); + if (capng_apply(CAPNG_SELECT_BOTH) < 0) { + SCLogError(SC_ERR_CHANGING_CAPS_FAILED, "failed in dropping the caps"); + exit(EXIT_FAILURE); + } +} + +/** + * \brief Drop the previliges of the main thread + */ +void SCDropMainThreadCaps(uint32_t userid, uint32_t groupid) +{ + if (sc_set_caps == FALSE) + return; + + capng_clear(CAPNG_SELECT_BOTH); + capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_NET_RAW, /* needed for pcap live mode */ +#ifdef NFQ + CAP_NET_ADMIN, /* needed for nfqueue inline mode */ +#endif + -1); + if (capng_change_id(userid, groupid, CAPNG_DROP_SUPP_GRP | + CAPNG_CLEAR_BOUNDING) < 0) + { + SCLogError(SC_ERR_CHANGING_CAPS_FAILED, "capng_change_id for main thread" + " failed"); + exit(EXIT_FAILURE); + } + + SCLogInfo("dropped the caps for main thread"); +} + +void SCDropCaps(ThreadVars *tv) { +#if 0 + capng_clear(CAPNG_SELECT_BOTH); + capng_apply(CAPNG_SELECT_BOTH); + if (tv->cap_flags & SC_CAP_IPC_LOCK) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_IPC_LOCK); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_IPC_LOCK has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_NET_ADMIN) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_ADMIN); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_NET_ADMIN has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_NET_BIND_SERVICE) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BIND_SERVICE); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_NET_BIND_SERVICE has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_NET_BROADCAST) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BROADCAST); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_NET_BROADCAST has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_NET_RAW) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_RAW); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_NET_RAW has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_SYS_ADMIN) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_ADMIN); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_SYS_ADMIN has been set", tv->name); + } + if (tv->cap_flags & SC_CAP_SYS_RAW_IO) { + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_RAWIO); + capng_apply(CAPNG_SELECT_CAPS); + SCLogDebug("For thread \"%s\" CAP_SYS_RAWIO has been set", tv->name); + } +#endif +} + +#endif /* HAVE_LIBCAP_NG */ + +/** + * \brief Function to get the user and group ID from the specified user name + * + * \param user_name pointer to the given user name + * \param uid pointer to the user id in which result will be stored + * \param gid pointer to the group id in which result will be stored + * + * \retval upon success it return 0 + */ +int SCGetUserID(char *user_name, char *group_name, uint32_t *uid, uint32_t *gid) +{ + uint32_t userid = 0; + uint32_t groupid = 0; + struct passwd *pw; + + /* Get the user ID */ + if (isdigit(user_name[0]) != 0) { + userid = atoi(user_name); + pw = getpwuid(userid); + if (pw == NULL) { + SCLogError(SC_ERR_UID_FAILED, "unable to get the user ID, " + "check if user exist!!"); + exit(EXIT_FAILURE); + } + } else { + pw = getpwnam(user_name); + if (pw == NULL) { + SCLogError(SC_ERR_UID_FAILED, "unable to get the user ID, " + "check if user exist!!"); + exit(EXIT_FAILURE); + } + userid = pw->pw_uid; + } + + /* Get the group ID */ + if (group_name != NULL) { + struct group *gp; + + if (isdigit(group_name[0]) != 0) { + groupid = atoi(group_name); + } else { + gp = getgrnam(group_name); + if (gp == NULL) { + SCLogError(SC_ERR_GID_FAILED, "unable to get the group" + " ID, check if group exist!!"); + exit(EXIT_FAILURE); + } + groupid = gp->gr_gid; + } + } else { + groupid = pw->pw_gid; + } + + /* close the group database */ + endgrent(); + /* close the user database */ + endpwent(); + + *uid = userid; + *gid = groupid; + + return 0; +} + +/** + * \brief Function to get the group ID from the specified group name + * + * \param group_name pointer to the given group name + * \param gid pointer to the group id in which result will be stored + * + * \retval upon success it return 0 + */ +int SCGetGroupID(char *group_name, uint32_t *gid) +{ + uint32_t grpid = 0; + struct group *gp; + + /* Get the group ID */ + if (isdigit(group_name[0]) != 0) { + grpid = atoi(group_name); + } else { + gp = getgrnam(group_name); + if (gp == NULL) { + SCLogError(SC_ERR_GID_FAILED, "unable to get the group ID," + " check if group exist!!"); + exit(EXIT_FAILURE); + } + grpid = gp->gr_gid; + } + + /* close the group database */ + endgrent(); + + *gid = grpid; + + return 0; +} diff --git a/src/util-privs.h b/src/util-privs.h new file mode 100644 index 0000000000..1f066a46db --- /dev/null +++ b/src/util-privs.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2010 Open Infomation Security Foundation */ + +/** + * \author Gurvinder Singh + * + */ + +#ifndef _UTIL_PRIVS_H +#define _UTIL_PRIVS_H + +#define SC_CAP_NONE 0x01 +#define SC_CAP_SYS_ADMIN 0x02 +#define SC_CAP_SYS_RAW_IO 0x04 +#define SC_CAP_IPC_LOCK 0x08 +#define SC_CAP_NET_ADMIN 0x10 +#define SC_CAP_NET_RAW 0x20 +#define SC_CAP_NET_BIND_SERVICE 0x40 +#define SC_CAP_NET_BROADCAST 0x80 + +#ifndef HAVE_LIBCAP_NG +#define SCDropCaps(...) +#define SCDropMainThreadCaps(...) +#else +#include "threadvars.h" +#include "util-debug.h" +#include + +/**Drop the previliges of the given thread tv, based on the thread cap_flags + * which implies the capability requirement of the given thread. Initially all + * caps are dropped and later, the required caps are set for the given thread + */ +void SCDropCaps(ThreadVars *tv); +/* +#define SCDropCaps(tv) ({ \ + capng_clear(CAPNG_SELECT_BOTH); \ + capng_apply(CAPNG_SELECT_BOTH); \ + if (tv->cap_flags & SC_CAP_IPC_LOCK) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_IPC_LOCK); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_IPC_LOCK has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_NET_ADMIN) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_ADMIN); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_NET_ADMIN has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_NET_BIND_SERVICE) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BIND_SERVICE); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_NET_BIND_SERVICE has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_NET_BROADCAST) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_BROADCAST); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_NET_BROADCAST has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_NET_RAW) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_NET_RAW); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_NET_RAW has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_SYS_ADMIN) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_ADMIN); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_SYS_ADMIN has been set", tv->name); \ + } \ + if (tv->cap_flags & SC_CAP_SYS_RAW_IO) { \ + capng_update(CAPNG_ADD, (capng_type_t) (CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SYS_RAWIO); \ + capng_apply(CAPNG_SELECT_CAPS); \ + SCLogDebug("For thread \"%s\" CAP_SYS_RAWIO has been set", tv->name); \ + } \ +}) +*/ +void SCDropMainThreadCaps(uint32_t , uint32_t ); + +#endif /* HAVE_LIBCAP_NG */ + +int SCGetUserID(char *, char *, uint32_t *, uint32_t *); +int SCGetGroupID(char *, uint32_t *); + +#endif /* _UTIL_PRIVS_H */ +