Update.
[glibc.git] / nis / nss_compat / compat-spwd.c
blobc33d5aa3393b5475090507a5ff44e5d22a78f56c
1 /* Copyright (C) 1996, 1997 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 /* Comment out the following line for the production version. */
34 /* #define NDEBUG 1 */
35 #include <assert.h>
37 #include "netgroup.h"
38 #include "nss-nisplus.h"
39 #include "nisplus-parser.h"
41 static service_user *ni = NULL;
42 static bool_t use_nisplus = FALSE; /* default: passwd_compat: nis */
43 static nis_name pwdtable = NULL; /* Name of the password table */
44 static size_t pwdtablelen = 0;
46 /* Get the declaration of the parser function. */
47 #define ENTNAME spent
48 #define STRUCTURE spwd
49 #define EXTERN_PARSER
50 #include <nss/nss_files/files-parse.c>
52 /* Structure for remembering -@netgroup and -user members ... */
53 #define BLACKLIST_INITIAL_SIZE 512
54 #define BLACKLIST_INCREMENT 256
55 struct blacklist_t
57 char *data;
58 int current;
59 int size;
62 struct ent_t
64 bool_t netgroup;
65 bool_t nis;
66 bool_t first;
67 char *oldkey;
68 int oldkeylen;
69 nis_result *result;
70 FILE *stream;
71 struct blacklist_t blacklist;
72 struct spwd pwd;
73 struct __netgrent netgrdata;
75 typedef struct ent_t ent_t;
77 static ent_t ext_ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
78 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
80 /* Protect global state against multiple changers. */
81 __libc_lock_define_initialized (static, lock)
83 /* Prototypes for local functions. */
84 static void blacklist_store_name (const char *, ent_t *);
85 static int in_blacklist (const char *, int, ent_t *);
87 static void
88 give_spwd_free (struct spwd *pwd)
90 if (pwd->sp_namp != NULL)
91 free (pwd->sp_namp);
92 if (pwd->sp_pwdp != NULL)
93 free (pwd->sp_pwdp);
95 memset (pwd, '\0', sizeof (struct spwd));
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 != 0)
135 dest->sp_warn = src->sp_warn;
136 if (src->sp_inact != 0)
137 dest->sp_inact = src->sp_inact;
138 if (src->sp_expire != 0)
139 dest->sp_expire = src->sp_expire;
140 if (src->sp_flag != 0)
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;
183 /* *Maybe* (I'm no NIS expert) we have to duplicate the `local_dir'
184 value since it might change during our work. So add a test here. */
185 assert (pwdtablelen == sizeof (key) + len_local_dir);
188 ent->blacklist.current = 0;
189 if (ent->blacklist.data != NULL)
190 ent->blacklist.data[0] = '\0';
192 if (ent->stream == NULL)
194 ent->stream = fopen ("/etc/shadow", "r");
196 if (ent->stream == NULL)
197 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
198 else
200 /* We have to make sure the file is `closed on exec'. */
201 int result, flags;
203 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
204 if (result >= 0)
206 flags |= FD_CLOEXEC;
207 result = fcntl (fileno (ent->stream), F_SETFD, flags);
209 if (result < 0)
211 /* Something went wrong. Close the stream and return a
212 failure. */
213 fclose (ent->stream);
214 ent->stream = NULL;
215 status = NSS_STATUS_UNAVAIL;
219 else
220 rewind (ent->stream);
222 give_spwd_free (&ent->pwd);
224 return status;
228 enum nss_status
229 _nss_compat_setspent (void)
231 enum nss_status result;
233 __libc_lock_lock (lock);
235 if (ni == NULL)
237 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
238 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
241 result = internal_setspent (&ext_ent);
243 __libc_lock_unlock (lock);
245 return result;
249 static enum nss_status
250 internal_endspent (ent_t *ent)
252 if (ent->stream != NULL)
254 fclose (ent->stream);
255 ent->stream = NULL;
258 if (ent->netgroup)
259 __internal_endnetgrent (&ent->netgrdata);
261 ent->nis = ent->first = ent->netgroup = 0;
263 if (ent->oldkey != NULL)
265 free (ent->oldkey);
266 ent->oldkey = NULL;
267 ent->oldkeylen = 0;
270 if (ent->result != NULL)
272 nis_freeresult (ent->result);
273 ent->result = NULL;
276 ent->blacklist.current = 0;
277 if (ent->blacklist.data != NULL)
278 ent->blacklist.data[0] = '\0';
280 give_spwd_free (&ent->pwd);
282 return NSS_STATUS_SUCCESS;
285 enum nss_status
286 _nss_compat_endspent (void)
288 enum nss_status result;
290 __libc_lock_lock (lock);
292 result = internal_endspent (&ext_ent);
294 __libc_lock_unlock (lock);
296 return result;
300 static enum nss_status
301 getspent_next_nis_netgr (const char *name, struct spwd *result, ent_t *ent,
302 char *group, char *buffer, size_t buflen)
304 struct parser_data *data = (void *) buffer;
305 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
306 int status, outvallen;
307 size_t p2len;
309 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
311 ent->netgroup = 0;
312 ent->first = 0;
313 give_spwd_free (&ent->pwd);
314 return NSS_STATUS_UNAVAIL;
317 if (ent->first == TRUE)
319 bzero (&ent->netgrdata, sizeof (struct __netgrent));
320 __internal_setnetgrent (group, &ent->netgrdata);
321 ent->first = FALSE;
324 while (1)
326 char *saved_cursor;
327 int parse_res;
329 saved_cursor = ent->netgrdata.cursor;
330 status = __internal_getnetgrent_r (&host, &user, &domain,
331 &ent->netgrdata, buffer, buflen);
332 if (status != 1)
334 __internal_endnetgrent (&ent->netgrdata);
335 ent->netgroup = 0;
336 give_spwd_free (&ent->pwd);
337 return NSS_STATUS_RETURN;
340 if (user == NULL || user[0] == '-')
341 continue;
343 if (domain != NULL && strcmp (ypdomain, domain) != 0)
344 continue;
346 /* If name != NULL, we are called from getpwnam */
347 if (name != NULL)
348 if (strcmp (user, name) != 0)
349 continue;
351 if (yp_match (ypdomain, "shadow.byname", user,
352 strlen (user), &outval, &outvallen)
353 != YPERR_SUCCESS)
354 continue;
356 p2len = spwd_need_buflen (&ent->pwd);
357 if (p2len > buflen)
359 __set_errno (ERANGE);
360 return NSS_STATUS_TRYAGAIN;
362 p2 = buffer + (buflen - p2len);
363 buflen -= p2len;
364 p = strncpy (buffer, outval, buflen);
365 while (isspace (*p))
366 p++;
367 free (outval);
368 if ((parse_res = _nss_files_parse_spent (p, result, data, buflen)) == -1)
370 ent->netgrdata.cursor = saved_cursor;
371 return NSS_STATUS_TRYAGAIN;
374 if (parse_res)
376 /* Store the User in the blacklist for the "+" at the end of
377 /etc/passwd */
378 blacklist_store_name (result->sp_namp, ent);
379 copy_spwd_changes (result, &ent->pwd, p2, p2len);
380 break;
384 return NSS_STATUS_SUCCESS;
387 static enum nss_status
388 getspent_next_nisplus_netgr (const char *name, struct spwd *result,
389 ent_t *ent, char *group, char *buffer,
390 size_t buflen)
392 char *ypdomain, *host, *user, *domain, *p2;
393 int status, parse_res;
394 size_t p2len;
395 nis_result *nisres;
397 /* Maybe we should use domainname here ? We need the current
398 domainname for the domain field in netgroups */
399 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
401 ent->netgroup = 0;
402 ent->first = 0;
403 give_spwd_free (&ent->pwd);
404 return NSS_STATUS_UNAVAIL;
407 if (ent->first == TRUE)
409 bzero (&ent->netgrdata, sizeof (struct __netgrent));
410 __internal_setnetgrent (group, &ent->netgrdata);
411 ent->first = FALSE;
414 while (1)
416 char *saved_cursor;
418 saved_cursor = ent->netgrdata.cursor;
419 status = __internal_getnetgrent_r (&host, &user, &domain,
420 &ent->netgrdata, buffer, buflen);
421 if (status != 1)
423 __internal_endnetgrent (&ent->netgrdata);
424 ent->netgroup = 0;
425 give_spwd_free (&ent->pwd);
426 return NSS_STATUS_RETURN;
429 if (user == NULL || user[0] == '-')
430 continue;
432 if (domain != NULL && strcmp (ypdomain, domain) != 0)
433 continue;
435 /* If name != NULL, we are called from getpwnam */
436 if (name != NULL)
437 if (strcmp (user, name) != 0)
438 continue;
440 p2len = spwd_need_buflen (&ent->pwd);
441 if (p2len > buflen)
443 __set_errno (ERANGE);
444 return NSS_STATUS_TRYAGAIN;
446 p2 = buffer + (buflen - p2len);
447 buflen -= p2len;
449 char buf[strlen (user) + 30 + pwdtablelen];
450 sprintf(buf, "[name=%s],%s", user, pwdtable);
451 nisres = nis_list(buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
453 if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
455 nis_freeresult (nisres);
456 continue;
458 if ((parse_res = _nss_nisplus_parse_spent (nisres, result, buffer,
459 buflen)) == -1)
461 nis_freeresult (nisres);
462 return NSS_STATUS_TRYAGAIN;
464 nis_freeresult (nisres);
466 if (parse_res)
468 /* Store the User in the blacklist for the "+" at the end of
469 /etc/passwd */
470 blacklist_store_name (result->sp_namp, ent);
471 copy_spwd_changes (result, &ent->pwd, p2, p2len);
472 break;
476 return NSS_STATUS_SUCCESS;
479 static enum nss_status
480 getspent_next_nisplus (struct spwd *result, ent_t *ent, char *buffer,
481 size_t buflen)
483 int parse_res;
484 size_t p2len;
485 char *p2;
487 p2len = spwd_need_buflen (&ent->pwd);
488 if (p2len > buflen)
490 __set_errno (ERANGE);
491 return NSS_STATUS_TRYAGAIN;
493 p2 = buffer + (buflen - p2len);
494 buflen -= p2len;
497 bool_t saved_first;
498 nis_result *saved_res;
500 if (ent->first)
502 saved_first = TRUE;
503 saved_res = ent->result;
505 ent->result = nis_first_entry(pwdtable);
506 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
508 ent->nis = 0;
509 give_spwd_free (&ent->pwd);
510 return niserr2nss (ent->result->status);
512 ent->first = FALSE;
514 else
516 nis_result *res;
518 saved_first = FALSE;
519 saved_res = ent->result;
521 res = nis_next_entry(pwdtable, &ent->result->cookie);
522 ent->result = res;
523 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
525 nis_freeresult (saved_res);
526 ent->nis = 0;
527 give_spwd_free (&ent->pwd);
528 return niserr2nss (ent->result->status);
531 if ((parse_res = _nss_nisplus_parse_spent (ent->result, result, buffer,
532 buflen)) == -1)
534 ent->first = saved_first;
535 nis_freeresult (ent->result);
536 ent->result = saved_res;
537 __set_errno (ERANGE);
538 return NSS_STATUS_TRYAGAIN;
540 else
542 if (!saved_first)
543 nis_freeresult (saved_res);
545 if (parse_res &&
546 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
547 parse_res = 0; /* if result->pw_name in blacklist,search next entry */
549 while (!parse_res);
551 copy_spwd_changes (result, &ent->pwd, p2, p2len);
553 return NSS_STATUS_SUCCESS;
557 static enum nss_status
558 getspent_next_nis (struct spwd *result, ent_t *ent,
559 char *buffer, size_t buflen)
561 struct parser_data *data = (void *) buffer;
562 char *domain, *outkey, *outval, *p, *p2;
563 int outkeylen, outvallen, parse_res;
564 size_t p2len;
566 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
568 ent->nis = 0;
569 give_spwd_free (&ent->pwd);
570 return NSS_STATUS_UNAVAIL;
573 p2len = spwd_need_buflen (&ent->pwd);
574 if (p2len > buflen)
576 __set_errno (ERANGE);
577 return NSS_STATUS_TRYAGAIN;
579 p2 = buffer + (buflen - p2len);
580 buflen -= p2len;
583 bool_t saved_first;
584 char *saved_oldkey;
585 int saved_oldlen;
587 if (ent->first)
589 if (yp_first (domain, "shadow.byname", &outkey, &outkeylen,
590 &outval, &outvallen) != YPERR_SUCCESS)
592 ent->nis = 0;
593 give_spwd_free (&ent->pwd);
594 return NSS_STATUS_UNAVAIL;
596 saved_first = TRUE;
597 saved_oldkey = ent->oldkey;
598 saved_oldlen = ent->oldkeylen;
599 ent->oldkey = outkey;
600 ent->oldkeylen = outkeylen;
601 ent->first = FALSE;
603 else
605 if (yp_next (domain, "shadow.byname", ent->oldkey, ent->oldkeylen,
606 &outkey, &outkeylen, &outval, &outvallen)
607 != YPERR_SUCCESS)
609 ent->nis = 0;
610 give_spwd_free (&ent->pwd);
611 return NSS_STATUS_NOTFOUND;
614 saved_first = FALSE;
615 saved_oldkey = ent->oldkey;
616 saved_oldlen = ent->oldkeylen;
617 ent->oldkey = outkey;
618 ent->oldkeylen = outkeylen;
621 /* Copy the found data to our buffer */
622 p = strncpy (buffer, outval, buflen);
624 /* ...and free the data. */
625 free (outval);
627 while (isspace (*p))
628 ++p;
629 if ((parse_res = _nss_files_parse_spent (p, result, data, buflen)) == -1)
631 free (ent->oldkey);
632 ent->oldkey = saved_oldkey;
633 ent->oldkeylen = saved_oldlen;
634 ent->first = saved_first;
635 __set_errno (ERANGE);
636 return NSS_STATUS_TRYAGAIN;
638 else
640 if (!saved_first)
641 free (saved_oldkey);
643 if (parse_res &&
644 in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
645 parse_res = 0;
647 while (!parse_res);
649 copy_spwd_changes (result, &ent->pwd, p2, p2len);
651 return NSS_STATUS_SUCCESS;
654 /* This function handle the +user entrys in /etc/shadow */
655 static enum nss_status
656 getspnam_plususer (const char *name, struct spwd *result, char *buffer,
657 size_t buflen)
659 struct parser_data *data = (void *) buffer;
660 struct spwd pwd;
661 int parse_res;
662 char *p;
663 size_t plen;
665 memset (&pwd, '\0', sizeof (struct spwd));
667 copy_spwd_changes (&pwd, result, NULL, 0);
669 plen = spwd_need_buflen (&pwd);
670 if (plen > buflen)
672 __set_errno (ERANGE);
673 return NSS_STATUS_TRYAGAIN;
675 p = buffer + (buflen - plen);
676 buflen -= plen;
678 if (use_nisplus) /* Do the NIS+ query here */
680 nis_result *res;
681 char buf[strlen (name) + 24 + pwdtablelen];
683 sprintf(buf, "[name=%s],%s", name, pwdtable);
684 res = nis_list(buf, 0, NULL, NULL);
685 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
687 enum nss_status status = niserr2nss (res->status);
689 nis_freeresult (res);
690 return status;
692 if ((parse_res = _nss_nisplus_parse_spent (res, result, buffer,
693 buflen)) == -1)
695 nis_freeresult (res);
696 return NSS_STATUS_TRYAGAIN;
698 nis_freeresult (res);
700 else /* Use NIS */
702 char *domain, *outval, *ptr;
703 int outvallen;
705 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
706 return NSS_STATUS_TRYAGAIN;
708 if (yp_match (domain, "shadow.byname", name, strlen (name),
709 &outval, &outvallen)
710 != YPERR_SUCCESS)
711 return NSS_STATUS_TRYAGAIN;
712 ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
713 buflen : (size_t) outvallen);
714 buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
715 free (outval);
716 while (isspace (*ptr))
717 ptr++;
718 if ((parse_res = _nss_files_parse_spent (ptr, result, data, buflen))
719 == -1)
721 __set_errno (ERANGE);
722 return NSS_STATUS_TRYAGAIN;
726 if (parse_res)
728 copy_spwd_changes (result, &pwd, p, plen);
729 give_spwd_free (&pwd);
730 /* We found the entry. */
731 return NSS_STATUS_SUCCESS;
733 else
735 /* Give buffer the old len back */
736 buflen += plen;
737 give_spwd_free (&pwd);
739 return NSS_STATUS_RETURN;
742 static enum nss_status
743 getspent_next_file (struct spwd *result, ent_t *ent,
744 char *buffer, size_t buflen)
746 struct parser_data *data = (void *) buffer;
747 while (1)
749 fpos_t pos;
750 int parse_res = 0;
751 char *p;
755 fgetpos (ent->stream, &pos);
756 p = fgets (buffer, buflen, ent->stream);
757 if (p == NULL)
758 return NSS_STATUS_NOTFOUND;
760 /* Terminate the line for any case. */
761 buffer[buflen - 1] = '\0';
763 /* Skip leading blanks. */
764 while (isspace (*p))
765 ++p;
767 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
768 /* Parse the line. If it is invalid, loop to
769 get the next line of the file to parse. */
770 || !(parse_res = _nss_files_parse_spent (p, result, data,
771 buflen)));
773 if (parse_res == -1)
775 /* The parser ran out of space. */
776 fsetpos (ent->stream, &pos);
777 __set_errno (ERANGE);
778 return NSS_STATUS_TRYAGAIN;
781 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
782 /* This is a real entry. */
783 break;
785 /* -@netgroup */
786 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
787 && result->sp_namp[2] != '\0')
789 char buf2[1024];
790 char *user, *host, *domain;
791 struct __netgrent netgrdata;
793 bzero (&netgrdata, sizeof (struct __netgrent));
794 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
795 while (__internal_getnetgrent_r (&host, &user, &domain,
796 &netgrdata, buf2, sizeof (buf2)))
798 if (user != NULL && user[0] != '-')
799 blacklist_store_name (user, ent);
801 __internal_endnetgrent (&netgrdata);
802 continue;
805 /* +@netgroup */
806 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
807 && result->sp_namp[2] != '\0')
809 int status;
811 ent->netgroup = TRUE;
812 ent->first = TRUE;
813 copy_spwd_changes (&ent->pwd, result, NULL, 0);
815 if (use_nisplus)
816 status = getspent_next_nisplus_netgr (NULL, result, ent,
817 &result->sp_namp[2],
818 buffer, buflen);
819 else
820 status = getspent_next_nis_netgr (NULL, result, ent,
821 &result->sp_namp[2],
822 buffer, buflen);
823 if (status == NSS_STATUS_RETURN)
824 continue;
825 else
826 return status;
829 /* -user */
830 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
831 && result->sp_namp[1] != '@')
833 blacklist_store_name (&result->sp_namp[1], ent);
834 continue;
837 /* +user */
838 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
839 && result->sp_namp[1] != '@')
841 enum nss_status status;
843 /* Store the User in the blacklist for the "+" at the end of
844 /etc/passwd */
845 blacklist_store_name (&result->sp_namp[1], ent);
846 status = getspnam_plususer (&result->sp_namp[1], result, buffer,
847 buflen);
848 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
849 break;
850 else
851 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
852 continue;
853 else
854 return status;
857 /* +:... */
858 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
860 ent->nis = TRUE;
861 ent->first = TRUE;
862 copy_spwd_changes (&ent->pwd, result, NULL, 0);
864 if (use_nisplus)
865 return getspent_next_nisplus (result, ent, buffer, buflen);
866 else
867 return getspent_next_nis (result, ent, buffer, buflen);
871 return NSS_STATUS_SUCCESS;
875 static enum nss_status
876 internal_getspent_r (struct spwd *pw, ent_t *ent,
877 char *buffer, size_t buflen)
879 if (ent->netgroup)
881 int status;
883 /* We are searching members in a netgroup */
884 /* Since this is not the first call, we don't need the group name */
885 if (use_nisplus)
886 status = getspent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
887 buflen);
888 else
889 status = getspent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen);
890 if (status == NSS_STATUS_RETURN)
891 return getspent_next_file (pw, ent, buffer, buflen);
892 else
893 return status;
895 else
896 if (ent->nis)
898 if (use_nisplus)
899 return getspent_next_nisplus (pw, ent, buffer, buflen);
900 else
901 return getspent_next_nis (pw, ent, buffer, buflen);
903 else
904 return getspent_next_file (pw, ent, buffer, buflen);
907 enum nss_status
908 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen)
910 enum nss_status status = NSS_STATUS_SUCCESS;
912 __libc_lock_lock (lock);
914 if (ni == NULL)
916 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
917 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
920 /* Be prepared that the setspent function was not called before. */
921 if (ext_ent.stream == NULL)
922 status = internal_setspent (&ext_ent);
924 if (status == NSS_STATUS_SUCCESS)
925 status = internal_getspent_r (pwd, &ext_ent, buffer, buflen);
927 __libc_lock_unlock (lock);
929 return status;
932 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
933 static enum nss_status
934 internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
935 char *buffer, size_t buflen)
937 struct parser_data *data = (void *) buffer;
939 while (1)
941 fpos_t pos;
942 char *p;
943 int parse_res;
947 fgetpos (ent->stream, &pos);
948 p = fgets (buffer, buflen, ent->stream);
949 if (p == NULL)
950 return NSS_STATUS_NOTFOUND;
952 /* Terminate the line for any case. */
953 buffer[buflen - 1] = '\0';
955 /* Skip leading blanks. */
956 while (isspace (*p))
957 ++p;
959 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
960 /* Parse the line. If it is invalid, loop to
961 get the next line of the file to parse. */
962 !(parse_res = _nss_files_parse_spent (p, result, data, buflen)));
964 if (parse_res == -1)
966 /* The parser ran out of space. */
967 fsetpos (ent->stream, &pos);
968 __set_errno (ERANGE);
969 return NSS_STATUS_TRYAGAIN;
972 /* This is a real entry. */
973 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
975 if (strcmp (result->sp_namp, name) == 0)
976 return NSS_STATUS_SUCCESS;
977 else
978 continue;
981 /* -@netgroup */
982 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
983 && result->sp_namp[2] != '\0')
985 char buf2[1024];
986 char *user, *host, *domain;
987 struct __netgrent netgrdata;
989 bzero (&netgrdata, sizeof (struct __netgrent));
990 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
991 while (__internal_getnetgrent_r (&host, &user, &domain,
992 &netgrdata, buf2, sizeof (buf2)))
994 if (user != NULL && user[0] != '-')
995 if (strcmp (user, name) == 0)
996 return NSS_STATUS_NOTFOUND;
998 __internal_endnetgrent (&netgrdata);
999 continue;
1002 /* +@netgroup */
1003 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
1004 && result->sp_namp[2] != '\0')
1006 char buf[strlen (result->sp_namp)];
1007 int status;
1009 strcpy (buf, &result->sp_namp[2]);
1010 ent->netgroup = TRUE;
1011 ent->first = TRUE;
1012 copy_spwd_changes (&ent->pwd, result, NULL, 0);
1016 if (use_nisplus)
1017 status = getspent_next_nisplus_netgr (name, result, ent, buf,
1018 buffer, buflen);
1019 else
1020 status = getspent_next_nis_netgr (name, result, ent, buf,
1021 buffer, buflen);
1022 if (status == NSS_STATUS_RETURN)
1023 continue;
1025 if (status == NSS_STATUS_SUCCESS &&
1026 strcmp (result->sp_namp, name) == 0)
1027 return NSS_STATUS_SUCCESS;
1028 } while (status == NSS_STATUS_SUCCESS);
1029 continue;
1032 /* -user */
1033 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
1034 && result->sp_namp[1] != '@')
1036 if (strcmp (&result->sp_namp[1], name) == 0)
1037 return NSS_STATUS_NOTFOUND;
1038 else
1039 continue;
1042 /* +user */
1043 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
1044 && result->sp_namp[1] != '@')
1046 if (strcmp (name, &result->sp_namp[1]) == 0)
1048 enum nss_status status;
1050 status = getspnam_plususer (name, result, buffer, buflen);
1051 if (status == NSS_STATUS_RETURN)
1052 /* We couldn't parse the entry */
1053 return NSS_STATUS_NOTFOUND;
1054 else
1055 return status;
1059 /* +:... */
1060 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
1062 enum nss_status status;
1064 status = getspnam_plususer (name, result, buffer, buflen);
1065 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1066 return NSS_STATUS_NOTFOUND;
1067 else
1068 return status;
1071 return NSS_STATUS_SUCCESS;
1074 enum nss_status
1075 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
1076 char *buffer, size_t buflen)
1078 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1079 {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
1080 enum nss_status status;
1082 if (name[0] == '-' || name[0] == '+')
1083 return NSS_STATUS_NOTFOUND;
1085 if (ni == NULL)
1087 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
1088 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1091 status = internal_setspent (&ent);
1092 if (status != NSS_STATUS_SUCCESS)
1093 return status;
1095 status = internal_getspnam_r (name, pwd, &ent, buffer, buflen);
1097 internal_endspent (&ent);
1099 return status;
1102 /* Support routines for remembering -@netgroup and -user entries.
1103 The names are stored in a single string with `|' as separator. */
1104 static void
1105 blacklist_store_name (const char *name, ent_t *ent)
1107 int namelen = strlen (name);
1108 char *tmp;
1110 /* first call, setup cache */
1111 if (ent->blacklist.size == 0)
1113 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1114 ent->blacklist.data = malloc (ent->blacklist.size);
1115 if (ent->blacklist.data == NULL)
1116 return;
1117 ent->blacklist.data[0] = '|';
1118 ent->blacklist.data[1] = '\0';
1119 ent->blacklist.current = 1;
1121 else
1123 if (in_blacklist (name, namelen, ent))
1124 return; /* no duplicates */
1126 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1128 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1129 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1130 if (tmp == NULL)
1132 free (ent->blacklist.data);
1133 ent->blacklist.size = 0;
1134 return;
1136 ent->blacklist.data = tmp;
1140 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1141 *tmp++ = '|';
1142 *tmp = '\0';
1143 ent->blacklist.current += namelen + 1;
1145 return;
1148 /* Returns TRUE if ent->blacklist contains name, else FALSE. */
1149 static bool_t
1150 in_blacklist (const char *name, int namelen, ent_t *ent)
1152 char buf[namelen + 3];
1153 char *cp;
1155 if (ent->blacklist.data == NULL)
1156 return FALSE;
1158 buf[0] = '|';
1159 cp = stpcpy (&buf[1], name);
1160 *cp++= '|';
1161 *cp = '\0';
1162 return strstr (ent->blacklist.data, buf) != NULL;