/*
SSSD
Async LDAP Helper routines - retrieving groups
Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
Copyright (C) Jan Zeleny <jzeleny@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 "db/sysdb.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_idmap.h"
/* ==Group-Parsing Routines=============================================== */
static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
struct sysdb_ctx *ctx,
struct sss_domain_info *domain,
const char *orig_dn,
char **localdn)
{
TALLOC_CTX *tmpctx;
const char *no_attrs[] = { NULL };
struct ldb_dn *base_dn;
char *filter;
struct ldb_message **msgs;
size_t num_msgs;
int ret;
char *sanitized_dn;
tmpctx = talloc_new(NULL);
if (!tmpctx) {
return ENOMEM;
}
ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
if (ret != EOK) {
ret = ENOMEM;
goto done;
}
filter = talloc_asprintf(tmpctx, "%s=%s", SYSDB_ORIG_DN, sanitized_dn);
if (!filter) {
ret = ENOMEM;
goto done;
}
base_dn = sysdb_domain_dn(ctx, tmpctx, domain->name);
if (!base_dn) {
ret = ENOMEM;
goto done;
}
DEBUG(9, ("Searching cache for [%s].\n", sanitized_dn));
ret = sysdb_search_entry(tmpctx, ctx,
base_dn, LDB_SCOPE_SUBTREE, filter, no_attrs,
&num_msgs, &msgs);
if (ret) {
goto done;
}
if (num_msgs != 1) {
ret = ENOENT;
goto done;
}
*localdn = talloc_strdup(memctx, ldb_dn_get_linearized(msgs[0]->dn));
if (!*localdn) {
ret = ENOENT;
goto done;
}
ret = EOK;
done:
talloc_zfree(tmpctx);
return ret;
}
static int sdap_fill_memberships(struct sysdb_attrs *group_attrs,
struct sysdb_ctx *ctx,
struct sdap_options *opts,
struct sss_domain_info *domain,
hash_table_t *ghosts,
struct ldb_val *values,
int num_values)
{
struct ldb_message_element *el;
int i, j;
int ret;
errno_t hret;
hash_key_t key;
hash_value_t value;
ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
if (ret) {
goto done;
}
/* Just allocate both big enough to contain all members for now */
el->values = talloc_realloc(group_attrs, el->values, struct ldb_val,
el->num_values + num_values);
if (!el->values) {
ret = ENOMEM;
goto done;
}
/* Just allocate both big enough to contain all members for now */
j = el->num_values;
for (i = 0; i < num_values; i++) {
if (ghosts == NULL) {
hret = HASH_ERROR_KEY_NOT_FOUND;
} else {
key.type = HASH_KEY_STRING;
key.str = (char *)values[i].data;
hret = hash_lookup(ghosts, &key, &value);
}
if (hret == HASH_ERROR_KEY_NOT_FOUND) {
/* sync search entry with this as origDN */
ret = sdap_find_entry_by_origDN(el->values, ctx, domain,
(char *)values[i].data,
(char **)&el->values[j].data);
if (ret != EOK) {
/* This should never return ENOENT
* -> fail if it does
*/
goto done;
}
DEBUG(7, (" member #%d (%s): [%s]\n",
i, (char *)values[i].data,
(char *)el->values[j].data));
el->values[j].length = strlen((char *)el->values[j].data);
j++;
} else if (hret != HASH_SUCCESS) {
ret = EFAULT;
goto done;
}
/* If the member is in ghost table, it has
* already been processed - just skip it */
}
el->num_values = j;
ret = EOK;
done:
return ret;
}
/* ==Save-Group-Entry===================================================== */
/* FIXME: support non legacy */
/* FIXME: support storing additional attributes */
static errno_t
sdap_store_group_with_gid(struct sysdb_ctx *ctx,
const char *name,
gid_t gid,
struct sysdb_attrs *group_attrs,
uint64_t cache_timeout,
bool posix_group,
time_t now)
{
errno_t ret;
/* make sure that non-posix (empty or explicit gid=0) groups have the
* gidNumber set to zero even if updating existing group */
if (!posix_group) {
ret = sysdb_attrs_add_uint32(group_attrs, SYSDB_GIDNUM, 0);
if (ret) {
DEBUG(2, ("Could not set explicit GID 0 for %s\n", name));
return ret;
}
}
ret = sysdb_store_group(ctx, name, gid, group_attrs, cache_timeout, now);
if (ret) {
DEBUG(2, ("Could not store group %s\n", name));
return ret;
}
return ret;
}
static int sdap_save_group(TALLOC_CTX *memctx,
struct sysdb_ctx *ctx,
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
bool populate_members,
hash_table_t *ghosts,
char **_usn_value,
time_t now)
{
struct ldb_message_element *el;
struct ldb_message_element *el1;
struct ldb_message_element *gh;
struct sysdb_attrs *group_attrs;
const char *name = NULL;
gid_t gid;
int ret, cnt, i;
char *usn_value = NULL;
TALLOC_CTX *tmpctx = NULL;
bool posix_group;
bool use_id_mapping = dp_opt_get_bool(opts->basic, SDAP_ID_MAPPING);
char *sid_str;
hash_key_t key;
hash_value_t value;
tmpctx = talloc_new(memctx);
if (!tmpctx) {
ret = ENOMEM;
goto fail;
}
group_attrs = sysdb_new_attrs(tmpctx);
if (group_attrs == NULL) {
ret = ENOMEM;
goto fail;
}
ret = sysdb_attrs_primary_name(ctx, attrs,
opts->group_map[SDAP_AT_GROUP_NAME].name,
&name);
if (ret != EOK) {
DEBUG(1, ("Failed to save the group - entry has no name attribute\n"));
goto fail;
}
if (use_id_mapping) {
posix_group = true;
DEBUG(SSSDBG_TRACE_LIBS,
("Mapping group [%s] objectSID to unix ID\n", name));
ret = sdap_attrs_get_sid_str(
tmpctx, opts->idmap_ctx, attrs,
opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
&sid_str);
if (ret != EOK) goto fail;
/* Add string representation to the cache for easier
* debugging
*/
ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR, sid_str);
if (ret != EOK) goto fail;
/* Convert the SID into a UNIX group ID */
ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
if (ret != EOK) goto fail;
/* 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 fail;
} else {
ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
if (ret == ENOENT) {
posix_group = true;
} else if (ret != EOK) {
goto fail;
}
DEBUG(8, ("This is%s a posix group\n", (posix_group)?"":" not"));
ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
if (ret != EOK) {
goto fail;
}
ret = sysdb_attrs_get_uint32_t(attrs,
opts->group_map[SDAP_AT_GROUP_GID].sys_name,
&gid);
if (ret != EOK) {
DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
name, dom->name));
ret = EINVAL;
goto fail;
}
}
/* check that the gid is valid for this domain */
if (posix_group) {
if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
DEBUG(2, ("Group [%s] filtered out! (id out of range)\n",
name));
ret = EINVAL;
goto fail;
}
/* Group ID OK */
}
ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN",
name, group_attrs);
if (ret != EOK) {
goto fail;
}
ret = sdap_attrs_add_string(attrs,
opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
"original mod-Timestamp",
name, group_attrs);
if (ret != EOK) {
goto fail;
}
ret = sysdb_attrs_get_el(attrs,
opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
if (ret) {
goto fail;
}
if (el->num_values == 0) {
DEBUG(7, ("Original USN value is not available for [%s].\n",
name));
} else {
ret = sysdb_attrs_add_string(group_attrs,
opts->group_map[SDAP_AT_GROUP_USN].sys_name,
(const char*)el->values[0].data);
if (ret) {
goto fail;
}
usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
if (!usn_value) {
ret = ENOMEM;
goto fail;
}
}
ret = sysdb_attrs_get_el(attrs, opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el1);
if (ret != EOK) {
goto fail;
}
if (populate_members) {
ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
if (ret != EOK) {
goto fail;
}
el->values = el1->values;
el->num_values = el1->num_values;
}
ret = sysdb_attrs_get_el(attrs, SYSDB_GHOST, &gh);
if (ret != EOK) {
goto fail;
}
ret = sysdb_attrs_get_el(group_attrs, SYSDB_GHOST, &el);
if (ret != EOK) {
goto fail;
}
el->values = gh->values;
el->num_values = gh->num_values;
/* Now process RFC2307bis ghost hash table */
if (ghosts != NULL) {
cnt = el->num_values + el1->num_values;
el->values = talloc_realloc(attrs, el->values, struct ldb_val,
cnt);
if (el->values == NULL) {
ret = ENOMEM;
goto fail;
}
for (i = 0; i < el1->num_values; i++) {
key.type = HASH_KEY_STRING;
key.str = (char *)el1->values[i].data;
ret = hash_lookup(ghosts, &key, &value);
if (ret == HASH_ERROR_KEY_NOT_FOUND) {
continue;
} else if (ret != HASH_SUCCESS) {
ret = EFAULT;
goto fail;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Adding ghost member [%s] for group [%s]\n",
(char *)value.ptr, name));
el->values[el->num_values].data = (uint8_t *)talloc_strdup(el->values, value.ptr);
if (el->values[el->num_values].data == NULL) {
|