diff --git a/Makefile.am b/Makefile.am index 4bac10d86d4..4741765e618 100644 --- a/Makefile.am +++ b/Makefile.am @@ -103,12 +103,15 @@ condconfigexists = ConditionPathExists=\|/etc/sssd/sssd.conf\nConditionDirectory # originally then it's addition to CapabilityBoundingSet doesn't matter. if SSSD_NON_ROOT_USER additional_caps = CAP_DAC_OVERRIDE +supplementary_groups = \# If service to be configured to be run under "root", uncomment "SupplementaryGroups" and make sure\n\# that 'sssd' group is resolvable before hitting 'sss' in nsswitch.conf databases.\n\#SupplementaryGroups=$(SSSD_USER) +else +supplementary_groups = \#\n\# Warning: SSSD package was built without support of running as non-privileged user endif else ifp_dbus_exec_comment = \# "sss_signal" is used to force SSSD monitor to trigger "sssd_ifp" reconnection to dbus ifp_dbus_exec_cmd = $(sssdlibexecdir)/sss_signal ifp_systemdservice = -endif +endif # HAVE_SYSTEMD_UNIT secdbpath = @secdbpath@ @@ -5260,7 +5263,8 @@ edit_cmd = $(SED) \ -e 's|@prefix[@]|$(prefix)|g' \ -e 's|@SSSD_USER[@]|$(SSSD_USER)|g' \ -e 's|@condconfigexists[@]|$(condconfigexists)|g' \ - -e 's|@additional_caps[@]|$(additional_caps)|g' + -e 's|@additional_caps[@]|$(additional_caps)|g' \ + -e 's|@supplementary_groups[@]|$(supplementary_groups)|g' replace_script = \ @rm -f $@ $@.tmp; \ diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c index 2eebddce62d..08481596833 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -1977,6 +1977,56 @@ static void monitor_restart_service(struct mt_svc *svc) } } +#ifdef SSSD_NON_ROOT_USER +/* returns: -1 on error, 0 - group not set, 1 - group set */ +static int check_supplementary_group(gid_t gid) +{ + int ret; + int size; + gid_t *supp_gids = NULL; + + if (getegid() == gid) { + return 1; + } + + size = getgroups(0, NULL); + if (size == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "Getgroups failed! (%d, %s)\n", + ret, sss_strerror(ret)); + return -1; + } + + if (size > 0) { + supp_gids = talloc_zero_array(NULL, gid_t, size); + if (supp_gids == NULL) { + DEBUG(SSSDBG_TRACE_FUNC, "Allocation failed!\n"); + return -1; + } + + size = getgroups(size, supp_gids); + if (size == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "Getgroups failed! (%d, %s)\n", + ret, sss_strerror(ret)); + talloc_free(supp_gids); + return -1; + } + + for (int i = 0; i < size; i++) { + if (supp_gids[i] == gid) { + talloc_free(supp_gids); + return 1; + } + } + + talloc_free(supp_gids); + } + + return 0; +} +#endif /* SSSD_NON_ROOT_USER */ + int main(int argc, const char *argv[]) { int opt; @@ -2021,6 +2071,51 @@ int main(int argc, const char *argv[]) POPT_TABLEEND }; + +#ifdef SSSD_NON_ROOT_USER + /* In case SSSD is built with non-root user support, + * a number of files are sssd:sssd owned. + * Make sure all processes are added to sssd supplementary + * group to avoid the need for CAP_DAC_OVERRIDE / CAP_CHOWN. + * Don't bother to check if config says to change uid/gid later. + */ + gid_t sssd_gid = 0; + if ((getuid() == 0) || (geteuid() == 0)) { + sss_sssd_user_uid_and_gid(NULL, &sssd_gid); + ret = check_supplementary_group(sssd_gid); + if (ret == -1) { + DEBUG(SSSDBG_FATAL_FAILURE, "Can't check supplementary groups\n"); + return 1; + } + /* Expected outcome is 'ret == 1' since supplementary group should be set + by systemd service description. */ + if (ret == 0) { + /* Probably non-systemd based system or service file was edited, + let's try to set group manually. */ + sss_log(SSS_LOG_WARNING, + "SSSD is built with support of 'run under non-root user' " + "feature but started under 'root'. Trying to add process " + "to SSSD supplementary group."); + ret = setgroups(1, &sssd_gid); + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to add process to the sssd supplementary group.\n"); + sss_log(SSS_LOG_CRIT, + "Failed to add process to the sssd supplementary group. " + "Either run under 'sssd' or make sure that run-under-root " + "main SSSD process has CAP_SETGID."); + return 1; + } + } + + /* And drop CAP_SET_GID capability afterwards. */ + /* TODO + * sss_drop_cap(CAP_SETGID); + */ + } +#endif /* SSSD_NON_ROOT_USER */ + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ debug_level = SSSDBG_INVALID; diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h index e2f5a3e5a58..71e33ff1b80 100644 --- a/src/responder/nss/nss_private.h +++ b/src/responder/nss/nss_private.h @@ -93,8 +93,6 @@ struct sss_nss_ctx { struct sss_mc_ctx *grp_mc_ctx; struct sss_mc_ctx *initgr_mc_ctx; struct sss_mc_ctx *sid_mc_ctx; - uid_t mc_uid; - gid_t mc_gid; }; struct sss_cmd_table *get_sss_nss_cmds(void); diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c index 4673a64ca47..cd9ae01e1fc 100644 --- a/src/responder/nss/nsssrv.c +++ b/src/responder/nss/nsssrv.c @@ -92,7 +92,7 @@ sss_nss_clear_memcache(TALLOC_CTX *mem_ctx, } DEBUG(SSSDBG_TRACE_FUNC, "Clearing memory caches.\n"); - ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid, + ret = sss_mmap_cache_reinit(nctx, -1, -1, -1, /* keep current size */ (time_t) memcache_timeout, &nctx->pwd_mc_ctx); @@ -102,7 +102,7 @@ sss_nss_clear_memcache(TALLOC_CTX *mem_ctx, goto done; } - ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid, + ret = sss_mmap_cache_reinit(nctx, -1, -1, -1, /* keep current size */ (time_t) memcache_timeout, &nctx->grp_mc_ctx); @@ -112,7 +112,7 @@ sss_nss_clear_memcache(TALLOC_CTX *mem_ctx, goto done; } - ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid, + ret = sss_mmap_cache_reinit(nctx, -1, -1, -1, /* keep current size */ (time_t)memcache_timeout, &nctx->initgr_mc_ctx); @@ -287,6 +287,10 @@ static int setup_memcaches(struct sss_nss_ctx *nctx) int mc_size_group; int mc_size_initgroups; int mc_size_sid; + uid_t uid; + gid_t gid; + + sss_sssd_user_uid_and_gid(&uid, &gid); /* Remove the CLEAR_MC_FLAG file if exists. */ ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG); @@ -361,7 +365,7 @@ static int setup_memcaches(struct sss_nss_ctx *nctx) /* Initialize the fast in-memory caches if they were not disabled */ ret = sss_mmap_cache_init(nctx, "passwd", - nctx->mc_uid, nctx->mc_gid, + uid, gid, SSS_MC_PASSWD, mc_size_passwd * SSS_MC_CACHE_SLOTS_PER_MB, (time_t)memcache_timeout, @@ -373,7 +377,7 @@ static int setup_memcaches(struct sss_nss_ctx *nctx) } ret = sss_mmap_cache_init(nctx, "group", - nctx->mc_uid, nctx->mc_gid, + uid, gid, SSS_MC_GROUP, mc_size_group * SSS_MC_CACHE_SLOTS_PER_MB, (time_t)memcache_timeout, @@ -385,7 +389,7 @@ static int setup_memcaches(struct sss_nss_ctx *nctx) } ret = sss_mmap_cache_init(nctx, "initgroups", - nctx->mc_uid, nctx->mc_gid, + uid, gid, SSS_MC_INITGROUPS, mc_size_initgroups * SSS_MC_CACHE_SLOTS_PER_MB, (time_t)memcache_timeout, @@ -397,7 +401,7 @@ static int setup_memcaches(struct sss_nss_ctx *nctx) } ret = sss_mmap_cache_init(nctx, "sid", - nctx->mc_uid, nctx->mc_gid, + uid, gid, SSS_MC_SID, mc_size_sid * SSS_MC_CACHE_SLOTS_PER_MB, (time_t)memcache_timeout, @@ -441,79 +445,6 @@ sss_nss_register_service_iface(struct sss_nss_ctx *nss_ctx, return ret; } -static int sssd_supplementary_group(struct sss_nss_ctx *nss_ctx) -{ - errno_t ret; - int size; - gid_t *supp_gids = NULL; - - /* - * We explicitly read the IDs of the SSSD user even though the server - * receives --uid and --gid by parameters to account for the case where - * the SSSD is compiled --with-sssd-user=sssd but the default of the - * user option is root (this is what RHEL does) - */ - ret = sss_user_by_name_or_uid(SSSD_USER, - &nss_ctx->mc_uid, - &nss_ctx->mc_gid); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER); - return ret; - } - - if (getgid() == nss_ctx->mc_gid) { - DEBUG(SSSDBG_TRACE_FUNC, "Already running as the sssd group\n"); - return EOK; - } - - size = getgroups(0, NULL); - if (size == -1) { - ret = errno; - DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n", - ret, sss_strerror(ret)); - return ret; - } - - if (size > 0) { - supp_gids = talloc_zero_array(NULL, gid_t, size); - if (supp_gids == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n"); - ret = ENOMEM; - goto done; - } - - size = getgroups(size, supp_gids); - if (size == -1) { - ret = errno; - DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n", - ret, sss_strerror(ret)); - goto done; - } - - for (int i = 0; i < size; i++) { - if (supp_gids[i] == nss_ctx->mc_gid) { - DEBUG(SSSDBG_TRACE_FUNC, - "Already assigned to the SSSD supplementary group\n"); - ret = EOK; - goto done; - } - } - } - - ret = setgroups(1, &nss_ctx->mc_gid); - if (ret != EOK) { - ret = errno; - DEBUG(SSSDBG_OP_FAILURE, - "Cannot setgroups [%d]: %s\n", ret, sss_strerror(ret)); - goto done; - } - - ret = EOK; -done: - talloc_free(supp_gids); - return ret; -} - int sss_nss_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) @@ -613,19 +544,6 @@ int sss_nss_process_init(TALLOC_CTX *mem_ctx, goto fail; } - /* - * Adding the NSS process to the SSSD supplementary group avoids - * dac_override AVC messages from SELinux in case sssd_nss runs - * as root and tries to write to memcache owned by sssd:sssd - */ - ret = sssd_supplementary_group(nctx); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Cannot add process to the sssd supplementary group [%d]: %s\n", - ret, sss_strerror(ret)); - goto fail; - } - ret = setup_memcaches(nctx); if (ret != EOK) { goto fail; diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c index 90d12299a40..5563a709ff0 100644 --- a/src/responder/nss/nsssrv_mmap_cache.c +++ b/src/responder/nss/nsssrv_mmap_cache.c @@ -1271,6 +1271,7 @@ static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx) return ret; } +#ifdef SSSD_NON_ROOT_USER /* Make sure that the memory cache files are chowned to sssd.sssd even * if the nss responder runs as root. This is because the specfile * has the ownership recorded as sssd.sssd @@ -1284,6 +1285,7 @@ static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx) return ret; } } +#endif /* SSSD_NON_ROOT_USER */ ret = sss_br_lock_file(mc_ctx->fd, 0, 1, retries, t); if (ret != EOK) { diff --git a/src/sysv/systemd/sssd-autofs.service.in b/src/sysv/systemd/sssd-autofs.service.in index e917ed78e27..ce597602b24 100644 --- a/src/sysv/systemd/sssd-autofs.service.in +++ b/src/sysv/systemd/sssd-autofs.service.in @@ -16,3 +16,4 @@ ExecStart=@libexecdir@/sssd/sssd_autofs ${DEBUG_LOGGER} --socket-activated Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-ifp.service.in b/src/sysv/systemd/sssd-ifp.service.in index 4aed90bb96b..2e307f3b001 100644 --- a/src/sysv/systemd/sssd-ifp.service.in +++ b/src/sysv/systemd/sssd-ifp.service.in @@ -18,3 +18,4 @@ CapabilityBoundingSet= @additional_caps@ Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in index a8af4eadcff..6c86668dadb 100644 --- a/src/sysv/systemd/sssd-kcm.service.in +++ b/src/sysv/systemd/sssd-kcm.service.in @@ -15,3 +15,4 @@ ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 ${DEBUG_LOGGER} # ('User=' and 'Group=' defaults to 'root' for system services) # 'CapabilityBoundingSet' is used to limit privileges set: CapabilityBoundingSet= @additional_caps@ CAP_SETGID CAP_SETUID +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-nss.service.in b/src/sysv/systemd/sssd-nss.service.in index 6108959b02e..4383fec9fc9 100644 --- a/src/sysv/systemd/sssd-nss.service.in +++ b/src/sysv/systemd/sssd-nss.service.in @@ -16,3 +16,4 @@ Restart=on-failure # Currently SSSD NSS service ('sssd_nss') can't be started under 'sssd' user # via systemd due to NSS loop when systemd resolves getgrouplist(sssd). # Hence 'User=' and 'Group=' aren't set (defaults to root). +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-pac.service.in b/src/sysv/systemd/sssd-pac.service.in index db435897abb..b1bc2907303 100644 --- a/src/sysv/systemd/sssd-pac.service.in +++ b/src/sysv/systemd/sssd-pac.service.in @@ -16,3 +16,4 @@ ExecStart=@libexecdir@/sssd/sssd_pac ${DEBUG_LOGGER} --socket-activated Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-pam.service.in b/src/sysv/systemd/sssd-pam.service.in index 3b95a7d085e..b6e30f14b3e 100644 --- a/src/sysv/systemd/sssd-pam.service.in +++ b/src/sysv/systemd/sssd-pam.service.in @@ -16,3 +16,4 @@ ExecStart=@libexecdir@/sssd/sssd_pam ${DEBUG_LOGGER} --socket-activated Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-ssh.service.in b/src/sysv/systemd/sssd-ssh.service.in index 4a0e298468c..2a522a6b5cf 100644 --- a/src/sysv/systemd/sssd-ssh.service.in +++ b/src/sysv/systemd/sssd-ssh.service.in @@ -16,3 +16,4 @@ ExecStart=@libexecdir@/sssd/sssd_ssh ${DEBUG_LOGGER} --socket-activated Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd-sudo.service.in b/src/sysv/systemd/sssd-sudo.service.in index d99f0357225..32e8d87b8d2 100644 --- a/src/sysv/systemd/sssd-sudo.service.in +++ b/src/sysv/systemd/sssd-sudo.service.in @@ -16,3 +16,4 @@ ExecStart=@libexecdir@/sssd/sssd_sudo --socket-activated Restart=on-failure User=@SSSD_USER@ Group=@SSSD_USER@ +@supplementary_groups@ diff --git a/src/sysv/systemd/sssd.service.in b/src/sysv/systemd/sssd.service.in index 4d9596a8374..b988d43b6d9 100644 --- a/src/sysv/systemd/sssd.service.in +++ b/src/sysv/systemd/sssd.service.in @@ -19,6 +19,7 @@ PIDFile=@pidpath@/sssd.pid # 'CapabilityBoundingSet' is used to limit privileges set: CapabilityBoundingSet= @additional_caps@ CAP_CHOWN CAP_KILL CAP_SETGID CAP_SETUID Restart=on-abnormal +@supplementary_groups@ [Install] WantedBy=multi-user.target diff --git a/src/util/usertools.c b/src/util/usertools.c index 8084760a03e..d3150098bd5 100644 --- a/src/util/usertools.c +++ b/src/util/usertools.c @@ -832,16 +832,39 @@ int sss_output_fqname(TALLOC_CTX *mem_ctx, void sss_sssd_user_uid_and_gid(uid_t *_uid, gid_t *_gid) { - uid_t sssd_uid; - gid_t sssd_gid; + uid_t sssd_uid = 0; + gid_t sssd_gid = 0; +#ifdef SSSD_NON_ROOT_USER errno_t ret; + char *envval; + bool no_loops; + envval = getenv("_SSS_LOOPS"); + no_loops = (envval && strcmp(envval, "NO") == 0); + + if(!no_loops) { + ret = setenv("_SSS_LOOPS", "NO", 1); + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set _SSS_LOOPS\n"); + } + } ret = sss_user_by_name_or_uid(SSSD_USER, &sssd_uid, &sssd_gid); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "failed to get sssd user (" SSSD_USER ") uid/gid, using root\n"); sssd_uid = 0; sssd_gid = 0; } + if (!no_loops) { + if (envval) { + ret = setenv("_SSS_LOOPS", envval, 1); + } else { + ret = unsetenv("_SSS_LOOPS"); + } + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to restore _SSS_LOOPS\n"); + } + } +#endif /* SSSD_NON_ROOT_USER */ if (_uid != NULL) { *_uid = sssd_uid;