summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2014-09-05 10:34:01 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-09-30 18:24:53 +0200
commit28c70f003c7b330ab1d998a4eff1248d272a6ba9 (patch)
tree10142a1ee2c8491245bf4e8598014471a1310513
parentf3d4b3e03b1505a539977c86b59ff4aa967580d1 (diff)
downloadsssd-28c70f003c7b330ab1d998a4eff1248d272a6ba9.tar.gz
sssd-28c70f003c7b330ab1d998a4eff1248d272a6ba9.tar.xz
sssd-28c70f003c7b330ab1d998a4eff1248d272a6ba9.zip
IPA: add support for new extdom plugin version
Initially the extdom plugin was only used to translate SIDs of AD user and groups to names or POSIX IDs. On IPA clients group memberships were resolved with the help of the PAC in the Kerberos ticket which required that the user has logged in at least once. Home directory and the login shell were auto generated. The new version of the extdom plugin can return the complete list of group memberships of a user and the list of all members of a group. Additionally the gecos field, home directory and login shell are returned together with an optional list of key-value pairs for arbitrary data which is written unmodified to the cache. Fixes https://fedorahosted.org/sssd/ticket/2159 and https://fedorahosted.org/sssd/ticket/2041 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-rw-r--r--src/providers/ipa/ipa_s2n_exop.c889
1 files changed, 802 insertions, 87 deletions
diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index ccf4c2317..76494a031 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -36,18 +36,22 @@ enum input_types {
enum request_types {
REQ_SIMPLE = 1,
- REQ_FULL
+ REQ_FULL,
+ REQ_FULL_WITH_MEMBERS
};
enum response_types {
RESP_SID = 1,
RESP_NAME,
RESP_USER,
- RESP_GROUP
+ RESP_GROUP,
+ RESP_USER_GROUPLIST,
+ RESP_GROUP_MEMBERS
};
/* ==Sid2Name Extended Operation============================================= */
#define EXOP_SID2NAME_OID "2.16.840.1.113730.3.8.10.4"
+#define EXOP_SID2NAME_V1_OID "2.16.840.1.113730.3.8.10.4.1"
struct ipa_s2n_exop_state {
struct sdap_handle *sh;
@@ -65,6 +69,8 @@ static void ipa_s2n_exop_done(struct sdap_op *op,
static struct tevent_req *ipa_s2n_exop_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_handle *sh,
+ bool is_v1,
+ int timeout,
struct berval *bv)
{
struct tevent_req *req = NULL;
@@ -81,18 +87,19 @@ static struct tevent_req *ipa_s2n_exop_send(TALLOC_CTX *mem_ctx,
DEBUG(SSSDBG_TRACE_FUNC, "Executing extended operation\n");
- ret = ldap_extended_operation(state->sh->ldap, EXOP_SID2NAME_OID,
- bv, NULL, NULL, &msgid);
+ ret = ldap_extended_operation(state->sh->ldap,
+ is_v1 ? EXOP_SID2NAME_V1_OID : EXOP_SID2NAME_OID,
+ bv, NULL, NULL, &msgid);
if (ret == -1 || msgid == -1) {
DEBUG(SSSDBG_CRIT_FAILURE, "ldap_extended_operation failed\n");
ret = ERR_NETWORK_IO;
goto fail;
}
- DEBUG(SSSDBG_TRACE_INTERNAL, "ldap_extended_operation sent, msgid = %d\n", msgid);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "ldap_extended_operation sent, msgid = %d\n",
+ msgid);
- /* FIXME: get timeouts from configuration, for now 10 secs. */
- ret = sdap_op_add(state, ev, state->sh, msgid, ipa_s2n_exop_done, req, 10,
- &state->op);
+ ret = sdap_op_add(state, ev, state->sh, msgid, ipa_s2n_exop_done, req,
+ timeout, &state->op);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up operation!\n");
ret = ERR_INTERNAL;
@@ -129,7 +136,8 @@ static void ipa_s2n_exop_done(struct sdap_op *op,
&result, &errmsg, NULL, NULL,
NULL, 0);
if (ret != LDAP_SUCCESS) {
- DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_result failed (%d)\n", state->op->msgid);
+ DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_result failed (%d)\n",
+ state->op->msgid);
ret = ERR_NETWORK_IO;
goto done;
}
@@ -145,7 +153,8 @@ static void ipa_s2n_exop_done(struct sdap_op *op,
ret = ldap_parse_extended_result(state->sh->ldap, reply->msg,
&retoid, &retdata, 0);
if (ret != LDAP_SUCCESS) {
- DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_extendend_result failed (%d)\n", ret);
+ DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_extendend_result failed (%d)\n",
+ ret);
ret = ERR_NETWORK_IO;
goto done;
}
@@ -250,6 +259,7 @@ done:
* requestType ENUMERATED {
* simple (1),
* full (2)
+ * full_with_members (3)
* },
* data InputData
* }
@@ -370,7 +380,9 @@ done:
* sid (1),
* name (2),
* posix_user (3),
- * posix_group (4)
+ * posix_group (4),
+ * posix_user_grouplist (5),
+ * posix_group_members (6)
* },
* data OutputData
* }
@@ -379,7 +391,10 @@ done:
* sid OCTET STRING,
* name NameDomainData,
* user PosixUser,
- * group PosixGroup
+ * group PosixGroup,
+ * usergrouplist PosixUserGrouplist,
+ * groupmembers PosixGroupMembers
+ *
* }
*
* NameDomainData ::= SEQUENCE {
@@ -400,6 +415,27 @@ done:
* gid INTEGER
* }
*
+ * PosixUserGrouplist ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * user_name OCTET STRING,
+ * uid INTEGER,
+ * gid INTEGER,
+ * gecos OCTET STRING,
+ * home_directory OCTET STRING,
+ * shell OCTET STRING,
+ * grouplist GroupNameList
+ * }
+ *
+ * GroupNameList ::= SEQUENCE OF OCTET STRING
+ *
+ * PosixGroupMembers ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * group_name OCTET STRING,
+ * gid INTEGER,
+ * members GroupMemberList
+ * }
+ *
+ * GroupMemberList ::= SEQUENCE OF OCTET STRING
*/
struct resp_attrs {
@@ -411,8 +447,227 @@ struct resp_attrs {
char *sid_str;
char *name;
} a;
+ size_t ngroups;
+ char **groups;
+ struct sysdb_attrs *sysdb_attrs;
};
+static errno_t get_extra_attrs(BerElement *ber, struct resp_attrs *resp_attrs)
+{
+ ber_tag_t tag;
+ ber_len_t ber_len;
+ char *ber_cookie;
+ char *name;
+ struct berval **values;
+ struct ldb_val v;
+ int ret;
+ size_t c;
+
+ if (resp_attrs->sysdb_attrs == NULL) {
+ resp_attrs->sysdb_attrs = sysdb_new_attrs(resp_attrs);
+ if (resp_attrs->sysdb_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ return ENOMEM;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Found new sequence.\n");
+ for (tag = ber_first_element(ber, &ber_len, &ber_cookie);
+ tag != LBER_DEFAULT;
+ tag = ber_next_element(ber, &ber_len, ber_cookie)) {
+
+ tag = ber_scanf(ber, "{a{V}}", &name, &values);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ return EINVAL;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Extra attribute [%s].\n", name);
+
+ for (c = 0; values[c] != NULL; c++) {
+
+ v.data = (uint8_t *) values[c]->bv_val;
+ v.length = values[c]->bv_len;
+
+ ret = sysdb_attrs_add_val(resp_attrs->sysdb_attrs, name, &v);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n");
+ ldap_memfree(name);
+ ber_bvecfree(values);
+ return ret;
+ }
+ }
+
+ ldap_memfree(name);
+ ber_bvecfree(values);
+ }
+
+ return EOK;
+}
+
+static errno_t add_v1_user_data(BerElement *ber, struct resp_attrs *attrs)
+{
+ ber_tag_t tag;
+ ber_len_t ber_len;
+ int ret;
+ char *gecos = NULL;
+ char *homedir = NULL;
+ char *shell = NULL;
+ char **list = NULL;
+ size_t c;
+
+ tag = ber_scanf(ber, "aaa", &gecos, &homedir, &shell);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (gecos == NULL || *gecos == '\0') {
+ attrs->a.user.pw_gecos = NULL;
+ } else {
+ attrs->a.user.pw_gecos = talloc_strdup(attrs, gecos);
+ if (attrs->a.user.pw_gecos == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (homedir == NULL || *homedir == '\0') {
+ attrs->a.user.pw_dir = NULL;
+ } else {
+ attrs->a.user.pw_dir = talloc_strdup(attrs, homedir);
+ if (attrs->a.user.pw_dir == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (shell == NULL || *shell == '\0') {
+ attrs->a.user.pw_shell = NULL;
+ } else {
+ attrs->a.user.pw_shell = talloc_strdup(attrs, shell);
+ if (attrs->a.user.pw_shell == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tag = ber_scanf(ber, "{v}", &list);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (attrs->ngroups = 0; list[attrs->ngroups] != NULL;
+ attrs->ngroups++);
+
+ if (attrs->ngroups > 0) {
+ attrs->groups = talloc_array(attrs, char *, attrs->ngroups);
+ if (attrs->groups == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < attrs->ngroups; c++) {
+ attrs->groups[c] = talloc_strdup(attrs->groups,
+ list[c]);
+ if (attrs->groups[c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ tag = ber_peek_tag(ber, &ber_len);
+ DEBUG(SSSDBG_TRACE_ALL, "BER tag is [%d]\n", (int) tag);
+ if (tag == LBER_SEQUENCE) {
+ ret = get_extra_attrs(ber, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_extra_attrs failed.\n");
+ goto done;
+ }
+ }
+
+
+ ret = EOK;
+
+done:
+ ber_memfree(gecos);
+ ber_memfree(homedir);
+ ber_memfree(shell);
+ ber_memvfree((void **) list);
+
+ return ret;
+}
+
+static errno_t add_v1_group_data(BerElement *ber, struct resp_attrs *attrs)
+{
+ ber_tag_t tag;
+ ber_len_t ber_len;
+ int ret;
+ char **list = NULL;
+ size_t c;
+
+ tag = ber_scanf(ber, "{v}", &list);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (attrs->ngroups = 0; list[attrs->ngroups] != NULL;
+ attrs->ngroups++);
+
+ if (attrs->ngroups > 0) {
+ attrs->a.group.gr_mem = talloc_zero_array(attrs, char *,
+ attrs->ngroups + 1);
+ if (attrs->a.group.gr_mem == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < attrs->ngroups; c++) {
+ attrs->a.group.gr_mem[c] =
+ talloc_strdup(attrs->a.group.gr_mem,
+ list[c]);
+ if (attrs->a.group.gr_mem[c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ tag = ber_peek_tag(ber, &ber_len);
+ DEBUG(SSSDBG_TRACE_ALL, "BER tag is [%d]\n", (int) tag);
+ if (tag == LBER_SEQUENCE) {
+ ret = get_extra_attrs(ber, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_extra_attrs failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ ber_memvfree((void **) list);
+
+ return ret;
+}
+
+static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
+ struct req_input *req_input,
+ struct resp_attrs *attrs,
+ struct resp_attrs *simple_attrs);
+
static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
char *retoid,
struct berval *retdata,
@@ -428,16 +683,21 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
gid_t gid;
struct resp_attrs *attrs = NULL;
char *sid_str;
+ bool is_v1 = false;
if (retoid == NULL || retdata == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n");
return EINVAL;
}
- if (strcmp(retoid, EXOP_SID2NAME_OID) != 0) {
+ if (strcmp(retoid, EXOP_SID2NAME_V1_OID) == 0) {
+ is_v1 = true;
+ } else if (strcmp(retoid, EXOP_SID2NAME_OID) == 0) {
+ is_v1 = false;
+ } else {
DEBUG(SSSDBG_OP_FAILURE,
- "Result has wrong OID, expected [%s], got [%s].\n",
- EXOP_SID2NAME_OID, retoid);
+ "Result has wrong OID, expected [%s] or [%s], got [%s].\n",
+ EXOP_SID2NAME_OID, EXOP_SID2NAME_V1_OID, retoid);
return EINVAL;
}
@@ -463,7 +723,8 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
switch (type) {
case RESP_USER:
- tag = ber_scanf(ber, "{aaii}}", &domain_name, &name, &uid, &gid);
+ case RESP_USER_GROUPLIST:
+ tag = ber_scanf(ber, "{aaii", &domain_name, &name, &uid, &gid);
if (tag == LBER_ERROR) {
DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
ret = EINVAL;
@@ -485,9 +746,25 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
attrs->a.user.pw_uid = uid;
attrs->a.user.pw_gid = gid;
+ if (is_v1) {
+ ret = add_v1_user_data(ber, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_v1_user_data failed.\n");
+ goto done;
+ }
+ }
+
+ tag = ber_scanf(ber, "}}");
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
break;
case RESP_GROUP:
- tag = ber_scanf(ber, "{aai}}", &domain_name, &name, &gid);
+ case RESP_GROUP_MEMBERS:
+ tag = ber_scanf(ber, "{aai", &domain_name, &name, &gid);
if (tag == LBER_ERROR) {
DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
ret = EINVAL;
@@ -508,6 +785,21 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
attrs->a.group.gr_gid = gid;
+ if (is_v1) {
+ ret = add_v1_group_data(ber, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_v1_group_data failed.\n");
+ goto done;
+ }
+ }
+
+ tag = ber_scanf(ber, "}}");
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
break;
case RESP_SID:
tag = ber_scanf(ber, "a}", &sid_str);
@@ -572,6 +864,166 @@ done:
return ret;
}
+struct ipa_s2n_get_groups_state {
+ struct tevent_context *ev;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ struct req_input req_input;
+ char **group_list;
+ size_t group_idx;
+ int exop_timeout;
+};
+
+static errno_t ipa_s2n_get_groups_step(struct tevent_req *req);
+static void ipa_s2n_get_groups_next(struct tevent_req *subreq);
+
+static struct tevent_req *ipa_s2n_get_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ int exop_timeout,
+ char **group_list)
+{
+ int ret;
+ struct ipa_s2n_get_groups_state *state;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_s2n_get_groups_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->dom = dom;
+ state->sh = sh;
+ state->group_list = group_list;
+ state->group_idx = 0;
+ state->req_input.type = REQ_INP_NAME;
+ state->req_input.inp.name = NULL;
+ state->exop_timeout = exop_timeout;
+
+ ret = ipa_s2n_get_groups_step(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_groups_step failed.\n");
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t ipa_s2n_get_groups_step(struct tevent_req *req)
+{
+ int ret;
+ struct ipa_s2n_get_groups_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_groups_state);
+ struct berval *bv_req;
+ struct tevent_req *subreq;
+ struct sss_domain_info *obj_domain;
+ struct sss_domain_info *parent_domain;
+ char *group_name = NULL;
+ char *domain_name = NULL;
+
+ parent_domain = get_domains_head(state->dom);
+
+ ret = sss_parse_name(state, parent_domain->names,
+ state->group_list[state->group_idx],
+ &domain_name, &group_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n",
+ state->group_list[state->group_idx],
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ obj_domain = find_domain_by_name(parent_domain, domain_name, true);
+ if (obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
+ return ENOMEM;
+ }
+
+ state->req_input.inp.name = group_name;
+
+ ret = s2n_encode_request(state, obj_domain->name, BE_REQ_GROUP,
+ REQ_FULL_WITH_MEMBERS,
+ &state->req_input, &bv_req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_encode_request failed.\n");
+ return ret;
+ }
+
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, true,
+ state->exop_timeout, bv_req);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_groups_next, req);
+
+ return EOK;
+}
+
+static void ipa_s2n_get_groups_next(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_s2n_get_groups_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_groups_state);
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ struct resp_attrs *attrs;
+
+ ret = ipa_s2n_exop_recv(subreq, state, &retoid, &retdata);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n exop request failed.\n");
+ goto fail;
+ }
+
+ ret = s2n_response_to_attrs(state, retoid, retdata, &attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_response_to_attrs failed.\n");
+ goto fail;
+ }
+
+ ret = ipa_s2n_save_objects(state->dom, &state->req_input, attrs, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ goto fail;
+ }
+
+ state->group_idx++;
+ if (state->group_list[state->group_idx] == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ ret = ipa_s2n_get_groups_step(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_groups_step failed.\n");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tevent_req_error(req,ret);
+ return;
+}
+
+static int ipa_s2n_get_groups_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
struct ipa_s2n_get_user_state {
struct tevent_context *ev;
struct sdap_options *opts;
@@ -581,6 +1033,8 @@ struct ipa_s2n_get_user_state {
int entry_type;
enum request_types request_type;
struct resp_attrs *attrs;
+ struct resp_attrs *simple_attrs;
+ int exop_timeout;
};
static void ipa_s2n_get_user_done(struct tevent_req *subreq);
@@ -598,6 +1052,7 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
struct tevent_req *subreq;
struct berval *bv_req = NULL;
int ret = EFAULT;
+ bool is_v1 = false;
req = tevent_req_create(mem_ctx, &state, struct ipa_s2n_get_user_state);
if (req == NULL) {
@@ -610,7 +1065,22 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
state->sh = sh;
state->req_input = req_input;
state->entry_type = entry_type;
- state->request_type = REQ_FULL;
+ state->attrs = NULL;
+ state->simple_attrs = NULL;
+ state->exop_timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
+
+ if (sdap_is_extension_supported(sh, EXOP_SID2NAME_V1_OID)) {
+ state->request_type = REQ_FULL_WITH_MEMBERS;
+ is_v1 = true;
+ } else if (sdap_is_extension_supported(sh, EXOP_SID2NAME_OID)) {
+ state->request_type = REQ_FULL;
+ is_v1 = false;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Extdom not supported on the server, "
+ "cannot resolve objects from trusted domains.\n");
+ ret = EIO;
+ goto fail;
+ }
ret = s2n_encode_request(state, dom->name, entry_type, state->request_type,
req_input, &bv_req);
@@ -618,7 +1088,8 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
goto fail;
}
- subreq = ipa_s2n_exop_send(state, state->ev, state->sh, bv_req);
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, is_v1,
+ state->exop_timeout, bv_req);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
ret = ENOMEM;
@@ -635,6 +1106,159 @@ fail:
return req;
}
+static errno_t process_members(struct sss_domain_info *domain,
+ struct sysdb_attrs *group_attrs,
+ char **members)
+{
+ int ret;
+ size_t c;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ const char *dn_str;
+ struct sss_domain_info *obj_domain;
+ struct sss_domain_info *parent_domain;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ parent_domain = get_domains_head(domain);
+
+ for (c = 0; members[c] != NULL; c++) {
+ obj_domain = find_domain_by_object_name(parent_domain, members[c]);
+ if (obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_object_name failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, obj_domain, members[c], NULL,
+ &msg);
+ if (ret == EOK) {
+ dn_str = ldb_dn_get_linearized(msg->dn);
+ if (dn_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_get_linearized failed.\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Adding member [%s][%s]\n",
+ members[c], dn_str);
+
+ ret = sysdb_attrs_add_string(group_attrs, SYSDB_MEMBER, dn_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ goto done;
+ }
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Adding ghost member [%s]\n", members[c]);
+
+ ret = sysdb_attrs_add_string(group_attrs, SYSDB_GHOST, members[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ 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;
+}
+
+static errno_t get_group_dn_list(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ size_t ngroups, char **groups,
+ struct ldb_dn ***_dn_list,
+ char ***_missing_groups)
+{
+ int ret;
+ size_t c;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn **dn_list = NULL;
+ char **missing_groups = NULL;
+ struct ldb_message *msg = NULL;
+ size_t n_dns = 0;
+ size_t n_missing = 0;
+ struct sss_domain_info *obj_domain;
+ struct sss_domain_info *parent_domain;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ dn_list = talloc_zero_array(tmp_ctx, struct ldb_dn *, ngroups + 1);
+ missing_groups = talloc_zero_array(tmp_ctx, char *, ngroups + 1);
+ if (dn_list == NULL || missing_groups == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ parent_domain = (dom->parent == NULL) ? dom : dom->parent;
+
+ for (c = 0; c < ngroups; c++) {
+ obj_domain = find_domain_by_object_name(parent_domain, groups[c]);
+ if (obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_object_name failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_group_by_name(tmp_ctx, obj_domain, groups[c], NULL,
+ &msg);
+ if (ret == EOK) {
+ dn_list[n_dns] = ldb_dn_copy(dn_list, msg->dn);
+ if (dn_list[n_dns] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ n_dns++;
+ } else if (ret == ENOENT) {
+ missing_groups[n_missing] = talloc_strdup(missing_groups,
+ groups[c]);
+ if (missing_groups[n_missing] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ n_missing++;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_group_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ if (n_missing != 0) {
+ *_missing_groups = talloc_steal(mem_ctx, missing_groups);
+ } else {
+ *_missing_groups = NULL;
+ }
+
+ if (n_dns != 0) {
+ *_dn_list = talloc_steal(mem_ctx, dn_list);
+ } else {
+ *dn_list = NULL;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static void ipa_s2n_get_groups_done(struct tevent_req *subreq);
static void ipa_s2n_get_user_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
@@ -645,18 +1269,9 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
char *retoid = NULL;
struct berval *retdata = NULL;
struct resp_attrs *attrs = NULL;
- struct resp_attrs *simple_attrs = NULL;
- time_t now;
- uint64_t timeout = 10*60*60; /* FIXME: find a better timeout ! */
- struct sss_nss_homedir_ctx homedir_ctx;
- const char *homedir = NULL;
- struct sysdb_attrs *user_attrs = NULL;
- struct sysdb_attrs *group_attrs = NULL;
- char *name;
- char *realm;
- char *upn;
struct berval *bv_req = NULL;
- gid_t gid;
+ char **missing_groups = NULL;
+ struct ldb_dn **group_dn_list = NULL;
ret = ipa_s2n_exop_recv(subreq, state, &retoid, &retdata);
talloc_zfree(subreq);
@@ -666,6 +1281,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
}
switch (state->request_type) {
+ case REQ_FULL_WITH_MEMBERS:
case REQ_FULL:
ret = s2n_response_to_attrs(state, retoid, retdata, &attrs);
if (ret != EOK) {
@@ -688,7 +1304,34 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
state->attrs = attrs;
- if (state->req_input->type == REQ_INP_SECID) {
+ if (attrs->response_type == RESP_USER_GROUPLIST) {
+ ret = get_group_dn_list(state, state->dom,
+ attrs->ngroups, attrs->groups,
+ &group_dn_list, &missing_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_group_dn_list failed.\n");
+ goto done;
+ }
+
+ if (missing_groups != NULL) {
+ subreq = ipa_s2n_get_groups_send(state, state->ev, state->dom,
+ state->sh, state->exop_timeout,
+ missing_groups);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ipa_s2n_get_groups_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_groups_done,
+ req);
+
+ return;
+ }
+ }
+
+ if (state->req_input->type == REQ_INP_SECID
+ || attrs->response_type == RESP_GROUP_MEMBERS) {
/* We already know the SID, we do not have to read it. */
break;
}
@@ -702,7 +1345,8 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
goto done;
}
- subreq = ipa_s2n_exop_send(state, state->ev, state->sh, bv_req);
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, false,
+ state->exop_timeout, bv_req);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
ret = ENOMEM;
@@ -713,7 +1357,8 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
return;
case REQ_SIMPLE:
- ret = s2n_response_to_attrs(state, retoid, retdata, &simple_attrs);
+ ret = s2n_response_to_attrs(state, retoid, retdata,
+ &state->simple_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "s2n_response_to_attrs failed.\n");
goto done;
@@ -730,40 +1375,79 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
DEBUG(SSSDBG_CRIT_FAILURE, "Missing data of full request.\n");
ret = EINVAL;
goto done;
+ }
+
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
} else {
- attrs = state->attrs;
+ tevent_req_error(req, ret);
+ }
+ return;
+}
+
+static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
+ struct req_input *req_input,
+ struct resp_attrs *attrs,
+ struct resp_attrs *simple_attrs)
+{
+ int ret;
+ time_t now;
+ uint64_t timeout = 10*60*60; /* FIXME: find a better timeout ! */
+ struct sss_nss_homedir_ctx homedir_ctx;
+ char *name;
+ char *realm;
+ char *upn;
+ gid_t gid;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
}
now = time(NULL);
+ if (attrs->sysdb_attrs == NULL) {
+ attrs->sysdb_attrs = sysdb_new_attrs(attrs);
+ if (attrs->sysdb_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
switch (attrs->response_type) {
case RESP_USER:
- if (state->dom->subdomain_homedir) {
+ case RESP_USER_GROUPLIST:
+ if (dom->subdomain_homedir
+ && attrs->a.user.pw_dir == NULL) {
ZERO_STRUCT(homedir_ctx);
homedir_ctx.username = attrs->a.user.pw_name;
homedir_ctx.uid = attrs->a.user.pw_uid;
- homedir_ctx.domain = state->dom->name;
- homedir_ctx.flatname = state->dom->flat_name;
- homedir_ctx.config_homedir_substr = state->dom->homedir_substr;
+ homedir_ctx.domain = dom->name;
+ homedir_ctx.flatname = dom->flat_name;
+ homedir_ctx.config_homedir_substr = dom->homedir_substr;
- homedir = expand_homedir_template(state,
- state->dom->subdomain_homedir,
+ attrs->a.user.pw_dir = expand_homedir_template(attrs,
+ dom->subdomain_homedir,
&homedir_ctx);
- if (homedir == NULL) {
+ if (attrs->a.user.pw_dir == NULL) {
ret = ENOMEM;
goto done;
}
}
- user_attrs = sysdb_new_attrs(state);
- if (user_attrs == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
- ret = ENOMEM;
- goto done;
- }
-
/* we always use the fully qualified name for subdomain users */
- name = sss_tc_fqname(state, state->dom->names, state->dom,
+ name = sss_tc_fqname(tmp_ctx, dom->names, dom,
attrs->a.user.pw_name);
if (!name) {
DEBUG(SSSDBG_OP_FAILURE, "failed to format user name.\n");
@@ -771,7 +1455,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
goto done;
}
- ret = sysdb_attrs_add_lc_name_alias(user_attrs, name);
+ ret = sysdb_attrs_add_lc_name_alias(attrs->sysdb_attrs, name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_add_lc_name_alias failed.\n");
@@ -784,13 +1468,13 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
* access to the regex to deconstruct it */
/* FIXME: The real UPN is available from the PAC, we should get
* it from there. */
- realm = get_uppercase_realm(state, state->dom->name);
+ realm = get_uppercase_realm(tmp_ctx, dom->name);
if (!realm) {
DEBUG(SSSDBG_OP_FAILURE, "failed to get realm.\n");
ret = ENOMEM;
goto done;
}
- upn = talloc_asprintf(state, "%s@%s",
+ upn = talloc_asprintf(tmp_ctx, "%s@%s",
attrs->a.user.pw_name, realm);
if (!upn) {
DEBUG(SSSDBG_OP_FAILURE, "failed to format UPN.\n");
@@ -798,44 +1482,49 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
goto done;
}
- ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
+ ret = sysdb_attrs_add_string(attrs->sysdb_attrs, SYSDB_UPN, upn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
goto done;
}
- if (state->req_input->type == REQ_INP_SECID) {
- ret = sysdb_attrs_add_string(user_attrs, SYSDB_SID_STR,
- state->req_input->inp.secid);
+ if (req_input->type == REQ_INP_SECID) {
+ ret = sysdb_attrs_add_string(attrs->sysdb_attrs, SYSDB_SID_STR,
+ req_input->inp.secid);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
goto done;
}
}
- if (simple_attrs != NULL && simple_attrs->response_type == RESP_SID) {
- ret = sysdb_attrs_add_string(user_attrs, SYSDB_SID_STR,
+ if (simple_attrs != NULL
+ && simple_attrs->response_type == RESP_SID) {
+ ret = sysdb_attrs_add_string(attrs->sysdb_attrs, SYSDB_SID_STR,
simple_attrs->a.sid_str);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
goto done;
}
}
gid = 0;
- if (state->dom->mpg == false) {
+ if (dom->mpg == false) {
gid = attrs->a.user.pw_gid;
}
- ret = sysdb_store_user(state->dom, name, NULL,
+ ret = sysdb_store_user(dom, name, NULL,
attrs->a.user.pw_uid,
- gid, NULL, /* gecos */
- homedir, NULL, NULL, user_attrs, NULL,
+ gid, attrs->a.user.pw_gecos,
+ attrs->a.user.pw_dir, attrs->a.user.pw_shell,
+ NULL, attrs->sysdb_attrs, NULL,
timeout, now);
break;
case RESP_GROUP:
+ case RESP_GROUP_MEMBERS:
/* we always use the fully qualified name for subdomain users */
- name = sss_tc_fqname(state, state->dom->names, state->dom,
+ name = sss_tc_fqname(tmp_ctx, dom->names, dom,
attrs->a.group.gr_name);
if (!name) {
DEBUG(SSSDBG_OP_FAILURE, "failed to format user name,\n");
@@ -843,40 +1532,43 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
goto done;
}
- group_attrs = sysdb_new_attrs(state);
- if (group_attrs == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_attrs_add_lc_name_alias(group_attrs, name);
+ ret = sysdb_attrs_add_lc_name_alias(attrs->sysdb_attrs, name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_add_lc_name_alias failed.\n");
goto done;
}
- if (state->req_input->type == REQ_INP_SECID) {
- ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR,
- state->req_input->inp.secid);
+ if (req_input->type == REQ_INP_SECID) {
+ ret = sysdb_attrs_add_string(attrs->sysdb_attrs, SYSDB_SID_STR,
+ req_input->inp.secid);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
goto done;
}
}
- if (simple_attrs != NULL && simple_attrs->response_type == RESP_SID) {
- ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR,
+ if (simple_attrs != NULL
+ && simple_attrs->response_type == RESP_SID) {
+ ret = sysdb_attrs_add_string(attrs->sysdb_attrs, SYSDB_SID_STR,
simple_attrs->a.sid_str);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
goto done;
}
}
- ret = sysdb_store_group(state->dom, name, attrs->a.group.gr_gid,
- group_attrs, timeout, now);
+ ret = process_members(dom, attrs->sysdb_attrs,
+ attrs->a.group.gr_mem);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "process_members failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_store_group(dom, name, attrs->a.group.gr_gid,
+ attrs->sysdb_attrs, timeout, now);
break;
default:
DEBUG(SSSDBG_OP_FAILURE, "Unexpected response type [%d].\n",
@@ -886,13 +1578,36 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
}
done:
- talloc_free(user_attrs);
- talloc_free(group_attrs);
- if (ret == EOK) {
- tevent_req_done(req);
- } else {
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static void ipa_s2n_get_groups_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_s2n_get_user_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_user_state);
+
+ ret = ipa_s2n_get_groups_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n get_groups request failed.\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
tevent_req_error(req, ret);
+ return;
}
+
+ tevent_req_done(req);
return;
}