diff options
-rw-r--r-- | Makefile.am | 17 | ||||
-rw-r--r-- | src/external/pac_responder.m4 | 3 | ||||
-rw-r--r-- | src/responder/pac/pacsrv.c | 19 | ||||
-rw-r--r-- | src/responder/pac/pacsrv.h | 20 | ||||
-rw-r--r-- | src/responder/pac/pacsrv_cmd.c | 649 | ||||
-rw-r--r-- | src/responder/pac/pacsrv_utils.c | 66 | ||||
-rw-r--r-- | src/tests/pac_responder-tests.c | 106 |
7 files changed, 703 insertions, 177 deletions
diff --git a/Makefile.am b/Makefile.am index fc796eddb..3b3c0fb51 100644 --- a/Makefile.am +++ b/Makefile.am @@ -122,7 +122,8 @@ if HAVE_CHECK util-tests \ debug-tests \ ipa_hbac-tests \ - sss_idmap-tests + sss_idmap-tests \ + pac_responder-tests endif check_PROGRAMS = \ @@ -599,6 +600,7 @@ endif sssd_pac_SOURCES = \ src/responder/pac/pacsrv.c \ src/responder/pac/pacsrv_cmd.c \ + src/responder/pac/pacsrv_utils.c \ $(SSSD_UTIL_OBJ) \ $(SSSD_RESPONDER_OBJ) sssd_pac_CFLAGS = \ @@ -608,6 +610,7 @@ sssd_pac_LDADD = \ $(NDR_KRB5PAC_LIBS) \ $(TDB_LIBS) \ $(SSSD_LIBS) \ + libsss_idmap.la \ libsss_util.la sssd_be_SOURCES = \ @@ -994,6 +997,18 @@ sss_idmap_tests_LDADD = \ libsss_test_common.la \ libsss_idmap.la +pac_responder_tests_SOURCES = \ + src/tests/pac_responder-tests.c \ + src/responder/pac/pacsrv_utils.c +pac_responder_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(NDR_KRB5PAC_CFLAGS) \ + $(CHECK_CFLAGS) +pac_responder_tests_LDADD = \ + $(CHECK_LIBS) \ + $(TALLOC_LIBS) \ + libsss_debug.la \ + libsss_test_common.la endif stress_tests_SOURCES = \ diff --git a/src/external/pac_responder.m4 b/src/external/pac_responder.m4 index e157910dc..fbad267e2 100644 --- a/src/external/pac_responder.m4 +++ b/src/external/pac_responder.m4 @@ -21,7 +21,8 @@ then AC_MSG_CHECKING(for supported MIT krb5 version) KRB5_VERSION="`$KRB5_CONFIG --version`" case $KRB5_VERSION in - Kerberos\ 5\ release\ 1.9*) + Kerberos\ 5\ release\ 1.9* | \ + Kerberos\ 5\ release\ 1.10*) AC_MSG_RESULT(yes) ;; *) diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c index 98d1aabff..6421fee93 100644 --- a/src/responder/pac/pacsrv.c +++ b/src/responder/pac/pacsrv.c @@ -103,6 +103,16 @@ static void pac_dp_reconnect_init(struct sbus_connection *conn, /* nss_shutdown(rctx); */ } +static void *idmap_talloc(size_t size, void *pvt) +{ + return talloc_size(pvt, size); +} + +static void idmap_free(void *ptr, void *pvt) +{ + talloc_free(ptr); +} + int pac_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) @@ -111,6 +121,7 @@ int pac_process_init(TALLOC_CTX *mem_ctx, struct be_conn *iter; struct pac_ctx *pac_ctx; int ret, max_retries; + enum idmap_error_code err; pac_ctx = talloc_zero(mem_ctx, struct pac_ctx); if (!pac_ctx) { @@ -149,6 +160,14 @@ int pac_process_init(TALLOC_CTX *mem_ctx, pac_dp_reconnect_init, iter); } + + err = sss_idmap_init(idmap_talloc, pac_ctx, idmap_free, + &pac_ctx->idmap_ctx); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_init failed.\n")); + return EFAULT; + } + DEBUG(SSSDBG_TRACE_FUNC, ("PAC Initialization complete\n")); return EOK; diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h index 1b28254de..1400074fc 100644 --- a/src/responder/pac/pacsrv.h +++ b/src/responder/pac/pacsrv.h @@ -32,6 +32,7 @@ #include "sbus/sssd_dbus.h" #include "responder/common/responder_packet.h" #include "responder/common/responder.h" +#include "lib/idmap/sss_idmap.h" #define PAC_SBUS_SERVICE_VERSION 0x0001 #define PAC_SBUS_SERVICE_NAME "pac" @@ -39,13 +40,32 @@ #define PAC_PACKET_MAX_RECV_SIZE 1024 struct getent_ctx; +struct dom_sid; struct pac_ctx { struct resp_ctx *rctx; + struct sss_idmap_ctx *idmap_ctx; + struct dom_sid *my_dom_sid; + struct local_mapping_ranges *range_map; +}; + +struct range { + uint32_t min; + uint32_t max; +}; + +struct local_mapping_ranges { + struct range local_ids; + struct range primary_rids; + struct range secondary_rids; }; int pac_cmd_execute(struct cli_ctx *cctx); struct sss_cmd_table *get_pac_cmds(void); +errno_t get_rid(struct dom_sid *sid, uint32_t *rid); + +errno_t local_sid_to_id(struct local_mapping_ranges *map, struct dom_sid *sid, + uint32_t *id); #endif /* __PACSRV_H__ */ diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c index 3f2420273..f4fec85f8 100644 --- a/src/responder/pac/pacsrv_cmd.c +++ b/src/responder/pac/pacsrv_cmd.c @@ -19,18 +19,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdbool.h> +#include <util/data_blob.h> +#include <ndr.h> +#include <gen_ndr/krb5pac.h> +#include <gen_ndr/ndr_krb5pac.h> #include "util/util.h" +#include "util/sss_nss.h" #include "responder/pac/pacsrv.h" #include "confdb/confdb.h" #include "db/sysdb.h" -#include <util/data_blob.h> -#include <ndr.h> - -#include "gen_ndr/krb5pac.h" -#include "gen_ndr/ndr_krb5pac.h" - #define PAC_USER_OFFSET 200000 #define PAC_HOME_PATH "/home/" #define PAC_DEFAULT_SHELL "/bin/bash" @@ -59,39 +59,301 @@ static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret) return EOK; } -static errno_t domsid_rid_to_uid(struct dom_sid2 *domsid, uint32_t rid, - uid_t *uid) +static void *idmap_talloc(size_t size, void *pvt) { - /* Replace with a proper mapping */ - *uid = (uid_t) rid + PAC_USER_OFFSET; - return EOK; + return talloc_size(pvt, size); +} + +static void idmap_free(void *ptr, void *pvt) +{ + talloc_free(ptr); } -static errno_t domsid_rid_to_gid(struct dom_sid2 *domsid, uint32_t rid, - gid_t *gid) +static errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx, + struct sysdb_ctx *sysdb, + const char *domain_name, + const char *dom_sid_str) { - /* Replace with a proper mapping */ - *gid = (gid_t) rid + PAC_USER_OFFSET; + struct sss_idmap_range range; + enum idmap_error_code err; + + /* TODO: read range form sysdb if + * https://fedorahosted.org/freeipa/ticket/2185 is fixed */ + range.min = 200000; + range.max = 400000; + + err = sss_idmap_add_domain(idmap_ctx, domain_name, dom_sid_str, &range); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_add_domain failed.\n")); + return EFAULT; + } + return EOK; } +static errno_t domsid_rid_to_uid(struct pac_ctx *pac_ctx, + struct sysdb_ctx *sysdb, + const char *domain_name, + struct dom_sid2 *domsid, uint32_t rid, + uid_t *uid) +{ + enum idmap_error_code err; + char *sid_str = NULL; + char *dom_sid_str = NULL; + uint32_t id; + int ret; + + if (pac_ctx->idmap_ctx == NULL) { + err = sss_idmap_init(idmap_talloc, pac_ctx, idmap_free, + &pac_ctx->idmap_ctx); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_init failed.\n")); + return EFAULT; + } + } + + err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, domsid, + &dom_sid_str); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n")); + ret = EFAULT; + goto done; + } + + sid_str = talloc_asprintf(NULL, "%s-%lu", dom_sid_str, (unsigned long) rid); + if (sid_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("dom_sid_and_rid_string failed.\n")); + return ENOMEM; + } + + err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, domsid, &sid_str); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n")); + ret = EFAULT; + goto done; + } + + err = sss_idmap_sid_to_unix(pac_ctx->idmap_ctx, sid_str, &id); + if (err == IDMAP_NO_DOMAIN) { + ret = add_idmap_domain(pac_ctx->idmap_ctx, sysdb, domain_name, + dom_sid_str); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("add_idmap_domain failed.\n")); + goto done; + } + + err = sss_idmap_sid_to_unix(pac_ctx->idmap_ctx, sid_str, &id); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_sid_to_unix failed " + "even in the second attempt.\n")); + ret = ENOENT; + goto done; + } + } else if (err != IDMAP_SUCCESS && err != IDMAP_NO_DOMAIN) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_unix failed.\n")); + ret = EFAULT; + goto done; + } + + *uid = (uid_t) id; + + ret = EOK; + +done: + talloc_free(dom_sid_str); + talloc_free(sid_str); + return ret; +} + +static errno_t get_my_domain_sid(struct pac_ctx *pac_ctx, + struct sss_domain_info *dom, + struct dom_sid **_sid) +{ + struct sysdb_ctx *sysdb; + int ret; + struct ldb_dn *basedn; + const char *attrs[] = {SYSDB_SUBDOMAIN_ID, + NULL}; + size_t msgs_count; + const char *sid_str; + struct ldb_message **msgs; + TALLOC_CTX *tmp_ctx = NULL; + struct dom_sid *sid = NULL; + char *dom_name; + enum idmap_error_code err; + + if (pac_ctx->my_dom_sid == NULL) { + if (dom->parent != NULL) { + sysdb = dom->parent->sysdb; + dom_name = dom->parent->name; + } else { + sysdb = dom->sysdb; + dom_name = dom->name; + } + + if (sysdb == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Missing sysdb context.\n")); + ret = EINVAL; + goto done; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); + ret = ENOMEM; + goto done; + } + + basedn = sysdb_domain_dn(sysdb, tmp_ctx, dom_name); + if (basedn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL, + attrs, &msgs_count, &msgs); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + if (msgs_count != 1) { + DEBUG(SSSDBG_OP_FAILURE, ("Base search returned [%d] results, " + "expected 1.\n", msgs_count)); + ret = EINVAL; + goto done; + } + + sid_str = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUBDOMAIN_ID, NULL); + if (sid_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("SID of my domain is not available.\n")); + ret = EINVAL; + goto done; + } + + err = sss_idmap_sid_to_smb_sid(pac_ctx->idmap_ctx, sid_str, &sid); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_smb_sid failed.\n")); + ret = EFAULT; + goto done; + } + + pac_ctx->my_dom_sid = talloc_memdup(pac_ctx, sid, + sizeof(struct dom_sid)); + if (pac_ctx->my_dom_sid == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_memdup failed.\n")); + ret = ENOMEM; + goto done; + } + } + + *_sid = pac_ctx->my_dom_sid; + + ret = EOK; + +done: + talloc_free(sid); + talloc_free(tmp_ctx); + + return ret; +} + +static bool dom_sid_in_domain(const struct dom_sid *domain_sid, + const struct dom_sid *sid) +{ + size_t c; + + if (!domain_sid || !sid) { + return false; + } + + if (domain_sid->sid_rev_num != sid->sid_rev_num) { + return false; + } + + for (c = 0; c < 6; c++) { + if (domain_sid->id_auth[c] != sid->id_auth[c]) { + return false; + } + } + + if (domain_sid->num_auths > sid->num_auths) { + return false; + } + + for (c = 0; c < domain_sid->num_auths-1; c++) { + if (domain_sid->sub_auths[c] != sid->sub_auths[c]) { + return false; + } + } + + return true; +} + +static errno_t get_gids_from_pac(TALLOC_CTX *mem_ctx, + struct local_mapping_ranges *range_map, + struct dom_sid *domain_sid, + struct PAC_LOGON_INFO *logon_info, + size_t *_gid_count, gid_t **_gids) +{ + int ret; + size_t g; + size_t s; + struct netr_SamInfo3 *info3; + gid_t *gids = NULL; + + info3 = &logon_info->info3; + + g = 0; + if (info3->sidcount == 0) { + DEBUG(SSSDBG_TRACE_ALL, ("No extra groups found.\n")); + ret = EOK; + goto done; + } + + gids = talloc_array(mem_ctx, gid_t, info3->sidcount); + if (gids == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n")); + ret = ENOMEM; + goto done; + } + + for(s = 0; s < info3->sidcount; s++) { + if (dom_sid_in_domain(domain_sid, info3->sids[s].sid)) { + ret = local_sid_to_id(range_map, info3->sids[s].sid, &gids[g]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_rid failed.\n")); + goto done; + } + DEBUG(SSSDBG_TRACE_ALL, ("Found extra group " + "with gid [%d].\n", gids[g])); + g++; + } + } + + ret = EOK; + +done: + if (ret == EOK) { + *_gid_count = g; + *_gids = gids; + } else { + talloc_free(gids); + } + + return ret; +} + static errno_t get_data_from_pac(TALLOC_CTX *mem_ctx, - uint8_t *pac_blob, size_t pac_len, - struct passwd **_pwd, char **domain, - struct dom_sid2 **domain_sid, - int *_rid_count, uint32_t **_rids) + uint8_t *pac_blob, size_t pac_len, + struct PAC_LOGON_INFO **_logon_info) { DATA_BLOB blob; struct ndr_pull *ndr_pull; struct PAC_DATA *pac_data; enum ndr_err_code ndr_err; - struct passwd *pwd = NULL; size_t c; - struct netr_SamBaseInfo *base_info; int ret; - int i, j; - uint32_t *rids; - uint32_t attrs; blob.data = pac_blob; blob.length = pac_len; @@ -106,7 +368,6 @@ static errno_t get_data_from_pac(TALLOC_CTX *mem_ctx, pac_data = talloc_zero(mem_ctx, struct PAC_DATA); if (pac_data == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); - return EOK; return ENOMEM; } @@ -116,146 +377,126 @@ static errno_t get_data_from_pac(TALLOC_CTX *mem_ctx, return EBADMSG; } - pwd = talloc_zero(mem_ctx, struct passwd); - if (pwd == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); - return ENOMEM; - } - for(c = 0; c < pac_data->num_buffers; c++) { if (pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) { - base_info = &pac_data->buffers[c].info->logon_info.info->info3.base; - - if (base_info->account_name.size != 0) { - pwd->pw_name = talloc_strdup(pwd, - base_info->account_name.string); - if (pwd->pw_name == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); - ret = ENOMEM; - goto done; - } - } else { - DEBUG(SSSDBG_OP_FAILURE, ("Missing account name in PAC.\n")); - ret = EINVAL; - goto done; - } + *_logon_info = pac_data->buffers[c].info->logon_info.info; - if (base_info->rid > 0) { - ret = domsid_rid_to_uid(base_info->domain_sid, - base_info->rid, - &pwd->pw_uid); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("domsid_rid_to_uid failed.\n")); - goto done; - } - } else { - DEBUG(SSSDBG_OP_FAILURE, ("Missing user RID in PAC.\n")); - ret = EINVAL; - goto done; - } + return EOK; + } + } - if (base_info->primary_gid > 0) { - ret = domsid_rid_to_gid(base_info->domain_sid, - base_info->primary_gid, - &pwd->pw_gid); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("domsid_rid_to_gid failed.\n")); - goto done; - } - } else { - DEBUG(SSSDBG_OP_FAILURE, ("Missing primary GID in PAC.\n")); - ret = EINVAL; - goto done; - } + ret = EINVAL; - if (base_info->full_name.size != 0) { - pwd->pw_gecos = talloc_strdup(pwd, base_info->full_name.string); - } else { - DEBUG(SSSDBG_OP_FAILURE, ("Missing full name in PAC " - "using account name for gecos.\n")); - pwd->pw_gecos = talloc_strdup(pwd, - base_info->account_name.string); - } - if (pwd->pw_gecos == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); - ret = ENOMEM; - goto done; - } + talloc_free(pac_data); + return ret; +} - pwd->pw_dir = talloc_asprintf(mem_ctx, PAC_HOME_PATH"%s", - base_info->account_name.string); - if (pwd->pw_dir == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n")); - ret = ENOMEM; - goto done; - } +static errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx, + struct pac_ctx *pac_ctx, + struct sss_domain_info *dom, + struct PAC_LOGON_INFO *logon_info, + struct passwd **_pwd) +{ + struct passwd *pwd = NULL; + struct netr_SamBaseInfo *base_info; + int ret; - pwd->pw_shell = talloc_strdup(pwd, PAC_DEFAULT_SHELL); - if (pwd->pw_shell == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); - ret = ENOMEM; - goto done; - } + pwd = talloc_zero(mem_ctx, struct passwd); + if (pwd == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); + return ENOMEM; + } - *domain = talloc_strdup(mem_ctx, base_info->logon_domain.string); - if (*domain == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); - ret = ENOMEM; - goto done; - } + base_info = &logon_info->info3.base; - *_pwd = pwd; + if (base_info->account_name.size != 0) { + pwd->pw_name = talloc_strdup(pwd, + base_info->account_name.string); + if (pwd->pw_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); + ret = ENOMEM; + goto done; + } + } else { + DEBUG(SSSDBG_OP_FAILURE, ("Missing account name in PAC.\n")); + ret = EINVAL; + goto done; + } - rids = talloc_array(mem_ctx, uint32_t, base_info->groups.count); - if (rids == NULL) { - ret = ENOMEM; - goto done; - } + if (base_info->rid > 0) { + ret = domsid_rid_to_uid(pac_ctx, dom->sysdb, dom->name, + base_info->domain_sid, + base_info->rid, &pwd->pw_uid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("domsid_rid_to_uid failed.\n")); + goto done; + } + } else { + DEBUG(SSSDBG_OP_FAILURE, ("Missing user RID in PAC.\n")); + ret = EINVAL; + goto done; + } - j = 0; - for (i = 0; i < base_info->groups.count; i++) { - attrs = base_info->groups.rids[i].attributes; - if (((attrs & SE_GROUP_ENABLED) != 0 || - (attrs & SE_GROUP_MANDATORY) != 0) && - (attrs & SE_GROUP_RESOURCE) == 0) { - rids[j] = base_info->groups.rids[i].rid; - j++; - } - } + pwd->pw_gid = 0; /* We use MPGs for sub-domains */ + + if (base_info->full_name.size != 0) { + pwd->pw_gecos = talloc_strdup(pwd, base_info->full_name.string); + } else { + DEBUG(SSSDBG_OP_FAILURE, ("Missing full name in PAC " + "using account name for gecos.\n")); + pwd->pw_gecos = talloc_strdup(pwd, + base_info->account_name.string); + } + if (pwd->pw_gecos == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); + ret = ENOMEM; + goto done; + } - *_rids = rids; - *_rid_count = j; - return EOK; + if (dom->subdomain_homedir) { + pwd->pw_dir = expand_homedir_template(pwd, dom->subdomain_homedir, + pwd->pw_name, pwd->pw_uid, + dom->name); + if (pwd->pw_dir == NULL) { + ret = ENOMEM; + goto done; } } - ret = EINVAL; + pwd->pw_shell = NULL; /* Using default */ done: - talloc_free(pwd); + if (ret != EOK) { + talloc_free(pwd); + } + return ret; } struct pac_req_ctx { struct cli_ctx *cctx; - struct passwd *pwd; - char *domain_name; + struct pac_ctx *pac_ctx; + const char *domain_name; + const char *user_name; struct sss_domain_info *dom; - int rid_count; - uint32_t *rids; + struct PAC_LOGON_INFO *logon_info; struct dom_sid2 *domain_sid; + + size_t gid_count; + gid_t *gids; }; +static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx); static void pac_get_domains_done(struct tevent_req *req); -errno_t save_pac_user(struct pac_req_ctx *pr_ctx); +static errno_t save_pac_user(struct pac_req_ctx *pr_ctx); static void pac_get_group_done(struct tevent_req *subreq); static errno_t pac_save_memberships_next(struct tevent_req *req); static errno_t pac_store_memberships(struct pac_req_ctx *pr_ctx, struct sysdb_ctx *group_sysdb, struct ldb_dn *user_dn, - int rid_iter); + int gid_iter); struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx); static void pac_save_memberships_done(struct tevent_req *req); @@ -278,20 +519,34 @@ static errno_t pac_add_pac_user(struct cli_ctx *cctx) pr_ctx->cctx = cctx; + pr_ctx->pac_ctx = talloc_get_type(cctx->rctx->pvt_ctx, struct pac_ctx); + if (pr_ctx->pac_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot find pac responder context.\n")); + return EINVAL; + } + ret = get_data_from_pac(pr_ctx, body, blen, - &pr_ctx->pwd, &pr_ctx->domain_name, - &pr_ctx->domain_sid, - &pr_ctx->rid_count, &pr_ctx->rids); + &pr_ctx->logon_info); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n")); + DEBUG(SSSDBG_OP_FAILURE, ("get_data_from_pac failed.\n")); goto done; } + + pr_ctx->domain_name = pr_ctx->logon_info->info3.base.logon_domain.string; if (pr_ctx->domain_name == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, ("No domain name in PAC")); ret = EINVAL; goto done; } + pr_ctx->user_name = pr_ctx->logon_info->info3.base.account_name.string; + if (pr_ctx->user_name == NULL) { + ret = EINVAL; + DEBUG(SSSDBG_FATAL_FAILURE, ("Missing account name in PAC.\n")); + goto done; + } + + pr_ctx->dom = responder_get_domain(pr_ctx, cctx->rctx, pr_ctx->domain_name); if (pr_ctx->dom == NULL) { req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, @@ -305,23 +560,7 @@ static errno_t pac_add_pac_user(struct cli_ctx *cctx) goto done; } - ret = save_pac_user(pr_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("save_pac_user failed.\n")); - goto done; - } - - - req = pac_save_memberships_send(pr_ctx); - if (req == NULL) { - ret = ENOMEM; - goto done; - } - - tevent_req_set_callback(req, pac_save_memberships_done, pr_ctx); - - - ret = EAGAIN; + ret = pac_add_user_next(pr_ctx); done: return pac_cmd_done(cctx, ret); @@ -346,12 +585,38 @@ static void pac_get_domains_done(struct tevent_req *req) goto done; } + ret = pac_add_user_next(pr_ctx); + +done: + pac_cmd_done(cctx, ret); +} + +static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx) +{ + int ret; + struct tevent_req *req; + struct dom_sid *my_dom_sid; + ret = save_pac_user(pr_ctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("save_pac_user failed.\n")); goto done; } + ret = get_my_domain_sid(pr_ctx->pac_ctx, pr_ctx->dom, &my_dom_sid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_my_domain_sid failed.\n")); + goto done; + } + + ret = get_gids_from_pac(pr_ctx, pr_ctx->pac_ctx->range_map, my_dom_sid, + pr_ctx->logon_info, &pr_ctx->gid_count, + &pr_ctx->gids); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_gids_from_pac failed.\n")); + goto done; + } + req = pac_save_memberships_send(pr_ctx); if (req == NULL) { ret = ENOMEM; @@ -360,18 +625,20 @@ static void pac_get_domains_done(struct tevent_req *req) tevent_req_set_callback(req, pac_save_memberships_done, pr_ctx); - return; + ret = EAGAIN; done: - pac_cmd_done(cctx, ret); + return ret; } -errno_t save_pac_user(struct pac_req_ctx *pr_ctx) +static errno_t save_pac_user(struct pac_req_ctx *pr_ctx) { struct sysdb_ctx *sysdb; - struct passwd *pwd = pr_ctx->pwd; - struct cli_ctx *cctx = pr_ctx->cctx; int ret; + const char *attrs[] = {SYSDB_NAME, SYSDB_UIDNUM, SYSDB_GIDNUM, NULL}; + struct ldb_message *msg; + struct passwd *pwd = NULL; + TALLOC_CTX *tmp_ctx = NULL; sysdb = pr_ctx->dom->sysdb; if (sysdb == NULL) { @@ -380,21 +647,49 @@ errno_t save_pac_user(struct pac_req_ctx *pr_ctx) goto done; } - ret = sysdb_store_user(sysdb, pwd->pw_name, NULL, - pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir, - pwd->pw_shell, NULL, NULL, 3600, 0); /* FIXME: cache_timeout */ - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_user failed [%d][%s].\n", - ret, strerror(ret))); + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); + goto done; + } + + ret = sysdb_search_user_by_name(tmp_ctx, sysdb, pr_ctx->user_name, attrs, + &msg); + if (ret == EOK) { + /* TODO: check id uid and gid are equal. */ + } else if (ret == ENOENT) { + ret = get_pwd_from_pac(tmp_ctx, pr_ctx->pac_ctx, pr_ctx->dom, + pr_ctx->logon_info, &pwd); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n")); + goto done; + } + + ret = sysdb_store_user(sysdb, pwd->pw_name, NULL, + pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell, NULL, NULL, + 3600, 0); /* FIXME: cache_timeout */ + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_user failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + } else { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_user_by_name failed.\n")); goto done; } + ret = EOK; + done: - return pac_cmd_done(cctx, ret); + talloc_free(tmp_ctx); + return ret; } struct pac_save_memberships_state { - int rid_iter; + int gid_iter; struct ldb_dn *user_dn; struct pac_req_ctx *pr_ctx; @@ -413,9 +708,9 @@ struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx) return NULL; } - state->rid_iter = 0; + state->gid_iter = 0; state->user_dn = sysdb_user_dn(dom->sysdb, state, dom->name, - pr_ctx->pwd->pw_name); + pr_ctx->user_name); if (state->user_dn == NULL) { ret = ENOMEM; goto done; @@ -430,6 +725,10 @@ struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx) } ret = pac_save_memberships_next(req); + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, pr_ctx->cctx->ev); + } done: if (ret != EOK && ret != EAGAIN) { @@ -452,18 +751,18 @@ errno_t pac_save_memberships_next(struct tevent_req *req) state = tevent_req_data(req, struct pac_save_memberships_state); pr_ctx = state->pr_ctx; - while (state->rid_iter < pr_ctx->rid_count) { - gid = pr_ctx->rids[state->rid_iter]; + while (state->gid_iter < pr_ctx->gid_count) { + gid = pr_ctx->gids[state->gid_iter]; ret = sysdb_search_group_by_gid(pr_ctx, state->group_dom->sysdb, gid, NULL, &group); if (ret == EOK) { ret = pac_store_memberships(state->pr_ctx, state->group_dom->sysdb, - state->user_dn, state->rid_iter); + state->user_dn, state->gid_iter); if (ret != EOK) { goto done; } - state->rid_iter++; + state->gid_iter++; continue; } else if (ret != ENOENT) { goto done; @@ -510,11 +809,11 @@ static void pac_get_group_done(struct tevent_req *subreq) } ret = pac_store_memberships(state->pr_ctx, state->group_dom->sysdb, - state->user_dn, state->rid_iter); + state->user_dn, state->gid_iter); if (ret != EOK) { goto error; } - state->rid_iter++; + state->gid_iter++; ret = pac_save_memberships_next(req); if (ret == EOK) { @@ -533,7 +832,7 @@ static errno_t pac_store_memberships(struct pac_req_ctx *pr_ctx, struct sysdb_ctx *group_sysdb, struct ldb_dn *user_dn, - int rid_iter) + int gid_iter) { TALLOC_CTX *tmp_ctx; const char *group_name; @@ -547,7 +846,7 @@ pac_store_memberships(struct pac_req_ctx *pr_ctx, return ENOMEM; } - gid = pr_ctx->rids[rid_iter]; + gid = pr_ctx->gids[gid_iter]; ret = sysdb_search_group_by_gid(tmp_ctx, group_sysdb, gid, NULL, &group); diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c new file mode 100644 index 000000000..9f2932be9 --- /dev/null +++ b/src/responder/pac/pacsrv_utils.c @@ -0,0 +1,66 @@ +/* + SSSD + + PAC Responder - utility finctions + + Copyright (C) Sumit Bose <sbose@redhat.com> 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdbool.h> +#include <util/data_blob.h> +#include <gen_ndr/security.h> + +#include "util/util.h" +#include "responder/pac/pacsrv.h" + +errno_t get_rid(struct dom_sid *sid, uint32_t *rid) +{ + if (sid == NULL || sid->num_auths < 1 || rid == NULL) { + return EINVAL; + } + + *rid = sid->sub_auths[sid->num_auths - 1]; + + return EOK; +} + +errno_t local_sid_to_id(struct local_mapping_ranges *map, struct dom_sid *sid, + uint32_t *id) +{ + int ret; + uint32_t rid; + + if (map == NULL || sid == NULL || id == NULL) { + return EINVAL; + } + + ret = get_rid(sid, &rid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_rid failed.\n")); + return ret; + } + + if (rid >= map->primary_rids.min && rid <= map->primary_rids.max) { + *id = map->local_ids.min + (rid - map->primary_rids.min); + } else if (rid >= map->secondary_rids.min && + rid <= map->secondary_rids.max) { + *id = map->local_ids.min + (rid - map->secondary_rids.min); + } else { + return ENOENT; + } + + return EOK; +} + diff --git a/src/tests/pac_responder-tests.c b/src/tests/pac_responder-tests.c new file mode 100644 index 000000000..720793cc6 --- /dev/null +++ b/src/tests/pac_responder-tests.c @@ -0,0 +1,106 @@ +/* + SSSD - Test for PAC reponder functions + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <check.h> + +#include <stdbool.h> +#include <util/data_blob.h> +#include <gen_ndr/security.h> + +#include "tests/common.h" +#include "responder/pac/pacsrv.h" + +struct dom_sid test_smb_sid = {1, 5, {0, 0, 0, 0, 0, 5}, + {21, 2127521184, 1604012920, 1887927527, 1123, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; +const uint32_t test_id = 1200123; + +struct dom_sid test_smb_sid_2nd = {1, 5, {0, 0, 0, 0, 0, 5}, + {21, 2127521184, 1604012920, 1887927527, 201456, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; +const uint32_t test_id_2nd = 1200456; + +struct local_mapping_ranges test_map = {{1200000, 1399999}, + {1000, 200999}, + {201000, 400999}}; + + +START_TEST(pac_test_local_sid_to_id) +{ + int ret; + uint32_t id; + + ret = local_sid_to_id(&test_map, &test_smb_sid, &id); + fail_unless(ret == EOK, + "Failed to convert local sid to id."); + fail_unless(id == test_id, "Wrong id returne, expected [%d], got [%d].", + test_id, id); +} +END_TEST + +START_TEST(pac_test_seondary_local_sid_to_id) +{ + int ret; + uint32_t id; + + ret = local_sid_to_id(&test_map, &test_smb_sid_2nd, &id); + fail_unless(ret == EOK, + "Failed to convert local sid to id."); + fail_unless(id == test_id_2nd, "Wrong id returne, expected [%d], got [%d].", + test_id_2nd, id); +} +END_TEST + + +Suite *idmap_test_suite (void) +{ + Suite *s = suite_create ("PAC responder"); + + TCase *tc_pac = tcase_create("PAC responder tests"); + /*tcase_add_checked_fixture(tc_init, + leak_check_setup, + leak_check_teardown);*/ + + tcase_add_test(tc_pac, pac_test_local_sid_to_id); + tcase_add_test(tc_pac, pac_test_seondary_local_sid_to_id); + + suite_add_tcase(s, tc_pac); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int number_failed; + + tests_set_cwd(); + + Suite *s = idmap_test_suite(); + SRunner *sr = srunner_create(s); + + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} |