示例#1
0
/* FIXME: support storing additional attributes */
int sdap_save_user(TALLOC_CTX *memctx,
                   struct sdap_options *opts,
                   struct sss_domain_info *dom,
                   struct sysdb_attrs *attrs,
                   char **_usn_value,
                   time_t now)
{
    struct ldb_message_element *el;
    int ret;
    const char *user_name = NULL;
    const char *fullname = NULL;
    const char *pwd;
    const char *gecos;
    const char *homedir;
    const char *shell;
    const char *orig_dn = NULL;
    uid_t uid;
    gid_t gid;
    struct sysdb_attrs *user_attrs;
    char *upn = NULL;
    size_t i;
    int cache_timeout;
    char *usn_value = NULL;
    char **missing = NULL;
    TALLOC_CTX *tmpctx = NULL;
    bool use_id_mapping;
    char *sid_str;
    char *dom_sid_str = NULL;
    struct sss_domain_info *subdomain;

    DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");

    tmpctx = talloc_new(NULL);
    if (!tmpctx) {
        ret = ENOMEM;
        goto done;
    }

    user_attrs = sysdb_new_attrs(tmpctx);
    if (user_attrs == NULL) {
        ret = ENOMEM;
        goto done;
    }

    /* Always store SID string if available */
    ret = sdap_attrs_get_sid_str(tmpctx, opts->idmap_ctx, attrs,
                                opts->user_map[SDAP_AT_USER_OBJECTSID].sys_name,
                                &sid_str);
    if (ret == EOK) {
        ret = sysdb_attrs_add_string(user_attrs, SYSDB_SID_STR, sid_str);
        if (ret != EOK) {
            DEBUG(SSSDBG_MINOR_FAILURE, "Could not add SID string: [%s]\n",
                                         sss_strerror(ret));
            goto done;
        }
    } else if (ret == ENOENT) {
        DEBUG(SSSDBG_TRACE_ALL, "objectSID: not available for user\n");
        sid_str = NULL;
    } else {
        DEBUG(SSSDBG_MINOR_FAILURE, "Could not identify objectSID: [%s]\n",
                                     sss_strerror(ret));
        sid_str = NULL;
    }

    /* Always store UUID if available */
    ret = sysdb_handle_original_uuid(opts->user_map[SDAP_AT_USER_UUID].def_name,
                                     attrs,
                                     opts->user_map[SDAP_AT_USER_UUID].sys_name,
                                     user_attrs, SYSDB_UUID);
    if (ret != EOK) {
        DEBUG((ret == ENOENT) ? SSSDBG_TRACE_ALL : SSSDBG_MINOR_FAILURE,
              "Failed to retrieve UUID [%d][%s].\n", ret, sss_strerror(ret));
    }

    /* If this object has a SID available, we will determine the correct
     * domain by its SID. */
    if (sid_str != NULL) {
        subdomain = find_domain_by_sid(get_domains_head(dom), sid_str);
        if (subdomain) {
            dom = subdomain;
        } else {
            DEBUG(SSSDBG_TRACE_FUNC, "SID %s does not belong to any known "
                                      "domain\n", sid_str);
        }
    }

    ret = sdap_get_user_primary_name(memctx, opts, attrs, dom, &user_name);
    if (ret != EOK) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to get user name\n");
        goto done;
    }
    DEBUG(SSSDBG_TRACE_FUNC, "Processing user %s\n", user_name);

    if (opts->schema_type == SDAP_SCHEMA_AD) {
        ret = sysdb_attrs_get_string(attrs,
                    opts->user_map[SDAP_AT_USER_FULLNAME].sys_name, &fullname);
        if (ret == EOK) {
            ret = sysdb_attrs_add_string(user_attrs, SYSDB_FULLNAME, fullname);
            if (ret != EOK) {
                goto done;
            }
        } else if (ret != ENOENT) {
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_PWD].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) pwd = NULL;
    else pwd = (const char *)el->values[0].data;

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_GECOS].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) gecos = NULL;
    else gecos = (const char *)el->values[0].data;

    if (!gecos) {
        /* Fall back to the user's full name */
        ret = sysdb_attrs_get_el(
                attrs,
                opts->user_map[SDAP_AT_USER_FULLNAME].sys_name, &el);
        if (ret) goto done;
        if (el->num_values > 0) gecos = (const char *)el->values[0].data;
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_HOME].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) homedir = NULL;
    else homedir = (const char *)el->values[0].data;

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_SHELL].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) shell = NULL;
    else shell = (const char *)el->values[0].data;

    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
                                                               dom->name,
                                                               sid_str);

    /* Retrieve or map the UID as appropriate */
    if (use_id_mapping) {

        if (sid_str == NULL) {
            DEBUG(SSSDBG_MINOR_FAILURE, "SID not available, cannot map a " \
                                         "unix ID to user [%s].\n", user_name);
            ret = ENOENT;
            goto done;
        }

        DEBUG(SSSDBG_TRACE_LIBS,
              "Mapping user [%s] objectSID [%s] to unix ID\n", user_name, sid_str);

        /* Convert the SID into a UNIX user ID */
        ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &uid);
        if (ret == ENOTSUP) {
            DEBUG(SSSDBG_TRACE_FUNC, "Skipping built-in object.\n");
            ret = EOK;
            goto done;
        } else if (ret != EOK) {
            goto done;
        }

        /* Store the UID in the ldap_attrs so it doesn't get
         * treated as a missing attribute from LDAP and removed.
         */
        ret = sdap_replace_id(attrs, SYSDB_UIDNUM, uid);
        if (ret) {
            DEBUG(SSSDBG_OP_FAILURE, "Cannot set the id-mapped UID\n");
            goto done;
        }
    } else {
        ret = sysdb_attrs_get_uint32_t(attrs,
                                       opts->user_map[SDAP_AT_USER_UID].sys_name,
                                       &uid);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "no uid provided for [%s] in domain [%s].\n",
                   user_name, dom->name);
            ret = EINVAL;
            goto done;
        }
    }
    /* check that the uid is valid for this domain */
    if (OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) {
            DEBUG(SSSDBG_OP_FAILURE,
                  "User [%s] filtered out! (uid out of range)\n",
                      user_name);
        ret = EINVAL;
        goto done;
    }

    if (use_id_mapping) {
        ret = sdap_get_idmap_primary_gid(opts, attrs, sid_str, dom_sid_str,
                                         &gid);
        if (ret) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Cannot get the GID for [%s] in domain [%s].\n",
                   user_name, dom->name);
            goto done;
        }

        if (IS_SUBDOMAIN(dom)) {
            /* For subdomain users, only create the private group as
             * the subdomain is an MPG domain.
             * But we have to save the GID of the original primary group
             * becasuse otherwise this information might be lost because
             * typically (Unix and AD) the user is not listed in his primary
             * group as a member.
             */
            ret = sysdb_attrs_add_uint32(user_attrs, SYSDB_PRIMARY_GROUP_GIDNUM,
                                         (uint32_t) gid);
            if (ret != EOK) {
                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_uint32 failed.\n");
                goto done;
            }

            gid = 0;
        }

        /* Store the GID in the ldap_attrs so it doesn't get
        * treated as a missing attribute from LDAP and removed.
        */
        ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
        if (ret != EOK) goto done;
    } else {
        ret = sysdb_attrs_get_uint32_t(attrs,
                                       opts->user_map[SDAP_AT_USER_GID].sys_name,
                                       &gid);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "no gid provided for [%s] in domain [%s].\n",
                  user_name, dom->name);
            ret = EINVAL;
            goto done;
        }
    }

    /* check that the gid is valid for this domain */
    if (IS_SUBDOMAIN(dom) == false &&
            OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "User [%s] filtered out! (primary gid out of range)\n",
               user_name);
        ret = EINVAL;
        goto done;
    }

    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
    if (ret) {
        goto done;
    }
    if (!el || el->num_values == 0) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              "originalDN is not available for [%s].\n", user_name);
    } else {
        orig_dn = (const char *) el->values[0].data;
        DEBUG(SSSDBG_TRACE_INTERNAL, "Adding originalDN [%s] to attributes "
                "of [%s].\n", orig_dn, user_name);

        ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_DN, orig_dn);
        if (ret) {
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs, SYSDB_MEMBEROF, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(SSSDBG_TRACE_FUNC,
              "Original memberOf is not available for [%s].\n", user_name);
    } else {
        DEBUG(SSSDBG_TRACE_FUNC,
              "Adding original memberOf attributes to [%s].\n", user_name);
        for (i = 0; i < el->num_values; i++) {
            ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
                    (const char *) el->values[i].data);
            if (ret) {
                goto done;
            }
        }
    }

    ret = sdap_attrs_add_string(attrs,
                            opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name,
                            "original mod-Timestamp",
                            user_name, user_attrs);
    if (ret != EOK) {
        goto done;
    }

    ret = sysdb_attrs_get_el(attrs,
                      opts->user_map[SDAP_AT_USER_USN].sys_name, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(SSSDBG_TRACE_FUNC,
              "Original USN value is not available for [%s].\n", user_name);
    } else {
        ret = sysdb_attrs_add_string(user_attrs,
                          opts->user_map[SDAP_AT_USER_USN].sys_name,
                          (const char*)el->values[0].data);
        if (ret) {
            goto done;
        }
        usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
        if (!usn_value) {
            ret = ENOMEM;
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_PRINC].sys_name, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(SSSDBG_TRACE_FUNC,
              "User principal is not available for [%s].\n", user_name);
    } else {
        upn = talloc_strdup(user_attrs, (const char*) el->values[0].data);
        if (!upn) {
            ret = ENOMEM;
            goto done;
        }
        if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
            make_realm_upper_case(upn);
        }
        DEBUG(SSSDBG_TRACE_FUNC,
              "Adding user principal [%s] to attributes of [%s].\n",
               upn, user_name);
        ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
        if (ret) {
            goto done;
        }
    }

    for (i = SDAP_FIRST_EXTRA_USER_AT; i < opts->user_map_cnt; i++) {
        ret = sdap_attrs_add_list(attrs, opts->user_map[i].sys_name,
                                  NULL, user_name, user_attrs);
        if (ret) {
            goto done;
        }
    }

    cache_timeout = dom->user_timeout;

    ret = sdap_save_all_names(user_name, attrs, dom, user_attrs);
    if (ret != EOK) {
        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to save user names\n");
        goto done;
    }

    /* Make sure that any attributes we requested from LDAP that we
     * did not receive are also removed from the sysdb
     */
    ret = list_missing_attrs(user_attrs, opts->user_map, opts->user_map_cnt,
                             attrs, &missing);
    if (ret != EOK) {
        goto done;
    }

    DEBUG(SSSDBG_TRACE_FUNC, "Storing info for user %s\n", user_name);

    ret = sysdb_store_user(dom, user_name, pwd, uid, gid,
                           gecos, homedir, shell, orig_dn,
                           user_attrs, missing, cache_timeout, now);
    if (ret) goto done;

    if (_usn_value) {
        *_usn_value = talloc_steal(memctx, usn_value);
    }

    talloc_steal(memctx, user_attrs);
    ret = EOK;

done:
    if (ret) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "Failed to save user [%s]\n",
               user_name ? user_name : "Unknown");
    }
    talloc_free(tmpctx);
    return ret;
}
示例#2
0
/* FIXME: support storing additional attributes */
int sdap_save_user(TALLOC_CTX *memctx,
                   struct sysdb_ctx *ctx,
                   struct sdap_options *opts,
                   struct sss_domain_info *dom,
                   struct sysdb_attrs *attrs,
                   bool is_initgr,
                   char **_usn_value,
                   time_t now)
{
    struct ldb_message_element *el;
    int ret;
    const char *name = NULL;
    const char *fullname = NULL;
    const char *pwd;
    const char *gecos;
    const char *homedir;
    const char *shell;
    const char *orig_dn = NULL;
    uid_t uid;
    gid_t gid, primary_gid;
    struct sysdb_attrs *user_attrs;
    char *upn = NULL;
    size_t i;
    int cache_timeout;
    char *usn_value = NULL;
    char **missing = NULL;
    TALLOC_CTX *tmpctx = NULL;
    bool use_id_mapping = dp_opt_get_bool(opts->basic, SDAP_ID_MAPPING);
    char *sid_str;
    char *dom_sid_str = NULL;
    char *group_sid_str;

    DEBUG(9, ("Save user\n"));

    tmpctx = talloc_new(NULL);
    if (!tmpctx) {
        ret = ENOMEM;
        goto done;
    }

    user_attrs = sysdb_new_attrs(tmpctx);
    if (user_attrs == NULL) {
        ret = ENOMEM;
        goto done;
    }

    ret = sysdb_attrs_primary_name(ctx, attrs,
                                   opts->user_map[SDAP_AT_USER_NAME].name,
                                   &name);
    if (ret != EOK) {
        DEBUG(1, ("Failed to save the user - entry has no name attribute\n"));
        goto done;
    }

    if (opts->schema_type == SDAP_SCHEMA_AD) {
        ret = sysdb_attrs_get_string(attrs,
                    opts->user_map[SDAP_AT_USER_FULLNAME].sys_name, &fullname);
        if (ret == EOK) {
            ret = sysdb_attrs_add_string(user_attrs, SYSDB_FULLNAME, fullname);
            if (ret != EOK) {
                goto done;
            }
        } else if (ret != ENOENT) {
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_PWD].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) pwd = NULL;
    else pwd = (const char *)el->values[0].data;

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_GECOS].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) gecos = NULL;
    else gecos = (const char *)el->values[0].data;

    if (!gecos) {
        /* Fall back to the user's full name */
        ret = sysdb_attrs_get_el(
                attrs,
                opts->user_map[SDAP_AT_USER_FULLNAME].sys_name, &el);
        if (ret) goto done;
        if (el->num_values > 0) gecos = (const char *)el->values[0].data;
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_HOME].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) homedir = NULL;
    else homedir = (const char *)el->values[0].data;

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_SHELL].sys_name, &el);
    if (ret) goto done;
    if (el->num_values == 0) shell = NULL;
    else shell = (const char *)el->values[0].data;

    /* Retrieve or map the UID as appropriate */
    if (use_id_mapping) {
        DEBUG(SSSDBG_TRACE_LIBS,
              ("Mapping user [%s] objectSID to unix ID\n", name));

        ret = sdap_attrs_get_sid_str(
                tmpctx, opts->idmap_ctx, attrs,
                opts->user_map[SDAP_AT_USER_OBJECTSID].sys_name,
                &sid_str);
        if (ret != EOK) goto done;

        /* Add string representation to the cache for easier
         * debugging
         */
        ret = sysdb_attrs_add_string(user_attrs, SYSDB_SID_STR, sid_str);
        if (ret != EOK) goto done;

        /* Convert the SID into a UNIX user ID */
        ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &uid);
        if (ret == ENOTSUP) {
            DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
            ret = EOK;
            goto done;
        } else if (ret != EOK) {
            goto done;
        }

        /* Store the UID in the ldap_attrs so it doesn't get
         * treated as a missing attribute from LDAP and removed.
         */
        ret = sdap_replace_id(attrs, SYSDB_UIDNUM, uid);
        if (ret) {
            DEBUG(SSSDBG_OP_FAILURE, ("Cannot set the id-mapped UID\n"));
            goto done;
        }
    } else {
        ret = sysdb_attrs_get_uint32_t(attrs,
                                       opts->user_map[SDAP_AT_USER_UID].sys_name,
                                       &uid);
        if (ret != EOK) {
            DEBUG(1, ("no uid provided for [%s] in domain [%s].\n",
                      name, dom->name));
            ret = EINVAL;
            goto done;
        }
    }
    /* check that the uid is valid for this domain */
    if (OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) {
            DEBUG(2, ("User [%s] filtered out! (uid out of range)\n",
                      name));
        ret = EINVAL;
        goto done;
    }

    if (use_id_mapping) {
        ret = sysdb_attrs_get_uint32_t(
                attrs,
                opts->user_map[SDAP_AT_USER_PRIMARY_GROUP].sys_name,
                &primary_gid);
        if (ret != EOK) {
            DEBUG(SSSDBG_MINOR_FAILURE,
                  ("no primary group ID provided for [%s] in domain [%s].\n",
                   name, dom->name));
            ret = EINVAL;
            goto done;
        }

        /* The primary group ID is just the RID part of the objectSID
         * of the group. Generate the GID by adding this to the domain
         * SID value.
         */

        /* First, get the domain SID if we didn't do so above */
        if (!dom_sid_str) {
            ret = sdap_idmap_get_dom_sid_from_object(tmpctx, sid_str,
                                                     &dom_sid_str);
            if (ret != EOK) {
                DEBUG(SSSDBG_MINOR_FAILURE,
                      ("Could not parse domain SID from [%s]\n", sid_str));
                goto done;
            }
        }

        /* Add the RID to the end */
        group_sid_str = talloc_asprintf(tmpctx, "%s-%lu",
                                        dom_sid_str,
                                        (unsigned long)primary_gid);
        if (!group_sid_str) {
            ret = ENOMEM;
            goto done;
        }

        /* Convert the SID into a UNIX group ID */
        ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, group_sid_str, &gid);
        if (ret != EOK) goto done;

        /* Store the GID in the ldap_attrs so it doesn't get
         * treated as a missing attribute from LDAP and removed.
         */
        ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
        if (ret != EOK) goto done;
    } else {
        ret = sysdb_attrs_get_uint32_t(attrs,
                                       opts->user_map[SDAP_AT_USER_GID].sys_name,
                                       &gid);
        if (ret != EOK) {
            DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
                      name, dom->name));
            ret = EINVAL;
            goto done;
        }
    }

    /* check that the gid is valid for this domain */
    if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
            DEBUG(2, ("User [%s] filtered out! (primary gid out of range)\n",
                      name));
        ret = EINVAL;
        goto done;
    }

    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
    if (ret) {
        goto done;
    }
    if (!el || el->num_values == 0) {
        DEBUG(SSSDBG_MINOR_FAILURE,
              ("originalDN is not available for [%s].\n", name));
    } else {
        orig_dn = (const char *) el->values[0].data;
        DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding originalDN [%s] to attributes "
                "of [%s].\n", orig_dn, name));

        ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_DN, orig_dn);
        if (ret) {
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs, SYSDB_MEMBEROF, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(7, ("Original memberOf is not available for [%s].\n",
                    name));
    } else {
        DEBUG(7, ("Adding original memberOf attributes to [%s].\n",
                    name));
        for (i = 0; i < el->num_values; i++) {
            ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
                    (const char *) el->values[i].data);
            if (ret) {
                goto done;
            }
        }
    }

    ret = sdap_attrs_add_string(attrs,
                            opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name,
                            "original mod-Timestamp",
                            name, user_attrs);
    if (ret != EOK) {
        goto done;
    }

    ret = sysdb_attrs_get_el(attrs,
                      opts->user_map[SDAP_AT_USER_USN].sys_name, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(7, ("Original USN value is not available for [%s].\n",
                  name));
    } else {
        ret = sysdb_attrs_add_string(user_attrs,
                          opts->user_map[SDAP_AT_USER_USN].sys_name,
                          (const char*)el->values[0].data);
        if (ret) {
            goto done;
        }
        usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
        if (!usn_value) {
            ret = ENOMEM;
            goto done;
        }
    }

    ret = sysdb_attrs_get_el(attrs,
                             opts->user_map[SDAP_AT_USER_PRINC].sys_name, &el);
    if (ret) {
        goto done;
    }
    if (el->num_values == 0) {
        DEBUG(7, ("User principal is not available for [%s].\n", name));
    } else {
        upn = talloc_strdup(user_attrs, (const char*) el->values[0].data);
        if (!upn) {
            ret = ENOMEM;
            goto done;
        }
        if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
            make_realm_upper_case(upn);
        }
        DEBUG(7, ("Adding user principal [%s] to attributes of [%s].\n",
                  upn, name));
        ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
        if (ret) {
            goto done;
        }
    }

    for (i = SDAP_FIRST_EXTRA_USER_AT; i < SDAP_OPTS_USER; i++) {
        ret = sdap_attrs_add_list(attrs, opts->user_map[i].sys_name,
                                  NULL, name, user_attrs);
        if (ret) {
            goto done;
        }
    }

    cache_timeout = dom->user_timeout;

    if (is_initgr) {
        ret = sysdb_attrs_add_time_t(user_attrs, SYSDB_INITGR_EXPIRE,
                                     (cache_timeout ?
                                      (time(NULL) + cache_timeout) : 0));
        if (ret) {
            goto done;
        }
    }

    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, user_attrs);
    if (ret != EOK) {
        DEBUG(1, ("Failed to save user names\n"));
        goto done;
    }

    /* Make sure that any attributes we requested from LDAP that we
     * did not receive are also removed from the sysdb
     */
    ret = list_missing_attrs(user_attrs, opts->user_map, SDAP_OPTS_USER,
                             attrs, &missing);
    if (ret != EOK) {
        goto done;
    }

    DEBUG(6, ("Storing info for user %s\n", name));

    ret = sysdb_store_user(ctx, dom, name, pwd, uid, gid,
                           gecos, homedir, shell, orig_dn,
                           user_attrs, missing, cache_timeout, now);
    if (ret) goto done;

    if (_usn_value) {
        *_usn_value = talloc_steal(memctx, usn_value);
    }

    talloc_steal(memctx, user_attrs);
    ret = EOK;

done:
    if (ret) {
        DEBUG(2, ("Failed to save user [%s]\n",
                  name ? name : "Unknown"));
    }
    talloc_free(tmpctx);
    return ret;
}