/* SSSD PAC Responder Copyright (C) Sumit Bose 2012, 2016 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 "responder/common/cache_req/cache_req.h" #include "confdb/confdb.h" #include "providers/ad/ad_pac.h" static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret) { struct cli_protocol *pctx; int ret; if (cmd_ret == EAGAIN) { /* async processing, just return here */ return EOK; } pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), &pctx->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(pctx->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; struct sss_domain_info *dom; uint8_t *blob; size_t blen; struct PAC_LOGON_INFO *logon_info; char *user_sid_str; char *user_dom_sid_str; }; static errno_t pac_resolve_user_sid_next(struct pac_req_ctx *pr_ctx); static void pac_resolve_user_sid_done(struct tevent_req *req); static void pac_get_domains_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; struct cli_protocol *pctx; pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); sss_packet_get_body(pctx->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->blob = body; pr_ctx->blen = blen; 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 = ad_get_data_from_pac(pr_ctx, body, blen, &pr_ctx->logon_info); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_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\n"); ret = EINVAL; 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; } talloc_steal(pr_ctx, pr_ctx->user_dom_sid_str); pr_ctx->user_sid_str = talloc_asprintf(pr_ctx, "%s-%"PRIu32, pr_ctx->user_dom_sid_str, pr_ctx->logon_info->info3.base.rid); if (pr_ctx->user_sid_str == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ret = ENOMEM; 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_user_sid_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_user_sid_next(pr_ctx); done: if (ret != EAGAIN) { talloc_free(pr_ctx); } pac_cmd_done(cctx, ret); } static errno_t pac_resolve_user_sid_next(struct pac_req_ctx *pr_ctx) { int ret; struct tevent_req *req; const char *pw_attrs[] = SYSDB_PW_ATTRS; req = cache_req_object_by_sid_send(pr_ctx, pr_ctx->cctx->ev, pr_ctx->cctx->rctx, pr_ctx->pac_ctx->rctx->ncache, 0, pr_ctx->dom->name, pr_ctx->user_sid_str, pw_attrs); if (req == NULL) { DEBUG(SSSDBG_OP_FAILURE, "cache_req_object_by_sid_send failed.\n"); return ENOMEM; } tevent_req_set_callback(req, pac_resolve_user_sid_done, pr_ctx); ret = EAGAIN; return ret; } static void pac_resolve_user_sid_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; struct cache_req_result *result; struct sysdb_attrs *user_attrs; ret = cache_req_object_by_sid_recv(pr_ctx, req, &result); talloc_zfree(req); if (ret != EOK) { talloc_free(pr_ctx); pac_cmd_done(cctx, ret); return; } user_attrs = sysdb_new_attrs(pr_ctx); if (user_attrs == NULL) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); ret = ENOMEM; goto done; } ret = sysdb_attrs_add_mem(user_attrs, SYSDB_PAC_BLOB, pr_ctx->blob, pr_ctx->blen); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_mem failed"); goto done; } ret = sysdb_attrs_add_time_t(user_attrs, SYSDB_PAC_BLOB_EXPIRE, (time(NULL) + pr_ctx->pac_ctx->pac_lifetime)); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_time_t failed"); goto done; } ret = sysdb_set_entry_attr(result->domain->sysdb, result->msgs[0]->dn, user_attrs, SYSDB_MOD_REP); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n"); goto done; } done: talloc_free(pr_ctx); pac_cmd_done(cctx, ret); return; } 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; }