/* SSSD PAC Responder Copyright (C) Sumit Bose 2012 Jan Zeleny 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 . */ #include "util/util.h" #include "responder/pac/pacsrv.h" #include "confdb/confdb.h" static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret) { int ret; if (cmd_ret == EAGAIN) { /* async processing, just return here */ return EOK; } ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sss_packet_new failed [%d][%s].\n", ret, strerror(ret))); return ret; } sss_packet_set_error(cctx->creq->out, cmd_ret); sss_cmd_done(cctx, NULL); return EOK; } struct pac_req_ctx { struct cli_ctx *cctx; struct pac_ctx *pac_ctx; const char *domain_name; const char *user_name; struct sss_domain_info *dom; struct PAC_LOGON_INFO *logon_info; struct dom_sid2 *domain_sid; size_t del_grp_count; struct grp_info *del_grp_list; size_t add_sid_count; char **add_sids; hash_table_t *sid_table; char *user_sid_str; char *user_dom_sid_str; char *primary_group_sid_str; }; static errno_t pac_resolve_sids_next(struct pac_req_ctx *pr_ctx); static void pac_lookup_sids_done(struct tevent_req *req); static struct tevent_req *pac_lookup_sids_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct pac_req_ctx *pr_ctx, struct pac_ctx *pac_ctx, hash_table_t *sid_table); static errno_t pac_lookup_sids_recv(struct tevent_req *req); static void pac_add_user_next(struct pac_req_ctx *pr_ctx); static void pac_get_domains_done(struct tevent_req *req); static errno_t pac_user_get_grp_info(TALLOC_CTX *mem_ctx, struct pac_req_ctx *pr_ctx, size_t *_del_grp_count, struct grp_info **_del_grp_list, size_t *_add_sid_count, char ***_add_sids); 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_membership(struct pac_req_ctx *pr_ctx, struct ldb_dn *user_dn, gid_t gid, struct sss_domain_info *grp_dom); struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx); static void pac_save_memberships_done(struct tevent_req *req); static errno_t pac_add_pac_user(struct cli_ctx *cctx) { int ret; uint8_t *body; size_t blen; struct pac_req_ctx *pr_ctx; struct tevent_req *req; enum idmap_error_code err; sss_packet_get_body(cctx->creq->in, &body, &blen); pr_ctx = talloc_zero(cctx, struct pac_req_ctx); if (pr_ctx == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); return ENOMEM; } 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->logon_info); if (ret != EOK) { 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; } if (pr_ctx->logon_info->info3.base.account_name.string == NULL) { ret = EINVAL; DEBUG(SSSDBG_FATAL_FAILURE, ("Missing account name in PAC.\n")); goto done; } /* To be compatible with winbind based lookups we have to use lower case * names only, effectively making the domain case-insenvitive. */ pr_ctx->user_name = sss_tc_utf8_str_tolower(pr_ctx, pr_ctx->logon_info->info3.base.account_name.string); if (pr_ctx->user_name == NULL) { ret = ENOMEM; DEBUG(SSSDBG_FATAL_FAILURE, ("sss_tc_utf8_str_tolower failed.\n")); goto done; } err = sss_idmap_smb_sid_to_sid(pr_ctx->pac_ctx->idmap_ctx, pr_ctx->logon_info->info3.base.domain_sid, &pr_ctx->user_dom_sid_str); if (err != IDMAP_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n")); ret = EFAULT; goto done; } ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str, &pr_ctx->dom); if (ret == EAGAIN || ret == ENOENT) { req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pr_ctx->domain_name); if (req == NULL) { ret = ENOMEM; } else { tevent_req_set_callback(req, pac_get_domains_done, pr_ctx); ret = EAGAIN; } goto done; } else if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("responder_get_domain_by_id failed.\n")); goto done; } ret = pac_resolve_sids_next(pr_ctx); done: if (ret != EAGAIN) { talloc_free(pr_ctx); } return pac_cmd_done(cctx, ret); } static void pac_get_domains_done(struct tevent_req *req) { struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx); struct cli_ctx *cctx = pr_ctx->cctx; int ret; ret = sss_dp_get_domains_recv(req); talloc_free(req); if (ret != EOK) { goto done; } ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str, &pr_ctx->dom); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Corresponding domain [%s] has not been " "found\n", pr_ctx->user_dom_sid_str)); ret = ENOENT; goto done; } ret = pac_resolve_sids_next(pr_ctx); done: if (ret != EAGAIN) { talloc_free(pr_ctx); } pac_cmd_done(cctx, ret); } static errno_t pac_resolve_sids_next(struct pac_req_ctx *pr_ctx) { int ret; struct tevent_req *req; ret = get_sids_from_pac(pr_ctx, pr_ctx->pac_ctx, pr_ctx->logon_info, &pr_ctx->user_sid_str, &pr_ctx->primary_group_sid_str, &pr_ctx->sid_table); if (ret != 0) { DEBUG(SSSDBG_OP_FAILURE, ("get_sids_from_pac failed.\n")); return ret; } req = pac_lookup_sids_send(pr_ctx, pr_ctx->cctx->ev, pr_ctx, pr_ctx->pac_ctx, pr_ctx->sid_table); if (req == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("pac_lookup_sids_send failed.\n")); return ENOMEM; } tevent_req_set_callback(req, pac_lookup_sids_done, pr_ctx); ret = EAGAIN; return ret; } static void pac_lookup_sids_done(struct tevent_req *req) { struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx); struct cli_ctx *cctx = pr_ctx->cctx; errno_t ret; unsigned long count; hash_entry_t *entries; hash_key_t key; hash_value_t value; size_t c; struct sss_domain_info *dom; uint64_t id; struct ldb_result *msg; ret = pac_lookup_sids_recv(req); talloc_zfree(req); if (ret != EOK) { talloc_free(pr_ctx); pac_cmd_done(cctx, ret); return; } key.type = HASH_KEY_STRING; value.type = HASH_VALUE_ULONG; ret = hash_entries(pr_ctx->sid_table, &count, &entries); for (c = 0; c < count; c++) { if (entries[c].value.ul == 0) { ret =responder_get_domain_by_id(cctx->rctx, entries[c].key.str, &dom); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("No domain found for SID [%s].\n", entries[c].key.str)); continue; } msg = NULL; ret = sysdb_search_object_by_sid(pr_ctx, dom->sysdb, dom, entries[c].key.str, NULL, &msg); if (ret != EOK) { if (ret == ENOENT) { DEBUG(SSSDBG_OP_FAILURE, ("No entry found for SID [%s].\n", entries[c].key.str)); } else { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_object_by_sid " \ "failed.\n")); } continue; } if (msg->count > 1) { DEBUG(SSSDBG_CRIT_FAILURE, ("More then one result returned " \ "for SID [%s].\n", entries[c].key.str)); talloc_free(msg); pac_cmd_done(cctx, EINVAL); return; } id = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_UIDNUM, 0); if (id == 0) { id = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_GIDNUM, 0); } if (id == 0) { DEBUG(SSSDBG_OP_FAILURE, ("No ID found in entry.\n")); talloc_free(msg); continue; } key.str = entries[c].key.str; value.ul = id; ret = hash_enter(pr_ctx->sid_table, &key, &value); if (ret != HASH_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, ("hash_enter failed [%d][%s].\n", ret, hash_error_string(ret))); continue; } talloc_free(msg); } } pac_add_user_next(pr_ctx); } static void pac_add_user_next(struct pac_req_ctx *pr_ctx) { int ret; struct tevent_req *req; struct cli_ctx *cctx = pr_ctx->cctx; ret = save_pac_user(pr_ctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("save_pac_user failed.\n")); goto done; } ret = pac_user_get_grp_info(pr_ctx, pr_ctx, &pr_ctx->del_grp_count, &pr_ctx->del_grp_list, &pr_ctx->add_sid_count, &pr_ctx->add_sids); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("pac_user_get_grp_info 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; done: if (ret != EAGAIN) { talloc_free(pr_ctx); } pac_cmd_done(cctx, ret); } static errno_t pac_user_get_grp_info(TALLOC_CTX *mem_ctx, struct pac_req_ctx *pr_ctx, size_t *_del_grp_count, struct grp_info **_del_grp_list, size_t *_add_sid_count, char ***_add_sids) { struct sysdb_ctx *sysdb; int ret; TALLOC_CTX *tmp_ctx = NULL; struct ldb_result *res = NULL; size_t c; const char *tmp_str; size_t add_sid_count = 0; char **add_sids = NULL; size_t del_idx; size_t del_grp_count = 0; struct grp_info *del_grp_list = NULL; const char *cur_sid; hash_key_t key; hash_value_t value; struct hash_iter_context_t *iter = NULL; hash_entry_t *entry; sysdb = pr_ctx->dom->sysdb; if (sysdb == NULL) { ret = EINVAL; DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for this domain!\n")); goto done; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { ret = ENOMEM; DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); goto done; } ret = sysdb_initgroups(tmp_ctx, sysdb, pr_ctx->dom, pr_ctx->user_name, &res); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_initgroups failed.\n")); goto done; } if (res->count == 0) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_initgroups did not found [%s].\n", pr_ctx->user_name)); ret = ENOENT; goto done; } /* First result is the user entry then the groups follow */ if (res->count > 1) { del_grp_count = res->count - 1; del_grp_list = talloc_zero_array(tmp_ctx, struct grp_info, del_grp_count); if (del_grp_list == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n")); ret = ENOMEM; goto done; } del_idx = 0; key.type = HASH_KEY_STRING; for (c = 0; c < (res->count - 1); c++) { cur_sid = ldb_msg_find_attr_as_string(res->msgs[c + 1], SYSDB_SID_STR, NULL); if (cur_sid == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("Missing SID in group entry.\n")); ret = EINVAL; goto done; } key.str = discard_const(cur_sid); ret = hash_lookup(pr_ctx->sid_table, &key, &value); if (ret == HASH_SUCCESS) { /* user is already member of the group */ ret = hash_delete(pr_ctx->sid_table, &key); if (ret != HASH_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, ("Failed to remove hash entry.\n")); ret = EIO; goto done; } } else if (ret == HASH_ERROR_KEY_NOT_FOUND) { /* group is not in the PAC anymore, membership must be removed */ del_grp_list[del_idx].gid = ldb_msg_find_attr_as_uint64(res->msgs[c + 1], SYSDB_GIDNUM, 0); if (del_grp_list[del_idx].gid == 0) { DEBUG(SSSDBG_OP_FAILURE, ("Missing GID.\n")); ret = EINVAL; goto done; } tmp_str = ldb_msg_find_attr_as_string(res->msgs[c + 1], SYSDB_ORIG_DN, NULL); if (tmp_str != NULL) { del_grp_list[del_idx].orig_dn = talloc_strdup(del_grp_list, tmp_str); if (del_grp_list[del_idx].orig_dn == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); ret = ENOMEM; goto done; } } del_grp_list[del_idx].dn = ldb_dn_copy(del_grp_list, res->msgs[c + 1]->dn); if (del_grp_list[del_idx].dn == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("ldb_dn_copy failed.\n")); ret = ENOMEM; goto done; } del_idx++; } } del_grp_count = del_idx; } add_sid_count = hash_count(pr_ctx->sid_table); if (add_sid_count > 0) { add_sids = talloc_array(tmp_ctx, char *, add_sid_count); if (add_sids == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n")); ret = ENOMEM; goto done; } iter = new_hash_iter_context(pr_ctx->sid_table); c = 0; while ((entry = iter->next(iter)) != NULL) { if (strcmp(entry->key.str, pr_ctx->user_sid_str) != 0) { add_sids[c] = talloc_strdup(add_sids, entry->key.str); if (add_sids[c] == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); ret = ENOMEM; goto done; } c++; } } add_sid_count = c; } *_del_grp_count = del_grp_count; *_del_grp_list = talloc_steal(mem_ctx, del_grp_list); *_add_sid_count = add_sid_count; *_add_sids = talloc_steal(mem_ctx, add_sids); ret = EOK; done: talloc_free(iter); talloc_free(tmp_ctx); return ret; } static errno_t save_pac_user(struct pac_req_ctx *pr_ctx) { struct sysdb_ctx *sysdb; int ret; const char *attrs[] = {SYSDB_NAME, SYSDB_NAME_ALIAS, SYSDB_UIDNUM, SYSDB_GIDNUM, SYSDB_GECOS, SYSDB_HOMEDIR, SYSDB_SHELL, NULL}; struct ldb_message *msg; struct passwd *pwd = NULL; TALLOC_CTX *tmp_ctx = NULL; struct sysdb_attrs *user_attrs = NULL; sysdb = pr_ctx->dom->sysdb; if (sysdb == NULL) { ret = EINVAL; DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for this domain!\n")); goto done; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { ret = ENOMEM; DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); goto done; } ret = get_pwd_from_pac(tmp_ctx, pr_ctx->dom, pr_ctx->user_sid_str, pr_ctx->primary_group_sid_str, pr_ctx->sid_table, pr_ctx->logon_info, &pwd, &user_attrs); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n")); goto done; } ret = sysdb_search_user_by_uid(tmp_ctx, sysdb, pr_ctx->dom, pwd->pw_uid, attrs, &msg); if (ret == EOK) { if (new_and_cached_user_differs(pwd, msg)) { ret = sysdb_delete_user(sysdb, pr_ctx->dom, NULL, pwd->pw_uid); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_delete_user failed.\n")); goto done; } } else { goto done; } } else if (ret != EOK && ret != ENOENT) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_user_by_name failed.\n")); goto done; } ret = sysdb_store_user(sysdb, pr_ctx->dom, pwd->pw_name, NULL, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell, NULL, user_attrs, NULL, pr_ctx->dom->user_timeout, 0); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_user failed [%d][%s].\n", ret, strerror(ret))); goto done; } ret = EOK; done: talloc_free(tmp_ctx); return ret; } struct pac_save_memberships_state { size_t sid_iter; struct ldb_dn *user_dn; struct pac_req_ctx *pr_ctx; }; static errno_t pac_save_memberships_delete(struct pac_save_memberships_state *state); struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx) { struct pac_save_memberships_state *state; struct sss_domain_info *dom = pr_ctx->dom; struct tevent_req *req; errno_t ret; char *fq_name = NULL; req = tevent_req_create(pr_ctx, &state, struct pac_save_memberships_state); if (req == NULL) { return NULL; } state->sid_iter = 0; if (IS_SUBDOMAIN(dom)) { fq_name = sss_tc_fqname(pr_ctx, pr_ctx->dom->names, pr_ctx->dom, pr_ctx->user_name); if (fq_name == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_sprintf failed.\n")); ret = ENOMEM; goto done; } state->user_dn = sysdb_user_dn(dom->sysdb, state, dom, fq_name); } else { state->user_dn = sysdb_user_dn(dom->sysdb, state, dom, pr_ctx->user_name); } if (state->user_dn == NULL) { ret = ENOMEM; goto done; } state->pr_ctx = pr_ctx; ret = pac_save_memberships_delete(state); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("pac_save_memberships_delete failed.\n")); goto done; } ret = pac_save_memberships_next(req); if (ret == EOK) { tevent_req_done(req); tevent_req_post(req, pr_ctx->cctx->ev); } done: talloc_free(fq_name); if (ret != EOK && ret != EAGAIN) { tevent_req_error(req, ret); tevent_req_post(req, pr_ctx->cctx->ev); } return req; } static errno_t pac_save_memberships_delete(struct pac_save_memberships_state *state) { int ret; int sret; size_t c; struct pac_req_ctx *pr_ctx; bool in_transaction = false; TALLOC_CTX *tmp_ctx; struct sysdb_attrs *user_attrs = NULL; pr_ctx = state->pr_ctx; if (pr_ctx->del_grp_count == 0) { return EOK; } if (pr_ctx->del_grp_list == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("Missing group list.\n")); return EINVAL; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); return ENOMEM; } user_attrs = sysdb_new_attrs(tmp_ctx); if (user_attrs == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_new_attrs failed.\n")); ret = ENOMEM; goto done; } ret = sysdb_transaction_start(pr_ctx->dom->sysdb); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_transaction_start failed.\n")); goto done; } in_transaction = true; for (c = 0; c < pr_ctx->del_grp_count; c++) { ret = sysdb_mod_group_member(pr_ctx->dom->sysdb, state->user_dn, pr_ctx->del_grp_list[c].dn, LDB_FLAG_MOD_DELETE); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_mod_group_member failed.\n")); goto done; } if (pr_ctx->del_grp_list[c].orig_dn != NULL) { ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF, pr_ctx->del_grp_list[c].orig_dn); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n")); goto done; } } } ret = sysdb_set_entry_attr(pr_ctx->dom->sysdb, state->user_dn, user_attrs, LDB_FLAG_MOD_DELETE); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_set_entry_attr failed.\n")); goto done; } ret = sysdb_transaction_commit(pr_ctx->dom->sysdb); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_transaction_commit failed.\n")); goto done; } in_transaction = false; ret = EOK; done: if (in_transaction) { sret = sysdb_transaction_cancel(pr_ctx->dom->sysdb); if (sret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_transaction_cancel failed.\n")); } } talloc_free(tmp_ctx); return ret; } static errno_t pac_save_memberships_next(struct tevent_req *req) { errno_t ret; uint32_t gid; char *sid; struct sss_domain_info *grp_dom; struct tevent_req *subreq; struct pac_save_memberships_state *state; struct pac_req_ctx *pr_ctx; hash_key_t key; hash_value_t value; key.type = HASH_KEY_STRING; state = tevent_req_data(req, struct pac_save_memberships_state); pr_ctx = state->pr_ctx; if (pr_ctx->add_sid_count == 0) { return EOK; } if (pr_ctx->add_sids == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("Missing list of SIDs.\n")); return EINVAL; } while (state->sid_iter < pr_ctx->add_sid_count) { sid = pr_ctx->add_sids[state->sid_iter]; ret = responder_get_domain_by_id(pr_ctx->pac_ctx->rctx, sid, &grp_dom); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("responder_get_domain_by_id failed.\n")); return ret; } key.str = sid; ret = hash_lookup(pr_ctx->sid_table, &key, &value); if (ret != HASH_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, ("hash_lookup failed.\n")); return EIO; } gid = value.ul; ret = pac_store_membership(state->pr_ctx, state->user_dn, gid, grp_dom); if (ret == EOK) { state->sid_iter++; continue; } else if (ret == ENOENT) { subreq = sss_dp_get_account_send(state, pr_ctx->cctx->rctx, grp_dom, true, SSS_DP_GROUP, NULL, gid, NULL); if (subreq == NULL) { ret = ENOMEM; goto done; } tevent_req_set_callback(subreq, pac_get_group_done, req); return EAGAIN; } else { goto done; } } ret = EOK; done: return ret; } static void pac_get_group_done(struct tevent_req *subreq) { struct tevent_req *req; struct pac_save_memberships_state *state; req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct pac_save_memberships_state); errno_t ret; dbus_uint16_t err_maj; dbus_uint32_t err_min; char *err_msg; gid_t gid; char *sid; struct sss_domain_info *grp_dom; struct pac_req_ctx *pr_ctx = state->pr_ctx; hash_key_t key; hash_value_t value; key.type = HASH_KEY_STRING; ret = sss_dp_get_account_recv(req, subreq, &err_maj, &err_min, &err_msg); talloc_zfree(subreq); talloc_zfree(err_msg); if (ret != EOK) { goto error; } sid = pr_ctx->add_sids[state->sid_iter]; ret = responder_get_domain_by_id(pr_ctx->pac_ctx->rctx,sid, &grp_dom); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("responder_get_domain_by_id failed.\n")); goto error; } key.str = sid; ret = hash_lookup(pr_ctx->sid_table, &key, &value); if (ret != HASH_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, ("hash_lookup failed.\n")); ret = EIO; goto error; } gid = value.ul; ret = pac_store_membership(state->pr_ctx, state->user_dn, gid, grp_dom); if (ret != EOK) { goto error; } state->sid_iter++; ret = pac_save_memberships_next(req); if (ret == EOK) { tevent_req_done(req); } else if (ret != EAGAIN) { goto error; } return; error: tevent_req_error(req, ret); } static errno_t pac_store_membership(struct pac_req_ctx *pr_ctx, struct ldb_dn *user_dn, gid_t gid, struct sss_domain_info *grp_dom) { TALLOC_CTX *tmp_ctx; struct sysdb_attrs *user_attrs; struct ldb_message *group; errno_t ret; const char *orig_group_dn; const char *group_attrs[] = { SYSDB_ORIG_DN, NULL }; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } ret = sysdb_search_group_by_gid(tmp_ctx, grp_dom->sysdb, grp_dom, gid, group_attrs, &group); if (ret != EOK) { DEBUG(SSSDBG_TRACE_INTERNAL, ("sysdb_search_group_by_gid for gid [%d]" \ "failed [%d][%s].\n", gid, ret, strerror(ret))); goto done; } ret = sysdb_mod_group_member(grp_dom->sysdb, user_dn, group->dn, LDB_FLAG_MOD_ADD); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_mod_group_member failed.\n")); goto done; } orig_group_dn = ldb_msg_find_attr_as_string(group, SYSDB_ORIG_DN, NULL); if (orig_group_dn != NULL) { user_attrs = sysdb_new_attrs(tmp_ctx); if (user_attrs == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_new_attrs failed.\n")); ret = ENOMEM; goto done; } ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF, orig_group_dn); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n")); goto done; } ret = sysdb_set_entry_attr(pr_ctx->dom->sysdb, user_dn, user_attrs, LDB_FLAG_MOD_ADD); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("sysdb_set_entry_attr failed.\n")); goto done; } } done: talloc_free(tmp_ctx); return ret; } static errno_t pac_save_memberships_recv(struct tevent_req *subreq) { TEVENT_REQ_RETURN_ON_ERROR(subreq); return EOK; } static void pac_save_memberships_done(struct tevent_req *req) { struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx); struct cli_ctx *cctx = pr_ctx->cctx; errno_t ret; ret = pac_save_memberships_recv(req); talloc_zfree(req); talloc_free(pr_ctx); pac_cmd_done(cctx, ret); } struct pac_lookup_sids_state { struct pac_ctx *pac_ctx; struct pac_req_ctx *pr_ctx; hash_table_t *sid_table; struct hash_iter_context_t *iter; }; static errno_t pac_lookup_sids_next(struct tevent_req *req); static void pac_lookup_sids_next_done(struct tevent_req *subreq); static struct tevent_req *pac_lookup_sids_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct pac_req_ctx *pr_ctx, struct pac_ctx *pac_ctx, hash_table_t *sid_table) { struct tevent_req *req; struct pac_lookup_sids_state *state; int ret; req = tevent_req_create(mem_ctx, &state, struct pac_lookup_sids_state); if (req == NULL) { return NULL; } state->pac_ctx = pac_ctx; state->pr_ctx = pr_ctx; state->sid_table = sid_table; state->iter = talloc_steal(state, new_hash_iter_context(state->sid_table)); ret = pac_lookup_sids_next(req); if (ret != EAGAIN) { if (ret == EOK) { tevent_req_done(req); } else { tevent_req_error(req, ret); } tevent_req_post(req, ev); } return req; } static errno_t pac_lookup_sids_next(struct tevent_req *req) { struct pac_lookup_sids_state *state; state = tevent_req_data(req, struct pac_lookup_sids_state); hash_entry_t *entry; struct tevent_req *subreq; struct sss_domain_info *dom; int ret; while ((entry = state->iter->next(state->iter)) != NULL) { if (entry->value.ul == 0) { ret = responder_get_domain_by_id(state->pac_ctx->rctx, entry->key.str, &dom); if (ret == EOK && dom != NULL) { subreq = sss_dp_get_account_send(state, state->pr_ctx->cctx->rctx, dom, true, SSS_DP_SECID, entry->key.str, 0, NULL); if (subreq == NULL) { return ENOMEM; } tevent_req_set_callback(subreq, pac_lookup_sids_next_done, req); return EAGAIN; } } } return EOK; } static void pac_lookup_sids_next_done(struct tevent_req *subreq) { struct tevent_req *req; req = tevent_req_callback_data(subreq, struct tevent_req); errno_t ret; dbus_uint16_t err_maj; dbus_uint32_t err_min; char *err_msg; ret = sss_dp_get_account_recv(req, subreq, &err_maj, &err_min, &err_msg); talloc_zfree(subreq); talloc_zfree(err_msg); /* Errors during individual lookups are ignored. */ ret = pac_lookup_sids_next(req); if (ret == EOK) { tevent_req_done(req); } else if (ret != EAGAIN) { tevent_req_error(req, ret); } return; } static errno_t pac_lookup_sids_recv(struct tevent_req *req) { TEVENT_REQ_RETURN_ON_ERROR(req); return EOK; } struct cli_protocol_version *register_cli_protocol_version(void) { static struct cli_protocol_version pac_cli_protocol_version[] = { {1, "2011-04-12", "initial version"}, {0, NULL, NULL} }; return pac_cli_protocol_version; } static struct sss_cmd_table pac_cmds[] = { {SSS_GET_VERSION, sss_cmd_get_version}, {SSS_PAC_ADD_PAC_USER, pac_add_pac_user}, {SSS_CLI_NULL, NULL} }; struct sss_cmd_table *get_pac_cmds(void) { return pac_cmds; }