diff --git a/configure.ac b/configure.ac index 9f73a708d0..958c26245e 100644 --- a/configure.ac +++ b/configure.ac @@ -377,6 +377,28 @@ AC_ARG_ENABLE(fmhash, [enable_fmhash=yes] ) +AC_ARG_ENABLE(libcap-ng, + [AS_HELP_STRING([--enable-libcap-ng],[Enable dropping capabilities to only the necessary set @<:@default=no@:>@])], + [case "${enableval}" in + yes) enable_libcapng="yes" ;; + no) enable_libcapng="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable_libcapng) ;; + esac], + [enable_libcapng=no] +) + +if test "$enable_libcapng" = "yes"; then + PKG_CHECK_MODULES( + [LIBCAPNG], + [libcap-ng >= 0.8.2], + [AC_DEFINE([ENABLE_LIBCAPNG], [1], [Indicator that libcap-ng is present])], + [AC_MSG_ERROR(libcap-ng is not present.)] + ) + CFLAGS="$CFLAGS $LIBCAPNG_CFLAGS" + LIBS="$LIBS $LIBCAPNG_LIBS" +fi + + AC_ARG_ENABLE(fmhash-xxhash, [AS_HELP_STRING([--enable-fmhash-xxhash],[Enable xxhash in fmhash support @<:@default=no@:>@])], [case "${enableval}" in @@ -2820,6 +2842,8 @@ echo " liblogging-stdlog support enabled: $enable_liblogging_stdlog" echo " libsystemd enabled: $enable_libsystemd" echo " kafka static linking enabled: $enable_kafka_static" echo " atomic operations enabled: $enable_atomic_operations" +echo " libcap-ng support enabled: $enable_libcapng" + echo echo "---{ input plugins }---" if test "$unamestr" != "AIX"; then diff --git a/runtime/debug.c b/runtime/debug.c index a655bc2e4e..6e6c9fd38f 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -250,7 +250,7 @@ r_dbgoprint( const char *srcname, obj_t *pObj, const char *fmt, ...) if(!(Debug && debugging_on)) return; - + if(!checkDbgFile(srcname)) { return; } @@ -435,7 +435,7 @@ rsRetVal dbgClassInit(void) { rsRetVal iRet; /* do not use DEFiRet, as this makes calls into the debug system! */ - + (void) pthread_key_create(&keyThrdName, dbgThrdNameDestruct); /* while we try not to use any of the real rsyslog code (to avoid infinite loops), we diff --git a/runtime/modules.c b/runtime/modules.c index 810b2e9b52..b39bd9f066 100644 --- a/runtime/modules.c +++ b/runtime/modules.c @@ -595,7 +595,7 @@ doModInit(pModInit_t modInit, uchar *name, void *pModHdlr, modInfo_t **pNewModul CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getKeepType", &modGetKeepType)); CHKiRet((*modGetKeepType)(&pNew->eKeepType)); dbgprintf("module %s of type %d being loaded (keepType=%d).\n", name, pNew->eType, pNew->eKeepType); - + /* OK, we know we can successfully work with the module. So we now fill the * rest of the data elements. First we load the interfaces common to all * module types. @@ -1242,7 +1242,7 @@ Load(uchar *const pModName, const sbool bConfLoad, struct nvlst *const lst) } iLoadCnt++; - + } while(pModHdlr == NULL && *pModName != '/' && pModDirNext); if(load_err_msg != NULL) { @@ -1323,7 +1323,7 @@ modulesProcessCnf(struct cnfobj *o) cnfModName = (uchar*)es_str2cstr(pvals[typeIdx].val.d.estr, NULL); iRet = Load(cnfModName, 1, o->nvlst); - + finalize_it: free(cnfModName); cnfparamvalsDestruct(pvals, &pblk); diff --git a/runtime/rsconf.c b/runtime/rsconf.c index 4620ff8d13..de2a21b406 100644 --- a/runtime/rsconf.c +++ b/runtime/rsconf.c @@ -34,6 +34,10 @@ #include #include #include +#ifdef ENABLE_LIBCAPNG + #include +#endif + #include "rsyslog.h" #include "obj.h" @@ -656,6 +660,7 @@ rsRetVal doDropPrivGid(rsconf_t *cnf) uchar szBuf[1024]; DEFiRet; +#ifndef ENABLE_LIBCAPNG if(!cnf->globals.gidDropPrivKeepSupplemental) { res = setgroups(0, NULL); /* remove all supplemental group IDs */ if(res) { @@ -668,9 +673,19 @@ rsRetVal doDropPrivGid(rsconf_t *cnf) res = setgid(cnf->globals.gidDropPriv); if(res) { LogError(errno, RS_RET_ERR_DROP_PRIV, - "could not set requested group id %d", cnf->globals.gidDropPriv); + "could not set requested group id %d via setgid()", cnf->globals.gidDropPriv); ABORT_FINALIZE(RS_RET_ERR_DROP_PRIV); } +#else + int capng_flags = cnf->globals.gidDropPrivKeepSupplemental ? CAPNG_NO_FLAG : CAPNG_DROP_SUPP_GRP; + res = capng_change_id(-1, cnf->globals.gidDropPriv, capng_flags); + if (res) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not set requested group id %d via capng_change_id()", cnf->globals.gidDropPriv); + ABORT_FINALIZE(RS_RET_LIBCAPNG_ERR); + } +#endif + DBGPRINTF("setgid(%d): %d\n", cnf->globals.gidDropPriv, res); snprintf((char*)szBuf, sizeof(szBuf), "rsyslogd's groupid changed to %d", cnf->globals.gidDropPriv); @@ -705,12 +720,18 @@ static void doDropPrivUid(rsconf_t *cnf) cnf->globals.uidDropPriv); } +#ifndef ENABLE_LIBCAPNG res = setuid(cnf->globals.uidDropPriv); +#else + int capng_flags = cnf->globals.gidDropPrivKeepSupplemental ? CAPNG_NO_FLAG : CAPNG_DROP_SUPP_GRP; + res = capng_change_id(cnf->globals.uidDropPriv, -1, capng_flags); +#endif if(res) { /* if we can not set the userid, this is fatal, so let's unconditionally abort */ perror("could not set requested userid"); exit(1); } + DBGPRINTF("setuid(%d): %d\n", cnf->globals.uidDropPriv, res); snprintf((char*)szBuf, sizeof(szBuf), "rsyslogd's userid changed to %d", cnf->globals.uidDropPriv); logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0); @@ -739,6 +760,29 @@ dropPrivileges(rsconf_t *cnf) cnf->globals.uidDropPriv); } +#ifdef ENABLE_LIBCAPNG + /* In case privileges were dropped, do not allow bypassing + * file read, write, and execute permission checks + */ + if (cnf->globals.gidDropPriv != 0 || cnf->globals.uidDropPriv != 0) { + int capng_rc; + if ((capng_rc = capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_DAC_OVERRIDE)) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not update the internal posix capabilities settings " + "based on the options passed to it, capng_update=%d\n", capng_rc); + exit(-1); + } + + if ((capng_rc = capng_apply(CAPNG_SELECT_BOTH)) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not transfer the specified internal posix capabilities " + "settings to the kernel, capng_apply=%d\n", capng_rc); + exit(-1); + } + } + +#endif + finalize_it: RETiRet; } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 908e5e7b73..01616d8f7d 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -604,6 +604,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_REDIS_ERROR = -2452, /**< redis-specific error. See message foe details. */ RS_RET_REDIS_AUTH_FAILED = -2453, /**< redis authentication failure */ RS_RET_FAUP_INIT_OPTIONS_FAILED = -2454, /**< could not initialize faup options */ + RS_RET_LIBCAPNG_ERR = -2455, /**< error during dropping the capabilities */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/tools/rsyslogd.c b/tools/rsyslogd.c index 31b91a1bd1..77d814b482 100644 --- a/tools/rsyslogd.c +++ b/tools/rsyslogd.c @@ -37,6 +37,9 @@ #ifdef HAVE_LIBSYSTEMD # include #endif +#ifdef ENABLE_LIBCAPNG + #include +#endif #include "rsyslog.h" #include "wti.h" @@ -2167,6 +2170,46 @@ main(int argc, char **argv) fjson_global_do_case_sensitive_comparison(0); dbgClassInit(); + +#ifdef ENABLE_LIBCAPNG + /* + * Drop capabilities to the necessary set + */ + int capng_rc; + capng_clear(CAPNG_SELECT_BOTH); + + if ((capng_rc = capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_BLOCK_SUSPEND, + CAP_CHOWN, + CAP_IPC_LOCK, + CAP_LEASE, + CAP_NET_ADMIN, + CAP_NET_BIND_SERVICE, + CAP_DAC_OVERRIDE, + CAP_SETGID, + CAP_SETUID, + CAP_SETPCAP, + CAP_SYS_ADMIN, + CAP_SYS_CHROOT, + CAP_SYS_RESOURCE, + CAP_SYSLOG, + -1 + )) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not update the internal posix capabilities settings " + "based on the options passed to it, capng_updatev=%d\n", capng_rc); + exit(-1); + } + + if ((capng_rc = capng_apply(CAPNG_SELECT_BOTH)) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not transfer the specified internal posix capabilities " + "settings to the kernel, capng_apply=%d\n", capng_rc); + exit(-1); + } + DBGPRINTF("Capabilities were dropped successfully\n"); +#endif + initAll(argc, argv); #ifdef HAVE_LIBSYSTEMD sd_notify(0, "READY=1");