]> git.ipfire.org Git - thirdparty/glibc.git/blob - nis/nss_compat/compat-spwd.c
Update to LGPL v2.1.
[thirdparty/glibc.git] / nis / nss_compat / compat-spwd.c
1 /* Copyright (C) 1996, 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20 #include <nss.h>
21 #include <errno.h>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <netdb.h>
25 #include <shadow.h>
26 #include <string.h>
27 #include <bits/libc-lock.h>
28 #include <rpcsvc/yp.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <rpcsvc/nis.h>
31 #include <nsswitch.h>
32
33 #include "netgroup.h"
34 #include "nss-nisplus.h"
35 #include "nisplus-parser.h"
36
37 static service_user *ni;
38 static bool_t use_nisplus; /* default: passwd_compat: nis */
39 static nis_name pwdtable; /* Name of the password table */
40 static size_t pwdtablelen;
41
42 /* Get the declaration of the parser function. */
43 #define ENTNAME spent
44 #define STRUCTURE spwd
45 #define EXTERN_PARSER
46 #include <nss/nss_files/files-parse.c>
47
48 /* Structure for remembering -@netgroup and -user members ... */
49 #define BLACKLIST_INITIAL_SIZE 512
50 #define BLACKLIST_INCREMENT 256
51 struct blacklist_t
52 {
53 char *data;
54 int current;
55 int size;
56 };
57
58 struct ent_t
59 {
60 bool_t netgroup;
61 bool_t nis;
62 bool_t first;
63 char *oldkey;
64 int oldkeylen;
65 nis_result *result;
66 FILE *stream;
67 struct blacklist_t blacklist;
68 struct spwd pwd;
69 struct __netgrent netgrdata;
70 };
71 typedef struct ent_t ent_t;
72
73 static ent_t ext_ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
74 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
75
76 /* Protect global state against multiple changers. */
77 __libc_lock_define_initialized (static, lock)
78
79 /* Prototypes for local functions. */
80 static void blacklist_store_name (const char *, ent_t *);
81 static int in_blacklist (const char *, int, ent_t *);
82
83 static void
84 give_spwd_free (struct spwd *pwd)
85 {
86 if (pwd->sp_namp != NULL)
87 free (pwd->sp_namp);
88 if (pwd->sp_pwdp != NULL)
89 free (pwd->sp_pwdp);
90
91 memset (pwd, '\0', sizeof (struct spwd));
92 pwd->sp_warn = -1;
93 pwd->sp_inact = -1;
94 pwd->sp_expire = -1;
95 pwd->sp_flag = ~0ul;
96 }
97
98 static int
99 spwd_need_buflen (struct spwd *pwd)
100 {
101 int len = 0;
102
103 if (pwd->sp_pwdp != NULL)
104 len += strlen (pwd->sp_pwdp) + 1;
105
106 return len;
107 }
108
109 static void
110 copy_spwd_changes (struct spwd *dest, struct spwd *src,
111 char *buffer, size_t buflen)
112 {
113 if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
114 {
115 if (buffer == NULL)
116 dest->sp_pwdp = strdup (src->sp_pwdp);
117 else if (dest->sp_pwdp &&
118 strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp))
119 strcpy (dest->sp_pwdp, src->sp_pwdp);
120 else
121 {
122 dest->sp_pwdp = buffer;
123 strcpy (dest->sp_pwdp, src->sp_pwdp);
124 buffer += strlen (dest->sp_pwdp) + 1;
125 buflen = buflen - (strlen (dest->sp_pwdp) + 1);
126 }
127 }
128 if (src->sp_lstchg != 0)
129 dest->sp_lstchg = src->sp_lstchg;
130 if (src->sp_min != 0)
131 dest->sp_min = src->sp_min;
132 if (src->sp_max != 0)
133 dest->sp_max = src->sp_max;
134 if (src->sp_warn != -1)
135 dest->sp_warn = src->sp_warn;
136 if (src->sp_inact != -1)
137 dest->sp_inact = src->sp_inact;
138 if (src->sp_expire != -1)
139 dest->sp_expire = src->sp_expire;
140 if (src->sp_flag != ~0ul)
141 dest->sp_flag = src->sp_flag;
142 }
143
144 static enum nss_status
145 internal_setspent (ent_t *ent)
146 {
147 enum nss_status status = NSS_STATUS_SUCCESS;
148
149 ent->nis = ent->first = ent->netgroup = 0;
150
151 /* If something was left over free it. */
152 if (ent->netgroup)
153 __internal_endnetgrent (&ent->netgrdata);
154
155 if (ent->oldkey != NULL)
156 {
157 free (ent->oldkey);
158 ent->oldkey = NULL;
159 ent->oldkeylen = 0;
160 }
161
162 if (ent->result != NULL)
163 {
164 nis_freeresult (ent->result);
165 ent->result = NULL;
166 }
167
168 if (pwdtable == NULL)
169 {
170 static const char key[] = "passwd.org_dir.";
171 const char *local_dir = nis_local_directory ();
172 size_t len_local_dir = strlen (local_dir);
173
174 pwdtable = malloc (sizeof (key) + len_local_dir);
175 if (pwdtable == NULL)
176 return NSS_STATUS_TRYAGAIN;
177
178 pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
179 key, sizeof (key) - 1),
180 local_dir, len_local_dir + 1)
181 - pwdtable) - 1;
182 }
183
184 if (ent->blacklist.data != NULL)
185 {
186 ent->blacklist.current = 1;
187 ent->blacklist.data[0] = '|';
188 ent->blacklist.data[1] = '\0';
189 }
190 else
191 ent->blacklist.current = 0;
192
193 if (ent->stream == NULL)
194 {
195 ent->stream = fopen ("/etc/shadow", "r");
196
197 if (ent->stream == NULL)
198 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
199 else
200 {
201 /* We have to make sure the file is `closed on exec'. */
202 int result, flags;
203
204 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
205 if (result >= 0)
206 {
207 flags |= FD_CLOEXEC;
208 result = fcntl (fileno (ent->stream), F_SETFD, flags);
209 }
210 if (result < 0)
211 {
212 /* Something went wrong. Close the stream and return a
213 failure. */
214 fclose (ent->stream);
215 ent->stream = NULL;
216 status = NSS_STATUS_UNAVAIL;
217 }
218 }
219 }
220 else
221 rewind (ent->stream);
222
223 give_spwd_free (&ent->pwd);
224
225 return status;
226 }
227
228
229 enum nss_status
230 _nss_compat_setspent (int stayopen)
231 {
232 enum nss_status result;
233
234 __libc_lock_lock (lock);
235
236 if (ni == NULL)
237 {
238 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
239 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
240 }
241
242 result = internal_setspent (&ext_ent);
243
244 __libc_lock_unlock (lock);
245
246 return result;
247 }
248
249
250 static enum nss_status
251 internal_endspent (ent_t *ent)
252 {
253 if (ent->stream != NULL)
254 {
255 fclose (ent->stream);
256 ent->stream = NULL;
257 }
258
259 if (ent->netgroup)
260 __internal_endnetgrent (&ent->netgrdata);
261
262 ent->nis = ent->first = ent->netgroup = 0;
263
264 if (ent->oldkey != NULL)
265 {
266 free (ent->oldkey);
267 ent->oldkey = NULL;
268 ent->oldkeylen = 0;
269 }
270
271 if (ent->result != NULL)
272 {
273 nis_freeresult (ent->result);
274 ent->result = NULL;
275 }
276
277 if (ent->blacklist.data != NULL)
278 {
279 ent->blacklist.current = 1;
280 ent->blacklist.data[0] = '|';
281 ent->blacklist.data[1] = '\0';
282 }
283 else
284 ent->blacklist.current = 0;
285
286 give_spwd_free (&ent->pwd);
287
288 return NSS_STATUS_SUCCESS;
289 }
290
291 enum nss_status
292 _nss_compat_endspent (void)
293 {
294 enum nss_status result;
295
296 __libc_lock_lock (lock);
297
298 result = internal_endspent (&ext_ent);
299
300 __libc_lock_unlock (lock);
301
302 return result;
303 }
304
305
306 static enum nss_status
307 getspent_next_nis_netgr (const char *name, struct spwd *result, ent_t *ent,
308 char *group, char *buffer, size_t buflen, int *errnop)
309 {
310 struct parser_data *data = (void *) buffer;
311 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
312 int status, outvallen;
313 size_t p2len;
314
315 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
316 {
317 ent->netgroup = 0;
318 ent->first = 0;
319 give_spwd_free (&ent->pwd);
320 return NSS_STATUS_UNAVAIL;
321 }
322
323 if (ent->first == TRUE)
324 {
325 bzero (&ent->netgrdata, sizeof (struct __netgrent));
326 __internal_setnetgrent (group, &ent->netgrdata);
327 ent->first = FALSE;
328 }
329
330 while (1)
331 {
332 char *saved_cursor;
333 int parse_res;
334
335 saved_cursor = ent->netgrdata.cursor;
336 status = __internal_getnetgrent_r (&host, &user, &domain,
337 &ent->netgrdata, buffer, buflen,
338 errnop);
339 if (status != 1)
340 {
341 __internal_endnetgrent (&ent->netgrdata);
342 ent->netgroup = 0;
343 give_spwd_free (&ent->pwd);
344 return NSS_STATUS_RETURN;
345 }
346
347 if (user == NULL || user[0] == '-')
348 continue;
349
350 if (domain != NULL && strcmp (ypdomain, domain) != 0)
351 continue;
352
353 /* If name != NULL, we are called from getpwnam */
354 if (name != NULL)
355 if (strcmp (user, name) != 0)
356 continue;
357
358 if (yp_match (ypdomain, "shadow.byname", user,
359 strlen (user), &outval, &outvallen)
360 != YPERR_SUCCESS)
361 continue;
362
363 p2len = spwd_need_buflen (&ent->pwd);
364 if (p2len > buflen)
365 {
366 free (outval);
367 *errnop = ERANGE;
368 return NSS_STATUS_TRYAGAIN;
369 }
370 p2 = buffer + (buflen - p2len);
371 buflen -= p2len;
372 if (buflen < ((size_t) outval + 1))
373 {
374 free (outval);
375 *errnop = ERANGE;
376 return NSS_STATUS_TRYAGAIN;
377 }
378 p = strncpy (buffer, outval, buflen);
379 while (isspace (*p))
380 p++;
381 free (outval);
382 parse_res = _nss_files_parse_spent (p, result, data, buflen, errnop);
383 if (parse_res == -1)
384 {
385 ent->netgrdata.cursor = saved_cursor;
386 *errnop = ERANGE;
387 return NSS_STATUS_TRYAGAIN;
388 }
389
390 if (parse_res)
391 {
392 /* Store the User in the blacklist for the "+" at the end of
393 /etc/passwd */
394 blacklist_store_name (result->sp_namp, ent);
395 copy_spwd_changes (result, &ent->pwd, p2, p2len);
396 break;
397 }
398 }
399
400 return NSS_STATUS_SUCCESS;
401 }
402
403 static enum nss_status
404 getspent_next_nisplus_netgr (const char *name, struct spwd *result,
405 ent_t *ent, char *group, char *buffer,
406 size_t buflen, int *errnop)
407 {
408 char *ypdomain, *host, *user, *domain, *p2;
409 int status, parse_res;
410 size_t p2len;
411 nis_result *nisres;
412
413 /* Maybe we should use domainname here ? We need the current
414 domainname for the domain field in netgroups */
415 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
416 {
417 ent->netgroup = 0;
418 ent->first = 0;
419 give_spwd_free (&ent->pwd);
420 return NSS_STATUS_UNAVAIL;
421 }
422
423 if (ent->first == TRUE)
424 {
425 bzero (&ent->netgrdata, sizeof (struct __netgrent));
426 __internal_setnetgrent (group, &ent->netgrdata);
427 ent->first = FALSE;
428 }
429
430 while (1)
431 {
432 char *saved_cursor;
433
434 saved_cursor = ent->netgrdata.cursor;
435 status = __internal_getnetgrent_r (&host, &user, &domain,
436 &ent->netgrdata, buffer, buflen,
437 errnop);
438 if (status != 1)
439 {
440 __internal_endnetgrent (&ent->netgrdata);
441 ent->netgroup = 0;
442 give_spwd_free (&ent->pwd);
443 return NSS_STATUS_RETURN;
444 }
445
446 if (user == NULL || user[0] == '-')
447 continue;
448
449 if (domain != NULL && strcmp (ypdomain, domain) != 0)
450 continue;
451
452 /* If name != NULL, we are called from getpwnam */
453 if (name != NULL)
454 if (strcmp (user, name) != 0)
455 continue;
456
457 p2len = spwd_need_buflen (&ent->pwd);
458 if (p2len > buflen)
459 {
460 *errnop = ERANGE;
461 return NSS_STATUS_TRYAGAIN;
462 }
463 p2 = buffer + (buflen - p2len);
464 buflen -= p2len;
465 {
466 char buf[strlen (user) + 30 + pwdtablelen];
467 sprintf (buf, "[name=%s],%s", user, pwdtable);
468 nisres = nis_list (buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
469 }
470 if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
471 {
472 nis_freeresult (nisres);
473 continue;
474 }
475 parse_res = _nss_nisplus_parse_spent (nisres, result, buffer,
476 buflen, errnop);
477 if (parse_res == -1)
478 {
479 nis_freeresult (nisres);
480 *errnop = ERANGE;
481 return NSS_STATUS_TRYAGAIN;
482 }
483 nis_freeresult (nisres);
484
485 if (parse_res)
486 {
487 /* Store the User in the blacklist for the "+" at the end of
488 /etc/passwd */
489 blacklist_store_name (result->sp_namp, ent);
490 copy_spwd_changes (result, &ent->pwd, p2, p2len);
491 break;
492 }
493 }
494
495 return NSS_STATUS_SUCCESS;
496 }
497
498 static enum nss_status
499 getspent_next_nisplus (struct spwd *result, ent_t *ent, char *buffer,
500 size_t buflen, int *errnop)
501 {
502 int parse_res;
503 size_t p2len;
504 char *p2;
505
506 p2len = spwd_need_buflen (&ent->pwd);
507 if (p2len > buflen)
508 {
509 *errnop = ERANGE;
510 return NSS_STATUS_TRYAGAIN;
511 }
512 p2 = buffer + (buflen - p2len);
513 buflen -= p2len;
514 do
515 {
516 bool_t saved_first;
517 nis_result *saved_res;
518
519 if (ent->first)
520 {
521 saved_first = TRUE;
522 saved_res = ent->result;
523
524 ent->result = nis_first_entry (pwdtable);
525 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
526 {
527 ent->nis = 0;
528 give_spwd_free (&ent->pwd);
529 return niserr2nss (ent->result->status);
530 }
531 ent->first = FALSE;
532 }
533 else
534 {
535 nis_result *res;
536
537 saved_first = FALSE;
538 saved_res = ent->result;
539
540 res = nis_next_entry (pwdtable, &ent->result->cookie);
541 ent->result = res;
542 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
543 {
544 nis_freeresult (saved_res);
545 ent->nis = 0;
546 give_spwd_free (&ent->pwd);
547 return niserr2nss (ent->result->status);
548 }
549 }
550 parse_res = _nss_nisplus_parse_spent (ent->result, result, buffer,
551 buflen, errnop);
552 if (parse_res == -1)
553 {
554 ent->first = saved_first;
555 nis_freeresult (ent->result);
556 ent->result = saved_res;
557 *errnop = ERANGE;
558 return NSS_STATUS_TRYAGAIN;
559 }
560 else
561 {
562 if (!saved_first)
563 nis_freeresult (saved_res);
564 }
565 if (parse_res &&
566 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
567 parse_res = 0; /* if result->pw_name in blacklist,search next entry */
568 }
569 while (!parse_res);
570
571 copy_spwd_changes (result, &ent->pwd, p2, p2len);
572
573 return NSS_STATUS_SUCCESS;
574 }
575
576
577 static enum nss_status
578 getspent_next_nis (struct spwd *result, ent_t *ent,
579 char *buffer, size_t buflen, int *errnop)
580 {
581 struct parser_data *data = (void *) buffer;
582 char *domain, *outkey, *outval, *p, *p2;
583 int outkeylen, outvallen, parse_res;
584 size_t p2len;
585
586 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
587 {
588 ent->nis = 0;
589 give_spwd_free (&ent->pwd);
590 return NSS_STATUS_UNAVAIL;
591 }
592
593 p2len = spwd_need_buflen (&ent->pwd);
594 if (p2len > buflen)
595 {
596 *errnop = ERANGE;
597 return NSS_STATUS_TRYAGAIN;
598 }
599 p2 = buffer + (buflen - p2len);
600 buflen -= p2len;
601 do
602 {
603 bool_t saved_first;
604 char *saved_oldkey;
605 int saved_oldlen;
606
607 if (ent->first)
608 {
609 if (yp_first (domain, "shadow.byname", &outkey, &outkeylen,
610 &outval, &outvallen) != YPERR_SUCCESS)
611 {
612 ent->nis = 0;
613 give_spwd_free (&ent->pwd);
614 return NSS_STATUS_UNAVAIL;
615 }
616
617 if (buflen < ((size_t) outvallen + 1))
618 {
619 free (outval);
620 *errnop = ERANGE;
621 return NSS_STATUS_TRYAGAIN;
622 }
623
624 saved_first = TRUE;
625 saved_oldkey = ent->oldkey;
626 saved_oldlen = ent->oldkeylen;
627 ent->oldkey = outkey;
628 ent->oldkeylen = outkeylen;
629 ent->first = FALSE;
630 }
631 else
632 {
633 if (yp_next (domain, "shadow.byname", ent->oldkey, ent->oldkeylen,
634 &outkey, &outkeylen, &outval, &outvallen)
635 != YPERR_SUCCESS)
636 {
637 ent->nis = 0;
638 give_spwd_free (&ent->pwd);
639 *errnop = ENOENT;
640 return NSS_STATUS_NOTFOUND;
641 }
642
643 if (buflen < ((size_t) outvallen + 1))
644 {
645 free (outval);
646 *errnop = ERANGE;
647 return NSS_STATUS_TRYAGAIN;
648 }
649
650 saved_first = FALSE;
651 saved_oldkey = ent->oldkey;
652 saved_oldlen = ent->oldkeylen;
653 ent->oldkey = outkey;
654 ent->oldkeylen = outkeylen;
655 }
656
657 /* Copy the found data to our buffer */
658 p = strncpy (buffer, outval, buflen);
659
660 /* ...and free the data. */
661 free (outval);
662
663 while (isspace (*p))
664 ++p;
665 parse_res = _nss_files_parse_spent (p, result, data, buflen, errnop);
666 if (parse_res == -1)
667 {
668 free (ent->oldkey);
669 ent->oldkey = saved_oldkey;
670 ent->oldkeylen = saved_oldlen;
671 ent->first = saved_first;
672 *errnop = ERANGE;
673 return NSS_STATUS_TRYAGAIN;
674 }
675 else
676 {
677 if (!saved_first)
678 free (saved_oldkey);
679 }
680 if (parse_res &&
681 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
682 parse_res = 0;
683 }
684 while (!parse_res);
685
686 copy_spwd_changes (result, &ent->pwd, p2, p2len);
687
688 return NSS_STATUS_SUCCESS;
689 }
690
691 /* This function handle the +user entrys in /etc/shadow */
692 static enum nss_status
693 getspnam_plususer (const char *name, struct spwd *result, char *buffer,
694 size_t buflen, int *errnop)
695 {
696 struct parser_data *data = (void *) buffer;
697 struct spwd pwd;
698 int parse_res;
699 char *p;
700 size_t plen;
701
702 memset (&pwd, '\0', sizeof (struct spwd));
703 pwd.sp_warn = -1;
704 pwd.sp_inact = -1;
705 pwd.sp_expire = -1;
706 pwd.sp_flag = ~0ul;
707
708 copy_spwd_changes (&pwd, result, NULL, 0);
709
710 plen = spwd_need_buflen (&pwd);
711 if (plen > buflen)
712 {
713 *errnop = ERANGE;
714 return NSS_STATUS_TRYAGAIN;
715 }
716 p = buffer + (buflen - plen);
717 buflen -= plen;
718
719 if (use_nisplus) /* Do the NIS+ query here */
720 {
721 nis_result *res;
722 char buf[strlen (name) + 24 + pwdtablelen];
723
724 sprintf(buf, "[name=%s],%s", name, pwdtable);
725 res = nis_list(buf, 0, NULL, NULL);
726 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
727 {
728 enum nss_status status = niserr2nss (res->status);
729
730 nis_freeresult (res);
731 return status;
732 }
733 parse_res = _nss_nisplus_parse_spent (res, result, buffer,
734 buflen, errnop);
735 if (parse_res == -1)
736 {
737 nis_freeresult (res);
738 *errnop = ERANGE;
739 return NSS_STATUS_TRYAGAIN;
740 }
741 nis_freeresult (res);
742 }
743 else /* Use NIS */
744 {
745 char *domain, *outval, *ptr;
746 int outvallen;
747
748 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
749 {
750 *errnop = ENOENT;
751 return NSS_STATUS_NOTFOUND;
752 }
753 if (yp_match (domain, "shadow.byname", name, strlen (name),
754 &outval, &outvallen) != YPERR_SUCCESS)
755 {
756 *errnop = ENOENT;
757 return NSS_STATUS_NOTFOUND;
758 }
759 if (buflen < ((size_t) outvallen + 1))
760 {
761 free (outval);
762 *errnop = ERANGE;
763 return NSS_STATUS_TRYAGAIN;
764 }
765
766 ptr = strncpy (buffer, outval, buflen);
767 free (outval);
768 while (isspace (*ptr))
769 ptr++;
770 parse_res = _nss_files_parse_spent (ptr, result, data, buflen, errnop);
771 if (parse_res == -1)
772 return NSS_STATUS_TRYAGAIN;
773 }
774
775 if (parse_res)
776 {
777 copy_spwd_changes (result, &pwd, p, plen);
778 give_spwd_free (&pwd);
779 /* We found the entry. */
780 return NSS_STATUS_SUCCESS;
781 }
782 else
783 {
784 /* Give buffer the old len back */
785 buflen += plen;
786 give_spwd_free (&pwd);
787 }
788 return NSS_STATUS_RETURN;
789 }
790
791 static enum nss_status
792 getspent_next_file (struct spwd *result, ent_t *ent,
793 char *buffer, size_t buflen, int *errnop)
794 {
795 struct parser_data *data = (void *) buffer;
796 while (1)
797 {
798 fpos_t pos;
799 int parse_res = 0;
800 char *p;
801
802 do
803 {
804 fgetpos (ent->stream, &pos);
805 buffer[buflen - 1] = '\xff';
806 p = fgets (buffer, buflen, ent->stream);
807 if (p == NULL && feof (ent->stream))
808 {
809 *errnop = ENOENT;
810 return NSS_STATUS_NOTFOUND;
811 }
812 if (p == NULL || buffer[buflen - 1] != '\xff')
813 {
814 fsetpos (ent->stream, &pos);
815 *errnop = ERANGE;
816 return NSS_STATUS_TRYAGAIN;
817 }
818
819 /* Skip leading blanks. */
820 while (isspace (*p))
821 ++p;
822 }
823 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
824 /* Parse the line. If it is invalid, loop to
825 get the next line of the file to parse. */
826 || !(parse_res = _nss_files_parse_spent (p, result, data,
827 buflen, errnop)));
828
829 if (parse_res == -1)
830 {
831 /* The parser ran out of space. */
832 fsetpos (ent->stream, &pos);
833 *errnop = ERANGE;
834 return NSS_STATUS_TRYAGAIN;
835 }
836
837 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
838 /* This is a real entry. */
839 break;
840
841 /* -@netgroup */
842 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
843 && result->sp_namp[2] != '\0')
844 {
845 /* XXX Do not use fixed length buffers. */
846 char buf2[1024];
847 char *user, *host, *domain;
848 struct __netgrent netgrdata;
849
850 bzero (&netgrdata, sizeof (struct __netgrent));
851 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
852 while (__internal_getnetgrent_r (&host, &user, &domain,
853 &netgrdata, buf2, sizeof (buf2),
854 errnop))
855 {
856 if (user != NULL && user[0] != '-')
857 blacklist_store_name (user, ent);
858 }
859 __internal_endnetgrent (&netgrdata);
860 continue;
861 }
862
863 /* +@netgroup */
864 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
865 && result->sp_namp[2] != '\0')
866 {
867 int status;
868
869 ent->netgroup = TRUE;
870 ent->first = TRUE;
871 copy_spwd_changes (&ent->pwd, result, NULL, 0);
872
873 if (use_nisplus)
874 status = getspent_next_nisplus_netgr (NULL, result, ent,
875 &result->sp_namp[2],
876 buffer, buflen, errnop);
877 else
878 status = getspent_next_nis_netgr (NULL, result, ent,
879 &result->sp_namp[2],
880 buffer, buflen, errnop);
881 if (status == NSS_STATUS_RETURN)
882 continue;
883 else
884 return status;
885 }
886
887 /* -user */
888 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
889 && result->sp_namp[1] != '@')
890 {
891 blacklist_store_name (&result->sp_namp[1], ent);
892 continue;
893 }
894
895 /* +user */
896 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
897 && result->sp_namp[1] != '@')
898 {
899 enum nss_status status;
900
901 /* Store the User in the blacklist for the "+" at the end of
902 /etc/passwd */
903 blacklist_store_name (&result->sp_namp[1], ent);
904 status = getspnam_plususer (&result->sp_namp[1], result, buffer,
905 buflen, errnop);
906 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
907 break;
908 else
909 if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
910 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
911 continue;
912 else
913 {
914 if (status == NSS_STATUS_TRYAGAIN)
915 {
916 fsetpos (ent->stream, &pos);
917 *errnop = ERANGE;
918 }
919 return status;
920 }
921 }
922
923 /* +:... */
924 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
925 {
926 ent->nis = TRUE;
927 ent->first = TRUE;
928 copy_spwd_changes (&ent->pwd, result, NULL, 0);
929
930 if (use_nisplus)
931 return getspent_next_nisplus (result, ent, buffer, buflen, errnop);
932 else
933 return getspent_next_nis (result, ent, buffer, buflen, errnop);
934 }
935 }
936
937 return NSS_STATUS_SUCCESS;
938 }
939
940
941 static enum nss_status
942 internal_getspent_r (struct spwd *pw, ent_t *ent,
943 char *buffer, size_t buflen, int *errnop)
944 {
945 if (ent->netgroup)
946 {
947 int status;
948
949 /* We are searching members in a netgroup */
950 /* Since this is not the first call, we don't need the group name */
951 if (use_nisplus)
952 status = getspent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
953 buflen, errnop);
954 else
955 status = getspent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
956 errnop);
957 if (status == NSS_STATUS_RETURN)
958 return getspent_next_file (pw, ent, buffer, buflen, errnop);
959 else
960 return status;
961 }
962 else
963 if (ent->nis)
964 {
965 if (use_nisplus)
966 return getspent_next_nisplus (pw, ent, buffer, buflen, errnop);
967 else
968 return getspent_next_nis (pw, ent, buffer, buflen, errnop);
969 }
970 else
971 return getspent_next_file (pw, ent, buffer, buflen, errnop);
972 }
973
974 enum nss_status
975 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen,
976 int *errnop)
977 {
978 enum nss_status status = NSS_STATUS_SUCCESS;
979
980 __libc_lock_lock (lock);
981
982 if (ni == NULL)
983 {
984 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
985 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
986 }
987
988 /* Be prepared that the setspent function was not called before. */
989 if (ext_ent.stream == NULL)
990 status = internal_setspent (&ext_ent);
991
992 if (status == NSS_STATUS_SUCCESS)
993 status = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop);
994
995 __libc_lock_unlock (lock);
996
997 return status;
998 }
999
1000 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1001 static enum nss_status
1002 internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
1003 char *buffer, size_t buflen, int *errnop)
1004 {
1005 struct parser_data *data = (void *) buffer;
1006
1007 while (1)
1008 {
1009 fpos_t pos;
1010 char *p;
1011 int parse_res;
1012
1013 do
1014 {
1015 fgetpos (ent->stream, &pos);
1016 buffer[buflen - 1] = '\xff';
1017 p = fgets (buffer, buflen, ent->stream);
1018 if (p == NULL && feof (ent->stream))
1019 {
1020 *errnop = ENOENT;
1021 return NSS_STATUS_NOTFOUND;
1022 }
1023 if (p == NULL || buffer[buflen - 1] != '\xff')
1024 {
1025 fsetpos (ent->stream, &pos);
1026 *errnop = ERANGE;
1027 return NSS_STATUS_TRYAGAIN;
1028 }
1029
1030 /* Skip leading blanks. */
1031 while (isspace (*p))
1032 ++p;
1033 }
1034 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1035 /* Parse the line. If it is invalid, loop to
1036 get the next line of the file to parse. */
1037 !(parse_res = _nss_files_parse_spent (p, result, data, buflen,
1038 errnop)));
1039
1040 if (parse_res == -1)
1041 {
1042 /* The parser ran out of space. */
1043 fsetpos (ent->stream, &pos);
1044 *errnop = ERANGE;
1045 return NSS_STATUS_TRYAGAIN;
1046 }
1047
1048 /* This is a real entry. */
1049 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
1050 {
1051 if (strcmp (result->sp_namp, name) == 0)
1052 return NSS_STATUS_SUCCESS;
1053 else
1054 continue;
1055 }
1056
1057 /* -@netgroup */
1058 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
1059 && result->sp_namp[2] != '\0')
1060 {
1061 /* XXX Do not use fixed length buffers. */
1062 char buf2[1024];
1063 char *user, *host, *domain;
1064 struct __netgrent netgrdata;
1065
1066 bzero (&netgrdata, sizeof (struct __netgrent));
1067 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
1068 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1069 buf2, sizeof (buf2), errnop))
1070 {
1071 if (user != NULL && user[0] != '-')
1072 if (strcmp (user, name) == 0)
1073 {
1074 *errnop = ENOENT;
1075 return NSS_STATUS_NOTFOUND;
1076 }
1077 }
1078 __internal_endnetgrent (&netgrdata);
1079 continue;
1080 }
1081
1082 /* +@netgroup */
1083 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
1084 && result->sp_namp[2] != '\0')
1085 {
1086 char *buf = strdupa (&result->sp_namp[2]);
1087 int status;
1088
1089 ent->netgroup = TRUE;
1090 ent->first = TRUE;
1091 copy_spwd_changes (&ent->pwd, result, NULL, 0);
1092
1093 do
1094 {
1095 if (use_nisplus)
1096 status = getspent_next_nisplus_netgr (name, result, ent, buf,
1097 buffer, buflen, errnop);
1098 else
1099 status = getspent_next_nis_netgr (name, result, ent, buf,
1100 buffer, buflen, errnop);
1101 if (status == NSS_STATUS_RETURN)
1102 continue;
1103
1104 if (status == NSS_STATUS_SUCCESS
1105 && strcmp (result->sp_namp, name) == 0)
1106 return NSS_STATUS_SUCCESS;
1107 } while (status == NSS_STATUS_SUCCESS);
1108 continue;
1109 }
1110
1111 /* -user */
1112 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
1113 && result->sp_namp[1] != '@')
1114 {
1115 if (strcmp (&result->sp_namp[1], name) == 0)
1116 {
1117 *errnop = ENOENT;
1118 return NSS_STATUS_NOTFOUND;
1119 }
1120 else
1121 continue;
1122 }
1123
1124 /* +user */
1125 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
1126 && result->sp_namp[1] != '@')
1127 {
1128 if (strcmp (name, &result->sp_namp[1]) == 0)
1129 {
1130 enum nss_status status;
1131
1132 status = getspnam_plususer (name, result, buffer, buflen,
1133 errnop);
1134 if (status == NSS_STATUS_RETURN)
1135 {
1136 /* We couldn't parse the entry */
1137 *errnop = ENOENT;
1138 return NSS_STATUS_NOTFOUND;
1139 }
1140 else
1141 return status;
1142 }
1143 }
1144
1145 /* +:... */
1146 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
1147 {
1148 enum nss_status status;
1149
1150 status = getspnam_plususer (name, result, buffer, buflen, errnop);
1151 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1152 {
1153 *errnop = ENOENT;
1154 return NSS_STATUS_NOTFOUND;
1155 }
1156 else
1157 return status;
1158 }
1159 }
1160 return NSS_STATUS_SUCCESS;
1161 }
1162
1163 enum nss_status
1164 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
1165 char *buffer, size_t buflen, int *errnop)
1166 {
1167 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1168 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
1169 enum nss_status status;
1170
1171 if (name[0] == '-' || name[0] == '+')
1172 {
1173 *errnop = ENOENT;
1174 return NSS_STATUS_NOTFOUND;
1175 }
1176
1177 if (ni == NULL)
1178 {
1179 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
1180 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1181 }
1182
1183 status = internal_setspent (&ent);
1184 if (status != NSS_STATUS_SUCCESS)
1185 return status;
1186
1187 status = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
1188
1189 internal_endspent (&ent);
1190
1191 return status;
1192 }
1193
1194 /* Support routines for remembering -@netgroup and -user entries.
1195 The names are stored in a single string with `|' as separator. */
1196 static void
1197 blacklist_store_name (const char *name, ent_t *ent)
1198 {
1199 int namelen = strlen (name);
1200 char *tmp;
1201
1202 /* first call, setup cache */
1203 if (ent->blacklist.size == 0)
1204 {
1205 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1206 ent->blacklist.data = malloc (ent->blacklist.size);
1207 if (ent->blacklist.data == NULL)
1208 return;
1209 ent->blacklist.data[0] = '|';
1210 ent->blacklist.data[1] = '\0';
1211 ent->blacklist.current = 1;
1212 }
1213 else
1214 {
1215 if (in_blacklist (name, namelen, ent))
1216 return; /* no duplicates */
1217
1218 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1219 {
1220 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1221 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1222 if (tmp == NULL)
1223 {
1224 free (ent->blacklist.data);
1225 ent->blacklist.size = 0;
1226 return;
1227 }
1228 ent->blacklist.data = tmp;
1229 }
1230 }
1231
1232 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1233 *tmp++ = '|';
1234 *tmp = '\0';
1235 ent->blacklist.current += namelen + 1;
1236
1237 return;
1238 }
1239
1240 /* Returns TRUE if ent->blacklist contains name, else FALSE. */
1241 static bool_t
1242 in_blacklist (const char *name, int namelen, ent_t *ent)
1243 {
1244 char buf[namelen + 3];
1245 char *cp;
1246
1247 if (ent->blacklist.data == NULL)
1248 return FALSE;
1249
1250 buf[0] = '|';
1251 cp = stpcpy (&buf[1], name);
1252 *cp++= '|';
1253 *cp = '\0';
1254 return strstr (ent->blacklist.data, buf) != NULL;
1255 }