Update.
[glibc.git] / nis / nss_compat / compat-spwd.c
blob1290346881404095a013b8b35428591c4aad0b56
1 /* Copyright (C) 1996, 1997, 1998, 1999 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.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
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 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
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>
33 #include "netgroup.h"
34 #include "nss-nisplus.h"
35 #include "nisplus-parser.h"
37 static service_user *ni = NULL;
38 static bool_t use_nisplus = FALSE; /* default: passwd_compat: nis */
39 static nis_name pwdtable = NULL; /* Name of the password table */
40 static size_t pwdtablelen = 0;
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>
48 /* Structure for remembering -@netgroup and -user members ... */
49 #define BLACKLIST_INITIAL_SIZE 512
50 #define BLACKLIST_INCREMENT 256
51 struct blacklist_t
53 char *data;
54 int current;
55 int size;
58 struct ent_t
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;
71 typedef struct ent_t ent_t;
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}};
76 /* Protect global state against multiple changers. */
77 __libc_lock_define_initialized (static, lock)
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 *);
83 static void
84 give_spwd_free (struct spwd *pwd)
86 if (pwd->sp_namp != NULL)
87 free (pwd->sp_namp);
88 if (pwd->sp_pwdp != NULL)
89 free (pwd->sp_pwdp);
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;
98 static int
99 spwd_need_buflen (struct spwd *pwd)
101 int len = 0;
103 if (pwd->sp_pwdp != NULL)
104 len += strlen (pwd->sp_pwdp) + 1;
106 return len;
109 static void
110 copy_spwd_changes (struct spwd *dest, struct spwd *src,
111 char *buffer, size_t buflen)
113 if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
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
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);
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;
144 static enum nss_status
145 internal_setspent (ent_t *ent)
147 enum nss_status status = NSS_STATUS_SUCCESS;
149 ent->nis = ent->first = ent->netgroup = 0;
151 /* If something was left over free it. */
152 if (ent->netgroup)
153 __internal_endnetgrent (&ent->netgrdata);
155 if (ent->oldkey != NULL)
157 free (ent->oldkey);
158 ent->oldkey = NULL;
159 ent->oldkeylen = 0;
162 if (ent->result != NULL)
164 nis_freeresult (ent->result);
165 ent->result = NULL;
168 if (pwdtable == NULL)
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);
174 pwdtable = malloc (sizeof (key) + len_local_dir);
175 if (pwdtable == NULL)
176 return NSS_STATUS_TRYAGAIN;
178 pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
179 key, sizeof (key) - 1),
180 local_dir, len_local_dir + 1)
181 - pwdtable) - 1;
184 if (ent->blacklist.data != NULL)
186 ent->blacklist.current = 1;
187 ent->blacklist.data[0] = '|';
188 ent->blacklist.data[1] = '\0';
190 else
191 ent->blacklist.current = 0;
193 if (ent->stream == NULL)
195 ent->stream = fopen ("/etc/shadow", "r");
197 if (ent->stream == NULL)
198 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
199 else
201 /* We have to make sure the file is `closed on exec'. */
202 int result, flags;
204 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
205 if (result >= 0)
207 flags |= FD_CLOEXEC;
208 result = fcntl (fileno (ent->stream), F_SETFD, flags);
210 if (result < 0)
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;
220 else
221 rewind (ent->stream);
223 give_spwd_free (&ent->pwd);
225 return status;
229 enum nss_status
230 _nss_compat_setspent (void)
232 enum nss_status result;
234 __libc_lock_lock (lock);
236 if (ni == NULL)
238 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
239 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
242 result = internal_setspent (&ext_ent);
244 __libc_lock_unlock (lock);
246 return result;
250 static enum nss_status
251 internal_endspent (ent_t *ent)
253 if (ent->stream != NULL)
255 fclose (ent->stream);
256 ent->stream = NULL;
259 if (ent->netgroup)
260 __internal_endnetgrent (&ent->netgrdata);
262 ent->nis = ent->first = ent->netgroup = 0;
264 if (ent->oldkey != NULL)
266 free (ent->oldkey);
267 ent->oldkey = NULL;
268 ent->oldkeylen = 0;
271 if (ent->result != NULL)
273 nis_freeresult (ent->result);
274 ent->result = NULL;
277 if (ent->blacklist.data != NULL)
279 ent->blacklist.current = 1;
280 ent->blacklist.data[0] = '|';
281 ent->blacklist.data[1] = '\0';
283 else
284 ent->blacklist.current = 0;
286 give_spwd_free (&ent->pwd);
288 return NSS_STATUS_SUCCESS;
291 enum nss_status
292 _nss_compat_endspent (void)
294 enum nss_status result;
296 __libc_lock_lock (lock);
298 result = internal_endspent (&ext_ent);
300 __libc_lock_unlock (lock);
302 return result;
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)
310 struct parser_data *data = (void *) buffer;
311 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
312 int status, outvallen;
313 size_t p2len;
315 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
317 ent->netgroup = 0;
318 ent->first = 0;
319 give_spwd_free (&ent->pwd);
320 return NSS_STATUS_UNAVAIL;
323 if (ent->first == TRUE)
325 bzero (&ent->netgrdata, sizeof (struct __netgrent));
326 __internal_setnetgrent (group, &ent->netgrdata);
327 ent->first = FALSE;
330 while (1)
332 char *saved_cursor;
333 int parse_res;
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)
341 __internal_endnetgrent (&ent->netgrdata);
342 ent->netgroup = 0;
343 give_spwd_free (&ent->pwd);
344 return NSS_STATUS_RETURN;
347 if (user == NULL || user[0] == '-')
348 continue;
350 if (domain != NULL && strcmp (ypdomain, domain) != 0)
351 continue;
353 /* If name != NULL, we are called from getpwnam */
354 if (name != NULL)
355 if (strcmp (user, name) != 0)
356 continue;
358 if (yp_match (ypdomain, "shadow.byname", user,
359 strlen (user), &outval, &outvallen)
360 != YPERR_SUCCESS)
361 continue;
363 p2len = spwd_need_buflen (&ent->pwd);
364 if (p2len > buflen)
366 free (outval);
367 *errnop = ERANGE;
368 return NSS_STATUS_TRYAGAIN;
370 p2 = buffer + (buflen - p2len);
371 buflen -= p2len;
372 if (buflen < ((size_t) outval + 1))
374 free (outval);
375 *errnop = ERANGE;
376 return NSS_STATUS_TRYAGAIN;
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)
385 ent->netgrdata.cursor = saved_cursor;
386 return NSS_STATUS_TRYAGAIN;
389 if (parse_res)
391 /* Store the User in the blacklist for the "+" at the end of
392 /etc/passwd */
393 blacklist_store_name (result->sp_namp, ent);
394 copy_spwd_changes (result, &ent->pwd, p2, p2len);
395 break;
399 return NSS_STATUS_SUCCESS;
402 static enum nss_status
403 getspent_next_nisplus_netgr (const char *name, struct spwd *result,
404 ent_t *ent, char *group, char *buffer,
405 size_t buflen, int *errnop)
407 char *ypdomain, *host, *user, *domain, *p2;
408 int status, parse_res;
409 size_t p2len;
410 nis_result *nisres;
412 /* Maybe we should use domainname here ? We need the current
413 domainname for the domain field in netgroups */
414 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
416 ent->netgroup = 0;
417 ent->first = 0;
418 give_spwd_free (&ent->pwd);
419 return NSS_STATUS_UNAVAIL;
422 if (ent->first == TRUE)
424 bzero (&ent->netgrdata, sizeof (struct __netgrent));
425 __internal_setnetgrent (group, &ent->netgrdata);
426 ent->first = FALSE;
429 while (1)
431 char *saved_cursor;
433 saved_cursor = ent->netgrdata.cursor;
434 status = __internal_getnetgrent_r (&host, &user, &domain,
435 &ent->netgrdata, buffer, buflen,
436 errnop);
437 if (status != 1)
439 __internal_endnetgrent (&ent->netgrdata);
440 ent->netgroup = 0;
441 give_spwd_free (&ent->pwd);
442 return NSS_STATUS_RETURN;
445 if (user == NULL || user[0] == '-')
446 continue;
448 if (domain != NULL && strcmp (ypdomain, domain) != 0)
449 continue;
451 /* If name != NULL, we are called from getpwnam */
452 if (name != NULL)
453 if (strcmp (user, name) != 0)
454 continue;
456 p2len = spwd_need_buflen (&ent->pwd);
457 if (p2len > buflen)
459 *errnop = ERANGE;
460 return NSS_STATUS_TRYAGAIN;
462 p2 = buffer + (buflen - p2len);
463 buflen -= p2len;
465 char buf[strlen (user) + 30 + pwdtablelen];
466 sprintf (buf, "[name=%s],%s", user, pwdtable);
467 nisres = nis_list (buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
469 if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
471 nis_freeresult (nisres);
472 continue;
474 parse_res = _nss_nisplus_parse_spent (nisres, result, buffer,
475 buflen, errnop);
476 if (parse_res == -1)
478 nis_freeresult (nisres);
479 *errnop = ERANGE;
480 return NSS_STATUS_TRYAGAIN;
482 nis_freeresult (nisres);
484 if (parse_res)
486 /* Store the User in the blacklist for the "+" at the end of
487 /etc/passwd */
488 blacklist_store_name (result->sp_namp, ent);
489 copy_spwd_changes (result, &ent->pwd, p2, p2len);
490 break;
494 return NSS_STATUS_SUCCESS;
497 static enum nss_status
498 getspent_next_nisplus (struct spwd *result, ent_t *ent, char *buffer,
499 size_t buflen, int *errnop)
501 int parse_res;
502 size_t p2len;
503 char *p2;
505 p2len = spwd_need_buflen (&ent->pwd);
506 if (p2len > buflen)
508 *errnop = ERANGE;
509 return NSS_STATUS_TRYAGAIN;
511 p2 = buffer + (buflen - p2len);
512 buflen -= p2len;
515 bool_t saved_first;
516 nis_result *saved_res;
518 if (ent->first)
520 saved_first = TRUE;
521 saved_res = ent->result;
523 ent->result = nis_first_entry (pwdtable);
524 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
526 ent->nis = 0;
527 give_spwd_free (&ent->pwd);
528 return niserr2nss (ent->result->status);
530 ent->first = FALSE;
532 else
534 nis_result *res;
536 saved_first = FALSE;
537 saved_res = ent->result;
539 res = nis_next_entry (pwdtable, &ent->result->cookie);
540 ent->result = res;
541 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
543 nis_freeresult (saved_res);
544 ent->nis = 0;
545 give_spwd_free (&ent->pwd);
546 return niserr2nss (ent->result->status);
549 parse_res = _nss_nisplus_parse_spent (ent->result, result, buffer,
550 buflen, errnop);
551 if (parse_res == -1)
553 ent->first = saved_first;
554 nis_freeresult (ent->result);
555 ent->result = saved_res;
556 *errnop = ERANGE;
557 return NSS_STATUS_TRYAGAIN;
559 else
561 if (!saved_first)
562 nis_freeresult (saved_res);
564 if (parse_res &&
565 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
566 parse_res = 0; /* if result->pw_name in blacklist,search next entry */
568 while (!parse_res);
570 copy_spwd_changes (result, &ent->pwd, p2, p2len);
572 return NSS_STATUS_SUCCESS;
576 static enum nss_status
577 getspent_next_nis (struct spwd *result, ent_t *ent,
578 char *buffer, size_t buflen, int *errnop)
580 struct parser_data *data = (void *) buffer;
581 char *domain, *outkey, *outval, *p, *p2;
582 int outkeylen, outvallen, parse_res;
583 size_t p2len;
585 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
587 ent->nis = 0;
588 give_spwd_free (&ent->pwd);
589 return NSS_STATUS_UNAVAIL;
592 p2len = spwd_need_buflen (&ent->pwd);
593 if (p2len > buflen)
595 *errnop = ERANGE;
596 return NSS_STATUS_TRYAGAIN;
598 p2 = buffer + (buflen - p2len);
599 buflen -= p2len;
602 bool_t saved_first;
603 char *saved_oldkey;
604 int saved_oldlen;
606 if (ent->first)
608 if (yp_first (domain, "shadow.byname", &outkey, &outkeylen,
609 &outval, &outvallen) != YPERR_SUCCESS)
611 ent->nis = 0;
612 give_spwd_free (&ent->pwd);
613 return NSS_STATUS_UNAVAIL;
616 if (buflen < ((size_t) outvallen + 1))
618 free (outval);
619 *errnop = ERANGE;
620 return NSS_STATUS_TRYAGAIN;
623 saved_first = TRUE;
624 saved_oldkey = ent->oldkey;
625 saved_oldlen = ent->oldkeylen;
626 ent->oldkey = outkey;
627 ent->oldkeylen = outkeylen;
628 ent->first = FALSE;
630 else
632 if (yp_next (domain, "shadow.byname", ent->oldkey, ent->oldkeylen,
633 &outkey, &outkeylen, &outval, &outvallen)
634 != YPERR_SUCCESS)
636 ent->nis = 0;
637 give_spwd_free (&ent->pwd);
638 *errnop = ENOENT;
639 return NSS_STATUS_NOTFOUND;
642 if (buflen < ((size_t) outvallen + 1))
644 free (outval);
645 *errnop = ERANGE;
646 return NSS_STATUS_TRYAGAIN;
649 saved_first = FALSE;
650 saved_oldkey = ent->oldkey;
651 saved_oldlen = ent->oldkeylen;
652 ent->oldkey = outkey;
653 ent->oldkeylen = outkeylen;
656 /* Copy the found data to our buffer */
657 p = strncpy (buffer, outval, buflen);
659 /* ...and free the data. */
660 free (outval);
662 while (isspace (*p))
663 ++p;
664 parse_res = _nss_files_parse_spent (p, result, data, buflen, errnop);
665 if (parse_res == -1)
667 free (ent->oldkey);
668 ent->oldkey = saved_oldkey;
669 ent->oldkeylen = saved_oldlen;
670 ent->first = saved_first;
671 *errnop = ERANGE;
672 return NSS_STATUS_TRYAGAIN;
674 else
676 if (!saved_first)
677 free (saved_oldkey);
679 if (parse_res &&
680 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
681 parse_res = 0;
683 while (!parse_res);
685 copy_spwd_changes (result, &ent->pwd, p2, p2len);
687 return NSS_STATUS_SUCCESS;
690 /* This function handle the +user entrys in /etc/shadow */
691 static enum nss_status
692 getspnam_plususer (const char *name, struct spwd *result, char *buffer,
693 size_t buflen, int *errnop)
695 struct parser_data *data = (void *) buffer;
696 struct spwd pwd;
697 int parse_res;
698 char *p;
699 size_t plen;
701 memset (&pwd, '\0', sizeof (struct spwd));
702 pwd.sp_warn = -1;
703 pwd.sp_inact = -1;
704 pwd.sp_expire = -1;
705 pwd.sp_flag = ~0ul;
707 copy_spwd_changes (&pwd, result, NULL, 0);
709 plen = spwd_need_buflen (&pwd);
710 if (plen > buflen)
712 *errnop = ERANGE;
713 return NSS_STATUS_TRYAGAIN;
715 p = buffer + (buflen - plen);
716 buflen -= plen;
718 if (use_nisplus) /* Do the NIS+ query here */
720 nis_result *res;
721 char buf[strlen (name) + 24 + pwdtablelen];
723 sprintf(buf, "[name=%s],%s", name, pwdtable);
724 res = nis_list(buf, 0, NULL, NULL);
725 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
727 enum nss_status status = niserr2nss (res->status);
729 nis_freeresult (res);
730 return status;
732 parse_res = _nss_nisplus_parse_spent (res, result, buffer,
733 buflen, errnop);
734 if (parse_res == -1)
736 nis_freeresult (res);
737 *errnop = ERANGE;
738 return NSS_STATUS_TRYAGAIN;
740 nis_freeresult (res);
742 else /* Use NIS */
744 char *domain, *outval, *ptr;
745 int outvallen;
747 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
749 *errnop = ENOENT;
750 return NSS_STATUS_NOTFOUND;
752 if (yp_match (domain, "shadow.byname", name, strlen (name),
753 &outval, &outvallen) != YPERR_SUCCESS)
755 *errnop = ENOENT;
756 return NSS_STATUS_NOTFOUND;
758 if (buflen < ((size_t) outvallen + 1))
760 free (outval);
761 *errnop = ERANGE;
762 return NSS_STATUS_TRYAGAIN;
765 ptr = strncpy (buffer, outval, buflen);
766 free (outval);
767 while (isspace (*ptr))
768 ptr++;
769 parse_res = _nss_files_parse_spent (ptr, result, data, buflen, errnop);
770 if (parse_res == -1)
771 return NSS_STATUS_TRYAGAIN;
774 if (parse_res)
776 copy_spwd_changes (result, &pwd, p, plen);
777 give_spwd_free (&pwd);
778 /* We found the entry. */
779 return NSS_STATUS_SUCCESS;
781 else
783 /* Give buffer the old len back */
784 buflen += plen;
785 give_spwd_free (&pwd);
787 return NSS_STATUS_RETURN;
790 static enum nss_status
791 getspent_next_file (struct spwd *result, ent_t *ent,
792 char *buffer, size_t buflen, int *errnop)
794 struct parser_data *data = (void *) buffer;
795 while (1)
797 fpos_t pos;
798 int parse_res = 0;
799 char *p;
803 fgetpos (ent->stream, &pos);
804 buffer[buflen - 1] = '\xff';
805 p = fgets (buffer, buflen, ent->stream);
806 if (p == NULL && feof (ent->stream))
808 *errnop = ENOENT;
809 return NSS_STATUS_NOTFOUND;
811 if (p == NULL || buffer[buflen - 1] != '\xff')
813 fsetpos (ent->stream, &pos);
814 *errnop = ERANGE;
815 return NSS_STATUS_TRYAGAIN;
818 /* Skip leading blanks. */
819 while (isspace (*p))
820 ++p;
822 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
823 /* Parse the line. If it is invalid, loop to
824 get the next line of the file to parse. */
825 || !(parse_res = _nss_files_parse_spent (p, result, data,
826 buflen, errnop)));
828 if (parse_res == -1)
830 /* The parser ran out of space. */
831 fsetpos (ent->stream, &pos);
832 *errnop = ERANGE;
833 return NSS_STATUS_TRYAGAIN;
836 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
837 /* This is a real entry. */
838 break;
840 /* -@netgroup */
841 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
842 && result->sp_namp[2] != '\0')
844 /* XXX Do not use fixed length buffers. */
845 char buf2[1024];
846 char *user, *host, *domain;
847 struct __netgrent netgrdata;
849 bzero (&netgrdata, sizeof (struct __netgrent));
850 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
851 while (__internal_getnetgrent_r (&host, &user, &domain,
852 &netgrdata, buf2, sizeof (buf2),
853 errnop))
855 if (user != NULL && user[0] != '-')
856 blacklist_store_name (user, ent);
858 __internal_endnetgrent (&netgrdata);
859 continue;
862 /* +@netgroup */
863 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
864 && result->sp_namp[2] != '\0')
866 int status;
868 ent->netgroup = TRUE;
869 ent->first = TRUE;
870 copy_spwd_changes (&ent->pwd, result, NULL, 0);
872 if (use_nisplus)
873 status = getspent_next_nisplus_netgr (NULL, result, ent,
874 &result->sp_namp[2],
875 buffer, buflen, errnop);
876 else
877 status = getspent_next_nis_netgr (NULL, result, ent,
878 &result->sp_namp[2],
879 buffer, buflen, errnop);
880 if (status == NSS_STATUS_RETURN)
881 continue;
882 else
883 return status;
886 /* -user */
887 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
888 && result->sp_namp[1] != '@')
890 blacklist_store_name (&result->sp_namp[1], ent);
891 continue;
894 /* +user */
895 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
896 && result->sp_namp[1] != '@')
898 enum nss_status status;
900 /* Store the User in the blacklist for the "+" at the end of
901 /etc/passwd */
902 blacklist_store_name (&result->sp_namp[1], ent);
903 status = getspnam_plususer (&result->sp_namp[1], result, buffer,
904 buflen, errnop);
905 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
906 break;
907 else
908 if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
909 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
910 continue;
911 else
913 if (status == NSS_STATUS_TRYAGAIN)
915 fsetpos (ent->stream, &pos);
916 *errnop = ERANGE;
918 return status;
922 /* +:... */
923 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
925 ent->nis = TRUE;
926 ent->first = TRUE;
927 copy_spwd_changes (&ent->pwd, result, NULL, 0);
929 if (use_nisplus)
930 return getspent_next_nisplus (result, ent, buffer, buflen, errnop);
931 else
932 return getspent_next_nis (result, ent, buffer, buflen, errnop);
936 return NSS_STATUS_SUCCESS;
940 static enum nss_status
941 internal_getspent_r (struct spwd *pw, ent_t *ent,
942 char *buffer, size_t buflen, int *errnop)
944 if (ent->netgroup)
946 int status;
948 /* We are searching members in a netgroup */
949 /* Since this is not the first call, we don't need the group name */
950 if (use_nisplus)
951 status = getspent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
952 buflen, errnop);
953 else
954 status = getspent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
955 errnop);
956 if (status == NSS_STATUS_RETURN)
957 return getspent_next_file (pw, ent, buffer, buflen, errnop);
958 else
959 return status;
961 else
962 if (ent->nis)
964 if (use_nisplus)
965 return getspent_next_nisplus (pw, ent, buffer, buflen, errnop);
966 else
967 return getspent_next_nis (pw, ent, buffer, buflen, errnop);
969 else
970 return getspent_next_file (pw, ent, buffer, buflen, errnop);
973 enum nss_status
974 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen,
975 int *errnop)
977 enum nss_status status = NSS_STATUS_SUCCESS;
979 __libc_lock_lock (lock);
981 if (ni == NULL)
983 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
984 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
987 /* Be prepared that the setspent function was not called before. */
988 if (ext_ent.stream == NULL)
989 status = internal_setspent (&ext_ent);
991 if (status == NSS_STATUS_SUCCESS)
992 status = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop);
994 __libc_lock_unlock (lock);
996 return status;
999 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1000 static enum nss_status
1001 internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
1002 char *buffer, size_t buflen, int *errnop)
1004 struct parser_data *data = (void *) buffer;
1006 while (1)
1008 fpos_t pos;
1009 char *p;
1010 int parse_res;
1014 fgetpos (ent->stream, &pos);
1015 buffer[buflen - 1] = '\xff';
1016 p = fgets (buffer, buflen, ent->stream);
1017 if (p == NULL && feof (ent->stream))
1019 *errnop = ENOENT;
1020 return NSS_STATUS_NOTFOUND;
1022 if (p == NULL || buffer[buflen - 1] != '\xff')
1024 fsetpos (ent->stream, &pos);
1025 *errnop = ERANGE;
1026 return NSS_STATUS_TRYAGAIN;
1029 /* Skip leading blanks. */
1030 while (isspace (*p))
1031 ++p;
1033 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1034 /* Parse the line. If it is invalid, loop to
1035 get the next line of the file to parse. */
1036 !(parse_res = _nss_files_parse_spent (p, result, data, buflen,
1037 errnop)));
1039 if (parse_res == -1)
1041 /* The parser ran out of space. */
1042 fsetpos (ent->stream, &pos);
1043 *errnop = ERANGE;
1044 return NSS_STATUS_TRYAGAIN;
1047 /* This is a real entry. */
1048 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
1050 if (strcmp (result->sp_namp, name) == 0)
1051 return NSS_STATUS_SUCCESS;
1052 else
1053 continue;
1056 /* -@netgroup */
1057 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
1058 && result->sp_namp[2] != '\0')
1060 /* XXX Do not use fixed length buffers. */
1061 char buf2[1024];
1062 char *user, *host, *domain;
1063 struct __netgrent netgrdata;
1065 bzero (&netgrdata, sizeof (struct __netgrent));
1066 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
1067 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1068 buf2, sizeof (buf2), errnop))
1070 if (user != NULL && user[0] != '-')
1071 if (strcmp (user, name) == 0)
1073 *errnop = ENOENT;
1074 return NSS_STATUS_NOTFOUND;
1077 __internal_endnetgrent (&netgrdata);
1078 continue;
1081 /* +@netgroup */
1082 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
1083 && result->sp_namp[2] != '\0')
1085 char *buf = strdupa (&result->sp_namp[2]);
1086 int status;
1088 ent->netgroup = TRUE;
1089 ent->first = TRUE;
1090 copy_spwd_changes (&ent->pwd, result, NULL, 0);
1094 if (use_nisplus)
1095 status = getspent_next_nisplus_netgr (name, result, ent, buf,
1096 buffer, buflen, errnop);
1097 else
1098 status = getspent_next_nis_netgr (name, result, ent, buf,
1099 buffer, buflen, errnop);
1100 if (status == NSS_STATUS_RETURN)
1101 continue;
1103 if (status == NSS_STATUS_SUCCESS
1104 && strcmp (result->sp_namp, name) == 0)
1105 return NSS_STATUS_SUCCESS;
1106 } while (status == NSS_STATUS_SUCCESS);
1107 continue;
1110 /* -user */
1111 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
1112 && result->sp_namp[1] != '@')
1114 if (strcmp (&result->sp_namp[1], name) == 0)
1115 return NSS_STATUS_NOTFOUND;
1116 else
1117 continue;
1120 /* +user */
1121 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
1122 && result->sp_namp[1] != '@')
1124 if (strcmp (name, &result->sp_namp[1]) == 0)
1126 enum nss_status status;
1128 status = getspnam_plususer (name, result, buffer, buflen,
1129 errnop);
1130 if (status == NSS_STATUS_RETURN)
1131 /* We couldn't parse the entry */
1132 return NSS_STATUS_NOTFOUND;
1133 else
1134 return status;
1138 /* +:... */
1139 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
1141 enum nss_status status;
1143 status = getspnam_plususer (name, result, buffer, buflen, errnop);
1144 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1145 return NSS_STATUS_NOTFOUND;
1146 else
1147 return status;
1150 return NSS_STATUS_SUCCESS;
1153 enum nss_status
1154 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
1155 char *buffer, size_t buflen, int *errnop)
1157 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1158 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
1159 enum nss_status status;
1161 if (name[0] == '-' || name[0] == '+')
1162 return NSS_STATUS_NOTFOUND;
1164 if (ni == NULL)
1166 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
1167 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1170 status = internal_setspent (&ent);
1171 if (status != NSS_STATUS_SUCCESS)
1172 return status;
1174 status = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
1176 internal_endspent (&ent);
1178 return status;
1181 /* Support routines for remembering -@netgroup and -user entries.
1182 The names are stored in a single string with `|' as separator. */
1183 static void
1184 blacklist_store_name (const char *name, ent_t *ent)
1186 int namelen = strlen (name);
1187 char *tmp;
1189 /* first call, setup cache */
1190 if (ent->blacklist.size == 0)
1192 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1193 ent->blacklist.data = malloc (ent->blacklist.size);
1194 if (ent->blacklist.data == NULL)
1195 return;
1196 ent->blacklist.data[0] = '|';
1197 ent->blacklist.data[1] = '\0';
1198 ent->blacklist.current = 1;
1200 else
1202 if (in_blacklist (name, namelen, ent))
1203 return; /* no duplicates */
1205 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1207 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1208 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1209 if (tmp == NULL)
1211 free (ent->blacklist.data);
1212 ent->blacklist.size = 0;
1213 return;
1215 ent->blacklist.data = tmp;
1219 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1220 *tmp++ = '|';
1221 *tmp = '\0';
1222 ent->blacklist.current += namelen + 1;
1224 return;
1227 /* Returns TRUE if ent->blacklist contains name, else FALSE. */
1228 static bool_t
1229 in_blacklist (const char *name, int namelen, ent_t *ent)
1231 char buf[namelen + 3];
1232 char *cp;
1234 if (ent->blacklist.data == NULL)
1235 return FALSE;
1237 buf[0] = '|';
1238 cp = stpcpy (&buf[1], name);
1239 *cp++= '|';
1240 *cp = '\0';
1241 return strstr (ent->blacklist.data, buf) != NULL;