diff --git a/include/nsswitch.h b/include/nsswitch.h --- a/include/nsswitch.h +++ b/include/nsswitch.h @@ -190,7 +190,8 @@ enum nss_lookup_type { nss_lt_name = 1, nss_lt_id = 2, - nss_lt_all = 3 + nss_lt_all = 3, + nss_lt_pivot= 4 }; #ifdef _NS_PRIVATE diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c --- a/lib/libc/gen/getgrent.c +++ b/lib/libc/gen/getgrent.c @@ -151,6 +151,8 @@ static int grp_id_func(char *, size_t *, va_list, void *); static int grp_marshal_func(char *, size_t *, void *, va_list, void *); static int grp_unmarshal_func(char *, size_t, void *, va_list, void *); +static int grplist_marshal_func(char *, size_t *, void *, va_list, void *); +static int grplist_unmarshal_func(char *, size_t, void *, va_list, void *); static int grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) @@ -167,15 +169,15 @@ switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); - size = strlen(name); - desired_size = sizeof(enum nss_lookup_type) + size + 1; + size = strlen(name) + 1; + desired_size = sizeof(enum nss_lookup_type) + size; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); - memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size); res = NS_SUCCESS; break; @@ -191,6 +193,22 @@ memcpy(buffer + sizeof(enum nss_lookup_type), &gid, sizeof(gid_t)); + res = NS_SUCCESS; + break; + case nss_lt_pivot: + name = va_arg(ap, char *); + gid = va_arg(ap, gid_t); + size = strlen(name) + 1; + + desired_size = sizeof(enum nss_lookup_type) + size + sizeof(gid_t); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof (enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size); + memcpy(buffer + sizeof(enum nss_lookup_type) + size, &gid, sizeof(gid_t)); res = NS_SUCCESS; break; default: @@ -203,6 +221,36 @@ return (res); } +static int +grplist_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name __unused; + gid_t basegid __unused; + gid_t *groups; + int maxgroups __unused, *ngroups; + size_t desired_size; + + name = va_arg(ap, char*); + basegid = va_arg(ap, gid_t); + groups = va_arg(ap, gid_t *); + maxgroups = va_arg(ap, int); + ngroups = va_arg(ap, int *); + + desired_size = sizeof(int) + (sizeof(gid_t) * (*ngroups)); + + if (buffer == NULL || desired_size > *buffer_size) { + *buffer_size = desired_size; + return (NS_RETURN); + } + + *buffer_size = desired_size; + memcpy(buffer, ngroups, sizeof(int)); + memcpy(buffer + sizeof(int), groups, sizeof(gid_t) * (*ngroups)); + + return (NS_SUCCESS); +} + static int grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) @@ -297,6 +345,38 @@ return (NS_SUCCESS); } +static int +grplist_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name __unused; + gid_t basegid __unused; + gid_t *groups; + int *pngroups, maxgroups, original_ngroups; + int *ret_errno; + + name = va_arg(ap, char *); + basegid = va_arg(ap, gid_t); + groups = va_arg(ap, gid_t *); + maxgroups = va_arg(ap, int); + pngroups = va_arg(ap, int *); + ret_errno = va_arg(ap, int *); + + if (buffer_size < sizeof(int)) { + *ret_errno = 0; + return (NS_NOTFOUND); + } + memcpy(&original_ngroups, buffer, sizeof(int)); + if (buffer_size < sizeof(int) + (original_ngroups * sizeof(gid_t))) { + return NS_UNAVAIL; + } + memcpy(groups, buffer + sizeof(int), sizeof(gid_t) * + ((original_ngroups > maxgroups) ? maxgroups : original_ngroups)); + *pngroups = original_ngroups; + *ret_errno = 0; + return NS_SUCCESS; +} + static int grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) @@ -476,7 +556,7 @@ gid_t *groups; gid_t agroup; int maxgrp, *grpcnt; - int i, rv, ret_errno; + int i, rv, grp_errno, *ret_errno; /* * As this is a fallback method, only provided src @@ -489,6 +569,7 @@ groups = va_arg(ap, gid_t *); maxgrp = va_arg(ap, int); grpcnt = va_arg(ap, int *); + ret_errno = va_arg(ap, int *); rv = NS_UNAVAIL; @@ -503,12 +584,12 @@ _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0); for (;;) { do { - ret_errno = 0; + grp_errno = 0; grp_p = NULL; rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP, - "getgrent_r", src, &grp, buf, bufsize, &ret_errno); + "getgrent_r", src, &grp, buf, bufsize, &grp_errno); - if (grp_p == NULL && ret_errno == ERANGE) { + if (grp_p == NULL && grp_errno == ERANGE) { free(buf); if ((bufsize << 1) > GRP_STORAGE_MAX) { buf = NULL; @@ -522,10 +603,10 @@ goto out; } } - } while (grp_p == NULL && ret_errno == ERANGE); + } while (grp_p == NULL && grp_errno == ERANGE); - if (ret_errno != 0) { - errno = ret_errno; + if (grp_errno != 0) { + *ret_errno = grp_errno; goto out; } @@ -538,6 +619,7 @@ } } + *ret_errno = 0; _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src); out: free(buf); @@ -665,8 +747,19 @@ __getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, int maxgrp, int *grpcnt) { + int ret_errno = 0; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_pivot, + grp_id_func, grplist_marshal_func, grplist_unmarshal_func); +#endif + static const ns_dtab dtab[] = { NS_FALLBACK_CB(getgroupmembership_fallback) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; @@ -676,9 +769,10 @@ *grpcnt = 0; (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership", - defaultsrc, uname, agroup, groups, maxgrp, grpcnt); + defaultsrc, uname, agroup, groups, maxgrp, grpcnt, &ret_errno); /* too many groups found? */ + errno = ret_errno; return (*grpcnt > maxgrp ? -1 : 0); } diff --git a/lib/libc/gen/getpwent.c b/lib/libc/gen/getpwent.c --- a/lib/libc/gen/getpwent.c +++ b/lib/libc/gen/getpwent.c @@ -214,15 +214,15 @@ switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); - size = strlen(name); - desired_size = sizeof(enum nss_lookup_type) + size + 1; + size = strlen(name) + 1; + desired_size = sizeof(enum nss_lookup_type) + size; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); - memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size); res = NS_SUCCESS; break; diff --git a/lib/libc/net/nscache.c b/lib/libc/net/nscache.c --- a/lib/libc/net/nscache.c +++ b/lib/libc/net/nscache.c @@ -107,8 +107,8 @@ do { connection = __open_cached_connection(¶ms); if (connection == NULL) { - res = -1; - break; + free(buffer); + return (NS_UNAVAIL); } res = __cached_read(connection, cache_info->entry_name, cache_data->key, cache_data->key_size, buffer, @@ -317,11 +317,11 @@ __close_cached_mp_read_session(rs); rs = INVALID_CACHED_MP_READ_SESSION; cache_info->set_mp_rs_func(rs); - return (res == -1 ? NS_RETURN : NS_UNAVAIL); + return (res == 1 ? NS_NOTFOUND : NS_UNAVAIL); } free(buffer); - return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); + return (NS_SUCCESS); } int diff --git a/lib/libc/net/nscachedcli.c b/lib/libc/net/nscachedcli.c --- a/lib/libc/net/nscachedcli.c +++ b/lib/libc/net/nscachedcli.c @@ -538,7 +538,7 @@ goto fin; if (rec_error_code != 0) { - error_code = rec_error_code; + error_code = -rec_error_code; goto fin; } diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c --- a/lib/libc/net/nsdispatch.c +++ b/lib/libc/net/nsdispatch.c @@ -124,10 +124,10 @@ #ifdef NS_CACHING /* - * Cache lookup cycle prevention function - if !NULL then no cache lookups + * Cache lookup cycle prevention symbol - if !NULL then no cache lookups * will be made */ -static void *nss_cache_cycle_prevention_func = NULL; +static void *nss_cache_cycle_prevention_sym = NULL; #endif /* @@ -394,8 +394,8 @@ #ifdef NS_CACHING handle = libc_dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); if (handle != NULL) { - nss_cache_cycle_prevention_func = dlsym(handle, - "_nss_cache_cycle_prevention_function"); + nss_cache_cycle_prevention_sym = dlsym(handle, + "_nss_cache_cycle_prevention_symbol"); dlclose(handle); } #endif @@ -641,6 +641,13 @@ nss_cache_data cache_data; nss_cache_data *cache_data_p; int cache_flag; + int cache_start; + int is_nscd; + + cache_start = 0; + cache_flag = 0; + cache_data_p = NULL; + is_nscd = nss_cache_cycle_prevention_sym != NULL; #endif dbt = NULL; @@ -685,19 +692,28 @@ srclistsize++; } + for (i = 0; i < srclistsize; i++) { #ifdef NS_CACHING - cache_data_p = NULL; - cache_flag = 0; + int is_cache; + + is_cache = strcmp(srclist[i].name, NSSRC_CACHE) == 0; + + if (is_cache) { + cache_start = 1; + + if (is_nscd) { + /* don't try to call nscd inside nscd */ + continue; + } + } #endif - for (i = 0; i < srclistsize; i++) { result = NS_NOTFOUND; method = nss_method_lookup(srclist[i].name, database, method_name, disp_tab, &mdata); if (method != NULL) { #ifdef NS_CACHING - if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && - nss_cache_cycle_prevention_func == NULL) { + if (is_cache) { #ifdef NS_STRICT_LIBC_EID_CHECKING if (issetugid() != 0) continue; @@ -733,8 +749,6 @@ va_end(ap); #endif /* NS_CACHING */ - if (result & (srclist[i].flags)) - break; } else { if (fb_method != NULL) { saved_depth = st->fallback_depth; @@ -749,10 +763,12 @@ "and no fallback provided", srclist[i].name, database, method_name); } + if (result & (srclist[i].flags)) + break; } #ifdef NS_CACHING - if (cache_data_p != NULL && + if (!is_nscd && cache_start && cache_data_p != NULL && (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { va_start(ap, defaults); if (result == NS_SUCCESS) { diff --git a/usr.sbin/nscd/Makefile b/usr.sbin/nscd/Makefile --- a/usr.sbin/nscd/Makefile +++ b/usr.sbin/nscd/Makefile @@ -9,6 +9,10 @@ CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/nscd.conf\"" LIBADD= util pthread +# This is needed to have the _nss_cache_cycle_prevention_symbol +# visible to dlsym() so the caching code can skip cache lookups in the +# caching daemon. DO NOT REMOVE +LDFLAGS= -Wl,-export-dynamic .PATH: ${.CURDIR}/agents .include "${.CURDIR}/agents/Makefile.inc" diff --git a/usr.sbin/nscd/agents/group.c b/usr.sbin/nscd/agents/group.c --- a/usr.sbin/nscd/agents/group.c +++ b/usr.sbin/nscd/agents/group.c @@ -28,20 +28,84 @@ #include #include +#include #include #include #include #include +#include #include "../debug.h" #include "group.h" +#define GROUP_STORAGE_INITIAL (1 << 10) +#define GROUP_STORAGE_MAX (1 << 20) + +typedef struct key { + char *name; + gid_t gid; +} st_key; + +static int wrap_getgrnam_r(st_key, struct group *, char *, size_t, + struct group **); +static int wrap_getgrgid_r(st_key, struct group *, char *, size_t, + struct group **); +static int wrap_getgrent_r(st_key, struct group *, char *, size_t, + struct group **); +static char *wrap_getgrouplist(st_key, size_t *); +static char *getgr(int (*)(st_key, struct group *, char *, size_t, + struct group **), st_key, size_t *); + static int group_marshal_func(struct group *, char *, size_t *); +static int grouplist_marshal_func(gid_t *, int, char *, size_t *); static int group_lookup_func(const char *, size_t, char **, size_t *); static void *group_mp_init_func(void); static int group_mp_lookup_func(char **, size_t *, void *); static void group_mp_destroy_func(void *); +static int +wrap_getgrnam_r(st_key skey, struct group *group, char *buffer, size_t size, + struct group **result) +{ + return (getgrnam_r(skey.name, group, buffer, size, result)); +} + +static int +wrap_getgrgid_r(st_key skey, struct group *group, char *buffer, size_t size, + struct group **result) +{ + return (getgrgid_r(skey.gid, group, buffer, size, result)); +} + +static int +wrap_getgrent_r(st_key skey __unused, struct group *group, char *buffer, + size_t size, struct group **result) +{ + return (getgrent_r(group, buffer, size, result)); +} + +static int +grouplist_marshal_func(gid_t *gids, int ngids, char *buffer, + size_t* buffer_size) +{ + size_t desired_size; + TRACE_IN(grouplist_marshal_func); + + desired_size = sizeof(int) + (ngids * sizeof(gid_t)); + if (buffer == NULL || desired_size > *buffer_size) { + *buffer_size = desired_size; + TRACE_OUT(grouplist_marshal_func); + + return NS_RETURN; + } + *buffer_size = desired_size; + memcpy(buffer, &ngids, sizeof(ngids)); + memcpy(buffer + sizeof(ngids), gids, sizeof(gid_t) * ngids); + TRACE_OUT(grouplist_marshal_func); + + return (NS_SUCCESS); +} + static int group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size) { @@ -119,15 +183,14 @@ size_t *buffer_size) { enum nss_lookup_type lookup_type; - char *name; + st_key stkey; size_t size; - gid_t gid; - - struct group *result; + int gr_errno; TRACE_IN(group_lookup_func); assert(buffer != NULL); assert(buffer_size != NULL); + *buffer_size = 0; if (key_size < sizeof(enum nss_lookup_type)) { TRACE_OUT(group_lookup_func); @@ -137,48 +200,67 @@ switch (lookup_type) { case nss_lt_name: - size = key_size - sizeof(enum nss_lookup_type) + 1; - name = calloc(1, size); - assert(name != NULL); - memcpy(name, key + sizeof(enum nss_lookup_type), size - 1); + if (key_size < sizeof(enum nss_lookup_type) + 2) { + TRACE_OUT(group_lookup_func); + + return (NS_UNAVAIL); + } + + size = key_size - sizeof(enum nss_lookup_type); + stkey.name = malloc(size); + assert(stkey.name != NULL); + + memcpy(stkey.name, key + sizeof(enum nss_lookup_type), size); + stkey.name[size-1] = 0x00; + + TRACE_STR(stkey.name); + errno = 0; + *buffer = getgr(wrap_getgrnam_r, stkey, buffer_size); + gr_errno = errno; + free(stkey.name); + break; case nss_lt_id: if (key_size < sizeof(enum nss_lookup_type) + sizeof(gid_t)) { - TRACE_OUT(passwd_lookup_func); + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); } + memcpy(&(stkey.gid), key + sizeof(enum nss_lookup_type), sizeof(gid_t)); + errno=0; + *buffer = getgr(wrap_getgrgid_r, stkey, buffer_size); + gr_errno = errno; - memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t)); break; - default: - TRACE_OUT(group_lookup_func); - return (NS_UNAVAIL); - } + case nss_lt_pivot: + if (key_size < sizeof(enum nss_lookup_type) + 2 + + sizeof(gid_t)) { + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); + } + size = key_size - sizeof(enum nss_lookup_type) - sizeof(gid_t); + stkey.name = malloc(size); + assert(stkey.name != NULL); + memcpy(stkey.name, key + sizeof(enum nss_lookup_type), size); + stkey.name[size-1]=0x00; + + memcpy (&(stkey.gid), key + sizeof(enum nss_lookup_type) + size, sizeof(gid_t)); + + errno = 0; + *buffer = wrap_getgrouplist(stkey, buffer_size); + gr_errno = errno; - switch (lookup_type) { - case nss_lt_name: - TRACE_STR(name); - result = getgrnam(name); - free(name); - break; - case nss_lt_id: - result = getgrgid(gid); break; default: - /* SHOULD NOT BE REACHED */ - break; - } - - if (result != NULL) { - group_marshal_func(result, NULL, buffer_size); - *buffer = malloc(*buffer_size); - assert(*buffer != NULL); - group_marshal_func(result, *buffer, buffer_size); + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); } TRACE_OUT(group_lookup_func); - return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); + return (*buffer == NULL ? + (gr_errno == 0 ? NS_NOTFOUND : NS_UNAVAIL) : + NS_SUCCESS); } static void * @@ -194,19 +276,20 @@ static int group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) { - struct group *result; + st_key stkey; TRACE_IN(group_mp_lookup_func); - result = getgrent(); - if (result != NULL) { - group_marshal_func(result, NULL, buffer_size); - *buffer = malloc(*buffer_size); - assert(*buffer != NULL); - group_marshal_func(result, *buffer, buffer_size); - } + assert(buffer != NULL); + assert(buffer_size != NULL); + *buffer_size = 0; + errno = 0; + stkey.name = NULL; + *buffer = getgr(wrap_getgrent_r, stkey, buffer_size); TRACE_OUT(group_mp_lookup_func); - return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); + return (*buffer == NULL ? + (errno == 0 ? NS_NOTFOUND : NS_UNAVAIL) : + NS_SUCCESS); } static void @@ -255,3 +338,83 @@ TRACE_OUT(init_group_mp_agent); return ((struct agent *)retval); } + +static char *wrap_getgrouplist(st_key skey, size_t *buffer_size) +{ + int return_value; + gid_t *gids = NULL; + int ngids = 0; + char *buffer; + + errno = 0; + return_value = getgrouplist(skey.name, skey.gid, gids, &ngids); + + while (return_value == -1 && errno == 0) { + gids = realloc(gids, ngids * sizeof(gid_t)); + assert (gids != NULL); + errno=0; + return_value=getgrouplist(skey.name, skey.gid, gids, &ngids); + } + + if (errno != 0) { + free (gids); + return (NULL); + } + + grouplist_marshal_func(gids, ngids, NULL, buffer_size); + buffer = malloc(*buffer_size); + assert(buffer != NULL); + grouplist_marshal_func(gids, ngids, buffer, buffer_size); + free(gids); + + return (buffer); +} + +static char * +getgr(int (*fn)(st_key, struct group *, char *, size_t, struct group **), + st_key skey, size_t *buffer_size) +{ + int return_value; + struct group group, *result; + char *group_storage, *buffer; + size_t group_storage_size = GROUP_STORAGE_INITIAL; + + group_storage = malloc(group_storage_size); + if (group_storage == NULL) { + + return (NULL); + } + do { + return_value = fn(skey, &group, group_storage, group_storage_size, &result); + if (result == NULL && return_value == ERANGE) { + free(group_storage); + group_storage = NULL; + group_storage_size <<= 1; + if (group_storage_size > GROUP_STORAGE_MAX) { + errno = ERANGE; + + return (NULL); + } + group_storage = malloc(group_storage_size); + if (group_storage == NULL) { + + return (NULL); + } + } + } while (result == NULL && return_value == ERANGE); + errno = return_value; + if (return_value != 0 || result == NULL) { + free(group_storage); + + return (NULL); + } + + group_marshal_func(&group, NULL, buffer_size); + buffer = malloc(*buffer_size); + assert(buffer != NULL); + group_marshal_func(&group, buffer, buffer_size); + + free(group_storage); + + return (buffer); +} diff --git a/usr.sbin/nscd/agents/passwd.c b/usr.sbin/nscd/agents/passwd.c --- a/usr.sbin/nscd/agents/passwd.c +++ b/usr.sbin/nscd/agents/passwd.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -36,12 +37,101 @@ #include "../debug.h" #include "passwd.h" +#define PASSWD_STORAGE_INITIAL (1<<10) +#define PASSWD_STORAGE_MAX (1<<20) + +typedef union key { + char *login; + uid_t uid; +} u_key; + static int passwd_marshal_func(struct passwd *, char *, size_t *); static int passwd_lookup_func(const char *, size_t, char **, size_t *); static void *passwd_mp_init_func(void); static int passwd_mp_lookup_func(char **, size_t *, void *); static void passwd_mp_destroy_func(void *mdata); +static char *getpw(int (*)(u_key, struct passwd *, char *, size_t, + struct passwd **), u_key, size_t *); +static int wrap_getpwnam_r(u_key, struct passwd *, char *, size_t, + struct passwd **); +static int wrap_getpwuid_r(u_key, struct passwd *, char *, size_t, + struct passwd **); +static int wrap_getpwent_r(u_key, struct passwd *, char *, size_t, + struct passwd **); + +static char * +getpw(int (*fn)(u_key, struct passwd *, char *, size_t, struct passwd **), + u_key ukey, size_t *buffer_size) +{ + int return_value; + struct passwd passwd, *result; + char *passwd_storage, *buffer; + size_t passwd_storage_size = PASSWD_STORAGE_INITIAL; + + passwd_storage = malloc(passwd_storage_size); + if (passwd_storage == NULL) { + + return (NULL); + } + do { + return_value = fn(ukey, &passwd, passwd_storage, + passwd_storage_size, &result); + if (result == NULL && return_value == ERANGE) { + free(passwd_storage); + passwd_storage = NULL; + passwd_storage_size <<= 1; + if (passwd_storage_size > PASSWD_STORAGE_MAX) { + errno = ERANGE; + + return (NULL); + } + passwd_storage = malloc(passwd_storage_size); + if (passwd_storage == NULL) { + + return (NULL); + } + } + } while (result == NULL && return_value == ERANGE); + errno = return_value; + if (return_value != 0 || result == NULL) { + free(passwd_storage); + + return (NULL); + } + + *buffer_size = 0; + passwd_marshal_func(&passwd, NULL, buffer_size); + buffer = malloc(*buffer_size); + assert(buffer != NULL); + passwd_marshal_func(&passwd, buffer, buffer_size); + + free(passwd_storage); + + return (buffer); +} + +static int +wrap_getpwnam_r(u_key ukey, struct passwd *pwd, char *buffer, size_t bufsize, + struct passwd **res) +{ + return (getpwnam_r(ukey.login, pwd, buffer, bufsize, res)); +} + +static int +wrap_getpwuid_r(u_key ukey, struct passwd *pwd, char *buffer, size_t bufsize, + struct passwd **res) +{ + return (getpwuid_r(ukey.uid, pwd, buffer, bufsize, res)); +} + +static int +wrap_getpwent_r(u_key ukey __unused, struct passwd *pwd, char *buffer, + size_t bufsize, struct passwd **res) +{ + return (getpwent_r(pwd, buffer, bufsize, res)); +} + static int passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size) { @@ -128,15 +218,14 @@ size_t *buffer_size) { enum nss_lookup_type lookup_type; - char *login; + u_key ukey; size_t size; - uid_t uid; - - struct passwd *result; + int pw_errno; TRACE_IN(passwd_lookup_func); assert(buffer != NULL); assert(buffer_size != NULL); + *buffer_size = 0; if (key_size < sizeof(enum nss_lookup_type)) { TRACE_OUT(passwd_lookup_func); @@ -146,10 +235,24 @@ switch (lookup_type) { case nss_lt_name: - size = key_size - sizeof(enum nss_lookup_type) + 1; - login = calloc(1, size); - assert(login != NULL); - memcpy(login, key + sizeof(enum nss_lookup_type), size - 1); + if (key_size < sizeof(enum nss_lookup_type) + 2) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + size = key_size - sizeof(enum nss_lookup_type); + ukey.login = malloc(size); + assert(ukey.login != NULL); + + memcpy(ukey.login, key + sizeof(enum nss_lookup_type), size); + ukey.login[size-1]=0x00; + + TRACE_STR(ukey.login); + errno = 0; + *buffer = getpw(wrap_getpwnam_r, ukey, buffer_size); + pw_errno = errno; + free(ukey.login); + break; case nss_lt_id: if (key_size < sizeof(enum nss_lookup_type) + @@ -158,35 +261,23 @@ return (NS_UNAVAIL); } - memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t)); + memcpy(&ukey.uid, key + sizeof(enum nss_lookup_type), + sizeof(uid_t)); + errno = 0; + *buffer = getpw(wrap_getpwuid_r, ukey, buffer_size); + pw_errno = errno; + break; default: TRACE_OUT(passwd_lookup_func); return (NS_UNAVAIL); } - switch (lookup_type) { - case nss_lt_name: - result = getpwnam(login); - free(login); - break; - case nss_lt_id: - result = getpwuid(uid); - break; - default: - /* SHOULD NOT BE REACHED */ - break; - } - - if (result != NULL) { - passwd_marshal_func(result, NULL, buffer_size); - *buffer = malloc(*buffer_size); - assert(*buffer != NULL); - passwd_marshal_func(result, *buffer, buffer_size); - } - TRACE_OUT(passwd_lookup_func); - return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); + + return (*buffer == NULL ? + (pw_errno == 0 ? NS_NOTFOUND : NS_UNAVAIL) : + NS_SUCCESS); } static void * @@ -202,19 +293,20 @@ static int passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) { - struct passwd *result; + u_key ukey; TRACE_IN(passwd_mp_lookup_func); - result = getpwent(); - if (result != NULL) { - passwd_marshal_func(result, NULL, buffer_size); - *buffer = malloc(*buffer_size); - assert(*buffer != NULL); - passwd_marshal_func(result, *buffer, buffer_size); - } + assert(buffer != NULL); + assert(buffer_size != NULL); + *buffer_size = 0; + errno = 0; + ukey.uid = 0; + *buffer = getpw(wrap_getpwent_r, ukey, buffer_size); TRACE_OUT(passwd_mp_lookup_func); - return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); + return (*buffer == NULL ? + (errno == 0 ? NS_NOTFOUND : NS_UNAVAIL) : + NS_SUCCESS); } static void diff --git a/usr.sbin/nscd/nscd.c b/usr.sbin/nscd/nscd.c --- a/usr.sbin/nscd/nscd.c +++ b/usr.sbin/nscd/nscd.c @@ -577,17 +577,14 @@ } /* - * The idea of _nss_cache_cycle_prevention_function is that nsdispatch + * The idea of _nss_cache_cycle_prevention_symbol is that nsdispatch * will search for this symbol in the executable. This symbol is the * attribute of the caching daemon. So, if it exists, nsdispatch won't try * to connect to the caching daemon and will just ignore the 'cache' * source in the nsswitch.conf. This method helps to avoid cycles and * organize self-performing requests. - * - * (not actually a function; it used to be, but it doesn't make any - * difference, as long as it has external linkage) */ -void *_nss_cache_cycle_prevention_function; +void *_nss_cache_cycle_prevention_symbol; int main(int argc, char *argv[]) diff --git a/usr.sbin/nscd/query.c b/usr.sbin/nscd/query.c --- a/usr.sbin/nscd/query.c +++ b/usr.sbin/nscd/query.c @@ -800,8 +800,13 @@ CELT_NEGATIVE); read_response->error_code = 0; - read_response->data = NULL; - read_response->data_size = 0; + read_response->data_size = + sizeof(negative_data); + read_response->data = + malloc(read_response->data_size); + memcpy(read_response->data, + negative_data, + read_response->data_size); } } }