/* 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; }
/* 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; }