From c1851b5fa4416eacf86dd81cfa925597c866859a Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Wed, 18 May 2011 13:28:17 +0200 Subject: [PATCH 3/7] PAC responder: add the core functionality This adds support for parsing PAC and storing information contained within. In particular the user and all his memberships are stored. In case it is necessary, getgrgid() requests are sent to provider for group resolution. --- src/responder/pac/pacsrv.c | 2 +- src/responder/pac/pacsrv_cmd.c | 875 ++++++++++++++++++++++++++++++++++++++++ src/sss_client/sss_cli.h | 3 + 3 files changed, 879 insertions(+), 1 deletions(-) diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c index feee3ae..b21bb96 100644 --- a/src/responder/pac/pacsrv.c +++ b/src/responder/pac/pacsrv.c @@ -48,7 +48,7 @@ struct sbus_method monitor_pac_methods[] = { { MON_CLI_METHOD_PING, monitor_common_pong }, { MON_CLI_METHOD_RES_INIT, monitor_common_res_init }, - { MON_CLI_METHOD_ROTATE, monitor_common_rotate_logs }, + { MON_CLI_METHOD_ROTATE, responder_logrotate }, { NULL, NULL } }; diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c index 892ef85..f86d107 100644 --- a/src/responder/pac/pacsrv_cmd.c +++ b/src/responder/pac/pacsrv_cmd.c @@ -19,12 +19,886 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include +#include +#include +#include +#include #include "util/util.h" +#include "util/sss_nss.h" #include "responder/pac/pacsrv.h" #include "confdb/confdb.h" #include "db/sysdb.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; +} + +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) +{ + 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; + + 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 = 0; + size_t s; + struct netr_SamInfo3 *info3; + gid_t *gids = NULL; + + info3 = &logon_info->info3; + + 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 PAC_LOGON_INFO **_logon_info) +{ + DATA_BLOB blob; + struct ndr_pull *ndr_pull; + struct PAC_DATA *pac_data; + enum ndr_err_code ndr_err; + size_t c; + int ret; + + blob.data = pac_blob; + blob.length = pac_len; + + ndr_pull = ndr_pull_init_blob(&blob, mem_ctx); + if (ndr_pull == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_init_blob failed.\n")); + return ENOMEM; + } + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; /* FIXME: is this really needed ? */ + + pac_data = talloc_zero(mem_ctx, struct PAC_DATA); + if (pac_data == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); + return ENOMEM; + } + + ndr_err = ndr_pull_PAC_DATA(ndr_pull, NDR_SCALARS|NDR_BUFFERS, pac_data); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_PAC_DATA failed [%d]\n", ndr_err)); + return EBADMSG; + } + + for(c = 0; c < pac_data->num_buffers; c++) { + if (pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) { + *_logon_info = pac_data->buffers[c].info->logon_info.info; + + return EOK; + } + } + + ret = EINVAL; + + talloc_free(pac_data); + return ret; +} + +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 = talloc_zero(mem_ctx, struct passwd); + if (pwd == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); + return ENOMEM; + } + + base_info = &logon_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; + } + + 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; + } + + 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; + } + + + 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; + } + } + + pwd->pw_shell = NULL; /* Using default */ + + *_pwd = pwd; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(pwd); + } + + return ret; +} + +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 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); +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 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); + + +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; + + 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; + } + + 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, + 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; + } + + ret = pac_add_user_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; + } + + pr_ctx->dom = responder_get_domain(pr_ctx, cctx->rctx, pr_ctx->domain_name); + if (pr_ctx->dom == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("Corresponding domain [%s] has not been " + "found\n", pr_ctx->domain_name)); + ret = ENOENT; + goto done; + } + + ret = pac_add_user_next(pr_ctx); + +done: + if (ret != EAGAIN) { + talloc_free(pr_ctx); + } + 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; + goto done; + } + + tevent_req_set_callback(req, pac_save_memberships_done, pr_ctx); + + ret = EAGAIN; + +done: + 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_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) { + 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_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, + 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; + } + } else { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_user_by_name failed.\n")); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +struct pac_save_memberships_state { + int gid_iter; + struct ldb_dn *user_dn; + + struct pac_req_ctx *pr_ctx; + struct sss_domain_info *group_dom; +}; + +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; + + req = tevent_req_create(pr_ctx, &state, struct pac_save_memberships_state); + if (req == NULL) { + return NULL; + } + + state->gid_iter = 0; + state->user_dn = sysdb_user_dn(dom->sysdb, state, dom->name, + pr_ctx->user_name); + if (state->user_dn == NULL) { + ret = ENOMEM; + goto done; + } + state->pr_ctx = pr_ctx; + + /* Remote users are members of local groups */ + if (pr_ctx->dom->parent != NULL) { + state->group_dom = pr_ctx->dom->parent; + } else { + state->group_dom = pr_ctx->dom; + } + + 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) { + tevent_req_error(req, ret); + tevent_req_post(req, pr_ctx->cctx->ev); + } + + return req; +} + +static errno_t pac_save_memberships_next(struct tevent_req *req) +{ + errno_t ret; + uint32_t gid; + struct tevent_req *subreq; + struct pac_save_memberships_state *state; + struct pac_req_ctx *pr_ctx; + + state = tevent_req_data(req, struct pac_save_memberships_state); + pr_ctx = state->pr_ctx; + + while (state->gid_iter < pr_ctx->gid_count) { + gid = pr_ctx->gids[state->gid_iter]; + + ret = pac_store_memberships(state->pr_ctx, state->group_dom->sysdb, + state->user_dn, state->gid_iter); + if (ret == EOK) { + state->gid_iter++; + continue; + } else if (ret == ENOENT) { + subreq = sss_dp_get_account_send(state, pr_ctx->cctx->rctx, + state->group_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; + + 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; + } + + ret = pac_store_memberships(state->pr_ctx, state->group_dom->sysdb, + state->user_dn, state->gid_iter); + if (ret != EOK) { + goto error; + } + state->gid_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_memberships(struct pac_req_ctx *pr_ctx, + struct sysdb_ctx *group_sysdb, + struct ldb_dn *user_dn, + int gid_iter) +{ + TALLOC_CTX *tmp_ctx; + const char *group_name; + struct sysdb_attrs *group_attrs; + struct ldb_message *group; + uint32_t gid; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + gid = pr_ctx->gids[gid_iter]; + + ret = sysdb_search_group_by_gid(tmp_ctx, group_sysdb, + gid, NULL, &group); + if (ret != EOK) { + goto done; + } + + group_name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL); + if (group_name == NULL) { + ret = EIO; + goto done; + } + + group_attrs = talloc_zero(tmp_ctx, struct sysdb_attrs); + if (group_attrs == NULL) { + ret = ENOMEM; + goto done; + } + group_attrs->num = 1; + group_attrs->a = ldb_msg_find_element(group, SYSDB_MEMBER); + if (group_attrs->a == NULL) { + group_attrs->a = talloc_zero(group_attrs, struct ldb_message_element); + if (group_attrs->a == NULL) { + ret = ENOMEM; + goto done; + } + group_attrs->a[0].name = talloc_strdup(group_attrs->a, SYSDB_MEMBER); + if (group_attrs->a[0].name == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = sysdb_attrs_add_string(group_attrs, SYSDB_MEMBER, + ldb_dn_get_linearized(user_dn)); + if (ret != EOK) { + goto done; + } + + ret = sysdb_store_group(group_sysdb, group_name, gid, + group_attrs, pr_ctx->dom->group_timeout, 0); + if (ret != EOK) { + 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 cli_protocol_version *register_cli_protocol_version(void) { static struct cli_protocol_version pac_cli_protocol_version[] = { @@ -37,6 +911,7 @@ struct cli_protocol_version *register_cli_protocol_version(void) 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} }; diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 647d233..30018d8 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -175,6 +175,9 @@ enum sss_cli_command { SSS_SSH_GET_USER_PUBKEYS = 0x00E1, SSS_SSH_GET_HOST_PUBKEYS = 0x00E2, +/* PAC responder calls */ + SSS_PAC_ADD_PAC_USER = 0x00E9, + /* PAM related calls */ SSS_PAM_AUTHENTICATE = 0x00F1, /**< see pam_sm_authenticate(3) for * details. -- 1.7.7.6