summaryrefslogtreecommitdiffstats
path: root/src/providers
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2016-02-10 14:59:06 +0100
committerJakub Hrozek <jhrozek@redhat.com>2016-04-13 10:43:18 +0200
commit63b8e826f62d2e8930c872de7d4cc8b5bc15d4a4 (patch)
treeafdc2fb931b8233671a9c79fd9245d65fc971bff /src/providers
parent28f336bdb32db0b89cb98174a3f8e308e4e928db (diff)
downloadsssd-63b8e826f62d2e8930c872de7d4cc8b5bc15d4a4.tar.gz
sssd-63b8e826f62d2e8930c872de7d4cc8b5bc15d4a4.tar.xz
sssd-63b8e826f62d2e8930c872de7d4cc8b5bc15d4a4.zip
AD: process PAC during initgroups request
If there is a recently attached PAC blob in the cached user entry the PAC data is used to update the group memberships data of the user. If there is no PAC attached or if it is too old the other configured methods will be used. Reviewed-by: Pavel Březina <pbrezina@redhat.com>
Diffstat (limited to 'src/providers')
-rw-r--r--src/providers/ad/ad_id.c52
-rw-r--r--src/providers/ad/ad_pac.c666
-rw-r--r--src/providers/ad/ad_pac.h82
-rw-r--r--src/providers/ad/ad_pac_common.c86
4 files changed, 878 insertions, 8 deletions
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index 9832aea97..c464fa948 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -24,6 +24,7 @@
#include "providers/ad/ad_common.h"
#include "providers/ad/ad_id.h"
#include "providers/ad/ad_domain_info.h"
+#include "providers/ad/ad_pac.h"
#include "providers/ldap/sdap_async_enum.h"
#include "providers/ldap/sdap_idmap.h"
@@ -57,6 +58,7 @@ struct ad_handle_acct_info_state {
struct sdap_domain *sdom;
size_t cindex;
struct ad_options *ad_options;
+ bool using_pac;
int dp_error;
const char *err;
@@ -117,10 +119,12 @@ immediate:
static errno_t
ad_handle_acct_info_step(struct tevent_req *req)
{
- struct tevent_req *subreq;
+ struct tevent_req *subreq = NULL;
struct ad_handle_acct_info_state *state = tevent_req_data(req,
struct ad_handle_acct_info_state);
bool noexist_delete = false;
+ struct ldb_message *msg;
+ int ret;
if (state->conn[state->cindex] == NULL) {
return EOK;
@@ -130,14 +134,42 @@ ad_handle_acct_info_step(struct tevent_req *req)
noexist_delete = true;
}
- subreq = sdap_handle_acct_req_send(state, state->ctx->be,
- state->ar, state->ctx,
- state->sdom,
- state->conn[state->cindex],
- noexist_delete);
+
+ state->using_pac = false;
+ if ((state->ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_INITGROUPS) {
+ ret = check_if_pac_is_available(state, state->sdom->dom,
+ state->ar, &msg);
+
+ if (ret == EOK) {
+ /* evaluate PAC */
+ state->using_pac = true;
+ subreq = ad_handle_pac_initgr_send(state, state->ctx->be,
+ state->ar, state->ctx,
+ state->sdom,
+ state->conn[state->cindex],
+ noexist_delete,
+ msg);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ad_handle_pac_initgr_send failed.\n");
+ return ENOMEM;
+ }
+
+ }
+
+ /* Fall through if there is no PAC or any other error */
+ }
+
if (subreq == NULL) {
- return ENOMEM;
+ subreq = sdap_handle_acct_req_send(state, state->ctx->be,
+ state->ar, state->ctx,
+ state->sdom,
+ state->conn[state->cindex],
+ noexist_delete);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
}
+
tevent_req_set_callback(subreq, ad_handle_acct_info_done, req);
return EAGAIN;
}
@@ -154,7 +186,11 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
struct ad_handle_acct_info_state *state = tevent_req_data(req,
struct ad_handle_acct_info_state);
- ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err);
+ if (state->using_pac) {
+ ret = ad_handle_pac_initgr_recv(subreq, &dp_error, &err, &sdap_err);
+ } else {
+ ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err);
+ }
if (dp_error == DP_ERR_OFFLINE
&& state->conn[state->cindex+1] != NULL
&& state->conn[state->cindex]->ignore_mark_offline) {
diff --git a/src/providers/ad/ad_pac.c b/src/providers/ad/ad_pac.c
new file mode 100644
index 000000000..70399ae0b
--- /dev/null
+++ b/src/providers/ad/ad_pac.c
@@ -0,0 +1,666 @@
+/*
+ SSSD
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 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 "util/util.h"
+#include "providers/ad/ad_pac.h"
+#include "providers/ad/ad_common.h"
+#include "providers/ad/ad_id.h"
+#include "providers/ldap/sdap_idmap.h"
+#include "providers/ldap/sdap_async_ad.h"
+
+static errno_t find_user_entry(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ struct be_acct_req *ar,
+ struct ldb_message **_msg)
+{
+ const char *user_attrs[] = { SYSDB_NAME, SYSDB_OBJECTCLASS,
+ SYSDB_PAC_BLOB, SYSDB_PAC_BLOB_EXPIRE,
+ NULL };
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ char *user_name;
+ int ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (dom == NULL || ar == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing arguments.\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ if (ar->extra_value && strcmp(ar->extra_value, EXTRA_NAME_IS_UPN) == 0) {
+ ret = sysdb_search_user_by_upn(tmp_ctx, dom, ar->filter_value,
+ user_attrs, &msg);
+ } else {
+ switch (ar->filter_type) {
+ case BE_FILTER_SECID:
+ ret = sysdb_search_user_by_sid_str(tmp_ctx, dom, ar->filter_value,
+ user_attrs, &msg);
+ break;
+ case BE_FILTER_UUID:
+ ret = sysdb_search_object_by_uuid(tmp_ctx, dom, ar->filter_value,
+ user_attrs, &res);
+
+ if (ret == EOK) {
+ if (res->count == 1) {
+ msg = res->msgs[0];
+ } else {
+ talloc_free(res);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Search by UUID returned multiple results.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ break;
+ case BE_FILTER_NAME:
+
+ user_name = sss_get_domain_name(tmp_ctx, ar->filter_value, dom);
+ if (user_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_domain_name failed.\n");
+ ret = EINVAL;
+ goto done;
+ } else {
+ ret = sysdb_search_user_by_name(tmp_ctx, dom, user_name,
+ user_attrs, &msg);
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unsupported filter type [%d].\n",
+ ar->filter_type);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No user found with filter [%s].\n",
+ ar->filter_value);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Looking up user in cache with filter [%s] failed.\n",
+ ar->filter_value);
+ }
+ goto done;
+ }
+
+ *_msg = talloc_steal(mem_ctx, msg);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t check_if_pac_is_available(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct be_acct_req *ar,
+ struct ldb_message **_msg)
+{
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ uint64_t pac_expires;
+ time_t now;
+ int ret;
+
+ ret = find_user_entry(mem_ctx, dom, ar, &msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_user_entry failed.\n");
+ return ret;
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_PAC_BLOB);
+ if (el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "No PAC available.\n");
+ talloc_free(msg);
+ return ENOENT;
+ }
+
+ pac_expires = ldb_msg_find_attr_as_uint64(msg, SYSDB_PAC_BLOB_EXPIRE, 0);
+ now = time(NULL);
+ if (pac_expires < now) {
+ DEBUG(SSSDBG_TRACE_FUNC, "PAC avaiable but too old.\n");
+ talloc_free(msg);
+ return ENOENT;
+ }
+
+ if (_msg != NULL) {
+ *_msg = msg;
+ }
+
+ return EOK;
+}
+
+errno_t ad_get_sids_from_pac(TALLOC_CTX *mem_ctx,
+ struct sss_idmap_ctx *idmap_ctx,
+ struct PAC_LOGON_INFO *logon_info,
+ char **_user_sid_str,
+ char **_primary_group_sid_str,
+ size_t *_num_sids,
+ char *** _sid_list)
+{
+ int ret;
+ size_t s;
+ struct netr_SamInfo3 *info3;
+ char *sid_str = NULL;
+ char *msid_str = NULL;
+ char *user_dom_sid_str = NULL;
+ size_t user_dom_sid_str_len;
+ enum idmap_error_code err;
+ hash_table_t *sid_table = NULL;
+ hash_key_t key;
+ hash_value_t value;
+ char *rid_start;
+ char *user_sid_str = NULL;
+ char *primary_group_sid_str = NULL;
+ size_t c;
+ size_t num_sids;
+ char **sid_list = NULL;
+ struct hash_iter_context_t *iter = NULL;
+ hash_entry_t *entry;
+ TALLOC_CTX *tmp_ctx;
+
+ if (idmap_ctx == NULL || logon_info == NULL
+ || _num_sids == NULL || _sid_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing parameter.\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ info3 = &logon_info->info3;
+
+ ret = sss_hash_create(tmp_ctx,
+ info3->sidcount + info3->base.groups.count + 2,
+ &sid_table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
+ goto done;
+ }
+
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_ULONG;
+
+ err = sss_idmap_smb_sid_to_sid(idmap_ctx, info3->base.domain_sid,
+ &user_dom_sid_str);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ user_dom_sid_str_len = strlen(user_dom_sid_str);
+ sid_str = talloc_zero_size(tmp_ctx, user_dom_sid_str_len + 12);
+ if (sid_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ rid_start = sid_str + user_dom_sid_str_len;
+
+ memcpy(sid_str, user_dom_sid_str, user_dom_sid_str_len);
+
+ memset(rid_start, '\0', 12);
+ ret = snprintf(rid_start, 12, "-%lu",
+ (unsigned long) info3->base.rid);
+ if (ret < 0 || ret > 12) {
+ DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ user_sid_str = talloc_strdup(tmp_ctx, sid_str);
+ if (user_sid_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key.str = sid_str;
+ value.ul = 0;
+
+ memset(rid_start, '\0', 12);
+ ret = snprintf(rid_start, 12, "-%lu",
+ (unsigned long) info3->base.primary_gid);
+ if (ret < 0 || ret > 12) {
+ DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ primary_group_sid_str = talloc_strdup(tmp_ctx, sid_str);
+ if (primary_group_sid_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key.str = sid_str;
+ value.ul = 0;
+
+ ret = hash_enter(sid_table, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
+ ret, hash_error_string(ret));
+ ret = EIO;
+ goto done;
+ }
+
+
+ for (s = 0; s < info3->base.groups.count; s++) {
+ memset(rid_start, '\0', 12);
+ ret = snprintf(rid_start, 12, "-%lu",
+ (unsigned long) info3->base.groups.rids[s].rid);
+ if (ret < 0 || ret > 12) {
+ DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ key.str = sid_str;
+ value.ul = 0;
+
+ ret = hash_enter(sid_table, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
+ ret, hash_error_string(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ }
+
+ for(s = 0; s < info3->sidcount; s++) {
+ err = sss_idmap_smb_sid_to_sid(idmap_ctx, info3->sids[s].sid,
+ &msid_str);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ key.str = msid_str;
+ value.ul = 0;
+
+ ret = hash_enter(sid_table, &key, &value);
+ sss_idmap_free_sid(idmap_ctx, msid_str);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
+ ret, hash_error_string(ret));
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ num_sids = hash_count(sid_table);
+ sid_list = talloc_array(tmp_ctx, char *, num_sids);
+ if (sid_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ iter = new_hash_iter_context(sid_table);
+ if (iter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ c = 0;
+ while ((entry = iter->next(iter)) != NULL) {
+ sid_list[c] = talloc_strdup(sid_list, entry->key.str);
+ if (sid_list[c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ c++;
+ }
+
+ ret = EOK;
+
+done:
+ sss_idmap_free_sid(idmap_ctx, user_dom_sid_str);
+ hash_destroy(sid_table);
+
+ if (ret == EOK) {
+ *_sid_list = talloc_steal(mem_ctx, sid_list);
+ *_user_sid_str = talloc_steal(mem_ctx, user_sid_str);
+ *_num_sids = num_sids;
+ *_primary_group_sid_str = talloc_steal(mem_ctx, primary_group_sid_str);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t ad_get_pac_data_from_user_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct sss_idmap_ctx *idmap_ctx,
+ char **_username,
+ char **user_sid,
+ char **primary_group_sid,
+ size_t *num_sids,
+ char ***group_sids)
+{
+ int ret;
+ struct ldb_message_element *el;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ const char *dummy;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *username;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_PAC_BLOB);
+ if (el == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing PAC blob.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (el->num_values != 1) {
+ DEBUG(SSSDBG_OP_FAILURE, "Expected only one PAC blob.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = ad_get_data_from_pac(tmp_ctx, el->values[0].data,
+ el->values[0].length,
+ &logon_info);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_data_from_pac failed.\n");
+ goto done;
+ }
+
+ dummy = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (dummy == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing user name in cache entry.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ username = talloc_strdup(tmp_ctx, dummy);
+ if (username == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ad_get_sids_from_pac(mem_ctx, idmap_ctx, logon_info,
+ user_sid, primary_group_sid,
+ num_sids, group_sids);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_sids_from_pac failed.\n");
+ goto done;
+ }
+
+ *_username = talloc_steal(mem_ctx, username);
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct ad_handle_pac_initgr_state {
+ struct be_acct_req *ar;
+ const char *err;
+ int dp_error;
+ int sdap_ret;
+
+ size_t num_missing_sids;
+ char **missing_sids;
+ size_t num_cached_groups;
+ char **cached_groups;
+ char *username;
+ struct sss_domain_info *user_dom;
+};
+
+static void ad_handle_pac_initgr_lookup_sids_done(struct tevent_req *subreq);
+
+struct tevent_req *ad_handle_pac_initgr_send(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct be_acct_req *ar,
+ struct sdap_id_ctx *id_ctx,
+ struct sdap_domain *sdom,
+ struct sdap_id_conn_ctx *conn,
+ bool noexist_delete,
+ struct ldb_message *msg)
+{
+ int ret;
+ struct ad_handle_pac_initgr_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ char *user_sid;
+ char *primary_group_sid;
+ size_t num_sids;
+ char **group_sids;
+ bool use_id_mapping;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ad_handle_pac_initgr_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+ state->user_dom = sdom->dom;
+
+ /* The following variables are currently unused because no sub-request
+ * returns any of them. But they are needed to allow the same signature as
+ * sdap_handle_acct_req_recv() from the alternative group-membership
+ * lookup path. */
+ state->err = NULL;
+ state->dp_error = DP_ERR_OK;
+ state->sdap_ret = EOK;
+
+ ret = ad_get_pac_data_from_user_entry(state, msg,
+ id_ctx->opts->idmap_ctx->map,
+ &state->username,
+ &user_sid, &primary_group_sid,
+ &num_sids, &group_sids);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ad_get_pac_data_from_user_entry failed.\n");
+ goto done;
+ }
+
+ use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
+ id_ctx->opts->idmap_ctx,
+ sdom->dom->name,
+ sdom->dom->domain_id);
+ if (use_id_mapping
+ && sdom->dom->ignore_group_members == false) {
+ /* In contrast to the tokenGroups based group-membership lookup the
+ * PAC based approach can be used for sub-domains with id-mapping as
+ * well because the PAC will only contain groups which are valid in
+ * the target domain, i.e. it will not contain domain-local groups for
+ * domains other than the user domain. This means the groups must not
+ * be looked up immediately to determine if they are domain-local or
+ * not.
+ *
+ * Additionally, as a temporary workaround until
+ * https://fedorahosted.org/sssd/ticket/2522 is fixed, we also fetch
+ * the group object if group members are ignored to avoid having to
+ * transfer and retain members when the fake tokengroups object
+ * without name is replaced by the full group object.
+ */
+
+ DEBUG(SSSDBG_TRACE_ALL, "Running PAC processing with id-mapping.\n");
+
+ ret = sdap_ad_save_group_membership_with_idmapping(state->username,
+ sdom->dom,
+ id_ctx->opts->idmap_ctx,
+ num_sids, group_sids);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sdap_ad_save_group_membership_with_idmapping failed.\n");
+ }
+
+ /* this path only includes cache operation, so we can finish the
+ * request immediately */
+ goto done;
+ } else {
+
+ DEBUG(SSSDBG_TRACE_ALL, "Running PAC processing with external IDs.\n");
+
+ ret = sdap_ad_tokengroups_get_posix_members(state, sdom->dom,
+ num_sids, group_sids,
+ &state->num_missing_sids,
+ &state->missing_sids,
+ &state->num_cached_groups,
+ &state->cached_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sdap_ad_tokengroups_get_posix_members failed.\n");
+ goto done;
+ }
+
+ /* download missing SIDs */
+ subreq = sdap_ad_resolve_sids_send(state, be_ctx->ev, id_ctx,
+ conn,
+ id_ctx->opts, sdom->dom,
+ state->missing_sids);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_resolve_sids_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ad_handle_pac_initgr_lookup_sids_done,
+ req);
+
+ }
+
+ return req;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, be_ctx->ev);
+
+ return req;
+}
+
+static void ad_handle_pac_initgr_lookup_sids_done(struct tevent_req *subreq)
+{
+ struct ad_handle_pac_initgr_state *state;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+ char **cached_groups;
+ size_t num_cached_groups;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ad_handle_pac_initgr_state);
+
+ ret = sdap_ad_resolve_sids_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to resolve missing SIDs "
+ "[%d]: %s\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sdap_ad_tokengroups_get_posix_members(state, state->user_dom,
+ state->num_missing_sids,
+ state->missing_sids,
+ NULL, NULL,
+ &num_cached_groups,
+ &cached_groups);
+ if (ret != EOK){
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sdap_ad_tokengroups_get_posix_members failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ state->cached_groups = concatenate_string_array(state,
+ state->cached_groups,
+ state->num_cached_groups,
+ cached_groups,
+ num_cached_groups);
+ if (state->cached_groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* update membership of existing groups */
+ ret = sdap_ad_tokengroups_update_members(state->username,
+ state->user_dom->sysdb,
+ state->user_dom,
+ state->cached_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Membership update failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t ad_handle_pac_initgr_recv(struct tevent_req *req,
+ int *_dp_error, const char **_err,
+ int *sdap_ret)
+{
+ struct ad_handle_pac_initgr_state *state;
+
+ state = tevent_req_data(req, struct ad_handle_pac_initgr_state);
+
+ if (_dp_error) {
+ *_dp_error = state->dp_error;
+ }
+
+ if (_err) {
+ *_err = state->err;
+ }
+
+ if (sdap_ret) {
+ *sdap_ret = state->sdap_ret;
+ }
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
diff --git a/src/providers/ad/ad_pac.h b/src/providers/ad/ad_pac.h
new file mode 100644
index 000000000..421811615
--- /dev/null
+++ b/src/providers/ad/ad_pac.h
@@ -0,0 +1,82 @@
+/*
+ SSSD
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 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/>.
+*/
+
+#ifndef AD_PAC_H_
+#define AD_PAC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+/* ldb_val is defined as datablob in the Samba header files data_blob.h which
+ * is included via ndr.h -> samba_util.h -> data_blob.h.
+ * To allow proper type checking we have to make sure to keep the original
+ * definition from ldb.h */
+#ifdef ldb_val
+#error Please make sure to include ad_pac.h before ldb.h
+#endif
+#include <ndr.h>
+#include <gen_ndr/krb5pac.h>
+#include <gen_ndr/ndr_krb5pac.h>
+#undef ldb_val
+
+#include "util/util.h"
+#include "providers/ldap/ldap_common.h"
+
+errno_t check_if_pac_is_available(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct be_acct_req *ar,
+ struct ldb_message **_msg);
+
+errno_t ad_get_data_from_pac(TALLOC_CTX *mem_ctx,
+ uint8_t *pac_blob, size_t pac_len,
+ struct PAC_LOGON_INFO **_logon_info);
+
+errno_t ad_get_sids_from_pac(TALLOC_CTX *mem_ctx,
+ struct sss_idmap_ctx *idmap_ctx,
+ struct PAC_LOGON_INFO *logon_info,
+ char **_user_sid_str,
+ char **_primary_group_sid_str,
+ size_t *_num_sids,
+ char *** _sid_list);
+
+errno_t ad_get_pac_data_from_user_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct sss_idmap_ctx *idmap_ctx,
+ char **username,
+ char **user_sid,
+ char **primary_group_sid,
+ size_t *num_sids,
+ char ***group_sids);
+
+struct tevent_req *ad_handle_pac_initgr_send(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct be_acct_req *ar,
+ struct sdap_id_ctx *id_ctx,
+ struct sdap_domain *sdom,
+ struct sdap_id_conn_ctx *conn,
+ bool noexist_delete,
+ struct ldb_message *msg);
+
+errno_t ad_handle_pac_initgr_recv(struct tevent_req *req,
+ int *_dp_error, const char **_err,
+ int *sdap_ret);
+
+#endif /* AD_PAC_H_ */
diff --git a/src/providers/ad/ad_pac_common.c b/src/providers/ad/ad_pac_common.c
new file mode 100644
index 000000000..64c7ba481
--- /dev/null
+++ b/src/providers/ad/ad_pac_common.c
@@ -0,0 +1,86 @@
+/*
+ SSSD
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 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 "providers/ad/ad_pac.h"
+#include "util/util.h"
+
+errno_t ad_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;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ blob.data = pac_blob;
+ blob.length = pac_len;
+
+ ndr_pull = ndr_pull_init_blob(&blob, tmp_ctx);
+ if (ndr_pull == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; /* FIXME: is this really needed ? */
+
+ pac_data = talloc_zero(tmp_ctx, struct PAC_DATA);
+ if (pac_data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ 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);
+ ret = EBADMSG;
+ goto done;
+ }
+
+ for(c = 0; c < pac_data->num_buffers; c++) {
+ if (pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) {
+ *_logon_info = talloc_steal(mem_ctx,
+ pac_data->buffers[c].info->logon_info.info);
+
+ ret = EOK;
+ goto done;
+ }
+ }
+
+ ret = EINVAL;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}