From 830ded27453015080a54d6ba85fd4999ee7e9af1 Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Thu, 25 Sep 2014 14:52:31 +0100 Subject: PAM: new options pam_trusted_users & pam_public_domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pam_public_domains option is a list of numerical UIDs or user names that are trusted. pam_public_domains option is a list of domains accessible even for untrusted users. Based on: https://fedorahosted.org/sssd/wiki/DesignDocs/RestrictDomainsInPAM Reviewed-by: Lukáš Slebodník --- src/confdb/confdb.h | 2 + src/config/SSSDConfig/__init__.py.in | 2 + src/config/etc/sssd.api.conf | 2 + src/man/sssd.conf.5.xml | 50 +++++++++++++++++++ src/responder/pam/pamsrv.c | 94 ++++++++++++++++++++++++++++++++++++ src/responder/pam/pamsrv.h | 7 +++ src/responder/pam/pamsrv_cmd.c | 80 ++++++++++++++++++++++++++++-- src/util/domain_info_utils.c | 60 +++++++++++++++++++++++ src/util/util.h | 5 ++ 9 files changed, 299 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 1697c69f0..720b7dc1c 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -111,6 +111,8 @@ #define CONFDB_PAM_VERBOSITY "pam_verbosity" #define CONFDB_PAM_ID_TIMEOUT "pam_id_timeout" #define CONFDB_PAM_PWD_EXPIRATION_WARNING "pam_pwd_expiration_warning" +#define CONFDB_PAM_TRUSTED_USERS "pam_trusted_users" +#define CONFDB_PAM_PUBLIC_DOMAINS "pam_public_domains" /* SUDO */ #define CONFDB_SUDO_CONF_ENTRY "config/sudo" diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index 9a49b91b9..ee48094d0 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -82,6 +82,8 @@ option_strings = { 'pam_verbosity' : _('What kind of messages are displayed to the user during authentication'), 'pam_id_timeout' : _('How many seconds to keep identity information cached for PAM requests'), 'pam_pwd_expiration_warning' : _('How many days before password expiration a warning should be displayed'), + 'pam_trusted_users' : _('List of trusted uids or user\'s name'), + 'pam_public_domains' : _('List of domains accessible even for untrusted users.'), # [sudo] 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index 52629ded4..c47ce348c 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -55,6 +55,8 @@ pam_verbosity = int, None, false pam_id_timeout = int, None, false pam_pwd_expiration_warning = int, None, false get_domains_timeout = int, None, false +pam_trusted_users = str, None, false +pam_public_domains = str, None, false [sudo] # sudo service diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index ad091e46e..d57341661 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -836,6 +836,56 @@ fallback_homedir = /home/%u + + pam_trusted_users (string) + + + Specifies the comma-separated list of UID values or + user names that are allowed to access the PAM + responder. User names are resolved to UIDs at + startup. + + + Default: all (All users are allowed to access + the PAM responder) + + + Please note that UID 0 is always allowed to access + the PAM responder even in case it is not in the + pam_trusted_users list. + + + Also please note that if there is a user name in + pam_trusted_users list which fails to be resolved + it will cause that SSSD will not be started. + + + + + pam_public_domains (string) + + + Specifies the comma-separated list of domain names + that are accessible even to untrusted users. + + + Two special values for pam_public_domains option + are defined: + + + all (Untrusted users are allowed to access + all domains in PAM responder.) + + + none (Untrusted users are not allowed to access + any domains PAM in responder.) + + + Default: none + + + + diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c index c0841d2bd..428b252ac 100644 --- a/src/responder/pam/pamsrv.c +++ b/src/responder/pam/pamsrv.c @@ -46,6 +46,10 @@ #include "responder/common/responder_sbus.h" #define DEFAULT_PAM_FD_LIMIT 8192 +#define ALL_UIDS_ALLOWED "all" +#define ALL_DOMAIMS_ARE_PUBLIC "all" +#define NO_DOMAIMS_ARE_PUBLIC "none" +#define DEFAULT_ALLOWED_UIDS ALL_UIDS_ALLOWED struct mon_cli_iface monitor_pam_methods = { { &mon_cli_iface_meta, 0 }, @@ -99,6 +103,82 @@ static void pam_dp_reconnect_init(struct sbus_connection *conn, int status, void /* pam_shutdown(rctx); */ } +static errno_t get_trusted_uids(struct pam_ctx *pctx) +{ + char *uid_str; + errno_t ret; + + ret = confdb_get_string(pctx->rctx->cdb, pctx->rctx, + CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_TRUSTED_USERS, + DEFAULT_ALLOWED_UIDS, &uid_str); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n"); + goto done; + } + + if (strcmp(uid_str, ALL_UIDS_ALLOWED) == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "All UIDs are allowed.\n"); + pctx->trusted_uids_count = 0; + } else { + ret = csv_string_to_uid_array(pctx->rctx, uid_str, true, + &pctx->trusted_uids_count, + &pctx->trusted_uids); + } + + talloc_free(uid_str); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set allowed UIDs.\n"); + goto done; + } + +done: + return ret; +} + +static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx) +{ + char *domains_str = NULL; + errno_t ret; + + ret = confdb_get_string(pctx->rctx->cdb, pctx->rctx, + CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_PUBLIC_DOMAINS, + NO_DOMAIMS_ARE_PUBLIC, &domains_str); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n"); + goto done; + } + + if (strcmp(domains_str, ALL_DOMAIMS_ARE_PUBLIC) == 0) { /* all */ + /* copy all domains */ + ret = get_dom_names(mem_ctx, + pctx->rctx->domains, + &pctx->public_domains, + &pctx->public_domains_count); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "get_dom_names failed.\n"); + goto done; + } + } else if (strcmp(domains_str, NO_DOMAIMS_ARE_PUBLIC) == 0) { /* none */ + pctx->public_domains = NULL; + pctx->public_domains_count = 0; + } else { + ret = split_on_separator(mem_ctx, domains_str, ',', true, false, + &pctx->public_domains, + &pctx->public_domains_count); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d][%s].\n", + ret, strerror(ret)); + goto done; + } + } + + ret = EOK; + +done: + talloc_free(domains_str); + return ret; +} + static int pam_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) @@ -136,6 +216,20 @@ static int pam_process_init(TALLOC_CTX *mem_ctx, pctx->rctx = rctx; pctx->rctx->pvt_ctx = pctx; + ret = get_trusted_uids(pctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "get_trusted_uids failed: %d:[%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = get_public_domains(pctx, pctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "get_public_domains failed: %d:[%s].\n", + ret, sss_strerror(ret)); + goto done; + } + /* Enable automatic reconnection to the Data Provider */ /* FIXME: "retries" is too generic, either get it from a global config diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index 1e37a77a1..f92e7f7db 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -37,6 +37,13 @@ struct pam_ctx { int neg_timeout; time_t id_timeout; hash_table_t *id_table; + size_t trusted_uids_count; + uid_t *trusted_uids; + bool is_uid_trusted; + + /* List of domains that are accessible even for untrusted users. */ + char **public_domains; + int public_domains_count; }; struct pam_auth_dp_req { diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 561bd3d96..eb6953a74 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -762,6 +762,45 @@ static int pam_auth_req_destructor(struct pam_auth_req *preq) return 0; } +static bool is_uid_trusted(uint32_t uid, + size_t trusted_uids_count, + uid_t *trusted_uids) +{ + size_t i; + + /* root is always trusted */ + if (uid == 0) { + return true; + } + + /* All uids are allowed */ + if (trusted_uids_count == 0) { + return true; + } + + for(i = 0; i < trusted_uids_count; i++) { + if (trusted_uids[i] == uid) { + return true; + } + } + + return false; +} + +static bool is_domain_public(char *name, + char **public_dom_names, + size_t public_dom_names_count) +{ + size_t i; + + for(i=0; i < public_dom_names_count; i++) { + if (strcmp(name, public_dom_names[i]) == 0) { + return true; + } + } + return false; +} + static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) { struct sss_domain_info *dom; @@ -773,6 +812,15 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) talloc_get_type(cctx->rctx->pvt_ctx, struct pam_ctx); struct tevent_req *req; + pctx->is_uid_trusted = is_uid_trusted(cctx->client_euid, + pctx->trusted_uids_count, + pctx->trusted_uids); + + if (!pctx->is_uid_trusted) { + DEBUG(SSSDBG_MINOR_FAILURE, "uid %"PRIu32" is not trusted.\n", + cctx->client_euid); + } + preq = talloc_zero(cctx, struct pam_auth_req); if (!preq) { return ENOMEM; @@ -813,6 +861,17 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) goto done; } + /* Untrusted users can access only public domains. */ + if (!pctx->is_uid_trusted && + !is_domain_public(pd->domain, pctx->public_domains, + pctx->public_domains_count)) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Untrusted user %"PRIu32" cannot access unpublic domain %s.\n", + cctx->client_euid, pd->domain); + ret = EPERM; + goto done; + } + ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout, preq->domain, pd->user); if (ncret == EEXIST) { @@ -826,6 +885,17 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) dom = get_next_domain(dom, false)) { if (dom->fqnames) continue; + /* Untrusted users can access only public domains. */ + if (!pctx->is_uid_trusted && + !is_domain_public(dom->name, pctx->public_domains, + pctx->public_domains_count)) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Untrusted user %"PRIu32" cannot access unpublic domain %s." + " Trying next domain.\n", + cctx->client_euid, dom->name); + continue; + } + ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout, dom, pd->user); if (ncret == ENOENT) { @@ -920,7 +990,6 @@ done: } static void pam_dp_send_acct_req_done(struct tevent_req *req); - static int pam_check_user_search(struct pam_auth_req *preq) { struct sss_domain_info *dom = preq->domain; @@ -936,9 +1005,14 @@ static int pam_check_user_search(struct pam_auth_req *preq) while (dom) { /* if it is a domainless search, skip domains that require fully - * qualified names instead */ + * qualified names instead, also untrusted users can access only + * public domains */ while (dom && !preq->pd->domain && !preq->pd->name_is_upn - && dom->fqnames) { + && (dom->fqnames || + (!pctx->is_uid_trusted && + !is_domain_public(dom->name, + pctx->public_domains, + pctx->public_domains_count)))) { dom = get_next_domain(dom, false); } diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c index 8933f5235..520feb36d 100644 --- a/src/util/domain_info_utils.c +++ b/src/util/domain_info_utils.c @@ -569,3 +569,63 @@ done: talloc_free(tmp_ctx); return ret; } + +/* Save domain names, do not descend. */ +errno_t get_dom_names(TALLOC_CTX *mem_ctx, + struct sss_domain_info *start_dom, + char ***_dom_names, + int *_dom_names_count) +{ + struct sss_domain_info *dom; + TALLOC_CTX *tmp_ctx; + char **dom_names; + size_t count, i; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + /* get count of domains*/ + count = 0; + dom = start_dom; + while (dom) { + count++; + dom = get_next_domain(dom, false); + } + + dom_names = talloc_array(tmp_ctx, char*, count); + if (dom_names == NULL) { + ret = ENOMEM; + goto done; + } + + /* copy names */ + i = 0; + dom = start_dom; + while (dom) { + dom_names[i] = talloc_strdup(dom_names, dom->name); + if (dom_names[i] == NULL) { + ret = ENOMEM; + goto done; + } + dom = get_next_domain(dom, false); + i++; + } + + if (_dom_names != NULL ) { + *_dom_names = talloc_steal(mem_ctx, dom_names); + } + + if (_dom_names_count != NULL ) { + *_dom_names_count = count; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/util/util.h b/src/util/util.h index d3b746be3..df82b3fa4 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -547,6 +547,11 @@ errno_t sssd_domain_init(TALLOC_CTX *mem_ctx, errno_t sss_write_domain_mappings(struct sss_domain_info *domain, bool add_capaths); +errno_t get_dom_names(TALLOC_CTX *mem_ctx, + struct sss_domain_info *start_dom, + char ***_dom_names, + int *_dom_names_count); + /* from util_lock.c */ errno_t sss_br_lock_file(int fd, size_t start, size_t len, int num_tries, useconds_t wait); -- cgit