Line data Source code
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 : #ifdef HAVE_PTHREAD_H
25 : #include <pthread.h>
26 : #endif
27 :
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. */
31 :
32 : #define MAX_GETPWENT_USERS 250
33 : #define MAX_GETGRENT_USERS 250
34 :
35 : /*************************************************************************
36 : ************************************************************************/
37 :
38 : #ifdef DEBUG_NSS
39 : static const char *nss_err_str(NSS_STATUS ret)
40 : {
41 : switch (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";
53 : #endif
54 : default:
55 : return "UNKNOWN RETURN CODE!!!!!!!";
56 : }
57 : }
58 : #endif
59 :
60 : /* Prototypes from wb_common.c */
61 :
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_*
64 : functions. */
65 :
66 346664 : static char *get_static(char **buffer, size_t *buflen, size_t len)
67 : {
68 3710 : char *result;
69 :
70 : /* Error check. We return false if things aren't set up right, or
71 : there isn't enough buffer space left. */
72 :
73 346664 : if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
74 0 : return NULL;
75 : }
76 :
77 : /* Return an index into the static buffer */
78 :
79 346664 : result = *buffer;
80 346664 : *buffer += len;
81 346664 : *buflen -= len;
82 :
83 346664 : return result;
84 : }
85 :
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. */
89 :
90 40 : static bool next_token_alloc(const char **ptr,
91 : char **pp_buff,
92 : const char *sep)
93 : {
94 0 : const char *s;
95 0 : const char *saved_s;
96 0 : char *pbuf;
97 0 : bool quoted;
98 40 : size_t len=1;
99 :
100 40 : *pp_buff = NULL;
101 40 : if (!ptr) {
102 0 : return(false);
103 : }
104 :
105 40 : s = *ptr;
106 :
107 : /* default to simple separators */
108 40 : if (!sep) {
109 0 : sep = " \t\n\r";
110 : }
111 :
112 : /* find the first non sep char */
113 40 : while (*s && strchr(sep,*s)) {
114 0 : s++;
115 : }
116 :
117 : /* nothing left? */
118 40 : if (!*s) {
119 20 : return false;
120 : }
121 :
122 : /* When restarting we need to go from here. */
123 20 : saved_s = s;
124 :
125 : /* Work out the length needed. */
126 396 : for (quoted = false; *s &&
127 752 : (quoted || !strchr(sep,*s)); s++) {
128 376 : if (*s == '\"') {
129 0 : quoted = !quoted;
130 : } else {
131 376 : len++;
132 : }
133 : }
134 :
135 : /* We started with len = 1 so we have space for the nul. */
136 20 : *pp_buff = (char *)malloc(len);
137 20 : if (!*pp_buff) {
138 0 : return false;
139 : }
140 :
141 : /* copy over the token */
142 20 : pbuf = *pp_buff;
143 20 : s = saved_s;
144 396 : for (quoted = false; *s &&
145 752 : (quoted || !strchr(sep,*s)); s++) {
146 376 : if ( *s == '\"' ) {
147 0 : quoted = !quoted;
148 : } else {
149 376 : *pbuf++ = *s;
150 : }
151 : }
152 :
153 20 : *ptr = (*s) ? s+1 : s;
154 20 : *pbuf = 0;
155 :
156 20 : return true;
157 : }
158 :
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. */
162 :
163 65538 : static NSS_STATUS fill_pwent(struct passwd *result,
164 : struct winbindd_pw *pw,
165 : char **buffer, size_t *buflen)
166 : {
167 742 : size_t len;
168 :
169 : /* User name */
170 65538 : len = strlen(pw->pw_name) + 1;
171 :
172 65538 : if ((result->pw_name =
173 65538 : get_static(buffer, buflen, len)) == NULL) {
174 :
175 : /* Out of memory */
176 :
177 0 : return NSS_STATUS_TRYAGAIN;
178 : }
179 :
180 65538 : memcpy(result->pw_name, pw->pw_name, len);
181 :
182 : /* Password */
183 65538 : len = strlen(pw->pw_passwd) + 1;
184 :
185 65538 : if ((result->pw_passwd =
186 65538 : get_static(buffer, buflen, len)) == NULL) {
187 :
188 : /* Out of memory */
189 :
190 0 : return NSS_STATUS_TRYAGAIN;
191 : }
192 :
193 65538 : memcpy(result->pw_passwd, pw->pw_passwd, len);
194 :
195 : /* [ug]id */
196 :
197 65538 : result->pw_uid = pw->pw_uid;
198 65538 : result->pw_gid = pw->pw_gid;
199 :
200 : /* GECOS */
201 65538 : len = strlen(pw->pw_gecos) + 1;
202 :
203 65538 : if ((result->pw_gecos =
204 65538 : get_static(buffer, buflen, len)) == NULL) {
205 :
206 : /* Out of memory */
207 :
208 0 : return NSS_STATUS_TRYAGAIN;
209 : }
210 :
211 65538 : memcpy(result->pw_gecos, pw->pw_gecos, len);
212 :
213 : /* Home directory */
214 65538 : len = strlen(pw->pw_dir) + 1;
215 :
216 65538 : if ((result->pw_dir =
217 65538 : get_static(buffer, buflen, len)) == NULL) {
218 :
219 : /* Out of memory */
220 :
221 0 : return NSS_STATUS_TRYAGAIN;
222 : }
223 :
224 65538 : memcpy(result->pw_dir, pw->pw_dir, len);
225 :
226 : /* Logon shell */
227 65538 : len = strlen(pw->pw_shell) + 1;
228 :
229 65538 : if ((result->pw_shell =
230 65538 : get_static(buffer, buflen, len)) == NULL) {
231 :
232 : /* Out of memory */
233 :
234 0 : return NSS_STATUS_TRYAGAIN;
235 : }
236 :
237 65538 : memcpy(result->pw_shell, pw->pw_shell, len);
238 :
239 : /* The struct passwd for Solaris has some extra fields which must
240 : be initialised or nscd crashes. */
241 :
242 : #ifdef HAVE_PASSWD_PW_COMMENT
243 : result->pw_comment = "";
244 : #endif
245 :
246 : #ifdef HAVE_PASSWD_PW_AGE
247 : result->pw_age = "";
248 : #endif
249 :
250 65538 : return NSS_STATUS_SUCCESS;
251 : }
252 :
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. */
256 :
257 6318 : static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
258 : const char *gr_mem, char **buffer, size_t *buflen)
259 : {
260 0 : char *name;
261 0 : int i;
262 0 : char *tst;
263 0 : size_t len;
264 :
265 : /* Group name */
266 6318 : len = strlen(gr->gr_name) + 1;
267 :
268 6318 : if ((result->gr_name =
269 6318 : get_static(buffer, buflen, len)) == NULL) {
270 :
271 : /* Out of memory */
272 :
273 0 : return NSS_STATUS_TRYAGAIN;
274 : }
275 :
276 6318 : memcpy(result->gr_name, gr->gr_name, len);
277 :
278 : /* Password */
279 6318 : len = strlen(gr->gr_passwd) + 1;
280 :
281 6318 : if ((result->gr_passwd =
282 6318 : get_static(buffer, buflen, len)) == NULL) {
283 :
284 : /* Out of memory */
285 0 : return NSS_STATUS_TRYAGAIN;
286 : }
287 :
288 6318 : memcpy(result->gr_passwd, gr->gr_passwd, len);
289 :
290 : /* gid */
291 :
292 6318 : result->gr_gid = gr->gr_gid;
293 :
294 : /* Group membership */
295 :
296 6318 : if (!gr_mem) {
297 4304 : gr->num_gr_mem = 0;
298 : }
299 :
300 : /* this next value is a pointer to a pointer so let's align it */
301 :
302 : /* Calculate number of extra bytes needed to align on pointer size boundary */
303 6318 : if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
304 6066 : i = sizeof(char*) - i;
305 :
306 6318 : if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
307 : sizeof(char *)+i))) == NULL) {
308 :
309 : /* Out of memory */
310 :
311 0 : return NSS_STATUS_TRYAGAIN;
312 : }
313 6318 : result->gr_mem = (char **)(tst + i);
314 :
315 6318 : if (gr->num_gr_mem == 0) {
316 :
317 : /* Group is empty */
318 :
319 6298 : *(result->gr_mem) = NULL;
320 6298 : return NSS_STATUS_SUCCESS;
321 : }
322 :
323 : /* Start looking at extra data */
324 :
325 20 : i = 0;
326 :
327 40 : while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
328 : /* Allocate space for member */
329 20 : len = strlen(name) + 1;
330 :
331 20 : if (((result->gr_mem)[i] =
332 20 : get_static(buffer, buflen, len)) == NULL) {
333 0 : free(name);
334 : /* Out of memory */
335 0 : return NSS_STATUS_TRYAGAIN;
336 : }
337 20 : memcpy((result->gr_mem)[i], name, len);
338 20 : free(name);
339 20 : i++;
340 : }
341 :
342 : /* Terminate list */
343 :
344 20 : (result->gr_mem)[i] = NULL;
345 :
346 20 : return NSS_STATUS_SUCCESS;
347 : }
348 :
349 : /*
350 : * NSS user functions
351 : */
352 :
353 : static __thread struct winbindd_response getpwent_response;
354 :
355 : static __thread int ndx_pw_cache; /* Current index into pwd cache */
356 : static __thread int num_pw_cache; /* Current size of pwd cache */
357 :
358 : /* Rewind "file pointer" to start of ntdom password database */
359 :
360 : _PUBLIC_ON_LINUX_
361 : NSS_STATUS
362 354 : _nss_winbind_setpwent(void)
363 : {
364 0 : NSS_STATUS ret;
365 : #ifdef DEBUG_NSS
366 : fprintf(stderr, "[%5d]: setpwent\n", getpid());
367 : #endif
368 :
369 354 : if (num_pw_cache > 0) {
370 0 : ndx_pw_cache = num_pw_cache = 0;
371 0 : winbindd_free_response(&getpwent_response);
372 : }
373 :
374 354 : winbind_set_client_name("nss_winbind");
375 354 : ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL);
376 : #ifdef DEBUG_NSS
377 : fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
378 : nss_err_str(ret), ret);
379 : #endif
380 :
381 354 : return ret;
382 : }
383 :
384 : /* Close ntdom password database "file pointer" */
385 :
386 : _PUBLIC_ON_LINUX_
387 : NSS_STATUS
388 11931 : _nss_winbind_endpwent(void)
389 : {
390 58 : NSS_STATUS ret;
391 : #ifdef DEBUG_NSS
392 : fprintf(stderr, "[%5d]: endpwent\n", getpid());
393 : #endif
394 :
395 11931 : if (num_pw_cache > 0) {
396 0 : ndx_pw_cache = num_pw_cache = 0;
397 0 : winbindd_free_response(&getpwent_response);
398 : }
399 :
400 11931 : winbind_set_client_name("nss_winbind");
401 11931 : ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL);
402 : #ifdef DEBUG_NSS
403 : fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
404 : nss_err_str(ret), ret);
405 : #endif
406 :
407 11931 : return ret;
408 : }
409 :
410 : /* Fetch the next password entry from ntdom password database */
411 :
412 : _PUBLIC_ON_LINUX_
413 : NSS_STATUS
414 2226 : _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
415 : size_t buflen, int *errnop)
416 : {
417 0 : NSS_STATUS ret;
418 0 : struct winbindd_request request;
419 0 : static __thread int called_again;
420 :
421 : #ifdef DEBUG_NSS
422 : fprintf(stderr, "[%5d]: getpwent\n", getpid());
423 : #endif
424 :
425 : /* Return an entry from the cache if we have one, or if we are
426 : called again because we exceeded our static buffer. */
427 :
428 2226 : if ((ndx_pw_cache < num_pw_cache) || called_again) {
429 1840 : goto return_result;
430 : }
431 :
432 : /* Else call winbindd to get a bunch of entries */
433 :
434 386 : if (num_pw_cache > 0) {
435 0 : winbindd_free_response(&getpwent_response);
436 : }
437 :
438 386 : ZERO_STRUCT(request);
439 386 : ZERO_STRUCT(getpwent_response);
440 :
441 386 : request.data.num_entries = MAX_GETPWENT_USERS;
442 :
443 386 : winbind_set_client_name("nss_winbind");
444 386 : ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request,
445 : &getpwent_response);
446 :
447 386 : if (ret == NSS_STATUS_SUCCESS) {
448 0 : struct winbindd_pw *pw_cache;
449 :
450 : /* Fill cache */
451 :
452 32 : ndx_pw_cache = 0;
453 32 : num_pw_cache = getpwent_response.data.num_entries;
454 :
455 : /* Return a result */
456 :
457 1872 : return_result:
458 :
459 1872 : pw_cache = (struct winbindd_pw *)
460 : getpwent_response.extra_data.data;
461 :
462 : /* Check data is valid */
463 :
464 1872 : if (pw_cache == NULL) {
465 0 : ret = NSS_STATUS_NOTFOUND;
466 0 : goto done;
467 : }
468 :
469 1872 : ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
470 : &buffer, &buflen);
471 :
472 : /* Out of memory - try again */
473 :
474 1872 : if (ret == NSS_STATUS_TRYAGAIN) {
475 0 : called_again = true;
476 0 : *errnop = errno = ERANGE;
477 0 : goto done;
478 : }
479 :
480 1872 : *errnop = errno = 0;
481 1872 : called_again = false;
482 1872 : ndx_pw_cache++;
483 :
484 : /* If we've finished with this lot of results free cache */
485 :
486 1872 : if (ndx_pw_cache == num_pw_cache) {
487 32 : ndx_pw_cache = num_pw_cache = 0;
488 32 : winbindd_free_response(&getpwent_response);
489 : }
490 : }
491 2194 : done:
492 : #ifdef DEBUG_NSS
493 : fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
494 : nss_err_str(ret), ret);
495 : #endif
496 :
497 2226 : return ret;
498 : }
499 :
500 : /* Return passwd struct from uid */
501 :
502 : _PUBLIC_ON_LINUX_
503 : NSS_STATUS
504 6314 : _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
505 : size_t buflen, int *errnop)
506 : {
507 0 : NSS_STATUS ret;
508 0 : static __thread struct winbindd_response response;
509 0 : struct winbindd_request request;
510 0 : static __thread int keep_response;
511 :
512 : #ifdef DEBUG_NSS
513 : fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
514 : #endif
515 :
516 : /* If our static buffer needs to be expanded we are called again */
517 12628 : if (!keep_response || uid != response.data.pw.pw_uid) {
518 :
519 : /* Call for the first time */
520 :
521 6314 : response = (struct winbindd_response) {
522 : .length = 0,
523 : };
524 6314 : request = (struct winbindd_request) {
525 : .wb_flags = WBFLAG_FROM_NSS,
526 : .data = {
527 : .uid = uid,
528 : },
529 : };
530 :
531 6314 : winbind_set_client_name("nss_winbind");
532 6314 : ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response);
533 :
534 6314 : if (ret == NSS_STATUS_SUCCESS) {
535 2904 : ret = fill_pwent(result, &response.data.pw,
536 : &buffer, &buflen);
537 :
538 2904 : if (ret == NSS_STATUS_TRYAGAIN) {
539 0 : keep_response = true;
540 0 : *errnop = errno = ERANGE;
541 0 : goto done;
542 : }
543 : }
544 :
545 : } else {
546 :
547 : /* We've been called again */
548 :
549 0 : ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
550 :
551 0 : if (ret == NSS_STATUS_TRYAGAIN) {
552 0 : *errnop = errno = ERANGE;
553 0 : goto done;
554 : }
555 :
556 0 : keep_response = false;
557 0 : *errnop = errno = 0;
558 : }
559 :
560 6314 : winbindd_free_response(&response);
561 :
562 6314 : done:
563 :
564 : #ifdef DEBUG_NSS
565 : fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
566 : (unsigned int)uid, nss_err_str(ret), ret);
567 : #endif
568 :
569 6314 : return ret;
570 : }
571 :
572 : /* Return passwd struct from username */
573 : _PUBLIC_ON_LINUX_
574 : NSS_STATUS
575 63687 : _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
576 : size_t buflen, int *errnop)
577 : {
578 758 : NSS_STATUS ret;
579 758 : static __thread struct winbindd_response response;
580 758 : struct winbindd_request request;
581 758 : static __thread int keep_response;
582 :
583 : #ifdef DEBUG_NSS
584 : fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
585 : #endif
586 :
587 : /* If our static buffer needs to be expanded we are called again */
588 :
589 126616 : if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
590 :
591 : /* Call for the first time */
592 :
593 63687 : response = (struct winbindd_response) {
594 : .length = 0,
595 : };
596 63687 : request = (struct winbindd_request) {
597 : .wb_flags = WBFLAG_FROM_NSS,
598 : };
599 :
600 63687 : strncpy(request.data.username, name,
601 : sizeof(request.data.username) - 1);
602 758 : request.data.username
603 62929 : [sizeof(request.data.username) - 1] = '\0';
604 :
605 63687 : winbind_set_client_name("nss_winbind");
606 63687 : ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response);
607 :
608 63687 : if (ret == NSS_STATUS_SUCCESS) {
609 60762 : ret = fill_pwent(result, &response.data.pw, &buffer,
610 : &buflen);
611 :
612 60762 : if (ret == NSS_STATUS_TRYAGAIN) {
613 0 : keep_response = true;
614 0 : *errnop = errno = ERANGE;
615 0 : goto done;
616 : }
617 : }
618 :
619 : } else {
620 :
621 : /* We've been called again */
622 :
623 0 : ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
624 :
625 0 : if (ret == NSS_STATUS_TRYAGAIN) {
626 0 : keep_response = true;
627 0 : *errnop = errno = ERANGE;
628 0 : goto done;
629 : }
630 :
631 0 : keep_response = false;
632 0 : *errnop = errno = 0;
633 : }
634 :
635 63687 : winbindd_free_response(&response);
636 63687 : done:
637 : #ifdef DEBUG_NSS
638 : fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
639 : name, nss_err_str(ret), ret);
640 : #endif
641 :
642 63687 : return ret;
643 : }
644 :
645 : /*
646 : * NSS group functions
647 : */
648 :
649 : static __thread struct winbindd_response getgrent_response;
650 :
651 : static __thread int ndx_gr_cache; /* Current index into grp cache */
652 : static __thread int num_gr_cache; /* Current size of grp cache */
653 :
654 : /* Rewind "file pointer" to start of ntdom group database */
655 :
656 : _PUBLIC_ON_LINUX_
657 : NSS_STATUS
658 36 : _nss_winbind_setgrent(void)
659 : {
660 0 : NSS_STATUS ret;
661 : #ifdef DEBUG_NSS
662 : fprintf(stderr, "[%5d]: setgrent\n", getpid());
663 : #endif
664 :
665 36 : if (num_gr_cache > 0) {
666 0 : ndx_gr_cache = num_gr_cache = 0;
667 0 : winbindd_free_response(&getgrent_response);
668 : }
669 :
670 36 : winbind_set_client_name("nss_winbind");
671 36 : ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL);
672 : #ifdef DEBUG_NSS
673 : fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
674 : nss_err_str(ret), ret);
675 : #endif
676 :
677 36 : return ret;
678 : }
679 :
680 : /* Close "file pointer" for ntdom group database */
681 :
682 : _PUBLIC_ON_LINUX_
683 : NSS_STATUS
684 36 : _nss_winbind_endgrent(void)
685 : {
686 0 : NSS_STATUS ret;
687 : #ifdef DEBUG_NSS
688 : fprintf(stderr, "[%5d]: endgrent\n", getpid());
689 : #endif
690 :
691 36 : if (num_gr_cache > 0) {
692 0 : ndx_gr_cache = num_gr_cache = 0;
693 0 : winbindd_free_response(&getgrent_response);
694 : }
695 :
696 36 : winbind_set_client_name("nss_winbind");
697 36 : ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL);
698 : #ifdef DEBUG_NSS
699 : fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
700 : nss_err_str(ret), ret);
701 : #endif
702 :
703 36 : return ret;
704 : }
705 :
706 : /* Get next entry from ntdom group database */
707 :
708 : static NSS_STATUS
709 2030 : winbind_getgrent(enum winbindd_cmd cmd,
710 : struct group *result,
711 : char *buffer, size_t buflen, int *errnop)
712 : {
713 0 : NSS_STATUS ret;
714 0 : static __thread struct winbindd_request request;
715 0 : static __thread int called_again;
716 :
717 :
718 : #ifdef DEBUG_NSS
719 : fprintf(stderr, "[%5d]: getgrent\n", getpid());
720 : #endif
721 :
722 : /* Return an entry from the cache if we have one, or if we are
723 : called again because we exceeded our static buffer. */
724 :
725 2030 : if ((ndx_gr_cache < num_gr_cache) || called_again) {
726 1968 : goto return_result;
727 : }
728 :
729 : /* Else call winbindd to get a bunch of entries */
730 :
731 62 : if (num_gr_cache > 0) {
732 0 : winbindd_free_response(&getgrent_response);
733 : }
734 :
735 62 : ZERO_STRUCT(request);
736 62 : ZERO_STRUCT(getgrent_response);
737 :
738 62 : request.data.num_entries = MAX_GETGRENT_USERS;
739 :
740 62 : winbind_set_client_name("nss_winbind");
741 62 : ret = winbindd_request_response(NULL, cmd, &request,
742 : &getgrent_response);
743 :
744 62 : if (ret == NSS_STATUS_SUCCESS) {
745 0 : struct winbindd_gr *gr_cache;
746 0 : int mem_ofs;
747 :
748 : /* Fill cache */
749 :
750 26 : ndx_gr_cache = 0;
751 26 : num_gr_cache = getgrent_response.data.num_entries;
752 :
753 : /* Return a result */
754 :
755 1994 : return_result:
756 :
757 1994 : gr_cache = (struct winbindd_gr *)
758 : getgrent_response.extra_data.data;
759 :
760 : /* Check data is valid */
761 :
762 1994 : if (gr_cache == NULL) {
763 0 : ret = NSS_STATUS_NOTFOUND;
764 0 : goto done;
765 : }
766 :
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. */
770 :
771 1994 : mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
772 : num_gr_cache * sizeof(struct winbindd_gr);
773 :
774 1994 : ret = fill_grent(result, &gr_cache[ndx_gr_cache],
775 1994 : ((char *)getgrent_response.extra_data.data)+mem_ofs,
776 : &buffer, &buflen);
777 :
778 : /* Out of memory - try again */
779 :
780 1994 : if (ret == NSS_STATUS_TRYAGAIN) {
781 0 : called_again = true;
782 0 : *errnop = errno = ERANGE;
783 0 : goto done;
784 : }
785 :
786 1994 : *errnop = 0;
787 1994 : called_again = false;
788 1994 : ndx_gr_cache++;
789 :
790 : /* If we've finished with this lot of results free cache */
791 :
792 1994 : if (ndx_gr_cache == num_gr_cache) {
793 26 : ndx_gr_cache = num_gr_cache = 0;
794 26 : winbindd_free_response(&getgrent_response);
795 : }
796 : }
797 2004 : done:
798 : #ifdef DEBUG_NSS
799 : fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
800 : nss_err_str(ret), ret);
801 : #endif
802 :
803 2030 : return ret;
804 : }
805 :
806 :
807 : _PUBLIC_ON_LINUX_
808 : NSS_STATUS
809 2030 : _nss_winbind_getgrent_r(struct group *result,
810 : char *buffer, size_t buflen, int *errnop)
811 : {
812 2030 : return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
813 : }
814 :
815 : _PUBLIC_ON_LINUX_
816 : NSS_STATUS
817 0 : _nss_winbind_getgrlst_r(struct group *result,
818 : char *buffer, size_t buflen, int *errnop)
819 : {
820 0 : return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
821 : }
822 :
823 : /* Return group struct from group name */
824 :
825 : _PUBLIC_ON_LINUX_
826 : NSS_STATUS
827 2456 : _nss_winbind_getgrnam_r(const char *name,
828 : struct group *result, char *buffer,
829 : size_t buflen, int *errnop)
830 : {
831 0 : NSS_STATUS ret;
832 0 : static __thread struct winbindd_response response;
833 0 : struct winbindd_request request;
834 0 : static __thread int keep_response;
835 :
836 : #ifdef DEBUG_NSS
837 : fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
838 : #endif
839 :
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. */
842 :
843 4912 : if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
844 :
845 : /* Call for the first time */
846 :
847 2456 : response = (struct winbindd_response) {
848 : .length = 0,
849 : };
850 2456 : request = (struct winbindd_request) {
851 : .wb_flags = WBFLAG_FROM_NSS,
852 : };
853 :
854 2456 : strncpy(request.data.groupname, name,
855 : sizeof(request.data.groupname));
856 0 : request.data.groupname
857 2456 : [sizeof(request.data.groupname) - 1] = '\0';
858 :
859 2456 : winbind_set_client_name("nss_winbind");
860 2456 : ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
861 : &request, &response);
862 :
863 2456 : if (ret == NSS_STATUS_SUCCESS) {
864 1960 : ret = fill_grent(result, &response.data.gr,
865 1960 : (char *)response.extra_data.data,
866 : &buffer, &buflen);
867 :
868 1960 : if (ret == NSS_STATUS_TRYAGAIN) {
869 0 : keep_response = true;
870 0 : *errnop = errno = ERANGE;
871 0 : goto done;
872 : }
873 : }
874 :
875 : } else {
876 :
877 : /* We've been called again */
878 :
879 0 : ret = fill_grent(result, &response.data.gr,
880 0 : (char *)response.extra_data.data, &buffer,
881 : &buflen);
882 :
883 0 : if (ret == NSS_STATUS_TRYAGAIN) {
884 0 : keep_response = true;
885 0 : *errnop = errno = ERANGE;
886 0 : goto done;
887 : }
888 :
889 0 : keep_response = false;
890 0 : *errnop = 0;
891 : }
892 :
893 2456 : winbindd_free_response(&response);
894 2456 : done:
895 : #ifdef DEBUG_NSS
896 : fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
897 : name, nss_err_str(ret), ret);
898 : #endif
899 :
900 2456 : return ret;
901 : }
902 :
903 : /* Return group struct from gid */
904 :
905 : _PUBLIC_ON_LINUX_
906 : NSS_STATUS
907 2378 : _nss_winbind_getgrgid_r(gid_t gid,
908 : struct group *result, char *buffer,
909 : size_t buflen, int *errnop)
910 : {
911 0 : NSS_STATUS ret;
912 0 : static __thread struct winbindd_response response;
913 0 : struct winbindd_request request;
914 0 : static __thread int keep_response;
915 :
916 : #ifdef DEBUG_NSS
917 : fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
918 : #endif
919 :
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. */
922 :
923 4756 : if (!keep_response || gid != response.data.gr.gr_gid) {
924 :
925 : /* Call for the first time */
926 :
927 2378 : response = (struct winbindd_response) {
928 : .length = 0,
929 : };
930 2378 : request = (struct winbindd_request) {
931 : .wb_flags = WBFLAG_FROM_NSS,
932 : };
933 :
934 :
935 2378 : request.data.gid = gid;
936 :
937 2378 : winbind_set_client_name("nss_winbind");
938 2378 : ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
939 : &request, &response);
940 :
941 2378 : if (ret == NSS_STATUS_SUCCESS) {
942 :
943 2364 : ret = fill_grent(result, &response.data.gr,
944 2364 : (char *)response.extra_data.data,
945 : &buffer, &buflen);
946 :
947 2364 : if (ret == NSS_STATUS_TRYAGAIN) {
948 0 : keep_response = true;
949 0 : *errnop = errno = ERANGE;
950 0 : goto done;
951 : }
952 : }
953 :
954 : } else {
955 :
956 : /* We've been called again */
957 :
958 0 : ret = fill_grent(result, &response.data.gr,
959 0 : (char *)response.extra_data.data, &buffer,
960 : &buflen);
961 :
962 0 : if (ret == NSS_STATUS_TRYAGAIN) {
963 0 : keep_response = true;
964 0 : *errnop = errno = ERANGE;
965 0 : goto done;
966 : }
967 :
968 0 : keep_response = false;
969 0 : *errnop = 0;
970 : }
971 :
972 2378 : winbindd_free_response(&response);
973 2378 : done:
974 : #ifdef DEBUG_NSS
975 : fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
976 : (unsigned int)gid, nss_err_str(ret), ret);
977 : #endif
978 :
979 2378 : return ret;
980 : }
981 :
982 : /* Initialise supplementary groups */
983 :
984 : _PUBLIC_ON_LINUX_
985 : NSS_STATUS
986 47284 : _nss_winbind_initgroups_dyn(const char *user, gid_t group, long int *start,
987 : long int *size, gid_t **groups, long int limit,
988 : int *errnop)
989 : {
990 0 : NSS_STATUS ret;
991 0 : struct winbindd_request request;
992 0 : struct winbindd_response response;
993 0 : int i;
994 :
995 : #ifdef DEBUG_NSS
996 : fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
997 : user, group);
998 : #endif
999 :
1000 47284 : ZERO_STRUCT(request);
1001 47284 : ZERO_STRUCT(response);
1002 :
1003 47284 : strncpy(request.data.username, user,
1004 : sizeof(request.data.username) - 1);
1005 :
1006 47284 : winbind_set_client_name("nss_winbind");
1007 47284 : ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
1008 : &request, &response);
1009 :
1010 47284 : if (ret == NSS_STATUS_SUCCESS) {
1011 400 : int num_gids = response.data.num_entries;
1012 400 : gid_t *gid_list = (gid_t *)response.extra_data.data;
1013 :
1014 : #ifdef DEBUG_NSS
1015 : fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
1016 : "and %d gids\n", getpid(),
1017 : user, num_gids);
1018 : #endif
1019 400 : if (gid_list == NULL) {
1020 0 : ret = NSS_STATUS_NOTFOUND;
1021 0 : goto done;
1022 : }
1023 :
1024 : /* Copy group list to client */
1025 :
1026 1151 : for (i = 0; i < num_gids; i++) {
1027 :
1028 : #ifdef DEBUG_NSS
1029 : fprintf(stderr, "[%5d]: initgroups %s (%d): "
1030 : "processing gid %d \n", getpid(),
1031 : user, group, gid_list[i]);
1032 : #endif
1033 :
1034 : /* Skip primary group */
1035 :
1036 751 : if (gid_list[i] == group) {
1037 400 : continue;
1038 : }
1039 :
1040 : /* Skip groups without a mapping */
1041 351 : if (gid_list[i] == (uid_t)-1) {
1042 0 : continue;
1043 : }
1044 :
1045 : /* Filled buffer ? If so, resize. */
1046 :
1047 351 : if (*start == *size) {
1048 0 : long int newsize;
1049 0 : gid_t *newgroups;
1050 :
1051 0 : newsize = 2 * (*size);
1052 0 : if (limit > 0) {
1053 0 : if (*size == limit) {
1054 0 : goto done;
1055 : }
1056 0 : if (newsize > limit) {
1057 0 : newsize = limit;
1058 : }
1059 : }
1060 :
1061 0 : newgroups = (gid_t *)
1062 0 : realloc((*groups),
1063 : newsize * sizeof(**groups));
1064 0 : if (!newgroups) {
1065 0 : *errnop = ENOMEM;
1066 0 : ret = NSS_STATUS_NOTFOUND;
1067 0 : goto done;
1068 : }
1069 0 : *groups = newgroups;
1070 0 : *size = newsize;
1071 : }
1072 :
1073 : /* Add to buffer */
1074 :
1075 351 : (*groups)[*start] = gid_list[i];
1076 351 : *start += 1;
1077 : }
1078 : }
1079 :
1080 : /* Back to your regularly scheduled programming */
1081 :
1082 47284 : done:
1083 47284 : winbindd_free_response(&response);
1084 : #ifdef DEBUG_NSS
1085 : fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
1086 : user, nss_err_str(ret), ret);
1087 : #endif
1088 :
1089 47284 : return ret;
1090 : }
|