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,88 @@ #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 = NULL, *buffer; + size_t passwd_storage_size = PASSWD_STORAGE_INITIAL; + + do { + passwd_storage = + realloc(passwd_storage, passwd_storage_size); + if (passwd_storage == NULL) { + return (NULL); + } + return_value = fn(ukey, &passwd, passwd_storage, + passwd_storage_size, &result); + passwd_storage_size <<= 1; + } while (result == NULL && + return_value == ERANGE && + passwd_storage_size < PASSWD_STORAGE_MAX); + if (return_value != 0 || result == NULL) { + free(passwd_storage); + errno = return_value; + 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); + errno = return_value; + 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 +205,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 +222,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] = 0; + + 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 +248,22 @@ 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 * @@ -195,26 +272,26 @@ TRACE_IN(passwd_mp_init_func); setpwent(); TRACE_OUT(passwd_mp_init_func); - return (NULL); } 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