1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-10 01:18:15 +03:00
samba-mirror/source3/nsswitch/winbind_nss.c
Andrew Bartlett 885f4f9379 Winbind client-side cleanups.
The global winbind file descriptor can cause havoc in some situations -
particulary when it becomes 0, 1 or 2.  This patch (based on some very nice
work by Hannes Schmidt <mail@schmidt-net.via.t-online.de>) starts to recitfy
the problem by ensuring that the close-on-exec flag is set, and that we move
above 3 in the file descriptor table.

I've also decided that the PAM module can close it's pipe handle on every
request - this isn't performance-critical code.

The next step is to do the same for nss_winbind.  (But things like getent()
might get in our way there).

This also cleans up some function prototypes, puts them in just one place.

Andrew Bartlett
(This used to be commit 442eb39657)
2002-09-07 05:41:23 +00:00

1342 lines
30 KiB
C

/*
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 Library General Public
License as published by the Free Software Foundation; either
version 2 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 Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include "winbind_client.h"
#ifdef HAVE_NS_API_H
#undef VOLATILE
#include <ns_daemon.h>
#endif
#define MAX_GETPWENT_USERS 250
#define MAX_GETGRENT_USERS 250
/* Prototypes from wb_common.c */
extern int winbindd_fd;
#ifdef HAVE_NS_API_H
/* IRIX version */
static int send_next_request(nsd_file_t *, struct winbindd_request *);
static int do_list(int state, nsd_file_t *rq);
static nsd_file_t *current_rq = NULL;
static int current_winbind_xid = 0;
static int next_winbind_xid = 0;
typedef struct winbind_xid {
int xid;
nsd_file_t *rq;
struct winbindd_request *request;
struct winbind_xid *next;
} winbind_xid_t;
static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
static int
winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
{
winbind_xid_t *new;
nsd_logprintf(NSD_LOG_LOW,
"entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
xid, rq, request);
new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
if (!new) {
nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
return NSD_ERROR;
}
new->xid = xid;
new->rq = rq;
new->request = request;
new->next = winbind_xids;
winbind_xids = new;
return NSD_CONTINUE;
}
/*
** This routine will look down the xid list and return the request
** associated with an xid. We remove the record if it is found.
*/
nsd_file_t *
winbind_xid_lookup(int xid, struct winbindd_request **requestp)
{
winbind_xid_t **last, *dx;
nsd_file_t *result=0;
for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
last = &dx->next, dx = dx->next);
if (dx) {
*last = dx->next;
result = dx->rq;
*requestp = dx->request;
SAFE_FREE(dx);
}
nsd_logprintf(NSD_LOG_LOW,
"entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
xid, result, dx->request);
return result;
}
static int
winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
{
nsd_file_t *rq;
struct winbindd_request *request;
nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
rq = to->t_file;
*rqp = rq;
nsd_timeout_remove(rq);
request = to->t_clientdata;
return(send_next_request(rq, request));
}
static void
dequeue_request()
{
nsd_file_t *rq;
struct winbindd_request *request;
/*
* Check for queued requests
*/
if (winbind_xids) {
nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
current_winbind_xid);
rq = winbind_xid_lookup(current_winbind_xid++, &request);
/* cause a timeout on the queued request so we can send it */
nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
}
}
static int
do_request(nsd_file_t *rq, struct winbindd_request *request)
{
if (winbind_xids == NULL) {
/*
* No outstanding requests.
* Send off the request to winbindd
*/
nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
return(send_next_request(rq, request));
} else {
/*
* Just queue it up for now - previous callout or timout
* will start it up
*/
nsd_logprintf(NSD_LOG_MIN,
"lookup (winbind): queue request xid = %d\n",
next_winbind_xid);
return(winbind_xid_new(next_winbind_xid++, rq, request));
}
}
static int
winbind_callback(nsd_file_t **rqp, int fd)
{
struct winbindd_response response;
struct winbindd_pw *pw = &response.data.pw;
struct winbindd_gr *gr = &response.data.gr;
nsd_file_t *rq;
NSS_STATUS status;
fstring result;
char *members;
int i, maxlen;
dequeue_request();
nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
rq = current_rq;
*rqp = rq;
nsd_timeout_remove(rq);
nsd_callback_remove(fd);
ZERO_STRUCT(response);
status = winbindd_get_response(&response);
if (status != NSS_STATUS_SUCCESS) {
/* free any extra data area in response structure */
free_response(&response);
nsd_logprintf(NSD_LOG_MIN,
"callback (winbind) returning not found, status = %d\n",
status);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
maxlen = sizeof(result) - 1;
switch ((int)rq->f_cmd_data) {
case WINBINDD_WINS_BYNAME:
case WINBINDD_WINS_BYIP:
snprintf(result,maxlen,"%s\n",response.data.winsresp);
break;
case WINBINDD_GETPWUID:
case WINBINDD_GETPWNAM:
snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
pw->pw_name,
pw->pw_passwd,
pw->pw_uid,
pw->pw_gid,
pw->pw_gecos,
pw->pw_dir,
pw->pw_shell);
break;
case WINBINDD_GETGRNAM:
case WINBINDD_GETGRGID:
if (gr->num_gr_mem && response.extra_data)
members = response.extra_data;
else
members = "";
snprintf(result,maxlen,"%s:%s:%d:%s\n",
gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
break;
case WINBINDD_SETGRENT:
case WINBINDD_SETPWENT:
nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
free_response(&response);
return(do_list(1,rq));
case WINBINDD_GETGRENT:
case WINBINDD_GETGRLST:
nsd_logprintf(NSD_LOG_MIN,
"callback (winbind) - %d GETGRENT responses\n",
response.data.num_entries);
if (response.data.num_entries) {
gr = (struct winbindd_gr *)response.extra_data;
if (! gr ) {
nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
free_response(&response);
return NSD_ERROR;
}
members = (char *)response.extra_data +
(response.data.num_entries * sizeof(struct winbindd_gr));
for (i = 0; i < response.data.num_entries; i++) {
snprintf(result,maxlen,"%s:%s:%d:%s\n",
gr->gr_name, gr->gr_passwd, gr->gr_gid,
&members[gr->gr_mem_ofs]);
nsd_logprintf(NSD_LOG_MIN, " GETGRENT %s\n",result);
nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
gr++;
}
}
i = response.data.num_entries;
free_response(&response);
if (i < MAX_GETPWENT_USERS)
return(do_list(2,rq));
else
return(do_list(1,rq));
case WINBINDD_GETPWENT:
nsd_logprintf(NSD_LOG_MIN,
"callback (winbind) - %d GETPWENT responses\n",
response.data.num_entries);
if (response.data.num_entries) {
pw = (struct winbindd_pw *)response.extra_data;
if (! pw ) {
nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
free_response(&response);
return NSD_ERROR;
}
for (i = 0; i < response.data.num_entries; i++) {
snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
pw->pw_name,
pw->pw_passwd,
pw->pw_uid,
pw->pw_gid,
pw->pw_gecos,
pw->pw_dir,
pw->pw_shell);
nsd_logprintf(NSD_LOG_MIN, " GETPWENT %s\n",result);
nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
pw++;
}
}
i = response.data.num_entries;
free_response(&response);
if (i < MAX_GETPWENT_USERS)
return(do_list(2,rq));
else
return(do_list(1,rq));
case WINBINDD_ENDGRENT:
case WINBINDD_ENDPWENT:
nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
nsd_append_element(rq,NS_SUCCESS,"\n",1);
free_response(&response);
return NSD_NEXT;
default:
free_response(&response);
nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
return NSD_NEXT;
}
nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
/* free any extra data area in response structure */
free_response(&response);
nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
return NSD_OK;
}
static int
winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
{
nsd_file_t *rq;
dequeue_request();
nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
rq = to->t_file;
*rqp = rq;
/* Remove the callback and timeout */
nsd_callback_remove(winbindd_fd);
nsd_timeout_remove(rq);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
static int
send_next_request(nsd_file_t *rq, struct winbindd_request *request)
{
NSS_STATUS status;
long timeout;
timeout = 1000;
nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
rq->f_cmd_data, timeout);
status = winbindd_send_request((int)rq->f_cmd_data,request);
SAFE_FREE(request);
if (status != NSS_STATUS_SUCCESS) {
nsd_logprintf(NSD_LOG_MIN,
"send_next_request (winbind) error status = %d\n",status);
rq->f_status = status;
return NSD_NEXT;
}
current_rq = rq;
/*
* Set up callback and timeouts
*/
nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
return NSD_CONTINUE;
}
int init(void)
{
nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
return(NSD_OK);
}
int lookup(nsd_file_t *rq)
{
char *map;
char *key;
struct winbindd_request *request;
nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
if (! rq)
return NSD_ERROR;
map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
if (! map || ! key) {
nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
rq->f_status = NS_BADREQ;
return NSD_ERROR;
}
nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
if (! request) {
nsd_logprintf(NSD_LOG_RESOURCE,
"lookup (winbind): failed malloc\n");
return NSD_ERROR;
}
if (strcasecmp(map,"passwd.byuid") == 0) {
request->data.uid = atoi(key);
rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
} else if (strcasecmp(map,"passwd.byname") == 0) {
strncpy(request->data.username, key,
sizeof(request->data.username) - 1);
request->data.username[sizeof(request->data.username) - 1] = '\0';
rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
} else if (strcasecmp(map,"group.byname") == 0) {
strncpy(request->data.groupname, key,
sizeof(request->data.groupname) - 1);
request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
} else if (strcasecmp(map,"group.bygid") == 0) {
request->data.gid = atoi(key);
rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
} else if (strcasecmp(map,"hosts.byname") == 0) {
strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
} else if (strcasecmp(map,"hosts.byaddr") == 0) {
strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
} else {
/*
* Don't understand this map - just return not found
*/
nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
SAFE_FREE(request);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
return(do_request(rq, request));
}
int list(nsd_file_t *rq)
{
char *map;
nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
if (! rq)
return NSD_ERROR;
map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
if (! map ) {
nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
rq->f_status = NS_BADREQ;
return NSD_ERROR;
}
nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
return (do_list(0,rq));
}
static int
do_list(int state, nsd_file_t *rq)
{
char *map;
struct winbindd_request *request;
nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
if (! request) {
nsd_logprintf(NSD_LOG_RESOURCE,
"do_list (winbind): failed malloc\n");
return NSD_ERROR;
}
if (strcasecmp(map,"passwd.byname") == 0) {
switch (state) {
case 0:
rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
break;
case 1:
request->data.num_entries = MAX_GETPWENT_USERS;
rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
break;
case 2:
rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
break;
default:
nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
SAFE_FREE(request);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
} else if (strcasecmp(map,"group.byname") == 0) {
switch (state) {
case 0:
rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
break;
case 1:
request->data.num_entries = MAX_GETGRENT_USERS;
rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
break;
case 2:
rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
break;
default:
nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
SAFE_FREE(request);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
} else {
/*
* Don't understand this map - just return not found
*/
nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
SAFE_FREE(request);
rq->f_status = NS_NOTFOUND;
return NSD_NEXT;
}
return(do_request(rq, request));
}
#else
/* 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, int *buflen, int 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() 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. */
BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
{
char *s;
BOOL quoted;
size_t len=1;
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);
/* copy over the token */
for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
if (*s == '\"') {
quoted = !quoted;
} else {
len++;
*buff++ = *s;
}
}
*ptr = (*s) ? s+1 : s;
*buff = 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)
{
/* User name */
if ((result->pw_name =
get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->pw_name, pw->pw_name);
/* Password */
if ((result->pw_passwd =
get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->pw_passwd, pw->pw_passwd);
/* [ug]id */
result->pw_uid = pw->pw_uid;
result->pw_gid = pw->pw_gid;
/* GECOS */
if ((result->pw_gecos =
get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->pw_gecos, pw->pw_gecos);
/* Home directory */
if ((result->pw_dir =
get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->pw_dir, pw->pw_dir);
/* Logon shell */
if ((result->pw_shell =
get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->pw_shell, pw->pw_shell);
/* The struct passwd for Solaris has some extra fields which must
be initialised or nscd crashes. */
#if HAVE_PASSWD_PW_COMMENT
result->pw_comment = "";
#endif
#if 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,
char *gr_mem, char **buffer, size_t *buflen)
{
fstring name;
int i;
char *tst;
/* Group name */
if ((result->gr_name =
get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->gr_name, gr->gr_name);
/* Password */
if ((result->gr_passwd =
get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy(result->gr_passwd, gr->gr_passwd);
/* gid */
result->gr_gid = gr->gr_gid;
/* Group membership */
if ((gr->num_gr_mem < 0) || !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((char **)&gr_mem, name, ",", sizeof(fstring))) {
/* Allocate space for member */
if (((result->gr_mem)[i] =
get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
/* Out of memory */
return NSS_STATUS_TRYAGAIN;
}
strcpy((result->gr_mem)[i], 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 */
NSS_STATUS
_nss_winbind_setpwent(void)
{
#ifdef DEBUG_NSS
fprintf(stderr, "[%5d]: setpwent\n", getpid());
#endif
if (num_pw_cache > 0) {
ndx_pw_cache = num_pw_cache = 0;
free_response(&getpwent_response);
}
return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
}
/* Close ntdom password database "file pointer" */
NSS_STATUS
_nss_winbind_endpwent(void)
{
#ifdef DEBUG_NSS
fprintf(stderr, "[%5d]: endpwent\n", getpid());
#endif
if (num_pw_cache > 0) {
ndx_pw_cache = num_pw_cache = 0;
free_response(&getpwent_response);
}
return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
}
/* Fetch the next password entry from ntdom password database */
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
/* 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) {
free_response(&getpwent_response);
}
ZERO_STRUCT(request);
ZERO_STRUCT(getpwent_response);
request.data.num_entries = MAX_GETPWENT_USERS;
ret = winbindd_request(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 = getpwent_response.extra_data;
/* Check data is valid */
if (pw_cache == NULL) {
return NSS_STATUS_NOTFOUND;
}
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;
return ret;
}
*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;
free_response(&getpwent_response);
}
}
return ret;
}
/* Return passwd struct from uid */
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=0;
/* If our static buffer needs to be expanded we are called again */
if (!keep_response) {
/* Call for the first time */
ZERO_STRUCT(response);
ZERO_STRUCT(request);
request.data.uid = uid;
ret = winbindd_request(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;
return ret;
}
}
} 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;
return ret;
}
keep_response = False;
*errnop = errno = 0;
}
free_response(&response);
return ret;
}
/* Return passwd struct from username */
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 %s\n", getpid(), name);
#endif
/* If our static buffer needs to be expanded we are called again */
if (!keep_response) {
/* Call for the first time */
ZERO_STRUCT(response);
ZERO_STRUCT(request);
strncpy(request.data.username, name,
sizeof(request.data.username) - 1);
request.data.username
[sizeof(request.data.username) - 1] = '\0';
ret = winbindd_request(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;
return ret;
}
}
} 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;
return ret;
}
keep_response = False;
*errnop = errno = 0;
}
free_response(&response);
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 */
NSS_STATUS
_nss_winbind_setgrent(void)
{
#ifdef DEBUG_NSS
fprintf(stderr, "[%5d]: setgrent\n", getpid());
#endif
if (num_gr_cache > 0) {
ndx_gr_cache = num_gr_cache = 0;
free_response(&getgrent_response);
}
return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
}
/* Close "file pointer" for ntdom group database */
NSS_STATUS
_nss_winbind_endgrent(void)
{
#ifdef DEBUG_NSS
fprintf(stderr, "[%5d]: endgrent\n", getpid());
#endif
if (num_gr_cache > 0) {
ndx_gr_cache = num_gr_cache = 0;
free_response(&getgrent_response);
}
return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
}
/* 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
/* 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) {
free_response(&getgrent_response);
}
ZERO_STRUCT(request);
ZERO_STRUCT(getgrent_response);
request.data.num_entries = MAX_GETGRENT_USERS;
ret = winbindd_request(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 = getgrent_response.extra_data;
/* Check data is valid */
if (gr_cache == NULL) {
return NSS_STATUS_NOTFOUND;
}
/* 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)+mem_ofs,
&buffer, &buflen);
/* Out of memory - try again */
if (ret == NSS_STATUS_TRYAGAIN) {
called_again = True;
*errnop = errno = ERANGE;
return ret;
}
*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;
free_response(&getgrent_response);
}
}
return ret;
}
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);
}
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 */
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
/* If our static buffer needs to be expanded we are called again */
if (!keep_response) {
/* Call for the first time */
ZERO_STRUCT(request);
ZERO_STRUCT(response);
strncpy(request.data.groupname, name,
sizeof(request.data.groupname));
request.data.groupname
[sizeof(request.data.groupname) - 1] = '\0';
ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
if (ret == NSS_STATUS_SUCCESS) {
ret = fill_grent(result, &response.data.gr,
response.extra_data,
&buffer, &buflen);
if (ret == NSS_STATUS_TRYAGAIN) {
keep_response = True;
*errnop = errno = ERANGE;
return ret;
}
}
} else {
/* We've been called again */
ret = fill_grent(result, &response.data.gr,
response.extra_data, &buffer, &buflen);
if (ret == NSS_STATUS_TRYAGAIN) {
keep_response = True;
*errnop = errno = ERANGE;
return ret;
}
keep_response = False;
*errnop = 0;
}
free_response(&response);
return ret;
}
/* Return group struct from gid */
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
/* If our static buffer needs to be expanded we are called again */
if (!keep_response) {
/* Call for the first time */
ZERO_STRUCT(request);
ZERO_STRUCT(response);
request.data.gid = gid;
ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
if (ret == NSS_STATUS_SUCCESS) {
ret = fill_grent(result, &response.data.gr,
response.extra_data,
&buffer, &buflen);
if (ret == NSS_STATUS_TRYAGAIN) {
keep_response = True;
*errnop = errno = ERANGE;
return ret;
}
}
} else {
/* We've been called again */
ret = fill_grent(result, &response.data.gr,
response.extra_data, &buffer, &buflen);
if (ret == NSS_STATUS_TRYAGAIN) {
keep_response = True;
*errnop = errno = ERANGE;
return ret;
}
keep_response = False;
*errnop = 0;
}
free_response(&response);
return ret;
}
/* Initialise supplementary groups */
NSS_STATUS
_nss_winbind_initgroups_dyn(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
ZERO_STRUCT(request);
ZERO_STRUCT(response);
strncpy(request.data.username, user,
sizeof(request.data.username) - 1);
ret = winbindd_request(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;
/* Copy group list to client */
for (i = 0; i < num_gids; i++) {
/* Skip primary group */
if (gid_list[i] == group) continue;
/* Add to buffer */
if (*start == *size && limit <= 0) {
(*groups) = realloc(
(*groups), (2 * (*size) + 1) * sizeof(**groups));
if (! *groups) goto done;
*size = 2 * (*size) + 1;
}
if (*start == *size) goto done;
(*groups)[*start] = gid_list[i];
*start += 1;
/* Filled buffer? */
if (*start == limit) goto done;
}
}
/* Back to your regularly scheduled programming */
done:
return ret;
}
#endif