/*
* System Security Services Daemon. NSS client interface
*
* Copyright (C) 2022 Red Hat
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
/* SID database NSS interface using mmap cache */
#include
#include
#include
#include
#include
#include
#include "nss_mc.h"
#include "util/mmap_cache.h"
#include "idmap/sss_nss_idmap.h"
#if HAVE_PTHREAD
static pthread_mutex_t sid_mc_ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct sss_cli_mc_ctx sid_mc_ctx = SSS_CLI_MC_CTX_INITIALIZER(&sid_mc_ctx_mutex);
#else
static struct sss_cli_mc_ctx sid_mc_ctx = SSS_CLI_MC_CTX_INITIALIZER;
#endif
static errno_t mc_get_sid_by_typed_id(uint32_t id, enum sss_id_type object_type,
char **sid, uint32_t *type,
uint32_t *populated_by)
{
int ret;
char key[16];
int key_len;
uint32_t hash;
uint32_t slot;
struct sss_mc_rec *rec = NULL;
const struct sss_mc_sid_data *data = NULL;
key_len = snprintf(key, sizeof(key), "%d-%ld", object_type, (long)id);
if (key_len > (sizeof(key) - 1)) {
return EINVAL;
}
ret = sss_nss_mc_get_ctx("sid", &sid_mc_ctx);
if (ret) {
return ret;
}
hash = sss_nss_mc_hash(&sid_mc_ctx, key, key_len + 1);
slot = sid_mc_ctx.hash_table[hash];
while (MC_SLOT_WITHIN_BOUNDS(slot, sid_mc_ctx.dt_size)) {
free(rec); /* free record from previous iteration */
rec = NULL;
ret = sss_nss_mc_get_record(&sid_mc_ctx, slot, &rec);
if (ret) {
goto done;
}
if (hash != rec->hash2) {
ret = EINVAL;
goto done;
}
data = (struct sss_mc_sid_data *)rec->data;
if (id == data->id) {
if (rec->expire < time(NULL)) {
ret = EINVAL;
goto done;
}
*type = data->type;
if (populated_by) {
*populated_by = data->populated_by;
}
*sid = strdup(data->sid);
if (!*sid) {
ret = ENOMEM;
}
goto done;
}
slot = sss_nss_mc_next_slot_with_hash(rec, hash);
}
ret = ENOENT;
done:
free(rec);
__sync_sub_and_fetch(&sid_mc_ctx.active_threads, 1);
return ret;
}
errno_t sss_nss_mc_get_sid_by_uid(uint32_t id, char **sid, uint32_t *type)
{
return mc_get_sid_by_typed_id(id, SSS_ID_TYPE_UID, sid, type, NULL);
}
errno_t sss_nss_mc_get_sid_by_gid(uint32_t id, char **sid, uint32_t *type)
{
return mc_get_sid_by_typed_id(id, SSS_ID_TYPE_GID, sid, type, NULL);
}
errno_t sss_nss_mc_get_sid_by_id(uint32_t id, char **sid, uint32_t *type)
{
errno_t ret;
uint32_t populated_by;
/* MC should behave the same way sssd_nss does.
* If user object exists sssd_nss would always return this user object.
*/
ret = sss_nss_mc_get_sid_by_uid(id, sid, type);
if (ret != ENOENT) {
return ret; /* found or fatal error */
}
/* This is where things get tricky.
* Consider a case of manually created user private group:
* since MC could be primed via explicit by-gid() lookup,
* missing user object doesn't mean sssd_nss wouldn't return
* it, hence only return group object if cache was primed via
* by-id() lookup.
*/
ret = mc_get_sid_by_typed_id(id, SSS_ID_TYPE_GID, sid, type, &populated_by);
if ((ret == 0) && (populated_by == 1)) {
/* Cache was primed via explicit by-gid() lookup - request should go to sssd_nss */
free(*sid);
ret = ENOENT;
}
return ret;
}
errno_t sss_nss_mc_get_id_by_sid(const char *sid, uint32_t *id, uint32_t *type)
{
int ret;
int key_len;
uint32_t hash;
uint32_t slot;
struct sss_mc_rec *rec = NULL;
const struct sss_mc_sid_data *data = NULL;
key_len = strlen(sid) + 1;
ret = sss_nss_mc_get_ctx("sid", &sid_mc_ctx);
if (ret) {
return ret;
}
hash = sss_nss_mc_hash(&sid_mc_ctx, sid, key_len);
slot = sid_mc_ctx.hash_table[hash];
while (MC_SLOT_WITHIN_BOUNDS(slot, sid_mc_ctx.dt_size)) {
free(rec); /* free record from previous iteration */
rec = NULL;
ret = sss_nss_mc_get_record(&sid_mc_ctx, slot, &rec);
if (ret) {
goto done;
}
if (hash != rec->hash1) {
ret = EINVAL;
goto done;
}
data = (struct sss_mc_sid_data *)rec->data;
if (strcmp(sid, data->sid) == 0) {
if (rec->expire < time(NULL)) {
ret = EINVAL;
goto done;
}
*type = data->type;
*id = data->id;
goto done; /* ret == 0 */
}
slot = sss_nss_mc_next_slot_with_hash(rec, hash);
}
ret = ENOENT;
done:
free(rec);
__sync_sub_and_fetch(&sid_mc_ctx.active_threads, 1);
return ret;
}