diff options
Diffstat (limited to '')
-rw-r--r-- | nsswitch/winbind_nss_linux.c | 1178 |
1 files changed, 1178 insertions, 0 deletions
diff --git a/nsswitch/winbind_nss_linux.c b/nsswitch/winbind_nss_linux.c new file mode 100644 index 0000000..1d647ca --- /dev/null +++ b/nsswitch/winbind_nss_linux.c @@ -0,0 +1,1178 @@ +/* + Unix SMB/CIFS implementation. + + Windows NT Domain nsswitch module + + Copyright (C) Tim Potter 2000 + + This library 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 3 of the License, or (at your option) any later version. + + This library 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 + Library 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 <http://www.gnu.org/licenses/>. +*/ + +#include "winbind_client.h" + +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#endif + +#ifdef HAVE_PTHREAD +static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +/* Maximum number of users to pass back over the unix domain socket + per call. This is not a static limit on the total number of users + or groups returned in total. */ + +#define MAX_GETPWENT_USERS 250 +#define MAX_GETGRENT_USERS 250 + +/************************************************************************* + ************************************************************************/ + +#ifdef DEBUG_NSS +static const char *nss_err_str(NSS_STATUS ret) +{ + switch (ret) { + case NSS_STATUS_TRYAGAIN: + return "NSS_STATUS_TRYAGAIN"; + case NSS_STATUS_SUCCESS: + return "NSS_STATUS_SUCCESS"; + case NSS_STATUS_NOTFOUND: + return "NSS_STATUS_NOTFOUND"; + case NSS_STATUS_UNAVAIL: + return "NSS_STATUS_UNAVAIL"; +#ifdef NSS_STATUS_RETURN + case NSS_STATUS_RETURN: + return "NSS_STATUS_RETURN"; +#endif + default: + return "UNKNOWN RETURN CODE!!!!!!!"; + } +} +#endif + +/* Prototypes from wb_common.c */ + +/* Allocate some space from the nss static buffer. The buffer and buflen + are the pointers passed in by the C library to the _nss_ntdom_* + functions. */ + +static char *get_static(char **buffer, size_t *buflen, size_t len) +{ + char *result; + + /* Error check. We return false if things aren't set up right, or + there isn't enough buffer space left. */ + + if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) { + return NULL; + } + + /* Return an index into the static buffer */ + + result = *buffer; + *buffer += len; + *buflen -= len; + + return result; +} + +/* I've copied the strtok() replacement function next_token_Xalloc() from + lib/util_str.c as I really don't want to have to link in any other + objects if I can possibly avoid it. */ + +static bool next_token_alloc(const char **ptr, + char **pp_buff, + const char *sep) +{ + const char *s; + const char *saved_s; + char *pbuf; + bool quoted; + size_t len=1; + + *pp_buff = NULL; + if (!ptr) { + return(false); + } + + s = *ptr; + + /* default to simple separators */ + if (!sep) { + sep = " \t\n\r"; + } + + /* find the first non sep char */ + while (*s && strchr(sep,*s)) { + s++; + } + + /* nothing left? */ + if (!*s) { + return false; + } + + /* When restarting we need to go from here. */ + saved_s = s; + + /* Work out the length needed. */ + for (quoted = false; *s && + (quoted || !strchr(sep,*s)); s++) { + if (*s == '\"') { + quoted = !quoted; + } else { + len++; + } + } + + /* We started with len = 1 so we have space for the nul. */ + *pp_buff = (char *)malloc(len); + if (!*pp_buff) { + return false; + } + + /* copy over the token */ + pbuf = *pp_buff; + s = saved_s; + for (quoted = false; *s && + (quoted || !strchr(sep,*s)); s++) { + if ( *s == '\"' ) { + quoted = !quoted; + } else { + *pbuf++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *pbuf = 0; + + return true; +} + +/* Fill a pwent structure from a winbindd_response structure. We use + the static data passed to us by libc to put strings and stuff in. + Return NSS_STATUS_TRYAGAIN if we run out of memory. */ + +static NSS_STATUS fill_pwent(struct passwd *result, + struct winbindd_pw *pw, + char **buffer, size_t *buflen) +{ + size_t len; + + /* User name */ + len = strlen(pw->pw_name) + 1; + + if ((result->pw_name = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->pw_name, pw->pw_name, len); + + /* Password */ + len = strlen(pw->pw_passwd) + 1; + + if ((result->pw_passwd = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->pw_passwd, pw->pw_passwd, len); + + /* [ug]id */ + + result->pw_uid = pw->pw_uid; + result->pw_gid = pw->pw_gid; + + /* GECOS */ + len = strlen(pw->pw_gecos) + 1; + + if ((result->pw_gecos = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->pw_gecos, pw->pw_gecos, len); + + /* Home directory */ + len = strlen(pw->pw_dir) + 1; + + if ((result->pw_dir = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->pw_dir, pw->pw_dir, len); + + /* Logon shell */ + len = strlen(pw->pw_shell) + 1; + + if ((result->pw_shell = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->pw_shell, pw->pw_shell, len); + + /* The struct passwd for Solaris has some extra fields which must + be initialised or nscd crashes. */ + +#ifdef HAVE_PASSWD_PW_COMMENT + result->pw_comment = ""; +#endif + +#ifdef HAVE_PASSWD_PW_AGE + result->pw_age = ""; +#endif + + return NSS_STATUS_SUCCESS; +} + +/* Fill a grent structure from a winbindd_response structure. We use + the static data passed to us by libc to put strings and stuff in. + Return NSS_STATUS_TRYAGAIN if we run out of memory. */ + +static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr, + const char *gr_mem, char **buffer, size_t *buflen) +{ + char *name; + int i; + char *tst; + size_t len; + + /* Group name */ + len = strlen(gr->gr_name) + 1; + + if ((result->gr_name = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->gr_name, gr->gr_name, len); + + /* Password */ + len = strlen(gr->gr_passwd) + 1; + + if ((result->gr_passwd = + get_static(buffer, buflen, len)) == NULL) { + + /* Out of memory */ + return NSS_STATUS_TRYAGAIN; + } + + memcpy(result->gr_passwd, gr->gr_passwd, len); + + /* gid */ + + result->gr_gid = gr->gr_gid; + + /* Group membership */ + + if (!gr_mem) { + gr->num_gr_mem = 0; + } + + /* this next value is a pointer to a pointer so let's align it */ + + /* Calculate number of extra bytes needed to align on pointer size boundry */ + if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0) + i = sizeof(char*) - i; + + if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) * + sizeof(char *)+i))) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + result->gr_mem = (char **)(tst + i); + + if (gr->num_gr_mem == 0) { + + /* Group is empty */ + + *(result->gr_mem) = NULL; + return NSS_STATUS_SUCCESS; + } + + /* Start looking at extra data */ + + i = 0; + + while(next_token_alloc((const char **)&gr_mem, &name, ",")) { + /* Allocate space for member */ + len = strlen(name) + 1; + + if (((result->gr_mem)[i] = + get_static(buffer, buflen, len)) == NULL) { + free(name); + /* Out of memory */ + return NSS_STATUS_TRYAGAIN; + } + memcpy((result->gr_mem)[i], name, len); + free(name); + i++; + } + + /* Terminate list */ + + (result->gr_mem)[i] = NULL; + + return NSS_STATUS_SUCCESS; +} + +/* + * NSS user functions + */ + +static struct winbindd_response getpwent_response; + +static int ndx_pw_cache; /* Current index into pwd cache */ +static int num_pw_cache; /* Current size of pwd cache */ + +/* Rewind "file pointer" to start of ntdom password database */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_setpwent(void) +{ + NSS_STATUS ret; +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: setpwent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + if (num_pw_cache > 0) { + ndx_pw_cache = num_pw_cache = 0; + winbindd_free_response(&getpwent_response); + } + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL); +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + return ret; +} + +/* Close ntdom password database "file pointer" */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_endpwent(void) +{ + NSS_STATUS ret; +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: endpwent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + if (num_pw_cache > 0) { + ndx_pw_cache = num_pw_cache = 0; + winbindd_free_response(&getpwent_response); + } + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL); +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* Fetch the next password entry from ntdom password database */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getpwent_r(struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_request request; + static int called_again; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* Return an entry from the cache if we have one, or if we are + called again because we exceeded our static buffer. */ + + if ((ndx_pw_cache < num_pw_cache) || called_again) { + goto return_result; + } + + /* Else call winbindd to get a bunch of entries */ + + if (num_pw_cache > 0) { + winbindd_free_response(&getpwent_response); + } + + ZERO_STRUCT(request); + ZERO_STRUCT(getpwent_response); + + request.data.num_entries = MAX_GETPWENT_USERS; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request, + &getpwent_response); + + if (ret == NSS_STATUS_SUCCESS) { + struct winbindd_pw *pw_cache; + + /* Fill cache */ + + ndx_pw_cache = 0; + num_pw_cache = getpwent_response.data.num_entries; + + /* Return a result */ + + return_result: + + pw_cache = (struct winbindd_pw *) + getpwent_response.extra_data.data; + + /* Check data is valid */ + + if (pw_cache == NULL) { + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + ret = fill_pwent(result, &pw_cache[ndx_pw_cache], + &buffer, &buflen); + + /* Out of memory - try again */ + + if (ret == NSS_STATUS_TRYAGAIN) { + called_again = true; + *errnop = errno = ERANGE; + goto done; + } + + *errnop = errno = 0; + called_again = false; + ndx_pw_cache++; + + /* If we've finished with this lot of results free cache */ + + if (ndx_pw_cache == num_pw_cache) { + ndx_pw_cache = num_pw_cache = 0; + winbindd_free_response(&getpwent_response); + } + } + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + return ret; +} + +/* Return passwd struct from uid */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* If our static buffer needs to be expanded we are called again */ + if (!keep_response || uid != response.data.pw.pw_uid) { + + /* Call for the first time */ + + response = (struct winbindd_response) { + .length = 0, + }; + request = (struct winbindd_request) { + .wb_flags = WBFLAG_FROM_NSS, + .data = { + .uid = uid, + }, + }; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response); + + if (ret == NSS_STATUS_SUCCESS) { + ret = fill_pwent(result, &response.data.pw, + &buffer, &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + } + + } else { + + /* We've been called again */ + + ret = fill_pwent(result, &response.data.pw, &buffer, &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + *errnop = errno = ERANGE; + goto done; + } + + keep_response = false; + *errnop = errno = 0; + } + + winbindd_free_response(&response); + + done: + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(), + (unsigned int)uid, nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* Return passwd struct from username */ +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* If our static buffer needs to be expanded we are called again */ + + if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) { + + /* Call for the first time */ + + response = (struct winbindd_response) { + .length = 0, + }; + request = (struct winbindd_request) { + .wb_flags = WBFLAG_FROM_NSS, + }; + + strncpy(request.data.username, name, + sizeof(request.data.username) - 1); + request.data.username + [sizeof(request.data.username) - 1] = '\0'; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response); + + if (ret == NSS_STATUS_SUCCESS) { + ret = fill_pwent(result, &response.data.pw, &buffer, + &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + } + + } else { + + /* We've been called again */ + + ret = fill_pwent(result, &response.data.pw, &buffer, &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + + keep_response = false; + *errnop = errno = 0; + } + + winbindd_free_response(&response); + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(), + name, nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* + * NSS group functions + */ + +static struct winbindd_response getgrent_response; + +static int ndx_gr_cache; /* Current index into grp cache */ +static int num_gr_cache; /* Current size of grp cache */ + +/* Rewind "file pointer" to start of ntdom group database */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_setgrent(void) +{ + NSS_STATUS ret; +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: setgrent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + if (num_gr_cache > 0) { + ndx_gr_cache = num_gr_cache = 0; + winbindd_free_response(&getgrent_response); + } + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL); +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* Close "file pointer" for ntdom group database */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_endgrent(void) +{ + NSS_STATUS ret; +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: endgrent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + if (num_gr_cache > 0) { + ndx_gr_cache = num_gr_cache = 0; + winbindd_free_response(&getgrent_response); + } + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL); +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* Get next entry from ntdom group database */ + +static NSS_STATUS +winbind_getgrent(enum winbindd_cmd cmd, + struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_request request; + static int called_again; + + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrent\n", getpid()); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* Return an entry from the cache if we have one, or if we are + called again because we exceeded our static buffer. */ + + if ((ndx_gr_cache < num_gr_cache) || called_again) { + goto return_result; + } + + /* Else call winbindd to get a bunch of entries */ + + if (num_gr_cache > 0) { + winbindd_free_response(&getgrent_response); + } + + ZERO_STRUCT(request); + ZERO_STRUCT(getgrent_response); + + request.data.num_entries = MAX_GETGRENT_USERS; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, cmd, &request, + &getgrent_response); + + if (ret == NSS_STATUS_SUCCESS) { + struct winbindd_gr *gr_cache; + int mem_ofs; + + /* Fill cache */ + + ndx_gr_cache = 0; + num_gr_cache = getgrent_response.data.num_entries; + + /* Return a result */ + + return_result: + + gr_cache = (struct winbindd_gr *) + getgrent_response.extra_data.data; + + /* Check data is valid */ + + if (gr_cache == NULL) { + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + /* Fill group membership. The offset into the extra data + for the group membership is the reported offset plus the + size of all the winbindd_gr records returned. */ + + mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs + + num_gr_cache * sizeof(struct winbindd_gr); + + ret = fill_grent(result, &gr_cache[ndx_gr_cache], + ((char *)getgrent_response.extra_data.data)+mem_ofs, + &buffer, &buflen); + + /* Out of memory - try again */ + + if (ret == NSS_STATUS_TRYAGAIN) { + called_again = true; + *errnop = errno = ERANGE; + goto done; + } + + *errnop = 0; + called_again = false; + ndx_gr_cache++; + + /* If we've finished with this lot of results free cache */ + + if (ndx_gr_cache == num_gr_cache) { + ndx_gr_cache = num_gr_cache = 0; + winbindd_free_response(&getgrent_response); + } + } + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(), + nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getgrent_r(struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop); +} + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getgrlst_r(struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop); +} + +/* Return group struct from group name */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getgrnam_r(const char *name, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* If our static buffer needs to be expanded we are called again */ + /* Or if the stored response group name differs from the request. */ + + if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) { + + /* Call for the first time */ + + response = (struct winbindd_response) { + .length = 0, + }; + request = (struct winbindd_request) { + .wb_flags = WBFLAG_FROM_NSS, + }; + + strncpy(request.data.groupname, name, + sizeof(request.data.groupname)); + request.data.groupname + [sizeof(request.data.groupname) - 1] = '\0'; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM, + &request, &response); + + if (ret == NSS_STATUS_SUCCESS) { + ret = fill_grent(result, &response.data.gr, + (char *)response.extra_data.data, + &buffer, &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + } + + } else { + + /* We've been called again */ + + ret = fill_grent(result, &response.data.gr, + (char *)response.extra_data.data, &buffer, + &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + + keep_response = false; + *errnop = 0; + } + + winbindd_free_response(&response); + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(), + name, nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} + +/* Return group struct from gid */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_getgrgid_r(gid_t gid, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + /* If our static buffer needs to be expanded we are called again */ + /* Or if the stored response group name differs from the request. */ + + if (!keep_response || gid != response.data.gr.gr_gid) { + + /* Call for the first time */ + + response = (struct winbindd_response) { + .length = 0, + }; + request = (struct winbindd_request) { + .wb_flags = WBFLAG_FROM_NSS, + }; + + + request.data.gid = gid; + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETGRGID, + &request, &response); + + if (ret == NSS_STATUS_SUCCESS) { + + ret = fill_grent(result, &response.data.gr, + (char *)response.extra_data.data, + &buffer, &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + } + + } else { + + /* We've been called again */ + + ret = fill_grent(result, &response.data.gr, + (char *)response.extra_data.data, &buffer, + &buflen); + + if (ret == NSS_STATUS_TRYAGAIN) { + keep_response = true; + *errnop = errno = ERANGE; + goto done; + } + + keep_response = false; + *errnop = 0; + } + + winbindd_free_response(&response); + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(), + (unsigned int)gid, nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + return ret; +} + +/* Initialise supplementary groups */ + +_PUBLIC_ON_LINUX_ +NSS_STATUS +_nss_winbind_initgroups_dyn(const char *user, gid_t group, long int *start, + long int *size, gid_t **groups, long int limit, + int *errnop) +{ + NSS_STATUS ret; + struct winbindd_request request; + struct winbindd_response response; + int i; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(), + user, group); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&winbind_nss_mutex); +#endif + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.username, user, + sizeof(request.data.username) - 1); + + winbind_set_client_name("nss_winbind"); + ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS, + &request, &response); + + if (ret == NSS_STATUS_SUCCESS) { + int num_gids = response.data.num_entries; + gid_t *gid_list = (gid_t *)response.extra_data.data; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS " + "and %d gids\n", getpid(), + user, num_gids); +#endif + if (gid_list == NULL) { + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + /* Copy group list to client */ + + for (i = 0; i < num_gids; i++) { + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s (%d): " + "processing gid %d \n", getpid(), + user, group, gid_list[i]); +#endif + + /* Skip primary group */ + + if (gid_list[i] == group) { + continue; + } + + /* Skip groups without a mapping */ + if (gid_list[i] == (uid_t)-1) { + continue; + } + + /* Filled buffer ? If so, resize. */ + + if (*start == *size) { + long int newsize; + gid_t *newgroups; + + newsize = 2 * (*size); + if (limit > 0) { + if (*size == limit) { + goto done; + } + if (newsize > limit) { + newsize = limit; + } + } + + newgroups = (gid_t *) + realloc((*groups), + newsize * sizeof(**groups)); + if (!newgroups) { + *errnop = ENOMEM; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + *groups = newgroups; + *size = newsize; + } + + /* Add to buffer */ + + (*groups)[*start] = gid_list[i]; + *start += 1; + } + } + + /* Back to your regularly scheduled programming */ + + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(), + user, nss_err_str(ret), ret); +#endif + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&winbind_nss_mutex); +#endif + + return ret; +} |