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/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 ] = 0; + + 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] = 0; + + 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,84 @@ 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); +}