diff options
author | Sumit Bose <sbose@redhat.com> | 2011-05-18 13:28:17 +0200 |
---|---|---|
committer | Jan Zeleny <jzeleny@redhat.com> | 2012-05-22 11:14:57 -0400 |
commit | 63442fce76ded69ba17d18457890ee059fc6e92a (patch) | |
tree | b0d13011f2bb07ce4430106400bdc4cc9f3eaf27 | |
parent | 288ec395858e387c03476164af8d56671414427c (diff) | |
download | sssd-63442fce76ded69ba17d18457890ee059fc6e92a.tar.gz sssd-63442fce76ded69ba17d18457890ee059fc6e92a.tar.xz sssd-63442fce76ded69ba17d18457890ee059fc6e92a.zip |
Add server-side pac support
-rw-r--r-- | src/responder/pac/pacsrv.c | 2 | ||||
-rw-r--r-- | src/responder/pac/pacsrv_cmd.c | 594 | ||||
-rw-r--r-- | src/sss_client/sss_cli.h | 3 |
3 files changed, 598 insertions, 1 deletions
diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c index d1e77ea14..98d1aabff 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 892ef8568..3f2420273 100644 --- a/src/responder/pac/pacsrv_cmd.c +++ b/src/responder/pac/pacsrv_cmd.c @@ -25,6 +25,599 @@ #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" + +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 domsid_rid_to_uid(struct dom_sid2 *domsid, uint32_t rid, + uid_t *uid) +{ + /* Replace with a proper mapping */ + *uid = (uid_t) rid + PAC_USER_OFFSET; + return EOK; +} + +static errno_t domsid_rid_to_gid(struct dom_sid2 *domsid, uint32_t rid, + gid_t *gid) +{ + /* Replace with a proper mapping */ + *gid = (gid_t) rid + PAC_USER_OFFSET; + return EOK; +} + +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) +{ + 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; + + 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 EOK; + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + *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; + } + + *_pwd = pwd; + + rids = talloc_array(mem_ctx, uint32_t, base_info->groups.count); + if (rids == NULL) { + ret = ENOMEM; + 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++; + } + } + + *_rids = rids; + *_rid_count = j; + + return EOK; + } + } + + ret = EINVAL; + +done: + talloc_free(pwd); + return ret; +} + +struct pac_req_ctx { + struct cli_ctx *cctx; + struct passwd *pwd; + char *domain_name; + struct sss_domain_info *dom; + + int rid_count; + uint32_t *rids; + struct dom_sid2 *domain_sid; +}; + +static void pac_get_domains_done(struct tevent_req *req); +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); +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; + + 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); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n")); + goto done; + } + if (pr_ctx->domain_name == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("No domain name in PAC")); + ret = EINVAL; + 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 = 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; + +done: + 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) { + ret = ENOENT; + 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); + + return; + +done: + pac_cmd_done(cctx, ret); +} + +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; + + 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; + } + + 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; + } + +done: + return pac_cmd_done(cctx, ret); +} + +struct pac_save_memberships_state { + int rid_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->rid_iter = 0; + state->user_dn = sysdb_user_dn(dom->sysdb, state, dom->name, + pr_ctx->pwd->pw_name); + if (state->user_dn == NULL) { + ret = ENOMEM; + goto done; + } + state->pr_ctx = pr_ctx; + + /* Remote users are member 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); + +done: + if (ret != EOK && ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, pr_ctx->cctx->ev); + } + + return req; +} + +errno_t pac_save_memberships_next(struct tevent_req *req) +{ + errno_t ret; + uint32_t gid; + struct tevent_req *subreq; + struct ldb_message *group; + 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->rid_iter < pr_ctx->rid_count) { + gid = pr_ctx->rids[state->rid_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); + if (ret != EOK) { + goto done; + } + state->rid_iter++; + continue; + } else if (ret != ENOENT) { + goto done; + } + + 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; + } + + 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->rid_iter); + if (ret != EOK) { + goto error; + } + state->rid_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 rid_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->rids[rid_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; +} + +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 +630,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 854963191..70e4f1fe3 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -176,6 +176,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. |