1/*
2   Unix SMB/CIFS implementation.
3
4   Windows NT Domain nsswitch module
5
6   Copyright (C) Tim Potter 2000
7
8   This library is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Lesser General Public
10   License as published by the Free Software Foundation; either
11   version 3 of the License, or (at your option) any later version.
12
13   This library is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   Library General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "winbind_client.h"
23
24#if HAVE_PTHREAD_H
25#include <pthread.h>
26#endif
27
28#if HAVE_PTHREAD
29static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
30#endif
31
32/* Maximum number of users to pass back over the unix domain socket
33   per call. This is not a static limit on the total number of users
34   or groups returned in total. */
35
36#define MAX_GETPWENT_USERS 250
37#define MAX_GETGRENT_USERS 250
38
39NSS_STATUS _nss_winbind_setpwent(void);
40NSS_STATUS _nss_winbind_endpwent(void);
41NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
42				   size_t buflen, int *errnop);
43NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
44				   char *buffer, size_t buflen, int *errnop);
45NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
46				   char *buffer, size_t buflen, int *errnop);
47NSS_STATUS _nss_winbind_setgrent(void);
48NSS_STATUS _nss_winbind_endgrent(void);
49NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
50				   size_t buflen, int *errnop);
51NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
52				   size_t buflen, int *errnop);
53NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
54				   char *buffer, size_t buflen, int *errnop);
55NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
56				   size_t buflen, int *errnop);
57NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
58				       long int *size, gid_t **groups,
59				       long int limit, int *errnop);
60NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids,
61				    int *num_groups, char *buffer, size_t buf_size,
62				    int *errnop);
63NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
64				  size_t buflen, int *errnop);
65NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
66				  size_t buflen, int *errnop);
67NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop);
68NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop);
69NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
70				 size_t buflen, int *errnop);
71NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
72				 size_t buflen, int *errnop);
73
74/* Prototypes from wb_common.c */
75
76/* Allocate some space from the nss static buffer.  The buffer and buflen
77   are the pointers passed in by the C library to the _nss_ntdom_*
78   functions. */
79
80static char *get_static(char **buffer, size_t *buflen, size_t len)
81{
82	char *result;
83
84	/* Error check.  We return false if things aren't set up right, or
85	   there isn't enough buffer space left. */
86
87	if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
88		return NULL;
89	}
90
91	/* Return an index into the static buffer */
92
93	result = *buffer;
94	*buffer += len;
95	*buflen -= len;
96
97	return result;
98}
99
100/* I've copied the strtok() replacement function next_token_Xalloc() from
101   lib/util_str.c as I really don't want to have to link in any other
102   objects if I can possibly avoid it. */
103
104static bool next_token_alloc(const char **ptr,
105                                char **pp_buff,
106                                const char *sep)
107{
108	const char *s;
109	const char *saved_s;
110	char *pbuf;
111	bool quoted;
112	size_t len=1;
113
114	*pp_buff = NULL;
115	if (!ptr) {
116		return(false);
117	}
118
119	s = *ptr;
120
121	/* default to simple separators */
122	if (!sep) {
123		sep = " \t\n\r";
124	}
125
126	/* find the first non sep char */
127	while (*s && strchr(sep,*s)) {
128		s++;
129	}
130
131	/* nothing left? */
132	if (!*s) {
133		return false;
134	}
135
136	/* When restarting we need to go from here. */
137	saved_s = s;
138
139	/* Work out the length needed. */
140	for (quoted = false; *s &&
141			(quoted || !strchr(sep,*s)); s++) {
142		if (*s == '\"') {
143			quoted = !quoted;
144		} else {
145			len++;
146		}
147	}
148
149	/* We started with len = 1 so we have space for the nul. */
150	*pp_buff = (char *)malloc(len);
151	if (!*pp_buff) {
152		return false;
153	}
154
155	/* copy over the token */
156	pbuf = *pp_buff;
157	s = saved_s;
158	for (quoted = false; *s &&
159			(quoted || !strchr(sep,*s)); s++) {
160		if ( *s == '\"' ) {
161			quoted = !quoted;
162		} else {
163			*pbuf++ = *s;
164		}
165	}
166
167	*ptr = (*s) ? s+1 : s;
168	*pbuf = 0;
169
170	return true;
171}
172
173/* Fill a pwent structure from a winbindd_response structure.  We use
174   the static data passed to us by libc to put strings and stuff in.
175   Return NSS_STATUS_TRYAGAIN if we run out of memory. */
176
177static NSS_STATUS fill_pwent(struct passwd *result,
178				  struct winbindd_pw *pw,
179				  char **buffer, size_t *buflen)
180{
181	/* User name */
182
183	if ((result->pw_name =
184	     get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
185
186		/* Out of memory */
187
188		return NSS_STATUS_TRYAGAIN;
189	}
190
191	strcpy(result->pw_name, pw->pw_name);
192
193	/* Password */
194
195	if ((result->pw_passwd =
196	     get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
197
198		/* Out of memory */
199
200		return NSS_STATUS_TRYAGAIN;
201	}
202
203	strcpy(result->pw_passwd, pw->pw_passwd);
204
205	/* [ug]id */
206
207	result->pw_uid = pw->pw_uid;
208	result->pw_gid = pw->pw_gid;
209
210	/* GECOS */
211
212	if ((result->pw_gecos =
213	     get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
214
215		/* Out of memory */
216
217		return NSS_STATUS_TRYAGAIN;
218	}
219
220	strcpy(result->pw_gecos, pw->pw_gecos);
221
222	/* Home directory */
223
224	if ((result->pw_dir =
225	     get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
226
227		/* Out of memory */
228
229		return NSS_STATUS_TRYAGAIN;
230	}
231
232	strcpy(result->pw_dir, pw->pw_dir);
233
234	/* Logon shell */
235
236	if ((result->pw_shell =
237	     get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
238
239		/* Out of memory */
240
241		return NSS_STATUS_TRYAGAIN;
242	}
243
244	strcpy(result->pw_shell, pw->pw_shell);
245
246	/* The struct passwd for Solaris has some extra fields which must
247	   be initialised or nscd crashes. */
248
249#if HAVE_PASSWD_PW_COMMENT
250	result->pw_comment = "";
251#endif
252
253#if HAVE_PASSWD_PW_AGE
254	result->pw_age = "";
255#endif
256
257	return NSS_STATUS_SUCCESS;
258}
259
260/* Fill a grent structure from a winbindd_response structure.  We use
261   the static data passed to us by libc to put strings and stuff in.
262   Return NSS_STATUS_TRYAGAIN if we run out of memory. */
263
264static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
265		      char *gr_mem, char **buffer, size_t *buflen)
266{
267	char *name;
268	int i;
269	char *tst;
270
271	/* Group name */
272
273	if ((result->gr_name =
274	     get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
275
276		/* Out of memory */
277
278		return NSS_STATUS_TRYAGAIN;
279	}
280
281	strcpy(result->gr_name, gr->gr_name);
282
283	/* Password */
284
285	if ((result->gr_passwd =
286	     get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
287
288		/* Out of memory */
289		return NSS_STATUS_TRYAGAIN;
290	}
291
292	strcpy(result->gr_passwd, gr->gr_passwd);
293
294	/* gid */
295
296	result->gr_gid = gr->gr_gid;
297
298	/* Group membership */
299
300	if ((gr->num_gr_mem < 0) || !gr_mem) {
301		gr->num_gr_mem = 0;
302	}
303
304	/* this next value is a pointer to a pointer so let's align it */
305
306	/* Calculate number of extra bytes needed to align on pointer size boundry */
307	if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
308		i = sizeof(char*) - i;
309
310	if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
311				 sizeof(char *)+i))) == NULL) {
312
313		/* Out of memory */
314
315		return NSS_STATUS_TRYAGAIN;
316	}
317	result->gr_mem = (char **)(tst + i);
318
319	if (gr->num_gr_mem == 0) {
320
321		/* Group is empty */
322
323		*(result->gr_mem) = NULL;
324		return NSS_STATUS_SUCCESS;
325	}
326
327	/* Start looking at extra data */
328
329	i = 0;
330
331	while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
332		/* Allocate space for member */
333		if (((result->gr_mem)[i] =
334		     get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
335			free(name);
336			/* Out of memory */
337			return NSS_STATUS_TRYAGAIN;
338		}
339		strcpy((result->gr_mem)[i], name);
340		free(name);
341		i++;
342	}
343
344	/* Terminate list */
345
346	(result->gr_mem)[i] = NULL;
347
348	return NSS_STATUS_SUCCESS;
349}
350
351/*
352 * NSS user functions
353 */
354
355static struct winbindd_response getpwent_response;
356
357static int ndx_pw_cache;                 /* Current index into pwd cache */
358static int num_pw_cache;                 /* Current size of pwd cache */
359
360/* Rewind "file pointer" to start of ntdom password database */
361
362NSS_STATUS
363_nss_winbind_setpwent(void)
364{
365	NSS_STATUS ret;
366#ifdef DEBUG_NSS
367	fprintf(stderr, "[%5d]: setpwent\n", getpid());
368#endif
369
370#if HAVE_PTHREAD
371	pthread_mutex_lock(&winbind_nss_mutex);
372#endif
373
374	if (num_pw_cache > 0) {
375		ndx_pw_cache = num_pw_cache = 0;
376		winbindd_free_response(&getpwent_response);
377	}
378
379	ret = winbindd_request_response(WINBINDD_SETPWENT, NULL, NULL);
380#ifdef DEBUG_NSS
381	fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
382		nss_err_str(ret), ret);
383#endif
384
385#if HAVE_PTHREAD
386	pthread_mutex_unlock(&winbind_nss_mutex);
387#endif
388	return ret;
389}
390
391/* Close ntdom password database "file pointer" */
392
393NSS_STATUS
394_nss_winbind_endpwent(void)
395{
396	NSS_STATUS ret;
397#ifdef DEBUG_NSS
398	fprintf(stderr, "[%5d]: endpwent\n", getpid());
399#endif
400
401#if HAVE_PTHREAD
402	pthread_mutex_lock(&winbind_nss_mutex);
403#endif
404
405	if (num_pw_cache > 0) {
406		ndx_pw_cache = num_pw_cache = 0;
407		winbindd_free_response(&getpwent_response);
408	}
409
410	ret = winbindd_request_response(WINBINDD_ENDPWENT, NULL, NULL);
411#ifdef DEBUG_NSS
412	fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
413		nss_err_str(ret), ret);
414#endif
415
416#if HAVE_PTHREAD
417	pthread_mutex_unlock(&winbind_nss_mutex);
418#endif
419
420	return ret;
421}
422
423/* Fetch the next password entry from ntdom password database */
424
425NSS_STATUS
426_nss_winbind_getpwent_r(struct passwd *result, char *buffer,
427			size_t buflen, int *errnop)
428{
429	NSS_STATUS ret;
430	struct winbindd_request request;
431	static int called_again;
432
433#ifdef DEBUG_NSS
434	fprintf(stderr, "[%5d]: getpwent\n", getpid());
435#endif
436
437#if HAVE_PTHREAD
438	pthread_mutex_lock(&winbind_nss_mutex);
439#endif
440
441	/* Return an entry from the cache if we have one, or if we are
442	   called again because we exceeded our static buffer.  */
443
444	if ((ndx_pw_cache < num_pw_cache) || called_again) {
445		goto return_result;
446	}
447
448	/* Else call winbindd to get a bunch of entries */
449
450	if (num_pw_cache > 0) {
451		winbindd_free_response(&getpwent_response);
452	}
453
454	ZERO_STRUCT(request);
455	ZERO_STRUCT(getpwent_response);
456
457	request.data.num_entries = MAX_GETPWENT_USERS;
458
459	ret = winbindd_request_response(WINBINDD_GETPWENT, &request,
460			       &getpwent_response);
461
462	if (ret == NSS_STATUS_SUCCESS) {
463		struct winbindd_pw *pw_cache;
464
465		/* Fill cache */
466
467		ndx_pw_cache = 0;
468		num_pw_cache = getpwent_response.data.num_entries;
469
470		/* Return a result */
471
472	return_result:
473
474		pw_cache = (struct winbindd_pw *)
475			getpwent_response.extra_data.data;
476
477		/* Check data is valid */
478
479		if (pw_cache == NULL) {
480			ret = NSS_STATUS_NOTFOUND;
481			goto done;
482		}
483
484		ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
485				 &buffer, &buflen);
486
487		/* Out of memory - try again */
488
489		if (ret == NSS_STATUS_TRYAGAIN) {
490			called_again = true;
491			*errnop = errno = ERANGE;
492			goto done;
493		}
494
495		*errnop = errno = 0;
496		called_again = false;
497		ndx_pw_cache++;
498
499		/* If we've finished with this lot of results free cache */
500
501		if (ndx_pw_cache == num_pw_cache) {
502			ndx_pw_cache = num_pw_cache = 0;
503			winbindd_free_response(&getpwent_response);
504		}
505	}
506	done:
507#ifdef DEBUG_NSS
508	fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
509		nss_err_str(ret), ret);
510#endif
511
512#if HAVE_PTHREAD
513	pthread_mutex_unlock(&winbind_nss_mutex);
514#endif
515	return ret;
516}
517
518/* Return passwd struct from uid */
519
520NSS_STATUS
521_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
522			size_t buflen, int *errnop)
523{
524	NSS_STATUS ret;
525	static struct winbindd_response response;
526	struct winbindd_request request;
527	static int keep_response;
528
529#ifdef DEBUG_NSS
530	fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
531#endif
532
533#if HAVE_PTHREAD
534	pthread_mutex_lock(&winbind_nss_mutex);
535#endif
536
537	/* If our static buffer needs to be expanded we are called again */
538	if (!keep_response || uid != response.data.pw.pw_uid) {
539
540		/* Call for the first time */
541
542		ZERO_STRUCT(response);
543		ZERO_STRUCT(request);
544
545		request.data.uid = uid;
546
547		ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
548
549		if (ret == NSS_STATUS_SUCCESS) {
550			ret = fill_pwent(result, &response.data.pw,
551					 &buffer, &buflen);
552
553			if (ret == NSS_STATUS_TRYAGAIN) {
554				keep_response = true;
555				*errnop = errno = ERANGE;
556				goto done;
557			}
558		}
559
560	} else {
561
562		/* We've been called again */
563
564		ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
565
566		if (ret == NSS_STATUS_TRYAGAIN) {
567			*errnop = errno = ERANGE;
568			goto done;
569		}
570
571		keep_response = false;
572		*errnop = errno = 0;
573	}
574
575	winbindd_free_response(&response);
576
577	done:
578
579#ifdef DEBUG_NSS
580	fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
581		(unsigned int)uid, nss_err_str(ret), ret);
582#endif
583
584#if HAVE_PTHREAD
585	pthread_mutex_unlock(&winbind_nss_mutex);
586#endif
587
588	return ret;
589}
590
591/* Return passwd struct from username */
592NSS_STATUS
593_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
594			size_t buflen, int *errnop)
595{
596	NSS_STATUS ret;
597	static struct winbindd_response response;
598	struct winbindd_request request;
599	static int keep_response;
600
601#ifdef DEBUG_NSS
602	fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
603#endif
604
605#if HAVE_PTHREAD
606	pthread_mutex_lock(&winbind_nss_mutex);
607#endif
608
609	/* If our static buffer needs to be expanded we are called again */
610
611	if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
612
613		/* Call for the first time */
614
615		ZERO_STRUCT(response);
616		ZERO_STRUCT(request);
617
618		strncpy(request.data.username, name,
619			sizeof(request.data.username) - 1);
620		request.data.username
621			[sizeof(request.data.username) - 1] = '\0';
622
623		ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
624
625		if (ret == NSS_STATUS_SUCCESS) {
626			ret = fill_pwent(result, &response.data.pw, &buffer,
627					 &buflen);
628
629			if (ret == NSS_STATUS_TRYAGAIN) {
630				keep_response = true;
631				*errnop = errno = ERANGE;
632				goto done;
633			}
634		}
635
636	} else {
637
638		/* We've been called again */
639
640		ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
641
642		if (ret == NSS_STATUS_TRYAGAIN) {
643			keep_response = true;
644			*errnop = errno = ERANGE;
645			goto done;
646		}
647
648		keep_response = false;
649		*errnop = errno = 0;
650	}
651
652	winbindd_free_response(&response);
653	done:
654#ifdef DEBUG_NSS
655	fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
656		name, nss_err_str(ret), ret);
657#endif
658
659#if HAVE_PTHREAD
660	pthread_mutex_unlock(&winbind_nss_mutex);
661#endif
662
663	return ret;
664}
665
666/*
667 * NSS group functions
668 */
669
670static struct winbindd_response getgrent_response;
671
672static int ndx_gr_cache;                 /* Current index into grp cache */
673static int num_gr_cache;                 /* Current size of grp cache */
674
675/* Rewind "file pointer" to start of ntdom group database */
676
677NSS_STATUS
678_nss_winbind_setgrent(void)
679{
680	NSS_STATUS ret;
681#ifdef DEBUG_NSS
682	fprintf(stderr, "[%5d]: setgrent\n", getpid());
683#endif
684
685#if HAVE_PTHREAD
686	pthread_mutex_lock(&winbind_nss_mutex);
687#endif
688
689	if (num_gr_cache > 0) {
690		ndx_gr_cache = num_gr_cache = 0;
691		winbindd_free_response(&getgrent_response);
692	}
693
694	ret = winbindd_request_response(WINBINDD_SETGRENT, NULL, NULL);
695#ifdef DEBUG_NSS
696	fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
697		nss_err_str(ret), ret);
698#endif
699
700#if HAVE_PTHREAD
701	pthread_mutex_unlock(&winbind_nss_mutex);
702#endif
703
704	return ret;
705}
706
707/* Close "file pointer" for ntdom group database */
708
709NSS_STATUS
710_nss_winbind_endgrent(void)
711{
712	NSS_STATUS ret;
713#ifdef DEBUG_NSS
714	fprintf(stderr, "[%5d]: endgrent\n", getpid());
715#endif
716
717#if HAVE_PTHREAD
718	pthread_mutex_lock(&winbind_nss_mutex);
719#endif
720
721	if (num_gr_cache > 0) {
722		ndx_gr_cache = num_gr_cache = 0;
723		winbindd_free_response(&getgrent_response);
724	}
725
726	ret = winbindd_request_response(WINBINDD_ENDGRENT, NULL, NULL);
727#ifdef DEBUG_NSS
728	fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
729		nss_err_str(ret), ret);
730#endif
731
732#if HAVE_PTHREAD
733	pthread_mutex_unlock(&winbind_nss_mutex);
734#endif
735
736	return ret;
737}
738
739/* Get next entry from ntdom group database */
740
741static NSS_STATUS
742winbind_getgrent(enum winbindd_cmd cmd,
743		 struct group *result,
744		 char *buffer, size_t buflen, int *errnop)
745{
746	NSS_STATUS ret;
747	static struct winbindd_request request;
748	static int called_again;
749
750
751#ifdef DEBUG_NSS
752	fprintf(stderr, "[%5d]: getgrent\n", getpid());
753#endif
754
755#if HAVE_PTHREAD
756	pthread_mutex_lock(&winbind_nss_mutex);
757#endif
758
759	/* Return an entry from the cache if we have one, or if we are
760	   called again because we exceeded our static buffer.  */
761
762	if ((ndx_gr_cache < num_gr_cache) || called_again) {
763		goto return_result;
764	}
765
766	/* Else call winbindd to get a bunch of entries */
767
768	if (num_gr_cache > 0) {
769		winbindd_free_response(&getgrent_response);
770	}
771
772	ZERO_STRUCT(request);
773	ZERO_STRUCT(getgrent_response);
774
775	request.data.num_entries = MAX_GETGRENT_USERS;
776
777	ret = winbindd_request_response(cmd, &request,
778			       &getgrent_response);
779
780	if (ret == NSS_STATUS_SUCCESS) {
781		struct winbindd_gr *gr_cache;
782		int mem_ofs;
783
784		/* Fill cache */
785
786		ndx_gr_cache = 0;
787		num_gr_cache = getgrent_response.data.num_entries;
788
789		/* Return a result */
790
791	return_result:
792
793		gr_cache = (struct winbindd_gr *)
794			getgrent_response.extra_data.data;
795
796		/* Check data is valid */
797
798		if (gr_cache == NULL) {
799			ret = NSS_STATUS_NOTFOUND;
800			goto done;
801		}
802
803		/* Fill group membership.  The offset into the extra data
804		   for the group membership is the reported offset plus the
805		   size of all the winbindd_gr records returned. */
806
807		mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
808			num_gr_cache * sizeof(struct winbindd_gr);
809
810		ret = fill_grent(result, &gr_cache[ndx_gr_cache],
811				 ((char *)getgrent_response.extra_data.data)+mem_ofs,
812				 &buffer, &buflen);
813
814		/* Out of memory - try again */
815
816		if (ret == NSS_STATUS_TRYAGAIN) {
817			called_again = true;
818			*errnop = errno = ERANGE;
819			goto done;
820		}
821
822		*errnop = 0;
823		called_again = false;
824		ndx_gr_cache++;
825
826		/* If we've finished with this lot of results free cache */
827
828		if (ndx_gr_cache == num_gr_cache) {
829			ndx_gr_cache = num_gr_cache = 0;
830			winbindd_free_response(&getgrent_response);
831		}
832	}
833	done:
834#ifdef DEBUG_NSS
835	fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
836		nss_err_str(ret), ret);
837#endif
838
839#if HAVE_PTHREAD
840	pthread_mutex_unlock(&winbind_nss_mutex);
841#endif
842
843	return ret;
844}
845
846
847NSS_STATUS
848_nss_winbind_getgrent_r(struct group *result,
849			char *buffer, size_t buflen, int *errnop)
850{
851	return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
852}
853
854NSS_STATUS
855_nss_winbind_getgrlst_r(struct group *result,
856			char *buffer, size_t buflen, int *errnop)
857{
858	return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
859}
860
861/* Return group struct from group name */
862
863NSS_STATUS
864_nss_winbind_getgrnam_r(const char *name,
865			struct group *result, char *buffer,
866			size_t buflen, int *errnop)
867{
868	NSS_STATUS ret;
869	static struct winbindd_response response;
870	struct winbindd_request request;
871	static int keep_response;
872
873#ifdef DEBUG_NSS
874	fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
875#endif
876
877#if HAVE_PTHREAD
878	pthread_mutex_lock(&winbind_nss_mutex);
879#endif
880
881	/* If our static buffer needs to be expanded we are called again */
882	/* Or if the stored response group name differs from the request. */
883
884	if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
885
886		/* Call for the first time */
887
888		ZERO_STRUCT(request);
889		ZERO_STRUCT(response);
890
891		strncpy(request.data.groupname, name,
892			sizeof(request.data.groupname));
893		request.data.groupname
894			[sizeof(request.data.groupname) - 1] = '\0';
895
896		ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
897
898		if (ret == NSS_STATUS_SUCCESS) {
899			ret = fill_grent(result, &response.data.gr,
900					 (char *)response.extra_data.data,
901					 &buffer, &buflen);
902
903			if (ret == NSS_STATUS_TRYAGAIN) {
904				keep_response = true;
905				*errnop = errno = ERANGE;
906				goto done;
907			}
908		}
909
910	} else {
911
912		/* We've been called again */
913
914		ret = fill_grent(result, &response.data.gr,
915				 (char *)response.extra_data.data, &buffer,
916				 &buflen);
917
918		if (ret == NSS_STATUS_TRYAGAIN) {
919			keep_response = true;
920			*errnop = errno = ERANGE;
921			goto done;
922		}
923
924		keep_response = false;
925		*errnop = 0;
926	}
927
928	winbindd_free_response(&response);
929	done:
930#ifdef DEBUG_NSS
931	fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
932		name, nss_err_str(ret), ret);
933#endif
934
935#if HAVE_PTHREAD
936	pthread_mutex_unlock(&winbind_nss_mutex);
937#endif
938
939	return ret;
940}
941
942/* Return group struct from gid */
943
944NSS_STATUS
945_nss_winbind_getgrgid_r(gid_t gid,
946			struct group *result, char *buffer,
947			size_t buflen, int *errnop)
948{
949	NSS_STATUS ret;
950	static struct winbindd_response response;
951	struct winbindd_request request;
952	static int keep_response;
953
954#ifdef DEBUG_NSS
955	fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
956#endif
957
958#if HAVE_PTHREAD
959	pthread_mutex_lock(&winbind_nss_mutex);
960#endif
961
962	/* If our static buffer needs to be expanded we are called again */
963	/* Or if the stored response group name differs from the request. */
964
965	if (!keep_response || gid != response.data.gr.gr_gid) {
966
967		/* Call for the first time */
968
969		ZERO_STRUCT(request);
970		ZERO_STRUCT(response);
971
972		request.data.gid = gid;
973
974		ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
975
976		if (ret == NSS_STATUS_SUCCESS) {
977
978			ret = fill_grent(result, &response.data.gr,
979					 (char *)response.extra_data.data,
980					 &buffer, &buflen);
981
982			if (ret == NSS_STATUS_TRYAGAIN) {
983				keep_response = true;
984				*errnop = errno = ERANGE;
985				goto done;
986			}
987		}
988
989	} else {
990
991		/* We've been called again */
992
993		ret = fill_grent(result, &response.data.gr,
994				 (char *)response.extra_data.data, &buffer,
995				 &buflen);
996
997		if (ret == NSS_STATUS_TRYAGAIN) {
998			keep_response = true;
999			*errnop = errno = ERANGE;
1000			goto done;
1001		}
1002
1003		keep_response = false;
1004		*errnop = 0;
1005	}
1006
1007	winbindd_free_response(&response);
1008	done:
1009#ifdef DEBUG_NSS
1010	fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
1011		(unsigned int)gid, nss_err_str(ret), ret);
1012#endif
1013
1014#if HAVE_PTHREAD
1015	pthread_mutex_unlock(&winbind_nss_mutex);
1016#endif
1017	return ret;
1018}
1019
1020/* Initialise supplementary groups */
1021
1022NSS_STATUS
1023_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1024			    long int *size, gid_t **groups, long int limit,
1025			    int *errnop)
1026{
1027	NSS_STATUS ret;
1028	struct winbindd_request request;
1029	struct winbindd_response response;
1030	int i;
1031
1032#ifdef DEBUG_NSS
1033	fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1034		user, group);
1035#endif
1036
1037#if HAVE_PTHREAD
1038	pthread_mutex_lock(&winbind_nss_mutex);
1039#endif
1040
1041	ZERO_STRUCT(request);
1042	ZERO_STRUCT(response);
1043
1044	strncpy(request.data.username, user,
1045		sizeof(request.data.username) - 1);
1046
1047	ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
1048
1049	if (ret == NSS_STATUS_SUCCESS) {
1050		int num_gids = response.data.num_entries;
1051		gid_t *gid_list = (gid_t *)response.extra_data.data;
1052
1053#ifdef DEBUG_NSS
1054		fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1055				"and %d gids\n", getpid(),
1056				user, num_gids);
1057#endif
1058		if (gid_list == NULL) {
1059			ret = NSS_STATUS_NOTFOUND;
1060			goto done;
1061		}
1062
1063		/* Copy group list to client */
1064
1065		for (i = 0; i < num_gids; i++) {
1066
1067#ifdef DEBUG_NSS
1068			fprintf(stderr, "[%5d]: initgroups %s (%d): "
1069					"processing gid %d \n", getpid(),
1070					user, group, gid_list[i]);
1071#endif
1072
1073			/* Skip primary group */
1074
1075			if (gid_list[i] == group) {
1076				continue;
1077			}
1078
1079			/* Filled buffer ? If so, resize. */
1080
1081			if (*start == *size) {
1082				long int newsize;
1083				gid_t *newgroups;
1084
1085				newsize = 2 * (*size);
1086				if (limit > 0) {
1087					if (*size == limit) {
1088						goto done;
1089					}
1090					if (newsize > limit) {
1091						newsize = limit;
1092					}
1093				}
1094
1095				newgroups = (gid_t *)
1096					realloc((*groups),
1097						newsize * sizeof(**groups));
1098				if (!newgroups) {
1099					*errnop = ENOMEM;
1100					ret = NSS_STATUS_NOTFOUND;
1101					goto done;
1102				}
1103				*groups = newgroups;
1104				*size = newsize;
1105			}
1106
1107			/* Add to buffer */
1108
1109			(*groups)[*start] = gid_list[i];
1110			*start += 1;
1111		}
1112	}
1113
1114	/* Back to your regularly scheduled programming */
1115
1116 done:
1117#ifdef DEBUG_NSS
1118	fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1119		user, nss_err_str(ret), ret);
1120#endif
1121
1122#if HAVE_PTHREAD
1123	pthread_mutex_unlock(&winbind_nss_mutex);
1124#endif
1125
1126	return ret;
1127}
1128
1129
1130/* return a list of group SIDs for a user SID */
1131NSS_STATUS
1132_nss_winbind_getusersids(const char *user_sid, char **group_sids,
1133			 int *num_groups,
1134			 char *buffer, size_t buf_size, int *errnop)
1135{
1136	NSS_STATUS ret;
1137	struct winbindd_request request;
1138	struct winbindd_response response;
1139
1140#ifdef DEBUG_NSS
1141	fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
1142#endif
1143
1144#if HAVE_PTHREAD
1145	pthread_mutex_lock(&winbind_nss_mutex);
1146#endif
1147
1148	ZERO_STRUCT(request);
1149	ZERO_STRUCT(response);
1150
1151	strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
1152	request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1153
1154	ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response);
1155
1156	if (ret != NSS_STATUS_SUCCESS) {
1157		goto done;
1158	}
1159
1160	if (buf_size < response.length - sizeof(response)) {
1161		ret = NSS_STATUS_TRYAGAIN;
1162		errno = *errnop = ERANGE;
1163		goto done;
1164	}
1165
1166	*num_groups = response.data.num_entries;
1167	*group_sids = buffer;
1168	memcpy(buffer, response.extra_data.data, response.length - sizeof(response));
1169	errno = *errnop = 0;
1170
1171 done:
1172	winbindd_free_response(&response);
1173
1174#if HAVE_PTHREAD
1175	pthread_mutex_unlock(&winbind_nss_mutex);
1176#endif
1177
1178	return ret;
1179}
1180
1181
1182/* map a user or group name to a SID string */
1183NSS_STATUS
1184_nss_winbind_nametosid(const char *name, char **sid, char *buffer,
1185		       size_t buflen, int *errnop)
1186{
1187	NSS_STATUS ret;
1188	struct winbindd_response response;
1189	struct winbindd_request request;
1190
1191#ifdef DEBUG_NSS
1192	fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
1193#endif
1194
1195#if HAVE_PTHREAD
1196	pthread_mutex_lock(&winbind_nss_mutex);
1197#endif
1198
1199	ZERO_STRUCT(response);
1200	ZERO_STRUCT(request);
1201
1202	strncpy(request.data.name.name, name,
1203		sizeof(request.data.name.name) - 1);
1204	request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
1205
1206	ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response);
1207	if (ret != NSS_STATUS_SUCCESS) {
1208		*errnop = errno = EINVAL;
1209		goto failed;
1210	}
1211
1212	if (buflen < strlen(response.data.sid.sid)+1) {
1213		ret = NSS_STATUS_TRYAGAIN;
1214		*errnop = errno = ERANGE;
1215		goto failed;
1216	}
1217
1218	*errnop = errno = 0;
1219	*sid = buffer;
1220	strcpy(*sid, response.data.sid.sid);
1221
1222failed:
1223	winbindd_free_response(&response);
1224
1225#if HAVE_PTHREAD
1226	pthread_mutex_unlock(&winbind_nss_mutex);
1227#endif
1228
1229	return ret;
1230}
1231
1232/* map a sid string to a user or group name */
1233NSS_STATUS
1234_nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
1235		       size_t buflen, int *errnop)
1236{
1237	NSS_STATUS ret;
1238	struct winbindd_response response;
1239	struct winbindd_request request;
1240	static char sep_char;
1241	unsigned needed;
1242
1243#ifdef DEBUG_NSS
1244	fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
1245#endif
1246
1247#if HAVE_PTHREAD
1248	pthread_mutex_lock(&winbind_nss_mutex);
1249#endif
1250
1251	ZERO_STRUCT(response);
1252	ZERO_STRUCT(request);
1253
1254	/* we need to fetch the separator first time through */
1255	if (!sep_char) {
1256		ret = winbindd_request_response(WINBINDD_INFO, &request, &response);
1257		if (ret != NSS_STATUS_SUCCESS) {
1258			*errnop = errno = EINVAL;
1259			goto failed;
1260		}
1261
1262		sep_char = response.data.info.winbind_separator;
1263		winbindd_free_response(&response);
1264	}
1265
1266
1267	strncpy(request.data.sid, sid,
1268		sizeof(request.data.sid) - 1);
1269	request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1270
1271	ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response);
1272	if (ret != NSS_STATUS_SUCCESS) {
1273		*errnop = errno = EINVAL;
1274		goto failed;
1275	}
1276
1277	needed =
1278		strlen(response.data.name.dom_name) +
1279		strlen(response.data.name.name) + 2;
1280
1281	if (buflen < needed) {
1282		ret = NSS_STATUS_TRYAGAIN;
1283		*errnop = errno = ERANGE;
1284		goto failed;
1285	}
1286
1287	snprintf(buffer, needed, "%s%c%s",
1288		 response.data.name.dom_name,
1289		 sep_char,
1290		 response.data.name.name);
1291
1292	*name = buffer;
1293	*errnop = errno = 0;
1294
1295failed:
1296	winbindd_free_response(&response);
1297
1298#if HAVE_PTHREAD
1299	pthread_mutex_unlock(&winbind_nss_mutex);
1300#endif
1301
1302	return ret;
1303}
1304
1305/* map a sid to a uid */
1306NSS_STATUS
1307_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
1308{
1309	NSS_STATUS ret;
1310	struct winbindd_response response;
1311	struct winbindd_request request;
1312
1313#ifdef DEBUG_NSS
1314	fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
1315#endif
1316
1317#if HAVE_PTHREAD
1318	pthread_mutex_lock(&winbind_nss_mutex);
1319#endif
1320
1321	ZERO_STRUCT(request);
1322	ZERO_STRUCT(response);
1323
1324	strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1325	request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1326
1327	ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
1328	if (ret != NSS_STATUS_SUCCESS) {
1329		*errnop = errno = EINVAL;
1330		goto failed;
1331	}
1332
1333	*uid = response.data.uid;
1334
1335failed:
1336
1337#if HAVE_PTHREAD
1338	pthread_mutex_unlock(&winbind_nss_mutex);
1339#endif
1340
1341	return ret;
1342}
1343
1344/* map a sid to a gid */
1345NSS_STATUS
1346_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
1347{
1348	NSS_STATUS ret;
1349	struct winbindd_response response;
1350	struct winbindd_request request;
1351
1352#ifdef DEBUG_NSS
1353	fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
1354#endif
1355
1356#if HAVE_PTHREAD
1357	pthread_mutex_lock(&winbind_nss_mutex);
1358#endif
1359
1360	ZERO_STRUCT(request);
1361	ZERO_STRUCT(response);
1362
1363	strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
1364	request.data.sid[sizeof(request.data.sid) - 1] = '\0';
1365
1366	ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
1367	if (ret != NSS_STATUS_SUCCESS) {
1368		*errnop = errno = EINVAL;
1369		goto failed;
1370	}
1371
1372	*gid = response.data.gid;
1373
1374failed:
1375
1376#if HAVE_PTHREAD
1377	pthread_mutex_unlock(&winbind_nss_mutex);
1378#endif
1379
1380	return ret;
1381}
1382
1383/* map a uid to a SID string */
1384NSS_STATUS
1385_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
1386		      size_t buflen, int *errnop)
1387{
1388	NSS_STATUS ret;
1389	struct winbindd_response response;
1390	struct winbindd_request request;
1391
1392#ifdef DEBUG_NSS
1393	fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
1394#endif
1395
1396#if HAVE_PTHREAD
1397	pthread_mutex_lock(&winbind_nss_mutex);
1398#endif
1399
1400	ZERO_STRUCT(response);
1401	ZERO_STRUCT(request);
1402
1403	request.data.uid = uid;
1404
1405	ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
1406	if (ret != NSS_STATUS_SUCCESS) {
1407		*errnop = errno = EINVAL;
1408		goto failed;
1409	}
1410
1411	if (buflen < strlen(response.data.sid.sid)+1) {
1412		ret = NSS_STATUS_TRYAGAIN;
1413		*errnop = errno = ERANGE;
1414		goto failed;
1415	}
1416
1417	*errnop = errno = 0;
1418	*sid = buffer;
1419	strcpy(*sid, response.data.sid.sid);
1420
1421failed:
1422	winbindd_free_response(&response);
1423
1424#if HAVE_PTHREAD
1425	pthread_mutex_unlock(&winbind_nss_mutex);
1426#endif
1427
1428	return ret;
1429}
1430
1431/* map a gid to a SID string */
1432NSS_STATUS
1433_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
1434		      size_t buflen, int *errnop)
1435{
1436	NSS_STATUS ret;
1437	struct winbindd_response response;
1438	struct winbindd_request request;
1439
1440#ifdef DEBUG_NSS
1441	fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
1442#endif
1443
1444#if HAVE_PTHREAD
1445	pthread_mutex_lock(&winbind_nss_mutex);
1446#endif
1447
1448	ZERO_STRUCT(response);
1449	ZERO_STRUCT(request);
1450
1451	request.data.gid = gid;
1452
1453	ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
1454	if (ret != NSS_STATUS_SUCCESS) {
1455		*errnop = errno = EINVAL;
1456		goto failed;
1457	}
1458
1459	if (buflen < strlen(response.data.sid.sid)+1) {
1460		ret = NSS_STATUS_TRYAGAIN;
1461		*errnop = errno = ERANGE;
1462		goto failed;
1463	}
1464
1465	*errnop = errno = 0;
1466	*sid = buffer;
1467	strcpy(*sid, response.data.sid.sid);
1468
1469failed:
1470	winbindd_free_response(&response);
1471
1472#if HAVE_PTHREAD
1473	pthread_mutex_unlock(&winbind_nss_mutex);
1474#endif
1475
1476	return ret;
1477}
1478