2 Unix SMB/CIFS implementation.
4 Windows NT Domain nsswitch module
6 Copyright (C) Tim Potter 2000
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.
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.
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/>.
22 #include "winbind_client.h"
28 /* Maximum number of users to pass back over the unix domain socket
29 per call. This is not a static limit on the total number of users
30 or groups returned in total. */
32 #define MAX_GETPWENT_USERS 250
33 #define MAX_GETGRENT_USERS 250
35 /*************************************************************************
36 ************************************************************************/
39 static const char *nss_err_str(NSS_STATUS ret
)
42 case NSS_STATUS_TRYAGAIN
:
43 return "NSS_STATUS_TRYAGAIN";
44 case NSS_STATUS_SUCCESS
:
45 return "NSS_STATUS_SUCCESS";
46 case NSS_STATUS_NOTFOUND
:
47 return "NSS_STATUS_NOTFOUND";
48 case NSS_STATUS_UNAVAIL
:
49 return "NSS_STATUS_UNAVAIL";
50 #ifdef NSS_STATUS_RETURN
51 case NSS_STATUS_RETURN
:
52 return "NSS_STATUS_RETURN";
55 return "UNKNOWN RETURN CODE!!!!!!!";
60 /* Prototypes from wb_common.c */
62 /* Allocate some space from the nss static buffer. The buffer and buflen
63 are the pointers passed in by the C library to the _nss_ntdom_*
66 static char *get_static(char **buffer
, size_t *buflen
, size_t len
)
70 /* Error check. We return false if things aren't set up right, or
71 there isn't enough buffer space left. */
73 if ((buffer
== NULL
) || (buflen
== NULL
) || (*buflen
< len
)) {
77 /* Return an index into the static buffer */
86 /* I've copied the strtok() replacement function next_token_Xalloc() from
87 lib/util_str.c as I really don't want to have to link in any other
88 objects if I can possibly avoid it. */
90 static bool next_token_alloc(const char **ptr
,
107 /* default to simple separators */
112 /* find the first non sep char */
113 while (*s
&& strchr(sep
,*s
)) {
122 /* When restarting we need to go from here. */
125 /* Work out the length needed. */
126 for (quoted
= false; *s
&&
127 (quoted
|| !strchr(sep
,*s
)); s
++) {
135 /* We started with len = 1 so we have space for the nul. */
136 *pp_buff
= (char *)malloc(len
);
141 /* copy over the token */
144 for (quoted
= false; *s
&&
145 (quoted
|| !strchr(sep
,*s
)); s
++) {
153 *ptr
= (*s
) ? s
+1 : s
;
159 /* Fill a pwent structure from a winbindd_response structure. We use
160 the static data passed to us by libc to put strings and stuff in.
161 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
163 static NSS_STATUS
fill_pwent(struct passwd
*result
,
164 struct winbindd_pw
*pw
,
165 char **buffer
, size_t *buflen
)
170 len
= strlen(pw
->pw_name
) + 1;
172 if ((result
->pw_name
=
173 get_static(buffer
, buflen
, len
)) == NULL
) {
177 return NSS_STATUS_TRYAGAIN
;
180 memcpy(result
->pw_name
, pw
->pw_name
, len
);
183 len
= strlen(pw
->pw_passwd
) + 1;
185 if ((result
->pw_passwd
=
186 get_static(buffer
, buflen
, len
)) == NULL
) {
190 return NSS_STATUS_TRYAGAIN
;
193 memcpy(result
->pw_passwd
, pw
->pw_passwd
, len
);
197 result
->pw_uid
= pw
->pw_uid
;
198 result
->pw_gid
= pw
->pw_gid
;
201 len
= strlen(pw
->pw_gecos
) + 1;
203 if ((result
->pw_gecos
=
204 get_static(buffer
, buflen
, len
)) == NULL
) {
208 return NSS_STATUS_TRYAGAIN
;
211 memcpy(result
->pw_gecos
, pw
->pw_gecos
, len
);
214 len
= strlen(pw
->pw_dir
) + 1;
216 if ((result
->pw_dir
=
217 get_static(buffer
, buflen
, len
)) == NULL
) {
221 return NSS_STATUS_TRYAGAIN
;
224 memcpy(result
->pw_dir
, pw
->pw_dir
, len
);
227 len
= strlen(pw
->pw_shell
) + 1;
229 if ((result
->pw_shell
=
230 get_static(buffer
, buflen
, len
)) == NULL
) {
234 return NSS_STATUS_TRYAGAIN
;
237 memcpy(result
->pw_shell
, pw
->pw_shell
, len
);
239 /* The struct passwd for Solaris has some extra fields which must
240 be initialised or nscd crashes. */
242 #ifdef HAVE_PASSWD_PW_COMMENT
243 result
->pw_comment
= "";
246 #ifdef HAVE_PASSWD_PW_AGE
250 return NSS_STATUS_SUCCESS
;
253 /* Fill a grent structure from a winbindd_response structure. We use
254 the static data passed to us by libc to put strings and stuff in.
255 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
257 static NSS_STATUS
fill_grent(struct group
*result
, struct winbindd_gr
*gr
,
258 const char *gr_mem
, char **buffer
, size_t *buflen
)
266 len
= strlen(gr
->gr_name
) + 1;
268 if ((result
->gr_name
=
269 get_static(buffer
, buflen
, len
)) == NULL
) {
273 return NSS_STATUS_TRYAGAIN
;
276 memcpy(result
->gr_name
, gr
->gr_name
, len
);
279 len
= strlen(gr
->gr_passwd
) + 1;
281 if ((result
->gr_passwd
=
282 get_static(buffer
, buflen
, len
)) == NULL
) {
285 return NSS_STATUS_TRYAGAIN
;
288 memcpy(result
->gr_passwd
, gr
->gr_passwd
, len
);
292 result
->gr_gid
= gr
->gr_gid
;
294 /* Group membership */
300 /* this next value is a pointer to a pointer so let's align it */
302 /* Calculate number of extra bytes needed to align on pointer size boundary */
303 if ((i
= (unsigned long)(*buffer
) % sizeof(char*)) != 0)
304 i
= sizeof(char*) - i
;
306 if ((tst
= get_static(buffer
, buflen
, ((gr
->num_gr_mem
+ 1) *
307 sizeof(char *)+i
))) == NULL
) {
311 return NSS_STATUS_TRYAGAIN
;
313 result
->gr_mem
= (char **)(tst
+ i
);
315 if (gr
->num_gr_mem
== 0) {
319 *(result
->gr_mem
) = NULL
;
320 return NSS_STATUS_SUCCESS
;
323 /* Start looking at extra data */
327 while(next_token_alloc((const char **)&gr_mem
, &name
, ",")) {
328 /* Allocate space for member */
329 len
= strlen(name
) + 1;
331 if (((result
->gr_mem
)[i
] =
332 get_static(buffer
, buflen
, len
)) == NULL
) {
335 return NSS_STATUS_TRYAGAIN
;
337 memcpy((result
->gr_mem
)[i
], name
, len
);
344 (result
->gr_mem
)[i
] = NULL
;
346 return NSS_STATUS_SUCCESS
;
353 static __thread
struct winbindd_response getpwent_response
;
355 static __thread
int ndx_pw_cache
; /* Current index into pwd cache */
356 static __thread
int num_pw_cache
; /* Current size of pwd cache */
358 /* Rewind "file pointer" to start of ntdom password database */
362 _nss_winbind_setpwent(void)
366 fprintf(stderr
, "[%5d]: setpwent\n", getpid());
369 if (num_pw_cache
> 0) {
370 ndx_pw_cache
= num_pw_cache
= 0;
371 winbindd_free_response(&getpwent_response
);
374 winbind_set_client_name("nss_winbind");
375 ret
= winbindd_request_response(NULL
, WINBINDD_SETPWENT
, NULL
, NULL
);
377 fprintf(stderr
, "[%5d]: setpwent returns %s (%d)\n", getpid(),
378 nss_err_str(ret
), ret
);
384 /* Close ntdom password database "file pointer" */
388 _nss_winbind_endpwent(void)
392 fprintf(stderr
, "[%5d]: endpwent\n", getpid());
395 if (num_pw_cache
> 0) {
396 ndx_pw_cache
= num_pw_cache
= 0;
397 winbindd_free_response(&getpwent_response
);
400 winbind_set_client_name("nss_winbind");
401 ret
= winbindd_request_response(NULL
, WINBINDD_ENDPWENT
, NULL
, NULL
);
403 fprintf(stderr
, "[%5d]: endpwent returns %s (%d)\n", getpid(),
404 nss_err_str(ret
), ret
);
410 /* Fetch the next password entry from ntdom password database */
414 _nss_winbind_getpwent_r(struct passwd
*result
, char *buffer
,
415 size_t buflen
, int *errnop
)
418 struct winbindd_request request
;
419 static __thread
int called_again
;
422 fprintf(stderr
, "[%5d]: getpwent\n", getpid());
425 /* Return an entry from the cache if we have one, or if we are
426 called again because we exceeded our static buffer. */
428 if ((ndx_pw_cache
< num_pw_cache
) || called_again
) {
432 /* Else call winbindd to get a bunch of entries */
434 if (num_pw_cache
> 0) {
435 winbindd_free_response(&getpwent_response
);
438 ZERO_STRUCT(request
);
439 ZERO_STRUCT(getpwent_response
);
441 request
.data
.num_entries
= MAX_GETPWENT_USERS
;
443 winbind_set_client_name("nss_winbind");
444 ret
= winbindd_request_response(NULL
, WINBINDD_GETPWENT
, &request
,
447 if (ret
== NSS_STATUS_SUCCESS
) {
448 struct winbindd_pw
*pw_cache
;
453 num_pw_cache
= getpwent_response
.data
.num_entries
;
455 /* Return a result */
459 pw_cache
= (struct winbindd_pw
*)
460 getpwent_response
.extra_data
.data
;
462 /* Check data is valid */
464 if (pw_cache
== NULL
) {
465 ret
= NSS_STATUS_NOTFOUND
;
469 ret
= fill_pwent(result
, &pw_cache
[ndx_pw_cache
],
472 /* Out of memory - try again */
474 if (ret
== NSS_STATUS_TRYAGAIN
) {
476 *errnop
= errno
= ERANGE
;
481 called_again
= false;
484 /* If we've finished with this lot of results free cache */
486 if (ndx_pw_cache
== num_pw_cache
) {
487 ndx_pw_cache
= num_pw_cache
= 0;
488 winbindd_free_response(&getpwent_response
);
493 fprintf(stderr
, "[%5d]: getpwent returns %s (%d)\n", getpid(),
494 nss_err_str(ret
), ret
);
500 /* Return passwd struct from uid */
504 _nss_winbind_getpwuid_r(uid_t uid
, struct passwd
*result
, char *buffer
,
505 size_t buflen
, int *errnop
)
508 static __thread
struct winbindd_response response
;
509 struct winbindd_request request
;
510 static __thread
int keep_response
;
513 fprintf(stderr
, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid
);
516 /* If our static buffer needs to be expanded we are called again */
517 if (!keep_response
|| uid
!= response
.data
.pw
.pw_uid
) {
519 /* Call for the first time */
521 response
= (struct winbindd_response
) {
524 request
= (struct winbindd_request
) {
525 .wb_flags
= WBFLAG_FROM_NSS
,
531 winbind_set_client_name("nss_winbind");
532 ret
= winbindd_request_response(NULL
, WINBINDD_GETPWUID
, &request
, &response
);
534 if (ret
== NSS_STATUS_SUCCESS
) {
535 ret
= fill_pwent(result
, &response
.data
.pw
,
538 if (ret
== NSS_STATUS_TRYAGAIN
) {
539 keep_response
= true;
540 *errnop
= errno
= ERANGE
;
547 /* We've been called again */
549 ret
= fill_pwent(result
, &response
.data
.pw
, &buffer
, &buflen
);
551 if (ret
== NSS_STATUS_TRYAGAIN
) {
552 *errnop
= errno
= ERANGE
;
556 keep_response
= false;
560 winbindd_free_response(&response
);
565 fprintf(stderr
, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
566 (unsigned int)uid
, nss_err_str(ret
), ret
);
572 /* Return passwd struct from username */
575 _nss_winbind_getpwnam_r(const char *name
, struct passwd
*result
, char *buffer
,
576 size_t buflen
, int *errnop
)
579 static __thread
struct winbindd_response response
;
580 struct winbindd_request request
;
581 static __thread
int keep_response
;
584 fprintf(stderr
, "[%5d]: getpwnam_r %s\n", getpid(), name
);
587 /* If our static buffer needs to be expanded we are called again */
589 if (!keep_response
|| strcmp(name
,response
.data
.pw
.pw_name
) != 0) {
591 /* Call for the first time */
593 response
= (struct winbindd_response
) {
596 request
= (struct winbindd_request
) {
597 .wb_flags
= WBFLAG_FROM_NSS
,
600 strncpy(request
.data
.username
, name
,
601 sizeof(request
.data
.username
) - 1);
602 request
.data
.username
603 [sizeof(request
.data
.username
) - 1] = '\0';
605 winbind_set_client_name("nss_winbind");
606 ret
= winbindd_request_response(NULL
, WINBINDD_GETPWNAM
, &request
, &response
);
608 if (ret
== NSS_STATUS_SUCCESS
) {
609 ret
= fill_pwent(result
, &response
.data
.pw
, &buffer
,
612 if (ret
== NSS_STATUS_TRYAGAIN
) {
613 keep_response
= true;
614 *errnop
= errno
= ERANGE
;
621 /* We've been called again */
623 ret
= fill_pwent(result
, &response
.data
.pw
, &buffer
, &buflen
);
625 if (ret
== NSS_STATUS_TRYAGAIN
) {
626 keep_response
= true;
627 *errnop
= errno
= ERANGE
;
631 keep_response
= false;
635 winbindd_free_response(&response
);
638 fprintf(stderr
, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
639 name
, nss_err_str(ret
), ret
);
646 * NSS group functions
649 static __thread
struct winbindd_response getgrent_response
;
651 static __thread
int ndx_gr_cache
; /* Current index into grp cache */
652 static __thread
int num_gr_cache
; /* Current size of grp cache */
654 /* Rewind "file pointer" to start of ntdom group database */
658 _nss_winbind_setgrent(void)
662 fprintf(stderr
, "[%5d]: setgrent\n", getpid());
665 if (num_gr_cache
> 0) {
666 ndx_gr_cache
= num_gr_cache
= 0;
667 winbindd_free_response(&getgrent_response
);
670 winbind_set_client_name("nss_winbind");
671 ret
= winbindd_request_response(NULL
, WINBINDD_SETGRENT
, NULL
, NULL
);
673 fprintf(stderr
, "[%5d]: setgrent returns %s (%d)\n", getpid(),
674 nss_err_str(ret
), ret
);
680 /* Close "file pointer" for ntdom group database */
684 _nss_winbind_endgrent(void)
688 fprintf(stderr
, "[%5d]: endgrent\n", getpid());
691 if (num_gr_cache
> 0) {
692 ndx_gr_cache
= num_gr_cache
= 0;
693 winbindd_free_response(&getgrent_response
);
696 winbind_set_client_name("nss_winbind");
697 ret
= winbindd_request_response(NULL
, WINBINDD_ENDGRENT
, NULL
, NULL
);
699 fprintf(stderr
, "[%5d]: endgrent returns %s (%d)\n", getpid(),
700 nss_err_str(ret
), ret
);
706 /* Get next entry from ntdom group database */
709 winbind_getgrent(enum winbindd_cmd cmd
,
710 struct group
*result
,
711 char *buffer
, size_t buflen
, int *errnop
)
714 static __thread
struct winbindd_request request
;
715 static __thread
int called_again
;
719 fprintf(stderr
, "[%5d]: getgrent\n", getpid());
722 /* Return an entry from the cache if we have one, or if we are
723 called again because we exceeded our static buffer. */
725 if ((ndx_gr_cache
< num_gr_cache
) || called_again
) {
729 /* Else call winbindd to get a bunch of entries */
731 if (num_gr_cache
> 0) {
732 winbindd_free_response(&getgrent_response
);
735 ZERO_STRUCT(request
);
736 ZERO_STRUCT(getgrent_response
);
738 request
.data
.num_entries
= MAX_GETGRENT_USERS
;
740 winbind_set_client_name("nss_winbind");
741 ret
= winbindd_request_response(NULL
, cmd
, &request
,
744 if (ret
== NSS_STATUS_SUCCESS
) {
745 struct winbindd_gr
*gr_cache
;
751 num_gr_cache
= getgrent_response
.data
.num_entries
;
753 /* Return a result */
757 gr_cache
= (struct winbindd_gr
*)
758 getgrent_response
.extra_data
.data
;
760 /* Check data is valid */
762 if (gr_cache
== NULL
) {
763 ret
= NSS_STATUS_NOTFOUND
;
767 /* Fill group membership. The offset into the extra data
768 for the group membership is the reported offset plus the
769 size of all the winbindd_gr records returned. */
771 mem_ofs
= gr_cache
[ndx_gr_cache
].gr_mem_ofs
+
772 num_gr_cache
* sizeof(struct winbindd_gr
);
774 ret
= fill_grent(result
, &gr_cache
[ndx_gr_cache
],
775 ((char *)getgrent_response
.extra_data
.data
)+mem_ofs
,
778 /* Out of memory - try again */
780 if (ret
== NSS_STATUS_TRYAGAIN
) {
782 *errnop
= errno
= ERANGE
;
787 called_again
= false;
790 /* If we've finished with this lot of results free cache */
792 if (ndx_gr_cache
== num_gr_cache
) {
793 ndx_gr_cache
= num_gr_cache
= 0;
794 winbindd_free_response(&getgrent_response
);
799 fprintf(stderr
, "[%5d]: getgrent returns %s (%d)\n", getpid(),
800 nss_err_str(ret
), ret
);
809 _nss_winbind_getgrent_r(struct group
*result
,
810 char *buffer
, size_t buflen
, int *errnop
)
812 return winbind_getgrent(WINBINDD_GETGRENT
, result
, buffer
, buflen
, errnop
);
817 _nss_winbind_getgrlst_r(struct group
*result
,
818 char *buffer
, size_t buflen
, int *errnop
)
820 return winbind_getgrent(WINBINDD_GETGRLST
, result
, buffer
, buflen
, errnop
);
823 /* Return group struct from group name */
827 _nss_winbind_getgrnam_r(const char *name
,
828 struct group
*result
, char *buffer
,
829 size_t buflen
, int *errnop
)
832 static __thread
struct winbindd_response response
;
833 struct winbindd_request request
;
834 static __thread
int keep_response
;
837 fprintf(stderr
, "[%5d]: getgrnam %s\n", getpid(), name
);
840 /* If our static buffer needs to be expanded we are called again */
841 /* Or if the stored response group name differs from the request. */
843 if (!keep_response
|| strcmp(name
,response
.data
.gr
.gr_name
) != 0) {
845 /* Call for the first time */
847 response
= (struct winbindd_response
) {
850 request
= (struct winbindd_request
) {
851 .wb_flags
= WBFLAG_FROM_NSS
,
854 strncpy(request
.data
.groupname
, name
,
855 sizeof(request
.data
.groupname
));
856 request
.data
.groupname
857 [sizeof(request
.data
.groupname
) - 1] = '\0';
859 winbind_set_client_name("nss_winbind");
860 ret
= winbindd_request_response(NULL
, WINBINDD_GETGRNAM
,
861 &request
, &response
);
863 if (ret
== NSS_STATUS_SUCCESS
) {
864 ret
= fill_grent(result
, &response
.data
.gr
,
865 (char *)response
.extra_data
.data
,
868 if (ret
== NSS_STATUS_TRYAGAIN
) {
869 keep_response
= true;
870 *errnop
= errno
= ERANGE
;
877 /* We've been called again */
879 ret
= fill_grent(result
, &response
.data
.gr
,
880 (char *)response
.extra_data
.data
, &buffer
,
883 if (ret
== NSS_STATUS_TRYAGAIN
) {
884 keep_response
= true;
885 *errnop
= errno
= ERANGE
;
889 keep_response
= false;
893 winbindd_free_response(&response
);
896 fprintf(stderr
, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
897 name
, nss_err_str(ret
), ret
);
903 /* Return group struct from gid */
907 _nss_winbind_getgrgid_r(gid_t gid
,
908 struct group
*result
, char *buffer
,
909 size_t buflen
, int *errnop
)
912 static __thread
struct winbindd_response response
;
913 struct winbindd_request request
;
914 static __thread
int keep_response
;
917 fprintf(stderr
, "[%5d]: getgrgid %d\n", getpid(), gid
);
920 /* If our static buffer needs to be expanded we are called again */
921 /* Or if the stored response group name differs from the request. */
923 if (!keep_response
|| gid
!= response
.data
.gr
.gr_gid
) {
925 /* Call for the first time */
927 response
= (struct winbindd_response
) {
930 request
= (struct winbindd_request
) {
931 .wb_flags
= WBFLAG_FROM_NSS
,
935 request
.data
.gid
= gid
;
937 winbind_set_client_name("nss_winbind");
938 ret
= winbindd_request_response(NULL
, WINBINDD_GETGRGID
,
939 &request
, &response
);
941 if (ret
== NSS_STATUS_SUCCESS
) {
943 ret
= fill_grent(result
, &response
.data
.gr
,
944 (char *)response
.extra_data
.data
,
947 if (ret
== NSS_STATUS_TRYAGAIN
) {
948 keep_response
= true;
949 *errnop
= errno
= ERANGE
;
956 /* We've been called again */
958 ret
= fill_grent(result
, &response
.data
.gr
,
959 (char *)response
.extra_data
.data
, &buffer
,
962 if (ret
== NSS_STATUS_TRYAGAIN
) {
963 keep_response
= true;
964 *errnop
= errno
= ERANGE
;
968 keep_response
= false;
972 winbindd_free_response(&response
);
975 fprintf(stderr
, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
976 (unsigned int)gid
, nss_err_str(ret
), ret
);
982 /* Initialise supplementary groups */
986 _nss_winbind_initgroups_dyn(const char *user
, gid_t group
, long int *start
,
987 long int *size
, gid_t
**groups
, long int limit
,
991 struct winbindd_request request
;
992 struct winbindd_response response
;
996 fprintf(stderr
, "[%5d]: initgroups %s (%d)\n", getpid(),
1000 ZERO_STRUCT(request
);
1001 ZERO_STRUCT(response
);
1003 strncpy(request
.data
.username
, user
,
1004 sizeof(request
.data
.username
) - 1);
1006 winbind_set_client_name("nss_winbind");
1007 ret
= winbindd_request_response(NULL
, WINBINDD_GETGROUPS
,
1008 &request
, &response
);
1010 if (ret
== NSS_STATUS_SUCCESS
) {
1011 int num_gids
= response
.data
.num_entries
;
1012 gid_t
*gid_list
= (gid_t
*)response
.extra_data
.data
;
1015 fprintf(stderr
, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1016 "and %d gids\n", getpid(),
1019 if (gid_list
== NULL
) {
1020 ret
= NSS_STATUS_NOTFOUND
;
1024 /* Copy group list to client */
1026 for (i
= 0; i
< num_gids
; i
++) {
1029 fprintf(stderr
, "[%5d]: initgroups %s (%d): "
1030 "processing gid %d \n", getpid(),
1031 user
, group
, gid_list
[i
]);
1034 /* Skip primary group */
1036 if (gid_list
[i
] == group
) {
1040 /* Skip groups without a mapping */
1041 if (gid_list
[i
] == (uid_t
)-1) {
1045 /* Filled buffer ? If so, resize. */
1047 if (*start
== *size
) {
1051 newsize
= 2 * (*size
);
1053 if (*size
== limit
) {
1056 if (newsize
> limit
) {
1061 newgroups
= (gid_t
*)
1063 newsize
* sizeof(**groups
));
1066 ret
= NSS_STATUS_NOTFOUND
;
1069 *groups
= newgroups
;
1075 (*groups
)[*start
] = gid_list
[i
];
1080 /* Back to your regularly scheduled programming */
1083 winbindd_free_response(&response
);
1085 fprintf(stderr
, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1086 user
, nss_err_str(ret
), ret
);