2 Unix SMB/Netbios implementation.
5 Windows NT Domain nsswitch module
7 Copyright (C) Tim Potter 2000
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this library; if not, write to the
21 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
25 #include "winbind_nss_config.h"
26 #include "winbindd_nss.h"
28 /* Prototypes from common.c */
30 void init_request(struct winbindd_request *req,int rq_type);
31 NSS_STATUS winbindd_request(int req_type,
32 struct winbindd_request *request,
33 struct winbindd_response *response);
34 int write_sock(void *buffer, int count);
35 int read_reply(struct winbindd_response *response);
36 void free_response(struct winbindd_response *response);
38 /* Allocate some space from the nss static buffer. The buffer and buflen
39 are the pointers passed in by the C library to the _nss_ntdom_*
42 static char *get_static(char **buffer, int *buflen, int len)
46 /* Error check. We return false if things aren't set up right, or
47 there isn't enough buffer space left. */
49 if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
53 /* Some architectures, like Sparc, need pointers aligned on
55 #if _ALIGNMENT_REQUIRED
57 int mod = len % _MAX_ALIGNMENT;
59 len += _MAX_ALIGNMENT - mod;
63 /* Return an index into the static buffer */
72 /* I've copied the strtok() replacement function next_token() from
73 lib/util_str.c as I really don't want to have to link in any other
74 objects if I can possibly avoid it. */
76 BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
82 if (!ptr) return(False);
86 /* default to simple separators */
87 if (!sep) sep = " \t\n\r";
89 /* find the first non sep char */
90 while (*s && strchr(sep,*s)) s++;
93 if (! *s) return(False);
95 /* copy over the token */
96 for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
105 *ptr = (*s) ? s+1 : s;
112 /* Fill a pwent structure from a winbindd_response structure. We use
113 the static data passed to us by libc to put strings and stuff in.
114 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
116 static NSS_STATUS fill_pwent(struct passwd *result,
117 struct winbindd_pw *pw,
118 char **buffer, int *buflen)
122 if ((result->pw_name =
123 get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
127 return NSS_STATUS_TRYAGAIN;
130 strcpy(result->pw_name, pw->pw_name);
134 if ((result->pw_passwd =
135 get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
139 return NSS_STATUS_TRYAGAIN;
142 strcpy(result->pw_passwd, pw->pw_passwd);
146 result->pw_uid = pw->pw_uid;
147 result->pw_gid = pw->pw_gid;
151 if ((result->pw_gecos =
152 get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
156 return NSS_STATUS_TRYAGAIN;
159 strcpy(result->pw_gecos, pw->pw_gecos);
163 if ((result->pw_dir =
164 get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
168 return NSS_STATUS_TRYAGAIN;
171 strcpy(result->pw_dir, pw->pw_dir);
175 if ((result->pw_shell =
176 get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
180 return NSS_STATUS_TRYAGAIN;
183 strcpy(result->pw_shell, pw->pw_shell);
185 return NSS_STATUS_SUCCESS;
188 /* Fill a grent structure from a winbindd_response structure. We use
189 the static data passed to us by libc to put strings and stuff in.
190 Return NSS_STATUS_TRYAGAIN if we run out of memory. */
192 static int fill_grent(struct group *result, struct winbindd_gr *gr,
193 char *gr_mem, char **buffer, int *buflen)
200 if ((result->gr_name =
201 get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
205 return NSS_STATUS_TRYAGAIN;
208 strcpy(result->gr_name, gr->gr_name);
212 if ((result->gr_passwd =
213 get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
217 return NSS_STATUS_TRYAGAIN;
220 strcpy(result->gr_passwd, gr->gr_passwd);
224 result->gr_gid = gr->gr_gid;
226 /* Group membership */
228 if ((gr->num_gr_mem < 0) || !gr_mem) {
232 if ((result->gr_mem =
233 (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) *
234 sizeof(char *))) == NULL) {
238 return NSS_STATUS_TRYAGAIN;
241 if (gr->num_gr_mem == 0) {
245 *(result->gr_mem) = NULL;
246 return NSS_STATUS_SUCCESS;
249 /* Start looking at extra data */
253 while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
255 /* Allocate space for member */
257 if (((result->gr_mem)[i] =
258 get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
262 return NSS_STATUS_TRYAGAIN;
265 strcpy((result->gr_mem)[i], name);
271 (result->gr_mem)[i] = NULL;
273 return NSS_STATUS_SUCCESS;
280 static struct winbindd_response getpwent_response;
282 static int ndx_pw_cache; /* Current index into pwd cache */
283 static int num_pw_cache; /* Current size of pwd cache */
285 /* Rewind "file pointer" to start of ntdom password database */
288 _nss_winbind_setpwent(void)
291 fprintf(stderr, "[%5d]: setpwent\n", getpid());
294 if (num_pw_cache > 0) {
295 ndx_pw_cache = num_pw_cache = 0;
296 free_response(&getpwent_response);
299 return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
302 /* Close ntdom password database "file pointer" */
305 _nss_winbind_endpwent(void)
308 fprintf(stderr, "[%5d]: endpwent\n", getpid());
311 if (num_pw_cache > 0) {
312 ndx_pw_cache = num_pw_cache = 0;
313 free_response(&getpwent_response);
316 return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
319 /* Fetch the next password entry from ntdom password database */
321 #define MAX_GETPWENT_USERS 250
324 _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
325 size_t buflen, int *errnop)
328 struct winbindd_request request;
329 static int called_again;
332 fprintf(stderr, "[%5d]: getpwent\n", getpid());
335 /* Return an entry from the cache if we have one, or if we are
336 called again because we exceeded our static buffer. */
338 if ((ndx_pw_cache < num_pw_cache) || called_again) {
342 /* Else call winbindd to get a bunch of entries */
344 if (num_pw_cache > 0) {
345 free_response(&getpwent_response);
348 ZERO_STRUCT(request);
349 ZERO_STRUCT(getpwent_response);
351 request.data.num_entries = MAX_GETPWENT_USERS;
353 ret = winbindd_request(WINBINDD_GETPWENT, &request,
356 if (ret == NSS_STATUS_SUCCESS) {
357 struct winbindd_pw *pw_cache;
362 num_pw_cache = getpwent_response.data.num_entries;
364 /* Return a result */
368 pw_cache = getpwent_response.extra_data;
370 /* Check data is valid */
372 if (pw_cache == NULL) {
373 return NSS_STATUS_NOTFOUND;
376 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
379 /* Out of memory - try again */
381 if (ret == NSS_STATUS_TRYAGAIN) {
383 *errnop = errno = ERANGE;
388 called_again = False;
391 /* If we've finished with this lot of results free cache */
393 if (ndx_pw_cache == num_pw_cache) {
394 ndx_pw_cache = num_pw_cache = 0;
395 free_response(&getpwent_response);
402 /* Return passwd struct from uid */
405 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
406 size_t buflen, int *errnop)
409 static struct winbindd_response response;
410 struct winbindd_request request;
411 static int keep_response=0;
413 /* If our static buffer needs to be expanded we are called again */
414 if (!keep_response) {
416 /* Call for the first time */
418 ZERO_STRUCT(response);
419 ZERO_STRUCT(request);
421 request.data.uid = uid;
423 ret = winbindd_request(WINBINDD_GETPWNAM_FROM_UID, &request,
426 if (ret == NSS_STATUS_SUCCESS) {
427 ret = fill_pwent(result, &response.data.pw,
430 if (ret == NSS_STATUS_TRYAGAIN) {
431 keep_response = True;
432 *errnop = errno = ERANGE;
439 /* We've been called again */
441 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
443 if (ret == NSS_STATUS_TRYAGAIN) {
444 keep_response = True;
445 *errnop = errno = ERANGE;
449 keep_response = False;
453 free_response(&response);
457 /* Return passwd struct from username */
460 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
461 size_t buflen, int *errnop)
464 static struct winbindd_response response;
465 struct winbindd_request request;
466 static int keep_response;
469 fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
472 /* If our static buffer needs to be expanded we are called again */
474 if (!keep_response) {
476 /* Call for the first time */
478 ZERO_STRUCT(response);
479 ZERO_STRUCT(request);
481 strncpy(request.data.username, name,
482 sizeof(request.data.username) - 1);
483 request.data.username
484 [sizeof(request.data.username) - 1] = '\0';
486 ret = winbindd_request(WINBINDD_GETPWNAM_FROM_USER, &request,
489 if (ret == NSS_STATUS_SUCCESS) {
490 ret = fill_pwent(result, &response.data.pw, &buffer,
493 if (ret == NSS_STATUS_TRYAGAIN) {
494 keep_response = True;
495 *errnop = errno = ERANGE;
502 /* We've been called again */
504 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
506 if (ret == NSS_STATUS_TRYAGAIN) {
507 keep_response = True;
508 *errnop = errno = ERANGE;
512 keep_response = False;
516 free_response(&response);
521 * NSS group functions
524 static struct winbindd_response getgrent_response;
526 static int ndx_gr_cache; /* Current index into grp cache */
527 static int num_gr_cache; /* Current size of grp cache */
529 /* Rewind "file pointer" to start of ntdom group database */
532 _nss_winbind_setgrent(void)
535 fprintf(stderr, "[%5d]: setgrent\n", getpid());
538 if (num_gr_cache > 0) {
539 ndx_gr_cache = num_gr_cache = 0;
540 free_response(&getgrent_response);
543 return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
546 /* Close "file pointer" for ntdom group database */
549 _nss_winbind_endgrent(void)
552 fprintf(stderr, "[%5d]: endgrent\n", getpid());
555 if (num_gr_cache > 0) {
556 ndx_gr_cache = num_gr_cache = 0;
557 free_response(&getgrent_response);
560 return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
563 /* Get next entry from ntdom group database */
565 #define MAX_GETGRENT_USERS 250
568 _nss_winbind_getgrent_r(struct group *result,
569 char *buffer, size_t buflen, int *errnop)
572 static struct winbindd_request request;
573 static int called_again;
576 fprintf(stderr, "[%5d]: getgrent\n", getpid());
579 /* Return an entry from the cache if we have one, or if we are
580 called again because we exceeded our static buffer. */
582 if ((ndx_gr_cache < num_gr_cache) || called_again) {
586 /* Else call winbindd to get a bunch of entries */
588 if (num_gr_cache > 0) {
589 free_response(&getgrent_response);
592 ZERO_STRUCT(request);
593 ZERO_STRUCT(getgrent_response);
595 request.data.num_entries = MAX_GETGRENT_USERS;
597 ret = winbindd_request(WINBINDD_GETGRENT, &request,
600 if (ret == NSS_STATUS_SUCCESS) {
601 struct winbindd_gr *gr_cache;
607 num_gr_cache = getgrent_response.data.num_entries;
609 /* Return a result */
613 gr_cache = getgrent_response.extra_data;
615 /* Check data is valid */
617 if (gr_cache == NULL) {
618 return NSS_STATUS_NOTFOUND;
621 /* Fill group membership. The offset into the extra data
622 for the group membership is the reported offset plus the
623 size of all the winbindd_gr records returned. */
625 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
626 num_gr_cache * sizeof(struct winbindd_gr);
628 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
629 ((char *)getgrent_response.extra_data)+mem_ofs,
632 /* Out of memory - try again */
634 if (ret == NSS_STATUS_TRYAGAIN) {
636 *errnop = errno = ERANGE;
641 called_again = False;
644 /* If we've finished with this lot of results free cache */
646 if (ndx_gr_cache == num_gr_cache) {
647 ndx_gr_cache = num_gr_cache = 0;
648 free_response(&getgrent_response);
655 /* Return group struct from group name */
658 _nss_winbind_getgrnam_r(const char *name,
659 struct group *result, char *buffer,
660 size_t buflen, int *errnop)
663 static struct winbindd_response response;
664 struct winbindd_request request;
665 static int keep_response;
668 fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
671 /* If our static buffer needs to be expanded we are called again */
673 if (!keep_response) {
675 /* Call for the first time */
677 ZERO_STRUCT(request);
678 ZERO_STRUCT(response);
680 strncpy(request.data.groupname, name,
681 sizeof(request.data.groupname));
682 request.data.groupname
683 [sizeof(request.data.groupname) - 1] = '\0';
685 ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GROUP,
686 &request, &response);
688 if (ret == NSS_STATUS_SUCCESS) {
689 ret = fill_grent(result, &response.data.gr,
693 if (ret == NSS_STATUS_TRYAGAIN) {
694 keep_response = True;
695 *errnop = errno = ERANGE;
702 /* We've been called again */
704 ret = fill_grent(result, &response.data.gr,
705 response.extra_data, &buffer, &buflen);
707 if (ret == NSS_STATUS_TRYAGAIN) {
708 keep_response = True;
709 *errnop = errno = ERANGE;
713 keep_response = False;
717 free_response(&response);
721 /* Return group struct from gid */
724 _nss_winbind_getgrgid_r(gid_t gid,
725 struct group *result, char *buffer,
726 size_t buflen, int *errnop)
729 static struct winbindd_response response;
730 struct winbindd_request request;
731 static int keep_response;
734 fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
737 /* If our static buffer needs to be expanded we are called again */
739 if (!keep_response) {
741 /* Call for the first time */
743 ZERO_STRUCT(request);
744 ZERO_STRUCT(response);
746 request.data.gid = gid;
748 ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GID, &request,
751 if (ret == NSS_STATUS_SUCCESS) {
753 ret = fill_grent(result, &response.data.gr,
757 if (ret == NSS_STATUS_TRYAGAIN) {
758 keep_response = True;
759 *errnop = errno = ERANGE;
766 /* We've been called again */
768 ret = fill_grent(result, &response.data.gr,
769 response.extra_data, &buffer, &buflen);
771 if (ret == NSS_STATUS_TRYAGAIN) {
772 keep_response = True;
773 *errnop = errno = ERANGE;
777 keep_response = False;
781 free_response(&response);
785 /* Initialise supplementary groups */
788 _nss_winbind_initgroups(char *user, gid_t group, long int *start,
789 long int *size, gid_t *groups, long int limit,
793 struct winbindd_request request;
794 struct winbindd_response response;
798 fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
802 ZERO_STRUCT(request);
803 ZERO_STRUCT(response);
805 strncpy(request.data.username, user,
806 sizeof(request.data.username) - 1);
808 ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
810 if (ret == NSS_STATUS_SUCCESS) {
811 int num_gids = response.data.num_entries;
812 gid_t *gid_list = (gid_t *)response.extra_data;
814 /* Copy group list to client */
816 for (i = 0; i < num_gids; i++) {
818 /* Skip primary group */
820 if (gid_list[i] == group) continue;
824 if (*start == *size && limit <= 0) {
826 groups, 2 * (*size) * sizeof(*groups));
827 if (!groups) goto done;
831 groups[*start] = gid_list[i];
836 if (*start == limit) goto done;
840 /* Back to your regularly scheduled programming */