Update.
[glibc.git] / nis / nss_compat / compat-spwd.c
blob1d4216393a87d652c263df9fd4d201f8cbc3ea95
1 /* Copyright (C) 1996, 1997, 1998 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));
94 static int
95 spwd_need_buflen (struct spwd *pwd)
97 int len = 0;
99 if (pwd->sp_pwdp != NULL)
100 len += strlen (pwd->sp_pwdp) + 1;
102 return len;
105 static void
106 copy_spwd_changes (struct spwd *dest, struct spwd *src,
107 char *buffer, size_t buflen)
109 if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
111 if (buffer == NULL)
112 dest->sp_pwdp = strdup (src->sp_pwdp);
113 else if (dest->sp_pwdp &&
114 strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp))
115 strcpy (dest->sp_pwdp, src->sp_pwdp);
116 else
118 dest->sp_pwdp = buffer;
119 strcpy (dest->sp_pwdp, src->sp_pwdp);
120 buffer += strlen (dest->sp_pwdp) + 1;
121 buflen = buflen - (strlen (dest->sp_pwdp) + 1);
124 if (src->sp_lstchg != 0)
125 dest->sp_lstchg = src->sp_lstchg;
126 if (src->sp_min != 0)
127 dest->sp_min = src->sp_min;
128 if (src->sp_max != 0)
129 dest->sp_max = src->sp_max;
130 if (src->sp_warn != 0)
131 dest->sp_warn = src->sp_warn;
132 if (src->sp_inact != 0)
133 dest->sp_inact = src->sp_inact;
134 if (src->sp_expire != 0)
135 dest->sp_expire = src->sp_expire;
136 if (src->sp_flag != 0)
137 dest->sp_flag = src->sp_flag;
140 static enum nss_status
141 internal_setspent (ent_t *ent)
143 enum nss_status status = NSS_STATUS_SUCCESS;
145 ent->nis = ent->first = ent->netgroup = 0;
147 /* If something was left over free it. */
148 if (ent->netgroup)
149 __internal_endnetgrent (&ent->netgrdata);
151 if (ent->oldkey != NULL)
153 free (ent->oldkey);
154 ent->oldkey = NULL;
155 ent->oldkeylen = 0;
158 if (ent->result != NULL)
160 nis_freeresult (ent->result);
161 ent->result = NULL;
164 if (pwdtable == NULL)
166 static const char key[] = "passwd.org_dir.";
167 const char *local_dir = nis_local_directory ();
168 size_t len_local_dir = strlen (local_dir);
170 pwdtable = malloc (sizeof (key) + len_local_dir);
171 if (pwdtable == NULL)
172 return NSS_STATUS_TRYAGAIN;
174 pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
175 key, sizeof (key) - 1),
176 local_dir, len_local_dir + 1)
177 - pwdtable) - 1;
180 if (ent->blacklist.data != NULL)
182 ent->blacklist.current = 1;
183 ent->blacklist.data[0] = '|';
184 ent->blacklist.data[1] = '\0';
186 else
187 ent->blacklist.current = 0;
189 if (ent->stream == NULL)
191 ent->stream = fopen ("/etc/shadow", "r");
193 if (ent->stream == NULL)
194 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
195 else
197 /* We have to make sure the file is `closed on exec'. */
198 int result, flags;
200 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
201 if (result >= 0)
203 flags |= FD_CLOEXEC;
204 result = fcntl (fileno (ent->stream), F_SETFD, flags);
206 if (result < 0)
208 /* Something went wrong. Close the stream and return a
209 failure. */
210 fclose (ent->stream);
211 ent->stream = NULL;
212 status = NSS_STATUS_UNAVAIL;
216 else
217 rewind (ent->stream);
219 give_spwd_free (&ent->pwd);
221 return status;
225 enum nss_status
226 _nss_compat_setspent (void)
228 enum nss_status result;
230 __libc_lock_lock (lock);
232 if (ni == NULL)
234 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
235 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
238 result = internal_setspent (&ext_ent);
240 __libc_lock_unlock (lock);
242 return result;
246 static enum nss_status
247 internal_endspent (ent_t *ent)
249 if (ent->stream != NULL)
251 fclose (ent->stream);
252 ent->stream = NULL;
255 if (ent->netgroup)
256 __internal_endnetgrent (&ent->netgrdata);
258 ent->nis = ent->first = ent->netgroup = 0;
260 if (ent->oldkey != NULL)
262 free (ent->oldkey);
263 ent->oldkey = NULL;
264 ent->oldkeylen = 0;
267 if (ent->result != NULL)
269 nis_freeresult (ent->result);
270 ent->result = NULL;
273 if (ent->blacklist.data != NULL)
275 ent->blacklist.current = 1;
276 ent->blacklist.data[0] = '|';
277 ent->blacklist.data[1] = '\0';
279 else
280 ent->blacklist.current = 0;
282 give_spwd_free (&ent->pwd);
284 return NSS_STATUS_SUCCESS;
287 enum nss_status
288 _nss_compat_endspent (void)
290 enum nss_status result;
292 __libc_lock_lock (lock);
294 result = internal_endspent (&ext_ent);
296 __libc_lock_unlock (lock);
298 return result;
302 static enum nss_status
303 getspent_next_nis_netgr (const char *name, struct spwd *result, ent_t *ent,
304 char *group, char *buffer, size_t buflen, int *errnop)
306 struct parser_data *data = (void *) buffer;
307 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
308 int status, outvallen;
309 size_t p2len;
311 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
313 ent->netgroup = 0;
314 ent->first = 0;
315 give_spwd_free (&ent->pwd);
316 return NSS_STATUS_UNAVAIL;
319 if (ent->first == TRUE)
321 bzero (&ent->netgrdata, sizeof (struct __netgrent));
322 __internal_setnetgrent (group, &ent->netgrdata);
323 ent->first = FALSE;
326 while (1)
328 char *saved_cursor;
329 int parse_res;
331 saved_cursor = ent->netgrdata.cursor;
332 status = __internal_getnetgrent_r (&host, &user, &domain,
333 &ent->netgrdata, buffer, buflen,
334 errnop);
335 if (status != 1)
337 __internal_endnetgrent (&ent->netgrdata);
338 ent->netgroup = 0;
339 give_spwd_free (&ent->pwd);
340 return NSS_STATUS_RETURN;
343 if (user == NULL || user[0] == '-')
344 continue;
346 if (domain != NULL && strcmp (ypdomain, domain) != 0)
347 continue;
349 /* If name != NULL, we are called from getpwnam */
350 if (name != NULL)
351 if (strcmp (user, name) != 0)
352 continue;
354 if (yp_match (ypdomain, "shadow.byname", user,
355 strlen (user), &outval, &outvallen)
356 != YPERR_SUCCESS)
357 continue;
359 p2len = spwd_need_buflen (&ent->pwd);
360 if (p2len > buflen)
362 free (outval);
363 *errnop = ERANGE;
364 return NSS_STATUS_TRYAGAIN;
366 p2 = buffer + (buflen - p2len);
367 buflen -= p2len;
368 if (buflen < ((size_t) outval + 1))
370 free (outval);
371 *errnop = ERANGE;
372 return NSS_STATUS_TRYAGAIN;
374 p = strncpy (buffer, outval, buflen);
375 while (isspace (*p))
376 p++;
377 free (outval);
378 parse_res = _nss_files_parse_spent (p, result, data, buflen, errnop);
379 if (parse_res == -1)
381 ent->netgrdata.cursor = saved_cursor;
382 return NSS_STATUS_TRYAGAIN;
385 if (parse_res)
387 /* Store the User in the blacklist for the "+" at the end of
388 /etc/passwd */
389 blacklist_store_name (result->sp_namp, ent);
390 copy_spwd_changes (result, &ent->pwd, p2, p2len);
391 break;
395 return NSS_STATUS_SUCCESS;
398 static enum nss_status
399 getspent_next_nisplus_netgr (const char *name, struct spwd *result,
400 ent_t *ent, char *group, char *buffer,
401 size_t buflen, int *errnop)
403 char *ypdomain, *host, *user, *domain, *p2;
404 int status, parse_res;
405 size_t p2len;
406 nis_result *nisres;
408 /* Maybe we should use domainname here ? We need the current
409 domainname for the domain field in netgroups */
410 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
412 ent->netgroup = 0;
413 ent->first = 0;
414 give_spwd_free (&ent->pwd);
415 return NSS_STATUS_UNAVAIL;
418 if (ent->first == TRUE)
420 bzero (&ent->netgrdata, sizeof (struct __netgrent));
421 __internal_setnetgrent (group, &ent->netgrdata);
422 ent->first = FALSE;
425 while (1)
427 char *saved_cursor;
429 saved_cursor = ent->netgrdata.cursor;
430 status = __internal_getnetgrent_r (&host, &user, &domain,
431 &ent->netgrdata, buffer, buflen,
432 errnop);
433 if (status != 1)
435 __internal_endnetgrent (&ent->netgrdata);
436 ent->netgroup = 0;
437 give_spwd_free (&ent->pwd);
438 return NSS_STATUS_RETURN;
441 if (user == NULL || user[0] == '-')
442 continue;
444 if (domain != NULL && strcmp (ypdomain, domain) != 0)
445 continue;
447 /* If name != NULL, we are called from getpwnam */
448 if (name != NULL)
449 if (strcmp (user, name) != 0)
450 continue;
452 p2len = spwd_need_buflen (&ent->pwd);
453 if (p2len > buflen)
455 *errnop = ERANGE;
456 return NSS_STATUS_TRYAGAIN;
458 p2 = buffer + (buflen - p2len);
459 buflen -= p2len;
461 char buf[strlen (user) + 30 + pwdtablelen];
462 sprintf (buf, "[name=%s],%s", user, pwdtable);
463 nisres = nis_list (buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
465 if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
467 nis_freeresult (nisres);
468 continue;
470 parse_res = _nss_nisplus_parse_spent (nisres, result, buffer,
471 buflen, errnop);
472 if (parse_res == -1)
474 nis_freeresult (nisres);
475 *errnop = ERANGE;
476 return NSS_STATUS_TRYAGAIN;
478 nis_freeresult (nisres);
480 if (parse_res)
482 /* Store the User in the blacklist for the "+" at the end of
483 /etc/passwd */
484 blacklist_store_name (result->sp_namp, ent);
485 copy_spwd_changes (result, &ent->pwd, p2, p2len);
486 break;
490 return NSS_STATUS_SUCCESS;
493 static enum nss_status
494 getspent_next_nisplus (struct spwd *result, ent_t *ent, char *buffer,
495 size_t buflen, int *errnop)
497 int parse_res;
498 size_t p2len;
499 char *p2;
501 p2len = spwd_need_buflen (&ent->pwd);
502 if (p2len > buflen)
504 *errnop = ERANGE;
505 return NSS_STATUS_TRYAGAIN;
507 p2 = buffer + (buflen - p2len);
508 buflen -= p2len;
511 bool_t saved_first;
512 nis_result *saved_res;
514 if (ent->first)
516 saved_first = TRUE;
517 saved_res = ent->result;
519 ent->result = nis_first_entry (pwdtable);
520 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
522 ent->nis = 0;
523 give_spwd_free (&ent->pwd);
524 return niserr2nss (ent->result->status);
526 ent->first = FALSE;
528 else
530 nis_result *res;
532 saved_first = FALSE;
533 saved_res = ent->result;
535 res = nis_next_entry (pwdtable, &ent->result->cookie);
536 ent->result = res;
537 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
539 nis_freeresult (saved_res);
540 ent->nis = 0;
541 give_spwd_free (&ent->pwd);
542 return niserr2nss (ent->result->status);
545 parse_res = _nss_nisplus_parse_spent (ent->result, result, buffer,
546 buflen, errnop);
547 if (parse_res == -1)
549 ent->first = saved_first;
550 nis_freeresult (ent->result);
551 ent->result = saved_res;
552 *errnop = ERANGE;
553 return NSS_STATUS_TRYAGAIN;
555 else
557 if (!saved_first)
558 nis_freeresult (saved_res);
560 if (parse_res &&
561 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
562 parse_res = 0; /* if result->pw_name in blacklist,search next entry */
564 while (!parse_res);
566 copy_spwd_changes (result, &ent->pwd, p2, p2len);
568 return NSS_STATUS_SUCCESS;
572 static enum nss_status
573 getspent_next_nis (struct spwd *result, ent_t *ent,
574 char *buffer, size_t buflen, int *errnop)
576 struct parser_data *data = (void *) buffer;
577 char *domain, *outkey, *outval, *p, *p2;
578 int outkeylen, outvallen, parse_res;
579 size_t p2len;
581 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
583 ent->nis = 0;
584 give_spwd_free (&ent->pwd);
585 return NSS_STATUS_UNAVAIL;
588 p2len = spwd_need_buflen (&ent->pwd);
589 if (p2len > buflen)
591 *errnop = ERANGE;
592 return NSS_STATUS_TRYAGAIN;
594 p2 = buffer + (buflen - p2len);
595 buflen -= p2len;
598 bool_t saved_first;
599 char *saved_oldkey;
600 int saved_oldlen;
602 if (ent->first)
604 if (yp_first (domain, "shadow.byname", &outkey, &outkeylen,
605 &outval, &outvallen) != YPERR_SUCCESS)
607 ent->nis = 0;
608 give_spwd_free (&ent->pwd);
609 return NSS_STATUS_UNAVAIL;
612 if (buflen < ((size_t) outvallen + 1))
614 free (outval);
615 *errnop = ERANGE;
616 return NSS_STATUS_TRYAGAIN;
619 saved_first = TRUE;
620 saved_oldkey = ent->oldkey;
621 saved_oldlen = ent->oldkeylen;
622 ent->oldkey = outkey;
623 ent->oldkeylen = outkeylen;
624 ent->first = FALSE;
626 else
628 if (yp_next (domain, "shadow.byname", ent->oldkey, ent->oldkeylen,
629 &outkey, &outkeylen, &outval, &outvallen)
630 != YPERR_SUCCESS)
632 ent->nis = 0;
633 give_spwd_free (&ent->pwd);
634 return NSS_STATUS_NOTFOUND;
637 if (buflen < ((size_t) outvallen + 1))
639 free (outval);
640 *errnop = ERANGE;
641 return NSS_STATUS_TRYAGAIN;
644 saved_first = FALSE;
645 saved_oldkey = ent->oldkey;
646 saved_oldlen = ent->oldkeylen;
647 ent->oldkey = outkey;
648 ent->oldkeylen = outkeylen;
651 /* Copy the found data to our buffer */
652 p = strncpy (buffer, outval, buflen);
654 /* ...and free the data. */
655 free (outval);
657 while (isspace (*p))
658 ++p;
659 parse_res = _nss_files_parse_spent (p, result, data, buflen, errnop);
660 if (parse_res == -1)
662 free (ent->oldkey);
663 ent->oldkey = saved_oldkey;
664 ent->oldkeylen = saved_oldlen;
665 ent->first = saved_first;
666 *errnop = ERANGE;
667 return NSS_STATUS_TRYAGAIN;
669 else
671 if (!saved_first)
672 free (saved_oldkey);
674 if (parse_res &&
675 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
676 parse_res = 0;
678 while (!parse_res);
680 copy_spwd_changes (result, &ent->pwd, p2, p2len);
682 return NSS_STATUS_SUCCESS;
685 /* This function handle the +user entrys in /etc/shadow */
686 static enum nss_status
687 getspnam_plususer (const char *name, struct spwd *result, char *buffer,
688 size_t buflen, int *errnop)
690 struct parser_data *data = (void *) buffer;
691 struct spwd pwd;
692 int parse_res;
693 char *p;
694 size_t plen;
696 memset (&pwd, '\0', sizeof (struct spwd));
698 copy_spwd_changes (&pwd, result, NULL, 0);
700 plen = spwd_need_buflen (&pwd);
701 if (plen > buflen)
703 *errnop = ERANGE;
704 return NSS_STATUS_TRYAGAIN;
706 p = buffer + (buflen - plen);
707 buflen -= plen;
709 if (use_nisplus) /* Do the NIS+ query here */
711 nis_result *res;
712 char buf[strlen (name) + 24 + pwdtablelen];
714 sprintf(buf, "[name=%s],%s", name, pwdtable);
715 res = nis_list(buf, 0, NULL, NULL);
716 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
718 enum nss_status status = niserr2nss (res->status);
720 nis_freeresult (res);
721 return status;
723 parse_res = _nss_nisplus_parse_spent (res, result, buffer,
724 buflen, errnop);
725 if (parse_res == -1)
727 nis_freeresult (res);
728 *errnop = ERANGE;
729 return NSS_STATUS_TRYAGAIN;
731 nis_freeresult (res);
733 else /* Use NIS */
735 char *domain, *outval, *ptr;
736 int outvallen;
738 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
739 return NSS_STATUS_NOTFOUND;
741 if (yp_match (domain, "shadow.byname", name, strlen (name),
742 &outval, &outvallen) != YPERR_SUCCESS)
743 return NSS_STATUS_NOTFOUND;
745 if (buflen < ((size_t) outvallen + 1))
747 free (outval);
748 *errnop = ERANGE;
749 return NSS_STATUS_TRYAGAIN;
752 ptr = strncpy (buffer, outval, buflen);
753 free (outval);
754 while (isspace (*ptr))
755 ptr++;
756 parse_res = _nss_files_parse_spent (ptr, result, data, buflen, errnop);
757 if (parse_res == -1)
758 return NSS_STATUS_TRYAGAIN;
761 if (parse_res)
763 copy_spwd_changes (result, &pwd, p, plen);
764 give_spwd_free (&pwd);
765 /* We found the entry. */
766 return NSS_STATUS_SUCCESS;
768 else
770 /* Give buffer the old len back */
771 buflen += plen;
772 give_spwd_free (&pwd);
774 return NSS_STATUS_RETURN;
777 static enum nss_status
778 getspent_next_file (struct spwd *result, ent_t *ent,
779 char *buffer, size_t buflen, int *errnop)
781 struct parser_data *data = (void *) buffer;
782 while (1)
784 fpos_t pos;
785 int parse_res = 0;
786 char *p;
790 fgetpos (ent->stream, &pos);
791 buffer[buflen - 1] = '\xff';
792 p = fgets (buffer, buflen, ent->stream);
793 if (p == NULL && feof (ent->stream))
794 return NSS_STATUS_NOTFOUND;
795 if (p == NULL || buffer[buflen - 1] != '\xff')
797 fsetpos (ent->stream, &pos);
798 *errnop = ERANGE;
799 return NSS_STATUS_TRYAGAIN;
802 /* Skip leading blanks. */
803 while (isspace (*p))
804 ++p;
806 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
807 /* Parse the line. If it is invalid, loop to
808 get the next line of the file to parse. */
809 || !(parse_res = _nss_files_parse_spent (p, result, data,
810 buflen, errnop)));
812 if (parse_res == -1)
814 /* The parser ran out of space. */
815 fsetpos (ent->stream, &pos);
816 *errnop = ERANGE;
817 return NSS_STATUS_TRYAGAIN;
820 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
821 /* This is a real entry. */
822 break;
824 /* -@netgroup */
825 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
826 && result->sp_namp[2] != '\0')
828 /* XXX Do not use fixed length buffers. */
829 char buf2[1024];
830 char *user, *host, *domain;
831 struct __netgrent netgrdata;
833 bzero (&netgrdata, sizeof (struct __netgrent));
834 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
835 while (__internal_getnetgrent_r (&host, &user, &domain,
836 &netgrdata, buf2, sizeof (buf2),
837 errnop))
839 if (user != NULL && user[0] != '-')
840 blacklist_store_name (user, ent);
842 __internal_endnetgrent (&netgrdata);
843 continue;
846 /* +@netgroup */
847 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
848 && result->sp_namp[2] != '\0')
850 int status;
852 ent->netgroup = TRUE;
853 ent->first = TRUE;
854 copy_spwd_changes (&ent->pwd, result, NULL, 0);
856 if (use_nisplus)
857 status = getspent_next_nisplus_netgr (NULL, result, ent,
858 &result->sp_namp[2],
859 buffer, buflen, errnop);
860 else
861 status = getspent_next_nis_netgr (NULL, result, ent,
862 &result->sp_namp[2],
863 buffer, buflen, errnop);
864 if (status == NSS_STATUS_RETURN)
865 continue;
866 else
867 return status;
870 /* -user */
871 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
872 && result->sp_namp[1] != '@')
874 blacklist_store_name (&result->sp_namp[1], ent);
875 continue;
878 /* +user */
879 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
880 && result->sp_namp[1] != '@')
882 enum nss_status status;
884 /* Store the User in the blacklist for the "+" at the end of
885 /etc/passwd */
886 blacklist_store_name (&result->sp_namp[1], ent);
887 status = getspnam_plususer (&result->sp_namp[1], result, buffer,
888 buflen, errnop);
889 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
890 break;
891 else
892 if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
893 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
894 continue;
895 else
897 if (status == NSS_STATUS_TRYAGAIN)
899 fsetpos (ent->stream, &pos);
900 *errnop = ERANGE;
902 return status;
906 /* +:... */
907 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
909 ent->nis = TRUE;
910 ent->first = TRUE;
911 copy_spwd_changes (&ent->pwd, result, NULL, 0);
913 if (use_nisplus)
914 return getspent_next_nisplus (result, ent, buffer, buflen, errnop);
915 else
916 return getspent_next_nis (result, ent, buffer, buflen, errnop);
920 return NSS_STATUS_SUCCESS;
924 static enum nss_status
925 internal_getspent_r (struct spwd *pw, ent_t *ent,
926 char *buffer, size_t buflen, int *errnop)
928 if (ent->netgroup)
930 int status;
932 /* We are searching members in a netgroup */
933 /* Since this is not the first call, we don't need the group name */
934 if (use_nisplus)
935 status = getspent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
936 buflen, errnop);
937 else
938 status = getspent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
939 errnop);
940 if (status == NSS_STATUS_RETURN)
941 return getspent_next_file (pw, ent, buffer, buflen, errnop);
942 else
943 return status;
945 else
946 if (ent->nis)
948 if (use_nisplus)
949 return getspent_next_nisplus (pw, ent, buffer, buflen, errnop);
950 else
951 return getspent_next_nis (pw, ent, buffer, buflen, errnop);
953 else
954 return getspent_next_file (pw, ent, buffer, buflen, errnop);
957 enum nss_status
958 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen,
959 int *errnop)
961 enum nss_status status = NSS_STATUS_SUCCESS;
963 __libc_lock_lock (lock);
965 if (ni == NULL)
967 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
968 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
971 /* Be prepared that the setspent function was not called before. */
972 if (ext_ent.stream == NULL)
973 status = internal_setspent (&ext_ent);
975 if (status == NSS_STATUS_SUCCESS)
976 status = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop);
978 __libc_lock_unlock (lock);
980 return status;
983 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
984 static enum nss_status
985 internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
986 char *buffer, size_t buflen, int *errnop)
988 struct parser_data *data = (void *) buffer;
990 while (1)
992 fpos_t pos;
993 char *p;
994 int parse_res;
998 fgetpos (ent->stream, &pos);
999 buffer[buflen - 1] = '\xff';
1000 p = fgets (buffer, buflen, ent->stream);
1001 if (p == NULL && feof (ent->stream))
1002 return NSS_STATUS_NOTFOUND;
1003 if (p == NULL || buffer[buflen - 1] != '\xff')
1005 fsetpos (ent->stream, &pos);
1006 *errnop = ERANGE;
1007 return NSS_STATUS_TRYAGAIN;
1010 /* Skip leading blanks. */
1011 while (isspace (*p))
1012 ++p;
1014 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1015 /* Parse the line. If it is invalid, loop to
1016 get the next line of the file to parse. */
1017 !(parse_res = _nss_files_parse_spent (p, result, data, buflen,
1018 errnop)));
1020 if (parse_res == -1)
1022 /* The parser ran out of space. */
1023 fsetpos (ent->stream, &pos);
1024 *errnop = ERANGE;
1025 return NSS_STATUS_TRYAGAIN;
1028 /* This is a real entry. */
1029 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
1031 if (strcmp (result->sp_namp, name) == 0)
1032 return NSS_STATUS_SUCCESS;
1033 else
1034 continue;
1037 /* -@netgroup */
1038 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
1039 && result->sp_namp[2] != '\0')
1041 /* XXX Do not use fixed length buffers. */
1042 char buf2[1024];
1043 char *user, *host, *domain;
1044 struct __netgrent netgrdata;
1046 bzero (&netgrdata, sizeof (struct __netgrent));
1047 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
1048 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1049 buf2, sizeof (buf2), errnop))
1051 if (user != NULL && user[0] != '-')
1052 if (strcmp (user, name) == 0)
1053 return NSS_STATUS_NOTFOUND;
1055 __internal_endnetgrent (&netgrdata);
1056 continue;
1059 /* +@netgroup */
1060 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
1061 && result->sp_namp[2] != '\0')
1063 char *buf = strdupa (&result->sp_namp[2]);
1064 int status;
1066 ent->netgroup = TRUE;
1067 ent->first = TRUE;
1068 copy_spwd_changes (&ent->pwd, result, NULL, 0);
1072 if (use_nisplus)
1073 status = getspent_next_nisplus_netgr (name, result, ent, buf,
1074 buffer, buflen, errnop);
1075 else
1076 status = getspent_next_nis_netgr (name, result, ent, buf,
1077 buffer, buflen, errnop);
1078 if (status == NSS_STATUS_RETURN)
1079 continue;
1081 if (status == NSS_STATUS_SUCCESS
1082 && strcmp (result->sp_namp, name) == 0)
1083 return NSS_STATUS_SUCCESS;
1084 } while (status == NSS_STATUS_SUCCESS);
1085 continue;
1088 /* -user */
1089 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
1090 && result->sp_namp[1] != '@')
1092 if (strcmp (&result->sp_namp[1], name) == 0)
1093 return NSS_STATUS_NOTFOUND;
1094 else
1095 continue;
1098 /* +user */
1099 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
1100 && result->sp_namp[1] != '@')
1102 if (strcmp (name, &result->sp_namp[1]) == 0)
1104 enum nss_status status;
1106 status = getspnam_plususer (name, result, buffer, buflen,
1107 errnop);
1108 if (status == NSS_STATUS_RETURN)
1109 /* We couldn't parse the entry */
1110 return NSS_STATUS_NOTFOUND;
1111 else
1112 return status;
1116 /* +:... */
1117 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
1119 enum nss_status status;
1121 status = getspnam_plususer (name, result, buffer, buflen, errnop);
1122 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1123 return NSS_STATUS_NOTFOUND;
1124 else
1125 return status;
1128 return NSS_STATUS_SUCCESS;
1131 enum nss_status
1132 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
1133 char *buffer, size_t buflen, int *errnop)
1135 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1136 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
1137 enum nss_status status;
1139 if (name[0] == '-' || name[0] == '+')
1140 return NSS_STATUS_NOTFOUND;
1142 if (ni == NULL)
1144 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
1145 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1148 status = internal_setspent (&ent);
1149 if (status != NSS_STATUS_SUCCESS)
1150 return status;
1152 status = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
1154 internal_endspent (&ent);
1156 return status;
1159 /* Support routines for remembering -@netgroup and -user entries.
1160 The names are stored in a single string with `|' as separator. */
1161 static void
1162 blacklist_store_name (const char *name, ent_t *ent)
1164 int namelen = strlen (name);
1165 char *tmp;
1167 /* first call, setup cache */
1168 if (ent->blacklist.size == 0)
1170 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1171 ent->blacklist.data = malloc (ent->blacklist.size);
1172 if (ent->blacklist.data == NULL)
1173 return;
1174 ent->blacklist.data[0] = '|';
1175 ent->blacklist.data[1] = '\0';
1176 ent->blacklist.current = 1;
1178 else
1180 if (in_blacklist (name, namelen, ent))
1181 return; /* no duplicates */
1183 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1185 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1186 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1187 if (tmp == NULL)
1189 free (ent->blacklist.data);
1190 ent->blacklist.size = 0;
1191 return;
1193 ent->blacklist.data = tmp;
1197 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1198 *tmp++ = '|';
1199 *tmp = '\0';
1200 ent->blacklist.current += namelen + 1;
1202 return;
1205 /* Returns TRUE if ent->blacklist contains name, else FALSE. */
1206 static bool_t
1207 in_blacklist (const char *name, int namelen, ent_t *ent)
1209 char buf[namelen + 3];
1210 char *cp;
1212 if (ent->blacklist.data == NULL)
1213 return FALSE;
1215 buf[0] = '|';
1216 cp = stpcpy (&buf[1], name);
1217 *cp++= '|';
1218 *cp = '\0';
1219 return strstr (ent->blacklist.data, buf) != NULL;