s4:dsdb/password_hash: allow pwdLastSet only changes
[Samba.git] / nsswitch / winbind_nss_linux.c
blobb5c50efcb39a48da940fcfbf9c50c9825aa80fd8
1 /*
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"
24 #if HAVE_PTHREAD_H
25 #include <pthread.h>
26 #endif
28 #if HAVE_PTHREAD
29 static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
30 #endif
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. */
36 #define MAX_GETPWENT_USERS 250
37 #define MAX_GETGRENT_USERS 250
39 NSS_STATUS _nss_winbind_setpwent(void);
40 NSS_STATUS _nss_winbind_endpwent(void);
41 NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
42 size_t buflen, int *errnop);
43 NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
44 char *buffer, size_t buflen, int *errnop);
45 NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
46 char *buffer, size_t buflen, int *errnop);
47 NSS_STATUS _nss_winbind_setgrent(void);
48 NSS_STATUS _nss_winbind_endgrent(void);
49 NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
50 size_t buflen, int *errnop);
51 NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
52 size_t buflen, int *errnop);
53 NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
54 char *buffer, size_t buflen, int *errnop);
55 NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
56 size_t buflen, int *errnop);
57 NSS_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);
61 /*************************************************************************
62 ************************************************************************/
64 #ifdef DEBUG_NSS
65 static const char *nss_err_str(NSS_STATUS ret)
67 switch (ret) {
68 case NSS_STATUS_TRYAGAIN:
69 return "NSS_STATUS_TRYAGAIN";
70 case NSS_STATUS_SUCCESS:
71 return "NSS_STATUS_SUCCESS";
72 case NSS_STATUS_NOTFOUND:
73 return "NSS_STATUS_NOTFOUND";
74 case NSS_STATUS_UNAVAIL:
75 return "NSS_STATUS_UNAVAIL";
76 #ifdef NSS_STATUS_RETURN
77 case NSS_STATUS_RETURN:
78 return "NSS_STATUS_RETURN";
79 #endif
80 default:
81 return "UNKNOWN RETURN CODE!!!!!!!";
84 #endif
86 /* Prototypes from wb_common.c */
88 /* Allocate some space from the nss static buffer. The buffer and buflen
89 are the pointers passed in by the C library to the _nss_ntdom_*
90 functions. */
92 static char *get_static(char **buffer, size_t *buflen, size_t len)
94 char *result;
96 /* Error check. We return false if things aren't set up right, or
97 there isn't enough buffer space left. */
99 if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
100 return NULL;
103 /* Return an index into the static buffer */
105 result = *buffer;
106 *buffer += len;
107 *buflen -= len;
109 return result;
112 /* I've copied the strtok() replacement function next_token_Xalloc() from
113 lib/util_str.c as I really don't want to have to link in any other
114 objects if I can possibly avoid it. */
116 static bool next_token_alloc(const char **ptr,
117 char **pp_buff,
118 const char *sep)
120 const char *s;
121 const char *saved_s;
122 char *pbuf;
123 bool quoted;
124 size_t len=1;
126 *pp_buff = NULL;
127 if (!ptr) {
128 return(false);
131 s = *ptr;
133 /* default to simple separators */
134 if (!sep) {
135 sep = " \t\n\r";
138 /* find the first non sep char */
139 while (*s && strchr(sep,*s)) {
140 s++;
143 /* nothing left? */
144 if (!*s) {
145 return false;
148 /* When restarting we need to go from here. */
149 saved_s = s;
151 /* Work out the length needed. */
152 for (quoted = false; *s &&
153 (quoted || !strchr(sep,*s)); s++) {
154 if (*s == '\"') {
155 quoted = !quoted;
156 } else {
157 len++;
161 /* We started with len = 1 so we have space for the nul. */
162 *pp_buff = (char *)malloc(len);
163 if (!*pp_buff) {
164 return false;
167 /* copy over the token */
168 pbuf = *pp_buff;
169 s = saved_s;
170 for (quoted = false; *s &&
171 (quoted || !strchr(sep,*s)); s++) {
172 if ( *s == '\"' ) {
173 quoted = !quoted;
174 } else {
175 *pbuf++ = *s;
179 *ptr = (*s) ? s+1 : s;
180 *pbuf = 0;
182 return true;
185 /* Fill a pwent structure from a winbindd_response structure. We use
186 the static data passed to us by libc to put strings and stuff in.
187 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
189 static NSS_STATUS fill_pwent(struct passwd *result,
190 struct winbindd_pw *pw,
191 char **buffer, size_t *buflen)
193 size_t len;
195 /* User name */
196 len = strlen(pw->pw_name) + 1;
198 if ((result->pw_name =
199 get_static(buffer, buflen, len)) == NULL) {
201 /* Out of memory */
203 return NSS_STATUS_TRYAGAIN;
206 memcpy(result->pw_name, pw->pw_name, len);
208 /* Password */
209 len = strlen(pw->pw_passwd) + 1;
211 if ((result->pw_passwd =
212 get_static(buffer, buflen, len)) == NULL) {
214 /* Out of memory */
216 return NSS_STATUS_TRYAGAIN;
219 memcpy(result->pw_passwd, pw->pw_passwd, len);
221 /* [ug]id */
223 result->pw_uid = pw->pw_uid;
224 result->pw_gid = pw->pw_gid;
226 /* GECOS */
227 len = strlen(pw->pw_gecos) + 1;
229 if ((result->pw_gecos =
230 get_static(buffer, buflen, len)) == NULL) {
232 /* Out of memory */
234 return NSS_STATUS_TRYAGAIN;
237 memcpy(result->pw_gecos, pw->pw_gecos, len);
239 /* Home directory */
240 len = strlen(pw->pw_dir) + 1;
242 if ((result->pw_dir =
243 get_static(buffer, buflen, len)) == NULL) {
245 /* Out of memory */
247 return NSS_STATUS_TRYAGAIN;
250 memcpy(result->pw_dir, pw->pw_dir, len);
252 /* Logon shell */
253 len = strlen(pw->pw_shell) + 1;
255 if ((result->pw_shell =
256 get_static(buffer, buflen, len)) == NULL) {
258 /* Out of memory */
260 return NSS_STATUS_TRYAGAIN;
263 memcpy(result->pw_shell, pw->pw_shell, len);
265 /* The struct passwd for Solaris has some extra fields which must
266 be initialised or nscd crashes. */
268 #if HAVE_PASSWD_PW_COMMENT
269 result->pw_comment = "";
270 #endif
272 #if HAVE_PASSWD_PW_AGE
273 result->pw_age = "";
274 #endif
276 return NSS_STATUS_SUCCESS;
279 /* Fill a grent structure from a winbindd_response structure. We use
280 the static data passed to us by libc to put strings and stuff in.
281 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
283 static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
284 const char *gr_mem, char **buffer, size_t *buflen)
286 char *name;
287 int i;
288 char *tst;
289 size_t len;
291 /* Group name */
292 len = strlen(gr->gr_name) + 1;
294 if ((result->gr_name =
295 get_static(buffer, buflen, len)) == NULL) {
297 /* Out of memory */
299 return NSS_STATUS_TRYAGAIN;
302 memcpy(result->gr_name, gr->gr_name, len);
304 /* Password */
305 len = strlen(gr->gr_passwd) + 1;
307 if ((result->gr_passwd =
308 get_static(buffer, buflen, len)) == NULL) {
310 /* Out of memory */
311 return NSS_STATUS_TRYAGAIN;
314 memcpy(result->gr_passwd, gr->gr_passwd, len);
316 /* gid */
318 result->gr_gid = gr->gr_gid;
320 /* Group membership */
322 if (!gr_mem) {
323 gr->num_gr_mem = 0;
326 /* this next value is a pointer to a pointer so let's align it */
328 /* Calculate number of extra bytes needed to align on pointer size boundry */
329 if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
330 i = sizeof(char*) - i;
332 if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
333 sizeof(char *)+i))) == NULL) {
335 /* Out of memory */
337 return NSS_STATUS_TRYAGAIN;
339 result->gr_mem = (char **)(tst + i);
341 if (gr->num_gr_mem == 0) {
343 /* Group is empty */
345 *(result->gr_mem) = NULL;
346 return NSS_STATUS_SUCCESS;
349 /* Start looking at extra data */
351 i = 0;
353 while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
354 /* Allocate space for member */
355 len = strlen(name) + 1;
357 if (((result->gr_mem)[i] =
358 get_static(buffer, buflen, len)) == NULL) {
359 free(name);
360 /* Out of memory */
361 return NSS_STATUS_TRYAGAIN;
363 memcpy((result->gr_mem)[i], name, len);
364 free(name);
365 i++;
368 /* Terminate list */
370 (result->gr_mem)[i] = NULL;
372 return NSS_STATUS_SUCCESS;
376 * NSS user functions
379 static struct winbindd_response getpwent_response;
381 static int ndx_pw_cache; /* Current index into pwd cache */
382 static int num_pw_cache; /* Current size of pwd cache */
384 /* Rewind "file pointer" to start of ntdom password database */
386 NSS_STATUS
387 _nss_winbind_setpwent(void)
389 NSS_STATUS ret;
390 #ifdef DEBUG_NSS
391 fprintf(stderr, "[%5d]: setpwent\n", getpid());
392 #endif
394 #if HAVE_PTHREAD
395 pthread_mutex_lock(&winbind_nss_mutex);
396 #endif
398 if (num_pw_cache > 0) {
399 ndx_pw_cache = num_pw_cache = 0;
400 winbindd_free_response(&getpwent_response);
403 ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL);
404 #ifdef DEBUG_NSS
405 fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
406 nss_err_str(ret), ret);
407 #endif
409 #if HAVE_PTHREAD
410 pthread_mutex_unlock(&winbind_nss_mutex);
411 #endif
412 return ret;
415 /* Close ntdom password database "file pointer" */
417 NSS_STATUS
418 _nss_winbind_endpwent(void)
420 NSS_STATUS ret;
421 #ifdef DEBUG_NSS
422 fprintf(stderr, "[%5d]: endpwent\n", getpid());
423 #endif
425 #if HAVE_PTHREAD
426 pthread_mutex_lock(&winbind_nss_mutex);
427 #endif
429 if (num_pw_cache > 0) {
430 ndx_pw_cache = num_pw_cache = 0;
431 winbindd_free_response(&getpwent_response);
434 ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL);
435 #ifdef DEBUG_NSS
436 fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
437 nss_err_str(ret), ret);
438 #endif
440 #if HAVE_PTHREAD
441 pthread_mutex_unlock(&winbind_nss_mutex);
442 #endif
444 return ret;
447 /* Fetch the next password entry from ntdom password database */
449 NSS_STATUS
450 _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
451 size_t buflen, int *errnop)
453 NSS_STATUS ret;
454 struct winbindd_request request;
455 static int called_again;
457 #ifdef DEBUG_NSS
458 fprintf(stderr, "[%5d]: getpwent\n", getpid());
459 #endif
461 #if HAVE_PTHREAD
462 pthread_mutex_lock(&winbind_nss_mutex);
463 #endif
465 /* Return an entry from the cache if we have one, or if we are
466 called again because we exceeded our static buffer. */
468 if ((ndx_pw_cache < num_pw_cache) || called_again) {
469 goto return_result;
472 /* Else call winbindd to get a bunch of entries */
474 if (num_pw_cache > 0) {
475 winbindd_free_response(&getpwent_response);
478 ZERO_STRUCT(request);
479 ZERO_STRUCT(getpwent_response);
481 request.data.num_entries = MAX_GETPWENT_USERS;
483 ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request,
484 &getpwent_response);
486 if (ret == NSS_STATUS_SUCCESS) {
487 struct winbindd_pw *pw_cache;
489 /* Fill cache */
491 ndx_pw_cache = 0;
492 num_pw_cache = getpwent_response.data.num_entries;
494 /* Return a result */
496 return_result:
498 pw_cache = (struct winbindd_pw *)
499 getpwent_response.extra_data.data;
501 /* Check data is valid */
503 if (pw_cache == NULL) {
504 ret = NSS_STATUS_NOTFOUND;
505 goto done;
508 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
509 &buffer, &buflen);
511 /* Out of memory - try again */
513 if (ret == NSS_STATUS_TRYAGAIN) {
514 called_again = true;
515 *errnop = errno = ERANGE;
516 goto done;
519 *errnop = errno = 0;
520 called_again = false;
521 ndx_pw_cache++;
523 /* If we've finished with this lot of results free cache */
525 if (ndx_pw_cache == num_pw_cache) {
526 ndx_pw_cache = num_pw_cache = 0;
527 winbindd_free_response(&getpwent_response);
530 done:
531 #ifdef DEBUG_NSS
532 fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
533 nss_err_str(ret), ret);
534 #endif
536 #if HAVE_PTHREAD
537 pthread_mutex_unlock(&winbind_nss_mutex);
538 #endif
539 return ret;
542 /* Return passwd struct from uid */
544 NSS_STATUS
545 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
546 size_t buflen, int *errnop)
548 NSS_STATUS ret;
549 static struct winbindd_response response;
550 struct winbindd_request request;
551 static int keep_response;
553 #ifdef DEBUG_NSS
554 fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
555 #endif
557 #if HAVE_PTHREAD
558 pthread_mutex_lock(&winbind_nss_mutex);
559 #endif
561 /* If our static buffer needs to be expanded we are called again */
562 if (!keep_response || uid != response.data.pw.pw_uid) {
564 /* Call for the first time */
566 ZERO_STRUCT(response);
567 ZERO_STRUCT(request);
569 request.data.uid = uid;
571 ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response);
573 if (ret == NSS_STATUS_SUCCESS) {
574 ret = fill_pwent(result, &response.data.pw,
575 &buffer, &buflen);
577 if (ret == NSS_STATUS_TRYAGAIN) {
578 keep_response = true;
579 *errnop = errno = ERANGE;
580 goto done;
584 } else {
586 /* We've been called again */
588 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
590 if (ret == NSS_STATUS_TRYAGAIN) {
591 *errnop = errno = ERANGE;
592 goto done;
595 keep_response = false;
596 *errnop = errno = 0;
599 winbindd_free_response(&response);
601 done:
603 #ifdef DEBUG_NSS
604 fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
605 (unsigned int)uid, nss_err_str(ret), ret);
606 #endif
608 #if HAVE_PTHREAD
609 pthread_mutex_unlock(&winbind_nss_mutex);
610 #endif
612 return ret;
615 /* Return passwd struct from username */
616 NSS_STATUS
617 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
618 size_t buflen, int *errnop)
620 NSS_STATUS ret;
621 static struct winbindd_response response;
622 struct winbindd_request request;
623 static int keep_response;
625 #ifdef DEBUG_NSS
626 fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
627 #endif
629 #if HAVE_PTHREAD
630 pthread_mutex_lock(&winbind_nss_mutex);
631 #endif
633 /* If our static buffer needs to be expanded we are called again */
635 if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
637 /* Call for the first time */
639 ZERO_STRUCT(response);
640 ZERO_STRUCT(request);
642 strncpy(request.data.username, name,
643 sizeof(request.data.username) - 1);
644 request.data.username
645 [sizeof(request.data.username) - 1] = '\0';
647 ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response);
649 if (ret == NSS_STATUS_SUCCESS) {
650 ret = fill_pwent(result, &response.data.pw, &buffer,
651 &buflen);
653 if (ret == NSS_STATUS_TRYAGAIN) {
654 keep_response = true;
655 *errnop = errno = ERANGE;
656 goto done;
660 } else {
662 /* We've been called again */
664 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
666 if (ret == NSS_STATUS_TRYAGAIN) {
667 keep_response = true;
668 *errnop = errno = ERANGE;
669 goto done;
672 keep_response = false;
673 *errnop = errno = 0;
676 winbindd_free_response(&response);
677 done:
678 #ifdef DEBUG_NSS
679 fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
680 name, nss_err_str(ret), ret);
681 #endif
683 #if HAVE_PTHREAD
684 pthread_mutex_unlock(&winbind_nss_mutex);
685 #endif
687 return ret;
691 * NSS group functions
694 static struct winbindd_response getgrent_response;
696 static int ndx_gr_cache; /* Current index into grp cache */
697 static int num_gr_cache; /* Current size of grp cache */
699 /* Rewind "file pointer" to start of ntdom group database */
701 NSS_STATUS
702 _nss_winbind_setgrent(void)
704 NSS_STATUS ret;
705 #ifdef DEBUG_NSS
706 fprintf(stderr, "[%5d]: setgrent\n", getpid());
707 #endif
709 #if HAVE_PTHREAD
710 pthread_mutex_lock(&winbind_nss_mutex);
711 #endif
713 if (num_gr_cache > 0) {
714 ndx_gr_cache = num_gr_cache = 0;
715 winbindd_free_response(&getgrent_response);
718 ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL);
719 #ifdef DEBUG_NSS
720 fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
721 nss_err_str(ret), ret);
722 #endif
724 #if HAVE_PTHREAD
725 pthread_mutex_unlock(&winbind_nss_mutex);
726 #endif
728 return ret;
731 /* Close "file pointer" for ntdom group database */
733 NSS_STATUS
734 _nss_winbind_endgrent(void)
736 NSS_STATUS ret;
737 #ifdef DEBUG_NSS
738 fprintf(stderr, "[%5d]: endgrent\n", getpid());
739 #endif
741 #if HAVE_PTHREAD
742 pthread_mutex_lock(&winbind_nss_mutex);
743 #endif
745 if (num_gr_cache > 0) {
746 ndx_gr_cache = num_gr_cache = 0;
747 winbindd_free_response(&getgrent_response);
750 ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL);
751 #ifdef DEBUG_NSS
752 fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
753 nss_err_str(ret), ret);
754 #endif
756 #if HAVE_PTHREAD
757 pthread_mutex_unlock(&winbind_nss_mutex);
758 #endif
760 return ret;
763 /* Get next entry from ntdom group database */
765 static NSS_STATUS
766 winbind_getgrent(enum winbindd_cmd cmd,
767 struct group *result,
768 char *buffer, size_t buflen, int *errnop)
770 NSS_STATUS ret;
771 static struct winbindd_request request;
772 static int called_again;
775 #ifdef DEBUG_NSS
776 fprintf(stderr, "[%5d]: getgrent\n", getpid());
777 #endif
779 #if HAVE_PTHREAD
780 pthread_mutex_lock(&winbind_nss_mutex);
781 #endif
783 /* Return an entry from the cache if we have one, or if we are
784 called again because we exceeded our static buffer. */
786 if ((ndx_gr_cache < num_gr_cache) || called_again) {
787 goto return_result;
790 /* Else call winbindd to get a bunch of entries */
792 if (num_gr_cache > 0) {
793 winbindd_free_response(&getgrent_response);
796 ZERO_STRUCT(request);
797 ZERO_STRUCT(getgrent_response);
799 request.data.num_entries = MAX_GETGRENT_USERS;
801 ret = winbindd_request_response(NULL, cmd, &request,
802 &getgrent_response);
804 if (ret == NSS_STATUS_SUCCESS) {
805 struct winbindd_gr *gr_cache;
806 int mem_ofs;
808 /* Fill cache */
810 ndx_gr_cache = 0;
811 num_gr_cache = getgrent_response.data.num_entries;
813 /* Return a result */
815 return_result:
817 gr_cache = (struct winbindd_gr *)
818 getgrent_response.extra_data.data;
820 /* Check data is valid */
822 if (gr_cache == NULL) {
823 ret = NSS_STATUS_NOTFOUND;
824 goto done;
827 /* Fill group membership. The offset into the extra data
828 for the group membership is the reported offset plus the
829 size of all the winbindd_gr records returned. */
831 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
832 num_gr_cache * sizeof(struct winbindd_gr);
834 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
835 ((char *)getgrent_response.extra_data.data)+mem_ofs,
836 &buffer, &buflen);
838 /* Out of memory - try again */
840 if (ret == NSS_STATUS_TRYAGAIN) {
841 called_again = true;
842 *errnop = errno = ERANGE;
843 goto done;
846 *errnop = 0;
847 called_again = false;
848 ndx_gr_cache++;
850 /* If we've finished with this lot of results free cache */
852 if (ndx_gr_cache == num_gr_cache) {
853 ndx_gr_cache = num_gr_cache = 0;
854 winbindd_free_response(&getgrent_response);
857 done:
858 #ifdef DEBUG_NSS
859 fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
860 nss_err_str(ret), ret);
861 #endif
863 #if HAVE_PTHREAD
864 pthread_mutex_unlock(&winbind_nss_mutex);
865 #endif
867 return ret;
871 NSS_STATUS
872 _nss_winbind_getgrent_r(struct group *result,
873 char *buffer, size_t buflen, int *errnop)
875 return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
878 NSS_STATUS
879 _nss_winbind_getgrlst_r(struct group *result,
880 char *buffer, size_t buflen, int *errnop)
882 return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
885 /* Return group struct from group name */
887 NSS_STATUS
888 _nss_winbind_getgrnam_r(const char *name,
889 struct group *result, char *buffer,
890 size_t buflen, int *errnop)
892 NSS_STATUS ret;
893 static struct winbindd_response response;
894 struct winbindd_request request;
895 static int keep_response;
897 #ifdef DEBUG_NSS
898 fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
899 #endif
901 #if HAVE_PTHREAD
902 pthread_mutex_lock(&winbind_nss_mutex);
903 #endif
905 /* If our static buffer needs to be expanded we are called again */
906 /* Or if the stored response group name differs from the request. */
908 if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
910 /* Call for the first time */
912 ZERO_STRUCT(request);
913 ZERO_STRUCT(response);
915 strncpy(request.data.groupname, name,
916 sizeof(request.data.groupname));
917 request.data.groupname
918 [sizeof(request.data.groupname) - 1] = '\0';
920 ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
921 &request, &response);
923 if (ret == NSS_STATUS_SUCCESS) {
924 ret = fill_grent(result, &response.data.gr,
925 (char *)response.extra_data.data,
926 &buffer, &buflen);
928 if (ret == NSS_STATUS_TRYAGAIN) {
929 keep_response = true;
930 *errnop = errno = ERANGE;
931 goto done;
935 } else {
937 /* We've been called again */
939 ret = fill_grent(result, &response.data.gr,
940 (char *)response.extra_data.data, &buffer,
941 &buflen);
943 if (ret == NSS_STATUS_TRYAGAIN) {
944 keep_response = true;
945 *errnop = errno = ERANGE;
946 goto done;
949 keep_response = false;
950 *errnop = 0;
953 winbindd_free_response(&response);
954 done:
955 #ifdef DEBUG_NSS
956 fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
957 name, nss_err_str(ret), ret);
958 #endif
960 #if HAVE_PTHREAD
961 pthread_mutex_unlock(&winbind_nss_mutex);
962 #endif
964 return ret;
967 /* Return group struct from gid */
969 NSS_STATUS
970 _nss_winbind_getgrgid_r(gid_t gid,
971 struct group *result, char *buffer,
972 size_t buflen, int *errnop)
974 NSS_STATUS ret;
975 static struct winbindd_response response;
976 struct winbindd_request request;
977 static int keep_response;
979 #ifdef DEBUG_NSS
980 fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
981 #endif
983 #if HAVE_PTHREAD
984 pthread_mutex_lock(&winbind_nss_mutex);
985 #endif
987 /* If our static buffer needs to be expanded we are called again */
988 /* Or if the stored response group name differs from the request. */
990 if (!keep_response || gid != response.data.gr.gr_gid) {
992 /* Call for the first time */
994 ZERO_STRUCT(request);
995 ZERO_STRUCT(response);
997 request.data.gid = gid;
999 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
1000 &request, &response);
1002 if (ret == NSS_STATUS_SUCCESS) {
1004 ret = fill_grent(result, &response.data.gr,
1005 (char *)response.extra_data.data,
1006 &buffer, &buflen);
1008 if (ret == NSS_STATUS_TRYAGAIN) {
1009 keep_response = true;
1010 *errnop = errno = ERANGE;
1011 goto done;
1015 } else {
1017 /* We've been called again */
1019 ret = fill_grent(result, &response.data.gr,
1020 (char *)response.extra_data.data, &buffer,
1021 &buflen);
1023 if (ret == NSS_STATUS_TRYAGAIN) {
1024 keep_response = true;
1025 *errnop = errno = ERANGE;
1026 goto done;
1029 keep_response = false;
1030 *errnop = 0;
1033 winbindd_free_response(&response);
1034 done:
1035 #ifdef DEBUG_NSS
1036 fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
1037 (unsigned int)gid, nss_err_str(ret), ret);
1038 #endif
1040 #if HAVE_PTHREAD
1041 pthread_mutex_unlock(&winbind_nss_mutex);
1042 #endif
1043 return ret;
1046 /* Initialise supplementary groups */
1048 NSS_STATUS
1049 _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
1050 long int *size, gid_t **groups, long int limit,
1051 int *errnop)
1053 NSS_STATUS ret;
1054 struct winbindd_request request;
1055 struct winbindd_response response;
1056 int i;
1058 #ifdef DEBUG_NSS
1059 fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
1060 user, group);
1061 #endif
1063 #if HAVE_PTHREAD
1064 pthread_mutex_lock(&winbind_nss_mutex);
1065 #endif
1067 ZERO_STRUCT(request);
1068 ZERO_STRUCT(response);
1070 strncpy(request.data.username, user,
1071 sizeof(request.data.username) - 1);
1073 ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
1074 &request, &response);
1076 if (ret == NSS_STATUS_SUCCESS) {
1077 int num_gids = response.data.num_entries;
1078 gid_t *gid_list = (gid_t *)response.extra_data.data;
1080 #ifdef DEBUG_NSS
1081 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1082 "and %d gids\n", getpid(),
1083 user, num_gids);
1084 #endif
1085 if (gid_list == NULL) {
1086 ret = NSS_STATUS_NOTFOUND;
1087 goto done;
1090 /* Copy group list to client */
1092 for (i = 0; i < num_gids; i++) {
1094 #ifdef DEBUG_NSS
1095 fprintf(stderr, "[%5d]: initgroups %s (%d): "
1096 "processing gid %d \n", getpid(),
1097 user, group, gid_list[i]);
1098 #endif
1100 /* Skip primary group */
1102 if (gid_list[i] == group) {
1103 continue;
1106 /* Skip groups without a mapping */
1107 if (gid_list[i] == (uid_t)-1) {
1108 continue;
1111 /* Filled buffer ? If so, resize. */
1113 if (*start == *size) {
1114 long int newsize;
1115 gid_t *newgroups;
1117 newsize = 2 * (*size);
1118 if (limit > 0) {
1119 if (*size == limit) {
1120 goto done;
1122 if (newsize > limit) {
1123 newsize = limit;
1127 newgroups = (gid_t *)
1128 realloc((*groups),
1129 newsize * sizeof(**groups));
1130 if (!newgroups) {
1131 *errnop = ENOMEM;
1132 ret = NSS_STATUS_NOTFOUND;
1133 goto done;
1135 *groups = newgroups;
1136 *size = newsize;
1139 /* Add to buffer */
1141 (*groups)[*start] = gid_list[i];
1142 *start += 1;
1146 /* Back to your regularly scheduled programming */
1148 done:
1149 #ifdef DEBUG_NSS
1150 fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1151 user, nss_err_str(ret), ret);
1152 #endif
1154 #if HAVE_PTHREAD
1155 pthread_mutex_unlock(&winbind_nss_mutex);
1156 #endif
1158 return ret;