summaryrefslogtreecommitdiffstats
path: root/src/providers/ipa/ipa_s2n_exop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ipa/ipa_s2n_exop.c')
-rw-r--r--src/providers/ipa/ipa_s2n_exop.c3228
1 files changed, 3228 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
new file mode 100644
index 0000000..ec944d9
--- /dev/null
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -0,0 +1,3228 @@
+/*
+ SSSD
+
+ IPA Helper routines - external users and groups with s2n plugin
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> - 2011
+
+ 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 "util/sss_nss.h"
+#include "util/strtonum.h"
+#include "util/crypto/sss_crypto.h"
+#include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/sdap_async_ad.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_idmap.h"
+#include "providers/ipa/ipa_id.h"
+#include "providers/ipa/ipa_subdomains.h"
+#include "providers/ad/ad_pac.h"
+#include "db/sysdb.h"
+
+enum input_types {
+ INP_SID = 1,
+ INP_NAME,
+ INP_POSIX_UID,
+ INP_POSIX_GID,
+ INP_CERT,
+ INP_USERNAME,
+ INP_GROUPNAME
+};
+
+enum request_types {
+ REQ_SIMPLE = 1,
+ REQ_FULL,
+ REQ_FULL_WITH_MEMBERS
+};
+
+enum response_types {
+ RESP_SID = 1,
+ RESP_NAME,
+ RESP_USER,
+ RESP_GROUP,
+ RESP_USER_GROUPLIST,
+ RESP_GROUP_MEMBERS,
+ RESP_NAME_LIST
+};
+
+struct extdom_protocol_map_item {
+ int protocol;
+ const char *oid;
+};
+
+static struct extdom_protocol_map_item extdom_protocol_map[] = {
+ { EXTDOM_V2, EXOP_SID2NAME_V2_OID },
+ { EXTDOM_V1, EXOP_SID2NAME_V1_OID },
+ { EXTDOM_V0, EXOP_SID2NAME_OID },
+ { EXTDOM_INVALID_VERSION, NULL }
+};
+
+static const char* extdom_protocol_to_oid(enum extdom_protocol protocol)
+{
+ int i;
+
+ for (i = 0; extdom_protocol_map[i].protocol != EXTDOM_INVALID_VERSION; ++i) {
+ if (extdom_protocol_map[i].protocol == protocol) {
+ return extdom_protocol_map[i].oid;
+ }
+ }
+
+ return NULL;
+}
+
+static enum extdom_protocol extdom_oid_to_protocol(const char *oid)
+{
+ int i;
+
+ if (oid == NULL) {
+ return EXTDOM_INVALID_VERSION;
+ }
+
+ for (i = 0; extdom_protocol_map[i].protocol != EXTDOM_INVALID_VERSION; ++i) {
+ if (strcmp(extdom_protocol_map[i].oid, oid) == 0) {
+ return extdom_protocol_map[i].protocol;
+ }
+ }
+
+ return EXTDOM_INVALID_VERSION;
+}
+
+static enum extdom_protocol extdom_preferred_protocol(struct sdap_handle *sh) {
+ if (sdap_is_extension_supported(sh, EXOP_SID2NAME_V2_OID)) {
+ return EXTDOM_V2;
+ }
+
+ if (sdap_is_extension_supported(sh, EXOP_SID2NAME_V1_OID)) {
+ return EXTDOM_V1;
+ }
+
+ if (sdap_is_extension_supported(sh, EXOP_SID2NAME_OID)) {
+ return EXTDOM_V0;
+ }
+
+ return EXTDOM_INVALID_VERSION;
+}
+
+static const char *ipa_s2n_reqtype2str(enum request_types request_type)
+{
+ switch (request_type) {
+ case REQ_SIMPLE:
+ return "REQ_SIMPLE";
+ case REQ_FULL:
+ return "REQ_FULL";
+ case REQ_FULL_WITH_MEMBERS:
+ return "REQ_FULL_WITH_MEMBERS";
+ default:
+ break;
+ }
+
+ return "Unknown request type";
+}
+
+/* ==Sid2Name Extended Operation============================================= */
+struct ipa_s2n_exop_state {
+ struct sdap_handle *sh;
+
+ struct sdap_op *op;
+
+ char *retoid;
+ struct berval *retdata;
+};
+
+static void ipa_s2n_exop_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt);
+
+static struct tevent_req *ipa_s2n_exop_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ enum extdom_protocol protocol,
+ int timeout,
+ struct berval *bv,
+ const char *stat_info_in)
+{
+ struct tevent_req *req = NULL;
+ struct ipa_s2n_exop_state *state;
+ int ret;
+ int msgid;
+ char *stat_info;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_s2n_exop_state);
+ if (!req) return NULL;
+
+ state->sh = sh;
+ state->retoid = NULL;
+ state->retdata = NULL;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Executing extended operation\n");
+
+ ret = ldap_extended_operation(state->sh->ldap,
+ extdom_protocol_to_oid(protocol),
+ 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);
+
+ stat_info = talloc_asprintf(state, "server: [%s] %s",
+ sdap_get_server_peer_str_safe(state->sh),
+ stat_info_in != NULL ? stat_info_in
+ : "IPA EXOP");
+ if (stat_info == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create info string, ignored.\n");
+ }
+
+ ret = sdap_op_add(state, ev, state->sh, msgid, stat_info,
+ ipa_s2n_exop_done, req, timeout, &state->op);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up operation!\n");
+ ret = ERR_INTERNAL;
+ goto fail;
+ }
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ipa_s2n_exop_done(struct sdap_op *op,
+ struct sdap_msg *reply,
+ int error, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct ipa_s2n_exop_state *state = tevent_req_data(req,
+ struct ipa_s2n_exop_state);
+ int ret;
+ char *errmsg = NULL;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ int result;
+
+ if (error) {
+ tevent_req_error(req, error);
+ return;
+ }
+
+ ret = ldap_parse_result(state->sh->ldap, reply->msg,
+ &result, NULL, &errmsg, NULL,
+ NULL, 0);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_result failed (%d)\n",
+ sdap_op_get_msgid(state->op));
+ ret = ERR_NETWORK_IO;
+ goto done;
+ }
+
+ DEBUG(((result == LDAP_SUCCESS) || (result == LDAP_NO_SUCH_OBJECT)) ?
+ SSSDBG_TRACE_FUNC : SSSDBG_OP_FAILURE,
+ "ldap_extended_operation result: %s(%d), %s.\n",
+ sss_ldap_err2string(result), result, errmsg);
+
+ if (result != LDAP_SUCCESS) {
+ if (result == LDAP_NO_SUCH_OBJECT) {
+ ret = ENOENT;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "ldap_extended_operation failed, server " \
+ "logs might contain more details.\n");
+ ret = ERR_NETWORK_IO;
+ }
+ goto done;
+ }
+
+ 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);
+ ret = ERR_NETWORK_IO;
+ goto done;
+ }
+ if (retdata == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing exop result data.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->retoid = talloc_strdup(state, retoid);
+ if (state->retoid == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->retdata = talloc(state, struct berval);
+ if (state->retdata == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->retdata->bv_len = retdata->bv_len;
+ state->retdata->bv_val = talloc_memdup(state->retdata, retdata->bv_val,
+ retdata->bv_len);
+ if (state->retdata->bv_val == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_memdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ ldap_memfree(errmsg);
+ ldap_memfree(retoid);
+ ber_bvfree(retdata);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+}
+
+static int ipa_s2n_exop_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **retoid, struct berval **retdata)
+{
+ struct ipa_s2n_exop_state *state = tevent_req_data(req,
+ struct ipa_s2n_exop_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *retoid = talloc_steal(mem_ctx, state->retoid);
+ *retdata = talloc_steal(mem_ctx, state->retdata);
+
+ return EOK;
+}
+
+static errno_t talloc_ber_flatten(TALLOC_CTX *mem_ctx, BerElement *ber,
+ struct berval **_bv)
+{
+ int ret;
+ struct berval *bv = NULL;
+ struct berval *tbv = NULL;
+
+ ret = ber_flatten(ber, &bv);
+ if (ret == -1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ tbv = talloc_zero(mem_ctx, struct berval);
+ if (tbv == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tbv->bv_len = bv->bv_len;
+ tbv->bv_val = talloc_memdup(tbv, bv->bv_val, bv->bv_len);
+ if (tbv->bv_val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ ber_bvfree(bv);
+ if (ret == EOK) {
+ *_bv = tbv;
+ } else {
+ talloc_free(tbv);
+ }
+
+ return ret;
+}
+
+/* The extended operation expect the following ASN.1 encoded request data:
+ *
+ * ExtdomRequestValue ::= SEQUENCE {
+ * inputType ENUMERATED {
+ * sid (1),
+ * name (2),
+ * posix uid (3),
+ * posix gid (3)
+ * },
+ * requestType ENUMERATED {
+ * simple (1),
+ * full (2)
+ * full_with_members (3)
+ * },
+ * data InputData
+ * }
+ *
+ * InputData ::= CHOICE {
+ * sid OCTET STRING,
+ * name NameDomainData
+ * uid PosixUid,
+ * gid PosixGid
+ * }
+ *
+ * NameDomainData ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * object_name OCTET STRING
+ * }
+ *
+ * PosixUid ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * uid INTEGER
+ * }
+ *
+ * PosixGid ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * gid INTEGER
+ * }
+ *
+ */
+
+static errno_t s2n_encode_request(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ int entry_type,
+ enum request_types request_type,
+ struct req_input *req_input,
+ enum extdom_protocol protocol,
+ struct berval **_bv,
+ char **stat_info)
+{
+ BerElement *ber = NULL;
+ int ret;
+ char *info = NULL;
+
+ if (protocol == EXTDOM_INVALID_VERSION) {
+ return EINVAL;
+ }
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if (ber == NULL) {
+ return ENOMEM;
+ }
+
+ switch (entry_type) {
+ case BE_REQ_USER:
+ case BE_REQ_USER_AND_GROUP: /* the extdom V0/V1 exop does not care if
+ the ID belongs to a user or a group */
+ if (req_input->type == REQ_INP_NAME) {
+ ret = ber_printf(ber, "{ee{ss}}",
+ (protocol == EXTDOM_V2
+ ? INP_USERNAME : INP_NAME),
+ request_type,
+ domain_name,
+ req_input->inp.name);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] domain: [%s] name: [%s]",
+ ipa_s2n_reqtype2str(request_type),
+ domain_name, req_input->inp.name);
+ } else if (req_input->type == REQ_INP_ID) {
+ ret = ber_printf(ber, "{ee{si}}", INP_POSIX_UID, request_type,
+ domain_name,
+ req_input->inp.id);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] domain: [%s] id: [%" PRIu32 "]",
+ ipa_s2n_reqtype2str(request_type),
+ domain_name, req_input->inp.id);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ case BE_REQ_GROUP:
+ if (req_input->type == REQ_INP_NAME) {
+ ret = ber_printf(ber, "{ee{ss}}",
+ (protocol == EXTDOM_V2
+ ? INP_GROUPNAME : INP_NAME),
+ request_type,
+ domain_name,
+ req_input->inp.name);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] domain: [%s] name: [%s]",
+ ipa_s2n_reqtype2str(request_type),
+ domain_name, req_input->inp.name);
+ } else if (req_input->type == REQ_INP_ID) {
+ ret = ber_printf(ber, "{ee{si}}", INP_POSIX_GID, request_type,
+ domain_name,
+ req_input->inp.id);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] domain: [%s] id: [%" PRIu32 "]",
+ ipa_s2n_reqtype2str(request_type),
+ domain_name, req_input->inp.id);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ case BE_REQ_BY_SECID:
+ if (req_input->type == REQ_INP_SECID) {
+ ret = ber_printf(ber, "{ees}", INP_SID, request_type,
+ req_input->inp.secid);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] sid: [%s]",
+ ipa_s2n_reqtype2str(request_type),
+ req_input->inp.secid);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ case BE_REQ_BY_CERT:
+ if (req_input->type == REQ_INP_CERT) {
+ ret = ber_printf(ber, "{ees}", INP_CERT, request_type,
+ req_input->inp.cert);
+ info = talloc_asprintf(mem_ctx,
+ "EXTDOM EXPO request: [%s] cert: [%s]",
+ ipa_s2n_reqtype2str(request_type),
+ req_input->inp.cert);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+ if (ret == -1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = talloc_ber_flatten(mem_ctx, ber, _bv);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ ber_free(ber, 1);
+ if (ret != EOK || (*stat_info == NULL)) {
+ talloc_free(info);
+ } else {
+ *stat_info = info;
+ }
+
+ return ret;
+}
+
+/* If the extendend operation is successful it returns the following ASN.1
+ * encoded response:
+ *
+ * ExtdomResponseValue ::= SEQUENCE {
+ * responseType ENUMERATED {
+ * sid (1),
+ * name (2),
+ * posix_user (3),
+ * posix_group (4),
+ * posix_user_grouplist (5),
+ * posix_group_members (6)
+ * },
+ * data OutputData
+ * }
+ *
+ * OutputData ::= CHOICE {
+ * sid OCTET STRING,
+ * name NameDomainData,
+ * user PosixUser,
+ * group PosixGroup,
+ * usergrouplist PosixUserGrouplist,
+ * groupmembers PosixGroupMembers
+ *
+ * }
+ *
+ * NameDomainData ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * object_name OCTET STRING
+ * }
+ *
+ * PosixUser ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * user_name OCTET STRING,
+ * uid INTEGER
+ * gid INTEGER
+ * }
+ *
+ * PosixGroup ::= SEQUENCE {
+ * domain_name OCTET STRING,
+ * group_name OCTET STRING,
+ * 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 name_list {
+ char *domain_name;
+ char *name;
+};
+
+struct resp_attrs {
+ enum response_types response_type;
+ char *domain_name;
+ union {
+ struct passwd user;
+ struct group group;
+ char *sid_str;
+ char *name;
+ } a;
+ size_t ngroups;
+ char **groups;
+ struct sysdb_attrs *sysdb_attrs;
+ char **name_list;
+};
+
+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++) {
+
+ if (strcmp(name, SYSDB_USER_CERT) == 0) {
+ if (values[c]->bv_val[values[c]->bv_len] != '\0') {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "base64 encoded certificate not 0-terminated.\n");
+ return EINVAL;
+ }
+
+ v.data = sss_base64_decode(NULL, values[c]->bv_val, &v.length);
+ if (v.data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return EINVAL;
+ }
+ } else {
+ v.data = (uint8_t *)values[c]->bv_val;
+ v.length = values[c]->bv_len;
+ }
+
+ ret = sysdb_attrs_add_val_safe(resp_attrs->sysdb_attrs, name, &v);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val_safe 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(struct sss_domain_info *dom,
+ BerElement *ber,
+ struct resp_attrs *attrs)
+{
+ ber_tag_t tag;
+ ber_len_t ber_len;
+ int ret;
+ char *gecos = NULL;
+ char *homedir = NULL;
+ char *name = NULL;
+ char *domain = NULL;
+ char *shell = NULL;
+ char **list = NULL;
+ size_t c, gc;
+ struct sss_domain_info *parent_domain;
+ struct sss_domain_info *obj_domain;
+
+ 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_zero_array(attrs, char *, attrs->ngroups + 1);
+ if (attrs->groups == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ attrs->ngroups = 0;
+ ret = ENOMEM;
+ goto done;
+ }
+
+ parent_domain = get_domains_head(dom);
+
+ for (c = 0, gc = 0; c < attrs->ngroups; c++) {
+ ret = sss_parse_name(attrs, dom->names, list[c],
+ &domain, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot parse member %s\n", list[c]);
+ continue;
+ }
+
+ if (domain != NULL) {
+ obj_domain = find_domain_by_name_ex(parent_domain, domain, true, SSS_GND_ALL_DOMAINS);
+ if (obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name_ex failed.\n");
+ attrs->ngroups = gc;
+ ret = ENOMEM;
+ goto done;
+ } else if (sss_domain_get_state(obj_domain) == DOM_DISABLED) {
+ /* skipping objects from disabled domains */
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Skipping object [%s] from disabled domain.\n",
+ list[c]);
+ continue;
+ }
+ } else {
+ obj_domain = parent_domain;
+ }
+
+ attrs->groups[gc] = sss_create_internal_fqname(attrs->groups,
+ name, obj_domain->name);
+ if (attrs->groups[gc] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n");
+ attrs->ngroups = gc;
+ ret = ENOMEM;
+ goto done;
+ }
+ gc++;
+ }
+ attrs->ngroups = gc;
+ }
+
+ 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 sss_domain_info *dom,
+ struct resp_attrs *attrs)
+{
+ ber_tag_t tag;
+ ber_len_t ber_len;
+ int ret;
+ char **list = NULL;
+ size_t c, mc;
+ char *name = NULL;
+ char *domain = NULL;
+
+ tag = ber_scanf(ber, "{v}", &list);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (list != NULL) {
+ 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, mc=0; c < attrs->ngroups; c++) {
+ ret = sss_parse_name(attrs, dom->names, list[c],
+ &domain, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot parse member %s\n", list[c]);
+ continue;
+ }
+
+ if (domain == NULL) {
+ domain = dom->name;
+ }
+
+ attrs->a.group.gr_mem[mc] =
+ sss_create_internal_fqname(attrs->a.group.gr_mem,
+ name, domain);
+ if (attrs->a.group.gr_mem[mc] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ mc++;
+ }
+ }
+ } else {
+ attrs->a.group.gr_mem = talloc_zero_array(attrs, char *, 1);
+ if (attrs->a.group.gr_mem == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array 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 char *s2n_response_to_attrs_fqname(TALLOC_CTX *mem_ctx,
+ enum extdom_protocol protocol,
+ const char *domain_name,
+ const char *name)
+{
+ char *lc_name;
+ char *out_name;
+
+ if (protocol == EXTDOM_V0) {
+ /* Compatibility with older IPA servers that may use winbind instead
+ * of SSSD's server mode.
+ *
+ * Winbind is not consistent with the case of the returned user
+ * name. In general all names should be lower case but there are
+ * bug in some version of winbind which might lead to upper case
+ * letters in the name. To be on the safe side we explicitly
+ * lowercase the name.
+ */
+
+ lc_name = sss_tc_utf8_str_tolower(NULL, name);
+ if (lc_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ out_name = sss_create_internal_fqname(mem_ctx, lc_name, domain_name);
+ talloc_free(lc_name);
+ } else {
+ /* Keep the original casing to support case_sensitive=Preserving */
+ out_name = sss_create_internal_fqname(mem_ctx, name, domain_name);
+ }
+
+ if (out_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ return out_name;
+}
+
+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,
+ const char *view_name,
+ struct sysdb_attrs *override_attrs,
+ struct sysdb_attrs *mapped_attrs,
+ bool update_initgr_timeout);
+
+static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ char *retoid,
+ struct berval *retdata,
+ struct resp_attrs **resp_attrs)
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+ int ret;
+ enum response_types type;
+ char *domain_name = NULL;
+ char *name = NULL;
+ uid_t uid;
+ gid_t gid;
+ struct resp_attrs *attrs = NULL;
+ char *sid_str;
+ enum extdom_protocol protocol;
+ char **name_list = NULL;
+ ber_len_t ber_len;
+ char *fq_name = NULL;
+ struct sss_domain_info *root_domain = NULL;
+
+ if (retoid == NULL || retdata == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n");
+ return EINVAL;
+ }
+
+ protocol = extdom_oid_to_protocol(retoid);
+ if (protocol == EXTDOM_INVALID_VERSION) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Result has wrong OID, expected [%s], [%s] or [%s], got [%s].\n",
+ EXOP_SID2NAME_OID, EXOP_SID2NAME_V1_OID,
+ EXOP_SID2NAME_V2_OID, retoid);
+ return EINVAL;
+ }
+
+ ber = ber_init(retdata);
+ if (ber == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_init failed.\n");
+ return EINVAL;
+ }
+
+ tag = ber_scanf(ber, "{e", &type);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ attrs = talloc_zero(mem_ctx, struct resp_attrs);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (type) {
+ case RESP_USER:
+ 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;
+ goto done;
+ }
+
+ attrs->a.user.pw_name = s2n_response_to_attrs_fqname(attrs,
+ protocol,
+ domain_name,
+ name);
+ if (attrs->a.user.pw_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ attrs->a.user.pw_uid = uid;
+ attrs->a.user.pw_gid = gid;
+
+ if (protocol > EXTDOM_V0 && type == RESP_USER_GROUPLIST) {
+ ret = add_v1_user_data(dom, 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:
+ 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;
+ goto done;
+ }
+
+ attrs->a.group.gr_name = s2n_response_to_attrs_fqname(attrs,
+ protocol,
+ domain_name,
+ name);
+ if (attrs->a.group.gr_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ attrs->a.group.gr_gid = gid;
+
+ if (protocol > EXTDOM_V0 && type == RESP_GROUP_MEMBERS) {
+ ret = add_v1_group_data(ber, dom, 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);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ attrs->a.sid_str = talloc_strdup(attrs, sid_str);
+ if (attrs->a.sid_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case RESP_NAME:
+ tag = ber_scanf(ber, "{aa}", &domain_name, &name);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ attrs->a.name = sss_tc_utf8_str_tolower(attrs, name);
+ if (attrs->a.name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_tc_utf8_str_tolower failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case RESP_NAME_LIST:
+ tag = ber_scanf(ber, "{");
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ root_domain = get_domains_head(dom);
+
+ while (ber_peek_tag(ber, &ber_len) == LBER_SEQUENCE) {
+ tag = ber_scanf(ber, "{aa}", &domain_name, &name);
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ fq_name = sss_create_internal_fqname(attrs, name, domain_name);
+ if (fq_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_create_internal_fqname failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "[%s][%s][%s].\n", domain_name, name,
+ fq_name);
+
+ if (strcasecmp(root_domain->name, domain_name) != 0) {
+ ret = add_string_to_list(attrs, fq_name, &name_list);
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "[%s] from root domain, skipping.\n", fq_name);
+ ret = EOK; /* Free resources and continue in the loop */
+ }
+ ber_memfree(domain_name);
+ ber_memfree(name);
+ talloc_free(fq_name);
+ domain_name = NULL;
+ name = NULL;
+ fq_name = NULL;
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_to_name_list failed.\n");
+ goto done;
+ }
+ }
+
+ tag = ber_scanf(ber, "}}");
+ if (tag == LBER_ERROR) {
+ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ attrs->name_list = name_list;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected response type [%d].\n",
+ type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ attrs->response_type = type;
+ if (type != RESP_SID && type != RESP_NAME_LIST) {
+ attrs->domain_name = talloc_strdup(attrs, domain_name);
+ if (attrs->domain_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ ber_memfree(domain_name);
+ ber_memfree(name);
+ talloc_free(fq_name);
+ ber_free(ber, 1);
+
+ if (ret == EOK) {
+ *resp_attrs = attrs;
+ } else {
+ talloc_free(attrs);
+ }
+
+ return ret;
+}
+
+static const char *ipa_s2n_reqinp2str(TALLOC_CTX *mem_ctx,
+ struct req_input *req_input)
+{
+ const char *str = NULL;
+
+ switch (req_input->type) {
+ case REQ_INP_NAME:
+ str = talloc_strdup(mem_ctx, req_input->inp.name);
+ break;
+ case REQ_INP_SECID:
+ str = talloc_strdup(mem_ctx, req_input->inp.secid);
+ break;
+ case REQ_INP_CERT:
+ str = talloc_strdup(mem_ctx, req_input->inp.cert);
+ break;
+ case REQ_INP_ID:
+ str = talloc_asprintf(mem_ctx, "%u", req_input->inp.id);
+ break;
+ }
+
+ if (str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ }
+
+ return str;
+}
+
+struct ipa_s2n_get_list_state {
+ struct tevent_context *ev;
+ struct ipa_id_ctx *ipa_ctx;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ enum extdom_protocol protocol;
+ struct req_input req_input;
+ char **list;
+ size_t list_idx;
+ int exop_timeout;
+ int entry_type;
+ enum request_types request_type;
+ struct resp_attrs *attrs;
+ struct sss_domain_info *obj_domain;
+ struct sysdb_attrs *override_attrs;
+ struct sysdb_attrs *mapped_attrs;
+};
+
+static errno_t ipa_s2n_get_list_step(struct tevent_req *req);
+static void ipa_s2n_get_list_get_override_done(struct tevent_req *subreq);
+static void ipa_s2n_get_list_next(struct tevent_req *subreq);
+static void ipa_s2n_get_list_ipa_next(struct tevent_req *subreq);
+static errno_t ipa_s2n_get_list_save_step(struct tevent_req *req);
+
+static struct tevent_req *ipa_s2n_get_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ipa_id_ctx *ipa_ctx,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ int exop_timeout,
+ int entry_type,
+ enum request_types request_type,
+ enum req_input_type list_type,
+ char **list,
+ struct sysdb_attrs *mapped_attrs)
+{
+ int ret;
+ struct ipa_s2n_get_list_state *state;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_s2n_get_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if ((entry_type == BE_REQ_BY_SECID && list_type != REQ_INP_SECID)
+ || (entry_type != BE_REQ_BY_SECID && list_type == REQ_INP_SECID)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid parameter combination [%d][%d].\n",
+ request_type, list_type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->ev = ev;
+ state->ipa_ctx = ipa_ctx;
+ state->dom = dom;
+ state->sh = sh;
+ state->protocol = extdom_preferred_protocol(sh);
+ state->list = list;
+ state->list_idx = 0;
+ state->req_input.type = list_type;
+ state->req_input.inp.name = NULL;
+ state->exop_timeout = exop_timeout;
+ state->entry_type = entry_type;
+ state->request_type = request_type;
+ state->attrs = NULL;
+ state->override_attrs = NULL;
+ state->mapped_attrs = mapped_attrs;
+
+ ret = ipa_s2n_get_list_step(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_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_list_step(struct tevent_req *req)
+{
+ int ret;
+ struct ipa_s2n_get_list_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_list_state);
+ struct berval *bv_req;
+ struct tevent_req *subreq;
+ struct sss_domain_info *parent_domain;
+ char *short_name = NULL;
+ char *domain_name = NULL;
+ uint32_t id;
+ char *endptr;
+ struct dp_id_data *ar;
+ char *stat_info = NULL;
+
+ parent_domain = get_domains_head(state->dom);
+ switch (state->req_input.type) {
+ case REQ_INP_NAME:
+
+ ret = sss_parse_name(state, state->dom->names, state->list[state->list_idx],
+ &domain_name, &short_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n",
+ state->list[state->list_idx],
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (domain_name) {
+ state->obj_domain = find_domain_by_name(parent_domain,
+ domain_name, true);
+ if (state->obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
+ return ENOMEM;
+ }
+ } else {
+ state->obj_domain = parent_domain;
+ }
+
+ state->req_input.inp.name = short_name;
+
+ if (strcmp(state->obj_domain->name,
+ state->ipa_ctx->sdap_id_ctx->be->domain->name) == 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Looking up IPA object [%s] from LDAP.\n",
+ state->list[state->list_idx]);
+ ret = get_dp_id_data_for_user_name(state,
+ state->list[state->list_idx],
+ state->obj_domain->name,
+ &ar);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create lookup date for IPA object [%s].\n",
+ state->list[state->list_idx]);
+ return ret;
+ }
+ ar->entry_type = state->entry_type;
+
+ subreq = ipa_id_get_account_info_send(state, state->ev,
+ state->ipa_ctx, ar);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ipa_id_get_account_info_send failed.\n");
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_ipa_next, req);
+
+ return EOK;
+ }
+
+ break;
+ case REQ_INP_ID:
+ id = strtouint32(state->list[state->list_idx], &endptr, 10);
+ if (errno != 0 || *endptr != '\0'
+ || (state->list[state->list_idx] == endptr)) {
+ DEBUG(SSSDBG_OP_FAILURE, "strtouint32 failed.\n");
+ return EINVAL;
+ }
+ state->req_input.inp.id = id;
+ state->obj_domain = state->dom;
+
+ break;
+ case REQ_INP_SECID:
+ state->req_input.inp.secid = state->list[state->list_idx];
+ state->obj_domain = find_domain_by_sid(parent_domain,
+ state->req_input.inp.secid);
+ if (state->obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "find_domain_by_sid failed for SID [%s].\n",
+ state->req_input.inp.secid);
+ return EINVAL;
+ }
+
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ state->req_input.type);
+ return EINVAL;
+ }
+
+ ret = s2n_encode_request(state, state->obj_domain->name, state->entry_type,
+ state->request_type, &state->req_input,
+ state->protocol, &bv_req, &stat_info);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_encode_request failed.\n");
+ return ret;
+ }
+
+ if (state->request_type == REQ_FULL_WITH_MEMBERS && state->protocol == EXTDOM_V0) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop failed, protocol > V0 needed for this request.\n");
+ return EINVAL;
+ }
+
+ if (state->req_input.type == REQ_INP_NAME
+ && state->req_input.inp.name != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Sending request_type: [%s] for object [%s].\n",
+ ipa_s2n_reqtype2str(state->request_type),
+ state->list[state->list_idx]);
+ }
+
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, state->protocol,
+ state->exop_timeout, bv_req, stat_info);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_next, req);
+
+ return EOK;
+}
+
+static void ipa_s2n_get_list_next(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_s2n_get_list_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_list_state);
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ const char *sid_str;
+ struct dp_id_data *ar;
+
+ 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;
+ }
+
+ talloc_zfree(state->attrs);
+ ret = s2n_response_to_attrs(state, state->dom, retoid, retdata,
+ &state->attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_response_to_attrs failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Received [%s] attributes from IPA server.\n",
+ state->attrs->a.name);
+
+ if (is_default_view(state->ipa_ctx->view_name)) {
+ ret = ipa_s2n_get_list_save_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_save_step failed.\n");
+ goto fail;
+ }
+
+ return;
+ }
+
+ ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
+ &sid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Object [%s] has no SID, please check the "
+ "ipaNTSecurityIdentifier attribute on the server-side",
+ state->attrs->a.name);
+ goto fail;
+ }
+
+ ret = get_dp_id_data_for_sid(state, sid_str, state->obj_domain->name, &ar);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
+ goto fail;
+ }
+
+ subreq = ipa_get_ad_override_send(state, state->ev,
+ state->ipa_ctx->sdap_id_ctx,
+ state->ipa_ctx->ipa_options,
+ dp_opt_get_string(state->ipa_ctx->ipa_options->basic,
+ IPA_KRB5_REALM),
+ state->ipa_ctx->view_name,
+ ar);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_get_override_done, req);
+
+ return;
+
+fail:
+ tevent_req_error(req,ret);
+ return;
+}
+
+static void ipa_s2n_get_list_ipa_next(struct tevent_req *subreq)
+{
+ int ret;
+ int dp_error;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_s2n_get_list_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_list_state);
+
+ ret = ipa_id_get_account_info_recv(subreq, &dp_error);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_id_get_account_info failed: %d %d\n", ret,
+ dp_error);
+ goto done;
+ }
+
+ state->list_idx++;
+ if (state->list[state->list_idx] == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ ret = ipa_s2n_get_list_step(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_step failed.\n");
+ goto done;
+ }
+
+ return;
+
+done:
+ tevent_req_error(req,ret);
+ return;
+}
+
+static void ipa_s2n_get_list_get_override_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_s2n_get_list_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_list_state);
+
+ ret = ipa_get_ad_override_recv(subreq, NULL, state, &state->override_attrs);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "IPA override lookup failed: %d\n", ret);
+ goto fail;
+ }
+
+ ret = ipa_s2n_get_list_save_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_save_step failed.\n");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tevent_req_error(req,ret);
+ return;
+}
+
+static errno_t ipa_s2n_get_list_save_step(struct tevent_req *req)
+{
+ int ret;
+ struct ipa_s2n_get_list_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_list_state);
+
+ ret = ipa_s2n_save_objects(state->dom, &state->req_input, state->attrs,
+ NULL, state->ipa_ctx->view_name,
+ state->override_attrs, state->mapped_attrs,
+ false);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ return ret;
+ }
+
+ state->list_idx++;
+ if (state->list[state->list_idx] == NULL) {
+ return EOK;
+ }
+
+ ret = ipa_s2n_get_list_step(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_step failed.\n");
+ return ret;
+ }
+
+ return EAGAIN;
+}
+
+static int ipa_s2n_get_list_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct ipa_s2n_get_user_state {
+ struct tevent_context *ev;
+ struct ipa_id_ctx *ipa_ctx;
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ enum extdom_protocol protocol;
+ struct req_input *req_input;
+ int entry_type;
+ enum request_types request_type;
+ struct resp_attrs *attrs;
+ struct resp_attrs *simple_attrs;
+ struct sysdb_attrs *override_attrs;
+ struct sysdb_attrs *mapped_attrs;
+ int exop_timeout;
+};
+
+static void ipa_s2n_get_user_done(struct tevent_req *subreq);
+
+struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ipa_id_ctx *ipa_ctx,
+ struct sdap_options *opts,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs *override_attrs,
+ struct sdap_handle *sh,
+ int entry_type,
+ struct req_input *req_input)
+{
+ struct ipa_s2n_get_user_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct berval *bv_req = NULL;
+ const char *input;
+ int ret = EFAULT;
+ char *stat_info = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_s2n_get_user_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->ipa_ctx = ipa_ctx;
+ state->opts = opts;
+ state->dom = dom;
+ state->sh = sh;
+ state->protocol = extdom_preferred_protocol(sh);
+ state->req_input = req_input;
+ state->entry_type = entry_type;
+ state->attrs = NULL;
+ state->simple_attrs = NULL;
+ state->exop_timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
+ state->override_attrs = override_attrs;
+
+ if (state->protocol == EXTDOM_V1 || state->protocol == EXTDOM_V2) {
+ state->request_type = REQ_FULL_WITH_MEMBERS;
+ } else if (state->protocol == EXTDOM_V0) {
+ state->request_type = REQ_FULL;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Extdom not supported on the server, "
+ "cannot resolve objects from trusted domains.\n");
+ ret = EIO;
+ goto fail;
+ }
+
+ if (entry_type == BE_REQ_BY_CERT) {
+ /* Only REQ_SIMPLE is supported for BE_REQ_BY_CERT */
+ state->request_type = REQ_SIMPLE;
+ }
+
+ ret = s2n_encode_request(state, dom->name, entry_type, state->request_type,
+ req_input, state->protocol, &bv_req, &stat_info);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ input = ipa_s2n_reqinp2str(state, req_input);
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Sending request_type: [%s] for trust user [%s] to IPA server\n",
+ ipa_s2n_reqtype2str(state->request_type),
+ input);
+ talloc_zfree(input);
+
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, state->protocol,
+ state->exop_timeout, bv_req, stat_info);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_user_done, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static errno_t process_members(struct sss_domain_info *domain,
+ bool is_default_view,
+ struct sysdb_attrs *group_attrs,
+ char **members,
+ TALLOC_CTX *mem_ctx, char ***_missing_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;
+ char **missing_members = NULL;
+ size_t miss_count = 0;
+ const char *attrs[] = {SYSDB_NAME, SYSDB_OVERRIDE_DN, NULL};
+
+ if (members == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No members\n");
+ if (_missing_members != NULL) {
+ *_missing_members = NULL;
+ }
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ if (_missing_members != NULL && mem_ctx != NULL) {
+ /* count members */
+ for (c = 0; members[c] != NULL; c++);
+ missing_members = talloc_zero_array(tmp_ctx, char *, c + 1);
+ if (missing_members == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ parent_domain = get_domains_head(domain);
+
+ for (c = 0; members[c] != NULL; c++) {
+ obj_domain = find_domain_by_object_name_ex(parent_domain, members[c],
+ false, SSS_GND_ALL_DOMAINS);
+ if (obj_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_object_name failed.\n");
+ ret = ENOMEM;
+ goto done;
+ } else if (sss_domain_get_state(obj_domain) == DOM_DISABLED) {
+ /* skip members from disabled domains */
+ continue;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, obj_domain, members[c], attrs,
+ &msg);
+ if (ret == EOK || ret == ENOENT) {
+ if (ret == ENOENT
+ || (!is_default_view
+ && ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN,
+ NULL) == NULL)) {
+ /* only add ghost if the member is really missing */
+ if (group_attrs != NULL && ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Adding ghost member [%s]\n",
+ members[c]);
+
+ /* There were cases where the server returned the same user
+ * multiple times */
+ ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_GHOST,
+ members[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
+ goto done;
+ }
+ }
+
+ if (missing_members != NULL) {
+ missing_members[miss_count] = talloc_strdup(missing_members,
+ members[c]);
+ if (missing_members[miss_count] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ miss_count++;
+ }
+ } else {
+ if (group_attrs != NULL) {
+ dn_str = ldb_dn_get_linearized(msg->dn);
+ if (dn_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_get_linearized failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Adding member [%s][%s]\n",
+ members[c], dn_str);
+
+ ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_MEMBER,
+ dn_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string_safe failed.\n");
+ goto done;
+ }
+ }
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ if (_missing_members != NULL) {
+ if (miss_count == 0) {
+ *_missing_members = NULL;
+ } else {
+ if (mem_ctx != NULL) {
+ *_missing_members = talloc_steal(mem_ctx, missing_members);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing memory context for missing members list.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t get_group_dn_list(TALLOC_CTX *mem_ctx,
+ bool is_default_view,
+ 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;
+ const char *attrs[] = {SYSDB_NAME, SYSDB_OVERRIDE_DN, NULL};
+
+ 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], attrs,
+ &msg);
+ if (ret == EOK || ret == ENOENT) {
+ if (ret == ENOENT
+ || (!is_default_view
+ && ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN,
+ NULL) == NULL)) {
+ 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 {
+ 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 {
+ 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 errno_t s2n_remove_missing_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ int entry_type,
+ struct req_input *req_input)
+{
+ int ret;
+ bool name_is_upn = false;
+ char *id_str = NULL;
+ char *fq_name = NULL;
+
+ if (req_input->type == REQ_INP_ID) {
+ id_str = talloc_asprintf(mem_ctx, "%"SPRIuid, req_input->inp.id);
+ if (id_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ switch (entry_type) {
+ case BE_REQ_USER_AND_GROUP:
+ case BE_REQ_USER:
+ if (req_input->type == REQ_INP_NAME) {
+ name_is_upn = strchr(req_input->inp.name, '@') == NULL ? false
+ : true;
+ /* Expand to fully-qualified internal name */
+ if (!name_is_upn) {
+ fq_name = sss_create_internal_fqname(mem_ctx,
+ req_input->inp.name,
+ domain->name);
+ if (fq_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_create_internal_fqname failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ ret = users_get_handle_no_user(mem_ctx, domain, BE_FILTER_NAME,
+ fq_name != NULL ? fq_name
+ : req_input->inp.name,
+ name_is_upn);
+ } else if (req_input->type == REQ_INP_ID) {
+ ret = users_get_handle_no_user(mem_ctx, domain, BE_FILTER_IDNUM,
+ id_str, false);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ if (ret != EOK || entry_type == BE_REQ_USER) {
+ break;
+ }
+ /* Fallthough if BE_REQ_USER_AND_GROUP */
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ case BE_REQ_GROUP:
+ if (req_input->type == REQ_INP_NAME) {
+ /* Expand to fully-qualified internal name */
+ fq_name = sss_create_internal_fqname(mem_ctx,
+ req_input->inp.name,
+ domain->name);
+ if (fq_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_create_internal_fqname failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = groups_get_handle_no_group(mem_ctx, domain, BE_FILTER_NAME,
+ fq_name);
+ } else if (req_input->type == REQ_INP_ID) {
+ ret = groups_get_handle_no_group(mem_ctx, domain,BE_FILTER_IDNUM,
+ id_str);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+ req_input->type);
+ ret = EINVAL;
+ goto done;
+ }
+ break;
+ case BE_REQ_BY_SECID:
+ ret = EOK;
+ break;
+ case BE_REQ_BY_CERT:
+ ret = EOK;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected entry type [%d].\n", entry_type);
+ ret = EINVAL;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error while trying to remove user or group from cache.\n");
+ }
+
+ talloc_free(id_str);
+ talloc_free(fq_name);
+ return ret;
+}
+
+static void ipa_s2n_get_list_done(struct tevent_req *subreq);
+static void ipa_s2n_get_user_get_override_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,
+ struct tevent_req);
+ struct ipa_s2n_get_user_state *state = tevent_req_data(req,
+ struct ipa_s2n_get_user_state);
+ int ret;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ struct resp_attrs *attrs = NULL;
+ struct berval *bv_req = NULL;
+ char **missing_list = NULL;
+ struct ldb_dn **group_dn_list = NULL;
+ const char *sid_str;
+ struct dp_id_data *ar;
+ char *stat_info = NULL;
+
+ ret = ipa_s2n_exop_recv(subreq, state, &retoid, &retdata);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ ret = s2n_remove_missing_object(state, state->dom,
+ state->entry_type,
+ state->req_input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "s2n_remove_missing_object failed [%d].\n", ret);
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n exop request failed.\n");
+ if (state->req_input->type == REQ_INP_CERT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Maybe the server does not support lookups by "
+ "certificates.\n");
+ }
+ }
+ goto done;
+ }
+
+ switch (state->request_type) {
+ case REQ_FULL_WITH_MEMBERS:
+ case REQ_FULL:
+ ret = s2n_response_to_attrs(state, state->dom, retoid, retdata,
+ &attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_response_to_attrs failed.\n");
+ goto done;
+ }
+
+ if (!(strcasecmp(state->dom->name, attrs->domain_name) == 0 ||
+ (state->dom->flat_name != NULL &&
+ strcasecmp(state->dom->flat_name, attrs->domain_name) == 0))) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected domain name returned, "
+ "expected [%s] or [%s], got [%s].\n",
+ state->dom->name,
+ state->dom->flat_name == NULL ? "" :
+ state->dom->flat_name,
+ attrs->domain_name);
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->attrs = attrs;
+
+ if (attrs->response_type == RESP_USER_GROUPLIST) {
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Received [%zu] groups in group list "
+ "from IPA Server\n", attrs->ngroups);
+
+ for (size_t c = 0; c < attrs->ngroups; c++) {
+ DEBUG(SSSDBG_TRACE_FUNC, "[%s].\n", attrs->groups[c]);
+ }
+
+
+ ret = get_group_dn_list(state,
+ is_default_view(state->ipa_ctx->view_name),
+ state->dom,
+ attrs->ngroups, attrs->groups,
+ &group_dn_list, &missing_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_group_dn_list failed.\n");
+ goto done;
+ }
+
+ if (missing_list != NULL) {
+ subreq = ipa_s2n_get_list_send(state, state->ev,
+ state->ipa_ctx, state->dom,
+ state->sh, state->exop_timeout,
+ BE_REQ_GROUP,
+ REQ_FULL_WITH_MEMBERS,
+ REQ_INP_NAME,
+ missing_list, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ipa_s2n_get_list_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_done,
+ req);
+
+ return;
+ }
+ break;
+ } else if (attrs->response_type == RESP_GROUP_MEMBERS) {
+ ret = process_members(state->dom,
+ is_default_view(state->ipa_ctx->view_name),
+ NULL, attrs->a.group.gr_mem, state,
+ &missing_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "process_members failed.\n");
+ goto done;
+ }
+
+ if (missing_list != NULL) {
+ subreq = ipa_s2n_get_list_send(state, state->ev,
+ state->ipa_ctx, state->dom,
+ state->sh, state->exop_timeout,
+ BE_REQ_USER,
+ REQ_FULL_WITH_MEMBERS,
+ REQ_INP_NAME,
+ missing_list, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ipa_s2n_get_list_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_done,
+ req);
+
+ return;
+ }
+ break;
+ }
+
+ if (state->req_input->type == REQ_INP_SECID) {
+ /* We already know the SID, we do not have to read it. */
+ break;
+ }
+
+ state->request_type = REQ_SIMPLE;
+
+ ret = s2n_encode_request(state, state->dom->name, state->entry_type,
+ state->request_type, state->req_input,
+ state->protocol,
+ &bv_req, &stat_info);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subreq = ipa_s2n_exop_send(state, state->ev, state->sh, false,
+ state->exop_timeout, bv_req, stat_info);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_exop_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_user_done, req);
+
+ return;
+
+ case REQ_SIMPLE:
+ ret = s2n_response_to_attrs(state, state->dom, retoid, retdata,
+ &state->simple_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n_response_to_attrs failed.\n");
+ goto done;
+ }
+
+ if (state->simple_attrs->response_type == RESP_NAME_LIST
+ && state->req_input->type == REQ_INP_CERT) {
+
+ if (state->simple_attrs->name_list == NULL) {
+ /* No results from sub-domains, nothing to do */
+ ret = EOK;
+ goto done;
+ }
+
+ state->mapped_attrs = sysdb_new_attrs(state);
+ if (state->mapped_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_base64_blob(state->mapped_attrs,
+ SYSDB_USER_MAPPED_CERT,
+ state->req_input->inp.cert);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n");
+ goto done;
+ }
+
+ subreq = ipa_s2n_get_list_send(state, state->ev,
+ state->ipa_ctx, state->dom,
+ state->sh, state->exop_timeout,
+ BE_REQ_USER,
+ REQ_FULL_WITH_MEMBERS,
+ REQ_INP_NAME,
+ state->simple_attrs->name_list,
+ state->mapped_attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ipa_s2n_get_list_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_list_done,
+ req);
+
+ return;
+ }
+
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unexpected request type %d.\n", state->request_type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (state->attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing data of full request.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (state->simple_attrs != NULL
+ && state->simple_attrs->response_type == RESP_SID) {
+ sid_str = state->simple_attrs->a.sid_str;
+ ret = EOK;
+ } else if (state->attrs->sysdb_attrs != NULL) {
+ ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
+ &sid_str);
+ } else if (state->req_input->type == REQ_INP_SECID) {
+ sid_str = state->req_input->inp.secid;
+ ret = EOK;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "No SID available.\n");
+ ret = ENOENT;
+ }
+
+ if (ret == ENOENT || is_default_view(state->ipa_ctx->view_name)) {
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs, NULL, NULL, NULL, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ goto done;
+ }
+ } else if (ret == EOK) {
+ ret = get_dp_id_data_for_sid(state, sid_str, state->dom->name, &ar);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
+ goto done;
+ }
+
+ subreq = ipa_get_ad_override_send(state, state->ev,
+ state->ipa_ctx->sdap_id_ctx,
+ state->ipa_ctx->ipa_options,
+ dp_opt_get_string(state->ipa_ctx->ipa_options->basic,
+ IPA_KRB5_REALM),
+ state->ipa_ctx->view_name,
+ ar);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_user_get_override_done,
+ req);
+
+ return;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ return;
+}
+
+static errno_t get_groups_dns(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ char **name_list, char ***_dn_list)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ int c;
+ struct sss_domain_info *root_domain;
+ char **dn_list;
+ size_t dn_list_c;
+ struct ldb_message *msg;
+ struct ldb_dn *user_base_dn = NULL;
+
+ if (name_list == NULL) {
+ *_dn_list = NULL;
+ return EOK;
+ }
+
+ /* To handle cross-domain memberships we have to check the domain for
+ * each group the member should be added or deleted. Since sub-domains
+ * use fully-qualified names by default any short name can only belong
+ * to the root/head domain. find_domain_by_object_name() will return
+ * the domain given in the first argument if the second argument is a
+ * a short name hence we always use root_domain as first argument. */
+ root_domain = get_domains_head(dom);
+ if (root_domain->fqnames) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Root domain uses fully-qualified names, " \
+ "objects might not be correctly added to groups with " \
+ "short names.\n");
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ for (c = 0; name_list[c] != NULL; c++);
+
+ dn_list = talloc_zero_array(tmp_ctx, char *, c + 1);
+ if (dn_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dn_list_c = 0;
+ for (c = 0; name_list[c] != NULL; c++) {
+ dom = find_domain_by_object_name(root_domain, name_list[c]);
+ if (dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot find domain for [%s].\n", name_list[c]);
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* If the group name is overridden in the default view we have to
+ * search for the name and cannot construct it because the extdom
+ * plugin will return the overridden name but the DN of the related
+ * group object in the cache will contain the original name. */
+
+ ret = sysdb_search_group_by_name(tmp_ctx, dom, name_list[c], NULL,
+ &msg);
+ if (ret == EOK) {
+ talloc_free(user_base_dn);
+ user_base_dn = sysdb_user_base_dn(tmp_ctx, dom);
+ if (user_base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_user_base_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ if (ldb_dn_compare_base(user_base_dn, msg->dn) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Skipping user private group [%s].\n",
+ ldb_dn_get_linearized(msg->dn));
+ continue;
+ }
+
+ dn_list[dn_list_c] = ldb_dn_alloc_linearized(dn_list, msg->dn);
+ } else {
+ /* best effort, try to construct the DN */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "sysdb_search_group_by_name failed with [%d], "
+ "generating DN for [%s] in domain [%s].\n",
+ ret, name_list[c], dom->name);
+ dn_list[dn_list_c] = sysdb_group_strdn(dn_list, dom->name,
+ name_list[c]);
+ }
+ if (dn_list[dn_list_c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_alloc_linearized failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Added [%s][%s].\n", name_list[c],
+ dn_list[dn_list_c]);
+ dn_list_c++;
+ }
+
+ *_dn_list = talloc_steal(mem_ctx, dn_list);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ 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,
+ const char *view_name,
+ struct sysdb_attrs *override_attrs,
+ struct sysdb_attrs *mapped_attrs,
+ bool update_initgr_timeout)
+{
+ int ret;
+ time_t now;
+ struct sss_nss_homedir_ctx homedir_ctx;
+ char *name = NULL;
+ char *upn = NULL;
+ gid_t gid;
+ gid_t orig_gid = 0;
+ TALLOC_CTX *tmp_ctx;
+ const char *sid_str;
+ const char *tmp_str;
+ struct ldb_result *res;
+ enum sysdb_member_type type;
+ char **sysdb_grouplist;
+ char **add_groups_dns;
+ char **del_groups_dns;
+ char **groups_dns;
+ bool in_transaction = false;
+ int tret;
+ struct sysdb_attrs *gid_override_attrs = NULL;
+ struct ldb_message *msg;
+ struct ldb_message_element *el = NULL;
+
+ /* The list of elements that might be missing are:
+ * - SYSDB_ORIG_MEMBEROF
+ * - SYSDB_SSH_PUBKEY
+ * - SYSDB_USER_CERT
+ * Note that the list includes the trailing NULL at the end. */
+ size_t missing_count = 0;
+ const char *missing[] = {NULL, NULL, NULL, NULL};
+
+ 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;
+ }
+ }
+
+ if (attrs->sysdb_attrs != NULL) {
+ ret = sysdb_attrs_get_string(attrs->sysdb_attrs,
+ ORIGINALAD_PREFIX SYSDB_NAME, &tmp_str);
+ if (ret == EOK) {
+ name = talloc_strdup(tmp_ctx, tmp_str);
+ if (name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found original AD name [%s].\n", name);
+ } else if (ret == ENOENT) {
+ name = NULL;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(attrs->sysdb_attrs,
+ SYSDB_DEFAULT_OVERRIDE_NAME, &tmp_str);
+ if (ret == EOK) {
+ ret = sysdb_attrs_add_lc_name_alias_safe(attrs->sysdb_attrs,
+ tmp_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_lc_name_alias_safe failed.\n");
+ goto done;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(attrs->sysdb_attrs, SYSDB_UPN, &tmp_str);
+ if (ret == EOK) {
+ upn = talloc_strdup(tmp_ctx, tmp_str);
+ if (upn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found original AD upn [%s].\n", upn);
+ } else if (ret == ENOENT) {
+ upn = NULL;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+ }
+
+ if (strcmp(dom->name, attrs->domain_name) != 0) {
+ dom = find_domain_by_name(get_domains_head(dom),
+ attrs->domain_name, true);
+ if (dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot find domain: [%s]\n", attrs->domain_name);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ switch (attrs->response_type) {
+ case RESP_USER:
+ case RESP_USER_GROUPLIST:
+ type = SYSDB_MEMBER_USER;
+ if (dom->subdomain_homedir
+ && attrs->a.user.pw_dir == NULL) {
+ memset(&homedir_ctx, 0, sizeof(homedir_ctx));
+ homedir_ctx.username = attrs->a.user.pw_name;
+ homedir_ctx.uid = attrs->a.user.pw_uid;
+ homedir_ctx.domain = dom->name;
+ homedir_ctx.flatname = dom->flat_name;
+ homedir_ctx.config_homedir_substr = dom->homedir_substr;
+
+ attrs->a.user.pw_dir = expand_homedir_template(attrs,
+ dom->subdomain_homedir,
+ dom->case_preserve,
+ &homedir_ctx);
+ if (attrs->a.user.pw_dir == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (name == NULL) {
+ name = attrs->a.user.pw_name;
+ }
+
+ ret = sysdb_attrs_add_lc_name_alias_safe(attrs->sysdb_attrs, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_lc_name_alias_safe failed.\n");
+ goto done;
+ }
+
+ if (req_input->type == REQ_INP_SECID) {
+ ret = sysdb_attrs_add_string_safe(attrs->sysdb_attrs,
+ SYSDB_SID_STR,
+ req_input->inp.secid);
+ if (ret != EOK) {
+ 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_safe(attrs->sysdb_attrs,
+ SYSDB_SID_STR,
+ simple_attrs->a.sid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
+ goto done;
+ }
+ }
+
+ if (attrs->response_type == RESP_USER_GROUPLIST
+ && update_initgr_timeout) {
+ /* Since RESP_USER_GROUPLIST contains all group memberships it
+ * is effectively an initgroups request hence
+ * SYSDB_INITGR_EXPIRE will be set.*/
+ ret = sysdb_attrs_add_time_t(attrs->sysdb_attrs,
+ SYSDB_INITGR_EXPIRE,
+ time(NULL) + dom->user_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_time_t failed.\n");
+ goto done;
+ }
+ }
+
+ gid = 0;
+ if (sss_domain_is_mpg(dom) == false) {
+ gid = attrs->a.user.pw_gid;
+ } else {
+ /* The extdom plugin always returns the objects with the
+ * default view applied. Since the GID is handled specially
+ * for MPG domains we have add any overridden GID separately.
+ */
+ ret = sysdb_attrs_get_uint32_t(attrs->sysdb_attrs,
+ ORIGINALAD_PREFIX SYSDB_GIDNUM,
+ &orig_gid);
+ if (ret == EOK || ret == ENOENT) {
+ if ((orig_gid != 0 && orig_gid != attrs->a.user.pw_gid)
+ || attrs->a.user.pw_uid != attrs->a.user.pw_gid) {
+
+ gid_override_attrs = sysdb_new_attrs(tmp_ctx);
+ if (gid_override_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_uint32(gid_override_attrs,
+ SYSDB_GIDNUM,
+ attrs->a.user.pw_gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_uint32 failed.\n");
+ goto done;
+ }
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_get_uint32_t failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_get_el_ext(attrs->sysdb_attrs,
+ SYSDB_ORIG_MEMBEROF, false, &el);
+ if (ret == ENOENT) {
+ missing[missing_count++] = SYSDB_ORIG_MEMBEROF;
+ }
+
+ ret = sysdb_attrs_get_el_ext(attrs->sysdb_attrs,
+ SYSDB_SSH_PUBKEY, false, &el);
+ if (ret == ENOENT) {
+ missing[missing_count++] = SYSDB_SSH_PUBKEY;
+ }
+
+ ret = sysdb_attrs_get_el_ext(attrs->sysdb_attrs,
+ SYSDB_USER_CERT, false, &el);
+ if (ret == ENOENT) {
+ missing[missing_count++] = SYSDB_USER_CERT;
+ }
+
+ ret = sysdb_transaction_start(dom->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ ret = sysdb_store_user(dom, name, NULL,
+ attrs->a.user.pw_uid,
+ gid, attrs->a.user.pw_gecos,
+ attrs->a.user.pw_dir, attrs->a.user.pw_shell,
+ NULL, attrs->sysdb_attrs,
+ missing[0] == NULL ? NULL
+ : discard_const(missing),
+ dom->user_timeout, now);
+ if (ret == EEXIST && sss_domain_is_mpg(dom) == true) {
+ /* This handles the case where getgrgid() was called for
+ * this user, so a group was created in the cache
+ */
+ ret = sysdb_search_group_by_name(tmp_ctx, dom, name, NULL, &msg);
+ if (ret != EOK) {
+ /* Fail even on ENOENT, the group must be around */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete MPG group [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_delete_group(dom, NULL, attrs->a.user.pw_uid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_delete_group failed for MPG group [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_store_user(dom, name, NULL,
+ attrs->a.user.pw_uid,
+ gid, attrs->a.user.pw_gecos,
+ attrs->a.user.pw_dir,
+ attrs->a.user.pw_shell,
+ NULL, attrs->sysdb_attrs, NULL,
+ dom->user_timeout, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_store_user failed for MPG user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_store_user failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (mapped_attrs != NULL) {
+ ret = sysdb_set_user_attr(dom, name, mapped_attrs,
+ SYSDB_MOD_ADD);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
+ goto done;
+ }
+ }
+
+ if (gid_override_attrs != NULL) {
+ ret = sysdb_set_user_attr(dom, name, gid_override_attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
+ goto done;
+ }
+ }
+
+ if (attrs->response_type == RESP_USER_GROUPLIST) {
+ ret = get_sysdb_grouplist_dn(tmp_ctx, dom->sysdb, dom, name,
+ &sysdb_grouplist);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_sysdb_grouplist failed.\n");
+ goto done;
+ }
+
+ ret = get_groups_dns(tmp_ctx, dom, attrs->groups, &groups_dns);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_groups_dns failed.\n");
+ goto done;
+ }
+
+ ret = diff_string_lists(tmp_ctx, groups_dns,
+ sysdb_grouplist, &add_groups_dns,
+ &del_groups_dns, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "diff_string_lists failed.\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Updating memberships for %s\n",
+ name);
+ ret = sysdb_update_members_dn(dom, name, SYSDB_MEMBER_USER,
+ (const char *const *) add_groups_dns,
+ (const char *const *) del_groups_dns);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Membership update failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(dom->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+ break;
+ case RESP_GROUP:
+ case RESP_GROUP_MEMBERS:
+ type = SYSDB_MEMBER_GROUP;
+
+ if (name == NULL) {
+ name = attrs->a.group.gr_name;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", name);
+
+ ret = sysdb_attrs_add_lc_name_alias_safe(attrs->sysdb_attrs, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_lc_name_alias_safe failed.\n");
+ goto done;
+ }
+
+ /* We might already have the SID from other sources hence
+ * sysdb_attrs_add_string_safe is used to avoid double entries. */
+ if (req_input->type == REQ_INP_SECID) {
+ ret = sysdb_attrs_add_string_safe(attrs->sysdb_attrs,
+ SYSDB_SID_STR,
+ req_input->inp.secid);
+ if (ret != EOK) {
+ 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_safe(attrs->sysdb_attrs,
+ SYSDB_SID_STR,
+ simple_attrs->a.sid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_string failed.\n");
+ goto done;
+ }
+ }
+
+ ret = process_members(dom, is_default_view(view_name),
+ attrs->sysdb_attrs, attrs->a.group.gr_mem,
+ NULL, NULL);
+ 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, dom->group_timeout,
+ now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_group failed.\n");
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected response type [%d].\n",
+ attrs->response_type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(attrs->sysdb_attrs, SYSDB_SID_STR, &sid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot find SID of object.\n");
+ if (name != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Object [%s] has no SID, please check the "
+ "ipaNTSecurityIdentifier attribute on the server-side.\n",
+ name);
+ }
+ goto done;
+ }
+
+ ret = sysdb_search_object_by_sid(tmp_ctx, dom, sid_str, NULL, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot find object with override with SID [%s].\n", sid_str);
+ goto done;
+ }
+
+ if (!is_default_view(view_name)) {
+ /* For the default view the data return by the extdom plugin already
+ * contains all needed data and it is not expected to have a separate
+ * override object. */
+ ret = sysdb_store_override(dom, view_name, type, override_attrs,
+ res->msgs[0]->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_override failed.\n");
+ goto done;
+ }
+ }
+
+done:
+ if (in_transaction) {
+ tret = sysdb_transaction_cancel(dom->sysdb);
+ if (tret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static void ipa_s2n_get_list_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);
+ const char *sid_str;
+ struct dp_id_data *ar;
+
+ ret = ipa_s2n_get_list_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n get_fqlist request failed.\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->attrs == NULL) {
+ /* If this is a request by certificate we are done */
+ if (state->req_input->type == REQ_INP_CERT) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, EINVAL);
+ }
+ return;
+ }
+
+ ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
+ &sid_str);
+ if (ret == ENOENT) {
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs, NULL, NULL, NULL, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ goto fail;
+ }
+ tevent_req_done(req);
+ return;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto fail;
+ }
+
+ ret = get_dp_id_data_for_sid(state, sid_str, state->dom->name, &ar);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
+ goto fail;
+ }
+
+ if (state->override_attrs == NULL
+ && !is_default_view(state->ipa_ctx->view_name)) {
+ subreq = ipa_get_ad_override_send(state, state->ev,
+ state->ipa_ctx->sdap_id_ctx,
+ state->ipa_ctx->ipa_options,
+ dp_opt_get_string(state->ipa_ctx->ipa_options->basic,
+ IPA_KRB5_REALM),
+ state->ipa_ctx->view_name,
+ ar);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, ipa_s2n_get_user_get_override_done,
+ req);
+ } else {
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs,
+ state->ipa_ctx->view_name,
+ state->override_attrs, NULL, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ }
+
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+ return;
+}
+
+static void ipa_s2n_get_user_get_override_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);
+ struct sysdb_attrs *override_attrs = NULL;
+
+ ret = ipa_get_ad_override_recv(subreq, NULL, state, &override_attrs);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "IPA override lookup failed: %d\n", ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
+ state->simple_attrs, state->ipa_ctx->view_name,
+ override_attrs, NULL, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+int ipa_s2n_get_acct_info_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct ipa_get_subdom_acct_process_pac_state {
+ struct tevent_context *ev;
+ struct sdap_handle *sh;
+ struct sss_domain_info *dom;
+ char *username;
+
+ size_t num_missing_sids;
+ char **missing_sids;
+ size_t num_cached_groups;
+ char **cached_groups;
+};
+
+static void ipa_get_subdom_acct_process_pac_done(struct tevent_req *subreq);
+
+struct tevent_req *ipa_get_subdom_acct_process_pac_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sdap_handle *sh,
+ struct ipa_id_ctx *ipa_ctx,
+ struct sss_domain_info *dom,
+ struct ldb_message *user_msg)
+{
+ int ret;
+ struct ipa_get_subdom_acct_process_pac_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ char *user_sid;
+ char *primary_group_sid;
+ size_t num_sids;
+ char **group_sids;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ipa_get_subdom_acct_process_pac_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sh = sh;
+ state->dom = dom;
+
+ ret = ad_get_pac_data_from_user_entry(state, user_msg,
+ ipa_ctx->sdap_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;
+ }
+
+ ret = sdap_ad_tokengroups_get_posix_members(state, state->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;
+ }
+
+
+ if (state->num_missing_sids == 0) {
+ ret = sdap_ad_tokengroups_update_members(state->username,
+ state->dom->sysdb,
+ state->dom,
+ state->cached_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Membership update failed [%d]: %s\n",
+ ret, strerror(ret));
+ }
+
+ goto done;
+ }
+
+
+ subreq = ipa_s2n_get_list_send(state, state->ev, ipa_ctx, state->dom,
+ state->sh,
+ dp_opt_get_int(ipa_ctx->sdap_id_ctx->opts->basic,
+ SDAP_SEARCH_TIMEOUT),
+ BE_REQ_BY_SECID, REQ_FULL, REQ_INP_SECID,
+ state->missing_sids, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_get_subdom_acct_process_pac_done, req);
+
+ return req;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static void ipa_get_subdom_acct_process_pac_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_get_subdom_acct_process_pac_state *state = tevent_req_data(req,
+ struct ipa_get_subdom_acct_process_pac_state);
+ char **cached_groups;
+ size_t num_cached_groups;
+
+ ret = ipa_s2n_get_list_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "s2n get_fqlist request failed.\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* from ad_pac.c */
+ ret = sdap_ad_tokengroups_get_posix_members(state, state->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->dom->sysdb,
+ state->dom,
+ state->cached_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Membership update failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+errno_t ipa_get_subdom_acct_process_pac_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}