diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/sss_client/nss_mc.h | 8 | ||||
-rw-r--r-- | src/sss_client/nss_mc_passwd.c | 217 | ||||
-rw-r--r-- | src/sss_client/nss_passwd.c | 37 |
4 files changed, 263 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 1c4d11e0..4c4f23d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -957,6 +957,7 @@ libnss_sss_la_SOURCES = \ src/sss_client/nss_compat.h \ src/sss_client/nss_mc_common.c \ src/util/murmurhash3.c \ + src/sss_client/nss_mc_passwd.c \ src/sss_client/nss_mc.h libnss_sss_la_LDFLAGS = \ -module \ diff --git a/src/sss_client/nss_mc.h b/src/sss_client/nss_mc.h index 35cefe5b..b1b929eb 100644 --- a/src/sss_client/nss_mc.h +++ b/src/sss_client/nss_mc.h @@ -59,4 +59,12 @@ errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx, errno_t sss_nss_str_ptr_from_buffer(char **str, void **cookie, char *buf, size_t len); +/* passwd db */ +errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len, + struct passwd *result, + char *buffer, size_t buflen); +errno_t sss_nss_mc_getpwuid(uid_t uid, + struct passwd *result, + char *buffer, size_t buflen); + #endif /* _NSS_MC_H_ */ diff --git a/src/sss_client/nss_mc_passwd.c b/src/sss_client/nss_mc_passwd.c new file mode 100644 index 00000000..ca9945e4 --- /dev/null +++ b/src/sss_client/nss_mc_passwd.c @@ -0,0 +1,217 @@ +/* + * System Security Services Daemon. NSS client interface + * + * Copyright (C) Simo Sorce 2011 + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* PASSWD database NSS interface using mmap cache */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <time.h> +#include "nss_mc.h" + +struct sss_cli_mc_ctx pw_mc_ctx = { false, -1, 0, NULL, 0, NULL, 0, NULL, 0 }; + +static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec, + struct passwd *result, + char *buffer, size_t buflen) +{ + struct sss_mc_pwd_data *data; + time_t expire; + void *cookie; + int ret; + + /* additional checks before filling result*/ + expire = rec->expire; + if (expire < time(NULL)) { + /* entry is now invalid */ + return EINVAL; + } + + data = (struct sss_mc_pwd_data *)rec->data; + + if (data->strs_len > buflen) { + return ERANGE; + } + + /* fill in glibc provided structs */ + + /* copy in buffer */ + memcpy(buffer, data->strs, data->strs_len); + + /* fill in passwd */ + result->pw_uid = data->uid; + result->pw_gid = data->gid; + + cookie = NULL; + ret = sss_nss_str_ptr_from_buffer(&result->pw_name, &cookie, + buffer, data->strs_len); + if (ret) { + return ret; + } + ret = sss_nss_str_ptr_from_buffer(&result->pw_passwd, &cookie, + buffer, data->strs_len); + if (ret) { + return ret; + } + ret = sss_nss_str_ptr_from_buffer(&result->pw_gecos, &cookie, + buffer, data->strs_len); + if (ret) { + return ret; + } + ret = sss_nss_str_ptr_from_buffer(&result->pw_dir, &cookie, + buffer, data->strs_len); + if (ret) { + return ret; + } + ret = sss_nss_str_ptr_from_buffer(&result->pw_shell, &cookie, + buffer, data->strs_len); + if (ret) { + return ret; + } + if (cookie != NULL) { + return EINVAL; + } + + return 0; +} + +errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len, + struct passwd *result, + char *buffer, size_t buflen) +{ + struct sss_mc_rec *rec = NULL; + struct sss_mc_pwd_data *data; + char *rec_name; + uint32_t hash; + uint32_t slot; + int ret; + + ret = sss_nss_mc_get_ctx("passwd", &pw_mc_ctx); + if (ret) { + return ret; + } + + /* hashes are calculated including the NULL terminator */ + hash = sss_nss_mc_hash(&pw_mc_ctx, name, name_len + 1); + slot = pw_mc_ctx.hash_table[hash]; + if (slot > MC_SIZE_TO_SLOTS(pw_mc_ctx.dt_size)) { + return ENOENT; + } + + while (slot != MC_INVALID_VAL) { + + ret = sss_nss_mc_get_record(&pw_mc_ctx, slot, &rec); + if (ret) { + goto done; + } + + /* check record matches what we are searching for */ + if (hash != rec->hash1) { + /* if name hash does not match we can skip this immediately */ + slot = rec->next; + continue; + } + + data = (struct sss_mc_pwd_data *)rec->data; + rec_name = (char *)data + data->name; + if (strcmp(name, rec_name) == 0) { + break; + } + + slot = rec->next; + } + + if (slot == MC_INVALID_VAL) { + ret = ENOENT; + goto done; + } + + ret = sss_nss_mc_parse_result(rec, result, buffer, buflen); + +done: + free(rec); + return ret; +} + +errno_t sss_nss_mc_getpwuid(uid_t uid, + struct passwd *result, + char *buffer, size_t buflen) +{ + struct sss_mc_rec *rec = NULL; + struct sss_mc_pwd_data *data; + char uidstr[11]; + uint32_t hash; + uint32_t slot; + int len; + int ret; + + ret = sss_nss_mc_get_ctx("passwd", &pw_mc_ctx); + if (ret) { + return ret; + } + + len = snprintf(uidstr, 11, "%ld", (long)uid); + if (len > 10) { + return EINVAL; + } + + /* hashes are calculated including the NULL terminator */ + hash = sss_nss_mc_hash(&pw_mc_ctx, uidstr, len+1); + slot = pw_mc_ctx.hash_table[hash]; + if (slot > MC_SIZE_TO_SLOTS(pw_mc_ctx.dt_size)) { + return ENOENT; + } + + while (slot != MC_INVALID_VAL) { + + ret = sss_nss_mc_get_record(&pw_mc_ctx, slot, &rec); + if (ret) { + goto done; + } + + /* check record matches what we are searching for */ + if (hash != rec->hash2) { + /* if uid hash does not match we can skip this immediately */ + slot = rec->next; + continue; + } + + data = (struct sss_mc_pwd_data *)rec->data; + if (uid == data->uid) { + break; + } + + slot = rec->next; + } + + if (slot == MC_INVALID_VAL) { + ret = ENOENT; + goto done; + } + + ret = sss_nss_mc_parse_result(rec, result, buffer, buflen); + +done: + free(rec); + return ret; +} + diff --git a/src/sss_client/nss_passwd.c b/src/sss_client/nss_passwd.c index 16124948..e2f488b9 100644 --- a/src/sss_client/nss_passwd.c +++ b/src/sss_client/nss_passwd.c @@ -28,6 +28,7 @@ #include <stdint.h> #include <string.h> #include "sss_cli.h" +#include "nss_mc.h" static struct sss_nss_getpwent_data { size_t len; @@ -154,6 +155,24 @@ enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result, return NSS_STATUS_NOTFOUND; } + ret = sss_nss_mc_getpwnam(name, name_len, result, buffer, buflen); + switch (ret) { + case 0: + *errnop = 0; + return NSS_STATUS_SUCCESS; + case ERANGE: + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + case ENOENT: + /* fall through, we need to actively ask the parent + * if no entry is found */ + break; + default: + /* if using the mmaped cache failed, + * fall back to socket based comms */ + break; + } + rd.len = name_len + 1; rd.data = name; @@ -214,6 +233,24 @@ enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result, /* Caught once glibc passing in buffer == 0x0 */ if (!buffer || !buflen) return ERANGE; + ret = sss_nss_mc_getpwuid(uid, result, buffer, buflen); + switch (ret) { + case 0: + *errnop = 0; + return NSS_STATUS_SUCCESS; + case ERANGE: + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + case ENOENT: + /* fall through, we need to actively ask the parent + * if no entry is found */ + break; + default: + /* if using the mmaped cache failed, + * fall back to socket based comms */ + break; + } + user_uid = uid; rd.len = sizeof(uint32_t); rd.data = &user_uid; |