Update.
[glibc.git] / nis / nss_compat / compat-pwd.c
blobeec2634f3ab1ab0d9d17ff7b377e3482d06908f0
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 <pwd.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <netdb.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 pwd table */
40 static size_t pwdtablelen = 0;
42 /* Get the declaration of the parser function. */
43 #define ENTNAME pwent
44 #define STRUCTURE passwd
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 passwd 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, NULL, NULL, NULL}};
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_pwd_free (struct passwd *pwd)
86 if (pwd->pw_name != NULL)
87 free (pwd->pw_name);
88 if (pwd->pw_passwd != NULL)
89 free (pwd->pw_passwd);
90 if (pwd->pw_gecos != NULL)
91 free (pwd->pw_gecos);
92 if (pwd->pw_dir != NULL)
93 free (pwd->pw_dir);
94 if (pwd->pw_shell != NULL)
95 free (pwd->pw_shell);
97 memset (pwd, '\0', sizeof (struct passwd));
100 static size_t
101 pwd_need_buflen (struct passwd *pwd)
103 size_t len = 0;
105 if (pwd->pw_passwd != NULL)
106 len += strlen (pwd->pw_passwd) + 1;
108 if (pwd->pw_gecos != NULL)
109 len += strlen (pwd->pw_gecos) + 1;
111 if (pwd->pw_dir != NULL)
112 len += strlen (pwd->pw_dir) + 1;
114 if (pwd->pw_shell != NULL)
115 len += strlen (pwd->pw_shell) + 1;
117 return len;
120 static void
121 copy_pwd_changes (struct passwd *dest, struct passwd *src,
122 char *buffer, size_t buflen)
124 if (src->pw_passwd != NULL && strlen (src->pw_passwd))
126 if (buffer == NULL)
127 dest->pw_passwd = strdup (src->pw_passwd);
128 else if (dest->pw_passwd &&
129 strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
130 strcpy (dest->pw_passwd, src->pw_passwd);
131 else
133 dest->pw_passwd = buffer;
134 strcpy (dest->pw_passwd, src->pw_passwd);
135 buffer += strlen (dest->pw_passwd) + 1;
136 buflen = buflen - (strlen (dest->pw_passwd) + 1);
140 if (src->pw_gecos != NULL && strlen (src->pw_gecos))
142 if (buffer == NULL)
143 dest->pw_gecos = strdup (src->pw_gecos);
144 else if (dest->pw_gecos &&
145 strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
146 strcpy (dest->pw_gecos, src->pw_gecos);
147 else
149 dest->pw_gecos = buffer;
150 strcpy (dest->pw_gecos, src->pw_gecos);
151 buffer += strlen (dest->pw_gecos) + 1;
152 buflen = buflen - (strlen (dest->pw_gecos) + 1);
155 if (src->pw_dir != NULL && strlen (src->pw_dir))
157 if (buffer == NULL)
158 dest->pw_dir = strdup (src->pw_dir);
159 else if (dest->pw_dir &&
160 strlen (dest->pw_dir) >= strlen (src->pw_dir))
161 strcpy (dest->pw_dir, src->pw_dir);
162 else
164 dest->pw_dir = buffer;
165 strcpy (dest->pw_dir, src->pw_dir);
166 buffer += strlen (dest->pw_dir) + 1;
167 buflen = buflen - (strlen (dest->pw_dir) + 1);
171 if (src->pw_shell != NULL && strlen (src->pw_shell))
173 if (buffer == NULL)
174 dest->pw_shell = strdup (src->pw_shell);
175 else if (dest->pw_shell &&
176 strlen (dest->pw_shell) >= strlen (src->pw_shell))
177 strcpy (dest->pw_shell, src->pw_shell);
178 else
180 dest->pw_shell = buffer;
181 strcpy (dest->pw_shell, src->pw_shell);
182 buffer += strlen (dest->pw_shell) + 1;
183 buflen = buflen - (strlen (dest->pw_shell) + 1);
188 static enum nss_status
189 internal_setpwent (ent_t *ent)
191 enum nss_status status = NSS_STATUS_SUCCESS;
193 ent->nis = ent->first = ent->netgroup = 0;
195 /* If something was left over free it. */
196 if (ent->netgroup)
197 __internal_endnetgrent (&ent->netgrdata);
199 if (ent->oldkey != NULL)
201 free (ent->oldkey);
202 ent->oldkey = NULL;
203 ent->oldkeylen = 0;
206 if (ent->result != NULL)
208 nis_freeresult (ent->result);
209 ent->result = NULL;
212 if (pwdtable == NULL)
214 static const char key[] = "passwd.org_dir.";
215 const char *local_dir = nis_local_directory ();
216 size_t len_local_dir = strlen (local_dir);
218 pwdtable = malloc (sizeof (key) + len_local_dir);
219 if (pwdtable == NULL)
220 return NSS_STATUS_TRYAGAIN;
222 pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
223 key, sizeof (key) - 1),
224 local_dir, len_local_dir + 1)
225 - pwdtable) - 1;
228 if (ent->blacklist.data != NULL)
230 ent->blacklist.current = 1;
231 ent->blacklist.data[0] = '|';
232 ent->blacklist.data[1] = '\0';
234 else
235 ent->blacklist.current = 0;
237 if (ent->stream == NULL)
239 ent->stream = fopen ("/etc/passwd", "r");
241 if (ent->stream == NULL)
242 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
243 else
245 /* We have to make sure the file is `closed on exec'. */
246 int result, flags;
248 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
249 if (result >= 0)
251 flags |= FD_CLOEXEC;
252 result = fcntl (fileno (ent->stream), F_SETFD, flags);
254 if (result < 0)
256 /* Something went wrong. Close the stream and return a
257 failure. */
258 fclose (ent->stream);
259 ent->stream = NULL;
260 status = NSS_STATUS_UNAVAIL;
264 else
265 rewind (ent->stream);
267 give_pwd_free (&ent->pwd);
269 return status;
273 enum nss_status
274 _nss_compat_setpwent (void)
276 enum nss_status result;
278 __libc_lock_lock (lock);
280 if (ni == NULL)
282 __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
283 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
286 result = internal_setpwent (&ext_ent);
288 __libc_lock_unlock (lock);
290 return result;
294 static enum nss_status
295 internal_endpwent (ent_t *ent)
297 if (ent->stream != NULL)
299 fclose (ent->stream);
300 ent->stream = NULL;
303 if (ent->netgroup)
304 __internal_endnetgrent (&ent->netgrdata);
306 ent->nis = ent->first = ent->netgroup = 0;
308 if (ent->oldkey != NULL)
310 free (ent->oldkey);
311 ent->oldkey = NULL;
312 ent->oldkeylen = 0;
315 if (ent->result != NULL)
317 nis_freeresult (ent->result);
318 ent->result = NULL;
321 if (ent->blacklist.data != NULL)
323 ent->blacklist.current = 1;
324 ent->blacklist.data[0] = '|';
325 ent->blacklist.data[1] = '\0';
327 else
328 ent->blacklist.current = 0;
330 give_pwd_free (&ent->pwd);
332 return NSS_STATUS_SUCCESS;
335 enum nss_status
336 _nss_compat_endpwent (void)
338 enum nss_status result;
340 __libc_lock_lock (lock);
342 result = internal_endpwent (&ext_ent);
344 __libc_lock_unlock (lock);
346 return result;
349 static enum nss_status
350 getpwent_next_nis_netgr (const char *name, struct passwd *result, ent_t *ent,
351 char *group, char *buffer, size_t buflen, int *errnop)
353 struct parser_data *data = (void *) buffer;
354 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
355 int status, outvallen;
356 size_t p2len;
358 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
360 ent->netgroup = 0;
361 ent->first = 0;
362 give_pwd_free (&ent->pwd);
363 return NSS_STATUS_UNAVAIL;
366 if (ent->first == TRUE)
368 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
369 __internal_setnetgrent (group, &ent->netgrdata);
370 ent->first = FALSE;
373 while (1)
375 char *saved_cursor;
376 int parse_res;
378 saved_cursor = ent->netgrdata.cursor;
379 status = __internal_getnetgrent_r (&host, &user, &domain,
380 &ent->netgrdata, buffer, buflen,
381 errnop);
382 if (status != 1)
384 __internal_endnetgrent (&ent->netgrdata);
385 ent->netgroup = 0;
386 give_pwd_free (&ent->pwd);
387 return NSS_STATUS_RETURN;
390 if (user == NULL || user[0] == '-')
391 continue;
393 if (domain != NULL && strcmp (ypdomain, domain) != 0)
394 continue;
396 /* If name != NULL, we are called from getpwnam. */
397 if (name != NULL)
398 if (strcmp (user, name) != 0)
399 continue;
401 if (yp_match (ypdomain, "passwd.byname", user,
402 strlen (user), &outval, &outvallen)
403 != YPERR_SUCCESS)
404 continue;
406 p2len = pwd_need_buflen (&ent->pwd);
407 if (p2len > buflen)
409 free (outval);
410 *errnop = ERANGE;
411 return NSS_STATUS_TRYAGAIN;
413 p2 = buffer + (buflen - p2len);
414 buflen -= p2len;
416 if (buflen < ((size_t) outvallen + 1))
418 free (outval);
419 *errnop = ERANGE;
420 return NSS_STATUS_TRYAGAIN;
422 p = strncpy (buffer, outval, buflen);
424 while (isspace (*p))
425 p++;
426 free (outval);
427 parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
428 if (parse_res == -1)
430 ent->netgrdata.cursor = saved_cursor;
431 return NSS_STATUS_TRYAGAIN;
434 if (parse_res)
436 /* Store the User in the blacklist for the "+" at the end of
437 /etc/passwd */
438 blacklist_store_name (result->pw_name, ent);
439 copy_pwd_changes (result, &ent->pwd, p2, p2len);
440 break;
444 return NSS_STATUS_SUCCESS;
447 static enum nss_status
448 getpwent_next_nisplus_netgr (const char *name, struct passwd *result,
449 ent_t *ent, char *group, char *buffer,
450 size_t buflen, int *errnop)
452 char *ypdomain, *host, *user, *domain, *p2;
453 int status, parse_res;
454 size_t p2len;
455 nis_result *nisres;
457 /* Maybe we should use domainname here ? We need the current
458 domainname for the domain field in netgroups */
459 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
461 ent->netgroup = 0;
462 ent->first = 0;
463 give_pwd_free (&ent->pwd);
464 return NSS_STATUS_UNAVAIL;
467 if (ent->first == TRUE)
469 bzero (&ent->netgrdata, sizeof (struct __netgrent));
470 __internal_setnetgrent (group, &ent->netgrdata);
471 ent->first = FALSE;
474 while (1)
476 char *saved_cursor;
478 saved_cursor = ent->netgrdata.cursor;
479 status = __internal_getnetgrent_r (&host, &user, &domain,
480 &ent->netgrdata, buffer, buflen,
481 errnop);
482 if (status != 1)
484 __internal_endnetgrent (&ent->netgrdata);
485 ent->netgroup = 0;
486 give_pwd_free (&ent->pwd);
487 return NSS_STATUS_RETURN;
490 if (user == NULL || user[0] == '-')
491 continue;
493 if (domain != NULL && strcmp (ypdomain, domain) != 0)
494 continue;
496 /* If name != NULL, we are called from getpwnam */
497 if (name != NULL)
498 if (strcmp (user, name) != 0)
499 continue;
501 p2len = pwd_need_buflen (&ent->pwd);
502 if (p2len > buflen)
504 *errnop = ERANGE;
505 return NSS_STATUS_TRYAGAIN;
507 p2 = buffer + (buflen - p2len);
508 buflen -= p2len;
510 char buf[strlen (user) + 30 + pwdtablelen];
511 sprintf(buf, "[name=%s],%s", user, pwdtable);
512 nisres = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
514 if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
516 nis_freeresult (nisres);
517 continue;
519 parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer,
520 buflen, errnop);
521 if (parse_res == -1)
523 nis_freeresult (nisres);
524 ent->netgrdata.cursor = saved_cursor;
525 *errnop = ERANGE;
526 return NSS_STATUS_TRYAGAIN;
528 nis_freeresult (nisres);
530 if (parse_res)
532 /* Store the User in the blacklist for the "+" at the end of
533 /etc/passwd */
534 blacklist_store_name (result->pw_name, ent);
535 copy_pwd_changes (result, &ent->pwd, p2, p2len);
536 break;
540 return NSS_STATUS_SUCCESS;
543 /* get the next user from NIS+ (+ entry) */
544 static enum nss_status
545 getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
546 size_t buflen, int *errnop)
548 int parse_res;
549 size_t p2len;
550 char *p2;
552 p2len = pwd_need_buflen (&ent->pwd);
553 if (p2len > buflen)
555 *errnop = ERANGE;
556 return NSS_STATUS_TRYAGAIN;
558 p2 = buffer + (buflen - p2len);
559 buflen -= p2len;
562 bool_t saved_first;
563 nis_result *saved_res;
565 if (ent->first)
567 saved_first = TRUE;
568 saved_res = ent->result;
570 ent->result = nis_first_entry (pwdtable);
571 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
573 ent->nis = 0;
574 give_pwd_free (&ent->pwd);
575 return niserr2nss (ent->result->status);
577 ent->first = FALSE;
579 else
581 nis_result *res;
583 res = nis_next_entry (pwdtable, &ent->result->cookie);
584 saved_res = ent->result;
585 saved_first = FALSE;
586 ent->result = res;
587 if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
589 ent->nis = 0;
590 nis_freeresult (saved_res);
591 give_pwd_free (&ent->pwd);
592 return niserr2nss (ent->result->status);
595 parse_res = _nss_nisplus_parse_pwent (ent->result, result, buffer,
596 buflen, errnop);
597 if (parse_res == -1)
599 nis_freeresult (ent->result);
600 ent->result = saved_res;
601 ent->first = saved_first;
602 *errnop = ERANGE;
603 return NSS_STATUS_TRYAGAIN;
605 else
607 if (!saved_first)
608 nis_freeresult (saved_res);
611 if (parse_res &&
612 in_blacklist (result->pw_name, strlen (result->pw_name), ent))
613 parse_res = 0; /* if result->pw_name in blacklist,search next entry */
615 while (!parse_res);
617 copy_pwd_changes (result, &ent->pwd, p2, p2len);
619 return NSS_STATUS_SUCCESS;
622 static enum nss_status
623 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
624 size_t buflen, int *errnop)
626 struct parser_data *data = (void *) buffer;
627 char *domain, *outkey, *outval, *p, *p2;
628 int outkeylen, outvallen, parse_res;
629 size_t p2len;
631 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
633 ent->nis = 0;
634 give_pwd_free (&ent->pwd);
635 return NSS_STATUS_UNAVAIL;
638 p2len = pwd_need_buflen (&ent->pwd);
639 if (p2len > buflen)
641 *errnop = ERANGE;
642 return NSS_STATUS_TRYAGAIN;
644 p2 = buffer + (buflen - p2len);
645 buflen -= p2len;
648 bool_t saved_first;
649 char *saved_oldkey;
650 int saved_oldlen;
652 if (ent->first)
654 if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
655 &outval, &outvallen) != YPERR_SUCCESS)
657 ent->nis = 0;
658 give_pwd_free (&ent->pwd);
659 return NSS_STATUS_UNAVAIL;
662 if (buflen < ((size_t) outvallen + 1))
664 free (outval);
665 *errnop = ERANGE;
666 return NSS_STATUS_TRYAGAIN;
669 saved_first = TRUE;
670 saved_oldkey = ent->oldkey;
671 saved_oldlen = ent->oldkeylen;
672 ent->oldkey = outkey;
673 ent->oldkeylen = outkeylen;
674 ent->first = FALSE;
676 else
678 if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
679 &outkey, &outkeylen, &outval, &outvallen)
680 != YPERR_SUCCESS)
682 ent->nis = 0;
683 give_pwd_free (&ent->pwd);
684 return NSS_STATUS_NOTFOUND;
687 if (buflen < ((size_t) outvallen + 1))
689 free (outval);
690 *errnop = ERANGE;
691 return NSS_STATUS_TRYAGAIN;
694 saved_first = FALSE;
695 saved_oldkey = ent->oldkey;
696 saved_oldlen = ent->oldkeylen;
697 ent->oldkey = outkey;
698 ent->oldkeylen = outkeylen;
701 /* Copy the found data to our buffer */
702 p = strncpy (buffer, outval, buflen);
704 /* ...and free the data. */
705 free (outval);
707 while (isspace (*p))
708 ++p;
709 parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
710 if (parse_res == -1)
712 free (ent->oldkey);
713 ent->oldkey = saved_oldkey;
714 ent->oldkeylen = saved_oldlen;
715 ent->first = saved_first;
716 *errnop = ERANGE;
717 return NSS_STATUS_TRYAGAIN;
719 else
721 if (!saved_first)
722 free (saved_oldkey);
724 if (parse_res
725 && in_blacklist (result->pw_name, strlen (result->pw_name), ent))
726 parse_res = 0;
728 while (!parse_res);
730 copy_pwd_changes (result, &ent->pwd, p2, p2len);
732 return NSS_STATUS_SUCCESS;
735 /* This function handle the +user entrys in /etc/passwd */
736 static enum nss_status
737 getpwnam_plususer (const char *name, struct passwd *result, char *buffer,
738 size_t buflen, int *errnop)
740 struct parser_data *data = (void *) buffer;
741 struct passwd pwd;
742 int parse_res;
743 char *p;
744 size_t plen;
746 memset (&pwd, '\0', sizeof (struct passwd));
748 copy_pwd_changes (&pwd, result, NULL, 0);
750 plen = pwd_need_buflen (&pwd);
751 if (plen > buflen)
753 *errnop = ERANGE;
754 return NSS_STATUS_TRYAGAIN;
756 p = buffer + (buflen - plen);
757 buflen -= plen;
759 if (use_nisplus) /* Do the NIS+ query here */
761 nis_result *res;
762 char buf[strlen (name) + 24 + pwdtablelen];
764 sprintf(buf, "[name=%s],%s", name, pwdtable);
765 res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
766 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
768 enum nss_status status = niserr2nss (res->status);
770 nis_freeresult (res);
771 return status;
773 parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
774 buflen, errnop);
775 if (parse_res == -1)
777 nis_freeresult (res);
778 *errnop = ERANGE;
779 return NSS_STATUS_TRYAGAIN;
781 nis_freeresult (res);
783 else /* Use NIS */
785 char *domain, *outval, *ptr;
786 int outvallen;
788 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
789 return NSS_STATUS_NOTFOUND;
791 if (yp_match (domain, "passwd.byname", name, strlen (name),
792 &outval, &outvallen) != YPERR_SUCCESS)
793 return NSS_STATUS_NOTFOUND;
795 if (buflen < ((size_t) outvallen + 1))
797 free (outval);
798 *errnop = ERANGE;
799 return NSS_STATUS_TRYAGAIN;
801 ptr = strncpy (buffer, outval, buflen);
802 free (outval);
803 while (isspace (*ptr))
804 ptr++;
805 parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
806 if (parse_res == -1)
807 return NSS_STATUS_TRYAGAIN;
810 if (parse_res > 0)
812 copy_pwd_changes (result, &pwd, p, plen);
813 give_pwd_free (&pwd);
814 /* We found the entry. */
815 return NSS_STATUS_SUCCESS;
817 else
819 /* Give buffer the old len back */
820 buflen += plen;
821 give_pwd_free (&pwd);
823 return NSS_STATUS_RETURN;
826 static enum nss_status
827 getpwent_next_file (struct passwd *result, ent_t *ent,
828 char *buffer, size_t buflen, int *errnop)
830 struct parser_data *data = (void *) buffer;
831 while (1)
833 fpos_t pos;
834 char *p;
835 int parse_res;
839 fgetpos (ent->stream, &pos);
840 buffer[buflen - 1] = '\xff';
841 p = fgets (buffer, buflen, ent->stream);
842 if (p == NULL && feof (ent->stream))
843 return NSS_STATUS_NOTFOUND;
844 if (p == NULL || buffer[buflen - 1] != '\xff')
846 fsetpos (ent->stream, &pos);
847 *errnop = ERANGE;
848 return NSS_STATUS_TRYAGAIN;
851 /* Terminate the line for any case. */
852 buffer[buflen - 1] = '\0';
854 /* Skip leading blanks. */
855 while (isspace (*p))
856 ++p;
858 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
859 /* Parse the line. If it is invalid, loop to
860 get the next line of the file to parse. */
861 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
862 errnop)));
864 if (parse_res == -1)
866 /* The parser ran out of space. */
867 fsetpos (ent->stream, &pos);
868 *errnop = ERANGE;
869 return NSS_STATUS_TRYAGAIN;
872 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
873 /* This is a real entry. */
874 break;
876 /* -@netgroup */
877 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
878 && result->pw_name[2] != '\0')
880 /* XXX Do not use fixed length buffer. */
881 char buf2[1024];
882 char *user, *host, *domain;
883 struct __netgrent netgrdata;
885 bzero (&netgrdata, sizeof (struct __netgrent));
886 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
887 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
888 buf2, sizeof (buf2), errnop))
890 if (user != NULL && user[0] != '-')
891 blacklist_store_name (user, ent);
893 __internal_endnetgrent (&netgrdata);
894 continue;
897 /* +@netgroup */
898 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
899 && result->pw_name[2] != '\0')
901 int status;
903 ent->netgroup = TRUE;
904 ent->first = TRUE;
905 copy_pwd_changes (&ent->pwd, result, NULL, 0);
907 if (use_nisplus)
908 status = getpwent_next_nisplus_netgr (NULL, result, ent,
909 &result->pw_name[2],
910 buffer, buflen, errnop);
911 else
912 status = getpwent_next_nis_netgr (NULL, result, ent,
913 &result->pw_name[2],
914 buffer, buflen, errnop);
915 if (status == NSS_STATUS_RETURN)
916 continue;
917 else
918 return status;
921 /* -user */
922 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
923 && result->pw_name[1] != '@')
925 blacklist_store_name (&result->pw_name[1], ent);
926 continue;
929 /* +user */
930 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
931 && result->pw_name[1] != '@')
933 enum nss_status status;
935 /* Store the User in the blacklist for the "+" at the end of
936 /etc/passwd */
937 blacklist_store_name (&result->pw_name[1], ent);
938 status = getpwnam_plususer (&result->pw_name[1], result, buffer,
939 buflen, errnop);
940 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
941 break;
942 else
943 if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
944 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
945 continue;
946 else
948 if (status == NSS_STATUS_TRYAGAIN)
950 /* The parser ran out of space */
951 fsetpos (ent->stream, &pos);
952 *errnop = ERANGE;
954 return status;
958 /* +:... */
959 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
961 ent->nis = TRUE;
962 ent->first = TRUE;
963 copy_pwd_changes (&ent->pwd, result, NULL, 0);
965 if (use_nisplus)
966 return getpwent_next_nisplus (result, ent, buffer, buflen, errnop);
967 else
968 return getpwent_next_nis (result, ent, buffer, buflen, errnop);
972 return NSS_STATUS_SUCCESS;
976 static enum nss_status
977 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
978 size_t buflen, int *errnop)
980 if (ent->netgroup)
982 int status;
984 /* We are searching members in a netgroup */
985 /* Since this is not the first call, we don't need the group name */
986 if (use_nisplus)
987 status = getpwent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
988 buflen, errnop);
989 else
990 status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
991 errnop);
992 if (status == NSS_STATUS_RETURN)
993 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
994 else
995 return status;
997 else
998 if (ent->nis)
1000 if (use_nisplus)
1001 return getpwent_next_nisplus (pw, ent, buffer, buflen, errnop);
1002 else
1003 return getpwent_next_nis (pw, ent, buffer, buflen, errnop);
1005 else
1006 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
1009 enum nss_status
1010 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
1011 int *errnop)
1013 enum nss_status status = NSS_STATUS_SUCCESS;
1015 __libc_lock_lock (lock);
1017 if (ni == NULL)
1019 __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1020 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1023 /* Be prepared that the setpwent function was not called before. */
1024 if (ext_ent.stream == NULL)
1025 status = internal_setpwent (&ext_ent);
1027 if (status == NSS_STATUS_SUCCESS)
1028 status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
1030 __libc_lock_unlock (lock);
1032 return status;
1035 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1036 static enum nss_status
1037 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
1038 char *buffer, size_t buflen, int *errnop)
1040 struct parser_data *data = (void *) buffer;
1042 while (1)
1044 fpos_t pos;
1045 char *p;
1046 int parse_res;
1050 fgetpos (ent->stream, &pos);
1051 buffer[buflen - 1] = '\xff';
1052 p = fgets (buffer, buflen, ent->stream);
1053 if (p == NULL && feof (ent->stream))
1054 return NSS_STATUS_NOTFOUND;
1055 if (p == NULL || buffer[buflen - 1] != '\xff')
1057 fsetpos (ent->stream, &pos);
1058 *errnop = ERANGE;
1059 return NSS_STATUS_TRYAGAIN;
1062 /* Terminate the line for any case. */
1063 buffer[buflen - 1] = '\0';
1065 /* Skip leading blanks. */
1066 while (isspace (*p))
1067 ++p;
1069 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1070 /* Parse the line. If it is invalid, loop to
1071 get the next line of the file to parse. */
1072 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1073 errnop)));
1075 if (parse_res == -1)
1077 /* The parser ran out of space. */
1078 fsetpos (ent->stream, &pos);
1079 *errnop = ERANGE;
1080 return NSS_STATUS_TRYAGAIN;
1083 /* This is a real entry. */
1084 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1086 if (strcmp (result->pw_name, name) == 0)
1087 return NSS_STATUS_SUCCESS;
1088 else
1089 continue;
1092 /* -@netgroup */
1093 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1094 && result->pw_name[2] != '\0')
1096 /* XXX Do not use fixed length buffers. */
1097 char buf2[1024];
1098 char *user, *host, *domain;
1099 struct __netgrent netgrdata;
1101 bzero (&netgrdata, sizeof (struct __netgrent));
1102 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1103 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1104 buf2, sizeof (buf2), errnop))
1106 if (user != NULL && user[0] != '-')
1107 if (strcmp (user, name) == 0)
1108 return NSS_STATUS_NOTFOUND;
1110 __internal_endnetgrent (&netgrdata);
1111 continue;
1114 /* +@netgroup */
1115 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1116 && result->pw_name[2] != '\0')
1118 char buf[strlen (result->pw_name)];
1119 int status;
1121 strcpy (buf, &result->pw_name[2]);
1122 ent->netgroup = TRUE;
1123 ent->first = TRUE;
1124 copy_pwd_changes (&ent->pwd, result, NULL, 0);
1128 if (use_nisplus)
1129 status = getpwent_next_nisplus_netgr (name, result, ent, buf,
1130 buffer, buflen, errnop);
1131 else
1132 status = getpwent_next_nis_netgr (name, result, ent, buf,
1133 buffer, buflen, errnop);
1134 if (status == NSS_STATUS_RETURN)
1135 continue;
1137 if (status == NSS_STATUS_SUCCESS &&
1138 strcmp (result->pw_name, name) == 0)
1139 return NSS_STATUS_SUCCESS;
1140 } while (status == NSS_STATUS_SUCCESS);
1141 continue;
1144 /* -user */
1145 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1146 && result->pw_name[1] != '@')
1148 if (strcmp (&result->pw_name[1], name) == 0)
1149 return NSS_STATUS_NOTFOUND;
1150 else
1151 continue;
1154 /* +user */
1155 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1156 && result->pw_name[1] != '@')
1158 if (strcmp (name, &result->pw_name[1]) == 0)
1160 enum nss_status status;
1162 status = getpwnam_plususer (name, result, buffer, buflen,
1163 errnop);
1164 if (status == NSS_STATUS_RETURN)
1165 /* We couldn't parse the entry */
1166 return NSS_STATUS_NOTFOUND;
1167 else
1168 return status;
1172 /* +:... */
1173 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1175 enum nss_status status;
1177 status = getpwnam_plususer (name, result, buffer, buflen, errnop);
1178 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1179 break;
1180 else
1181 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1182 return NSS_STATUS_NOTFOUND;
1183 else
1184 return status;
1187 return NSS_STATUS_SUCCESS;
1190 enum nss_status
1191 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
1192 char *buffer, size_t buflen, int *errnop)
1194 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1195 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1196 enum nss_status status;
1198 if (name[0] == '-' || name[0] == '+')
1199 return NSS_STATUS_NOTFOUND;
1201 __libc_lock_lock (lock);
1203 if (ni == NULL)
1205 __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1206 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1209 __libc_lock_unlock (lock);
1211 status = internal_setpwent (&ent);
1212 if (status != NSS_STATUS_SUCCESS)
1213 return status;
1215 status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
1217 internal_endpwent (&ent);
1219 return status;
1222 /* This function handle the + entry in /etc/passwd for getpwuid */
1223 static enum nss_status
1224 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
1225 size_t buflen, int *errnop)
1227 struct parser_data *data = (void *) buffer;
1228 struct passwd pwd;
1229 int parse_res;
1230 char *p;
1231 size_t plen;
1233 memset (&pwd, '\0', sizeof (struct passwd));
1235 copy_pwd_changes (&pwd, result, NULL, 0);
1237 plen = pwd_need_buflen (&pwd);
1238 if (plen > buflen)
1240 *errnop = ERANGE;
1241 return NSS_STATUS_TRYAGAIN;
1243 p = buffer + (buflen - plen);
1244 buflen -= plen;
1246 if (use_nisplus) /* Do the NIS+ query here */
1248 nis_result *res;
1249 char buf[1024 + pwdtablelen];
1251 snprintf(buf, sizeof (buf), "[uid=%d],%s", uid, pwdtable);
1252 res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
1253 if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
1255 enum nss_status status = niserr2nss (res->status);
1257 nis_freeresult (res);
1258 return status;
1260 if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
1261 buflen, errnop)) == -1)
1263 nis_freeresult (res);
1264 *errnop = ERANGE;
1265 return NSS_STATUS_TRYAGAIN;
1267 nis_freeresult (res);
1269 else /* Use NIS */
1271 char buf[1024];
1272 char *domain, *outval, *ptr;
1273 int outvallen;
1275 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
1277 *errnop = errno;
1278 return NSS_STATUS_TRYAGAIN;
1281 sprintf (buf, "%d", uid);
1282 if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
1283 &outval, &outvallen)
1284 != YPERR_SUCCESS)
1286 *errnop = errno;
1287 return NSS_STATUS_TRYAGAIN;
1290 if ( buflen < ((size_t) outvallen + 1))
1292 free (outval);
1293 *errnop = ERANGE;
1294 return NSS_STATUS_TRYAGAIN;
1297 ptr = strncpy (buffer, outval, buflen);
1298 free (outval);
1300 while (isspace (*ptr))
1301 ptr++;
1302 parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
1303 if (parse_res == -1)
1304 return NSS_STATUS_TRYAGAIN;
1307 if (parse_res > 0)
1309 copy_pwd_changes (result, &pwd, p, plen);
1310 give_pwd_free (&pwd);
1311 /* We found the entry. */
1312 return NSS_STATUS_SUCCESS;
1314 else
1316 /* Give buffer the old len back */
1317 buflen += plen;
1318 give_pwd_free (&pwd);
1320 return NSS_STATUS_RETURN;
1323 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
1324 static enum nss_status
1325 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
1326 char *buffer, size_t buflen, int *errnop)
1328 struct parser_data *data = (void *) buffer;
1330 while (1)
1332 fpos_t pos;
1333 char *p;
1334 int parse_res;
1338 fgetpos (ent->stream, &pos);
1339 buffer[buflen - 1] = '\xff';
1340 p = fgets (buffer, buflen, ent->stream);
1341 if (p == NULL && feof (ent->stream))
1342 return NSS_STATUS_NOTFOUND;
1343 if (p == NULL || buffer[buflen - 1] != '\xff')
1345 fsetpos (ent->stream, &pos);
1346 *errnop = ERANGE;
1347 return NSS_STATUS_TRYAGAIN;
1350 /* Terminate the line for any case. */
1351 buffer[buflen - 1] = '\0';
1353 /* Skip leading blanks. */
1354 while (isspace (*p))
1355 ++p;
1357 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1358 /* Parse the line. If it is invalid, loop to
1359 get the next line of the file to parse. */
1360 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1361 errnop)));
1363 if (parse_res == -1)
1365 /* The parser ran out of space. */
1366 fsetpos (ent->stream, &pos);
1367 *errnop = ERANGE;
1368 return NSS_STATUS_TRYAGAIN;
1371 /* This is a real entry. */
1372 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1374 if (result->pw_uid == uid)
1375 return NSS_STATUS_SUCCESS;
1376 else
1377 continue;
1380 /* -@netgroup */
1381 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1382 && result->pw_name[2] != '\0')
1384 /* XXX Do not use fixed length buffers. */
1385 char buf2[1024];
1386 char *user, *host, *domain;
1387 struct __netgrent netgrdata;
1389 bzero (&netgrdata, sizeof (struct __netgrent));
1390 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1391 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1392 buf2, sizeof (buf2), errnop))
1394 if (user != NULL && user[0] != '-')
1395 blacklist_store_name (user, ent);
1397 __internal_endnetgrent (&netgrdata);
1398 continue;
1401 /* +@netgroup */
1402 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1403 && result->pw_name[2] != '\0')
1405 char buf[strlen (result->pw_name)];
1406 int status;
1408 strcpy (buf, &result->pw_name[2]);
1409 ent->netgroup = TRUE;
1410 ent->first = TRUE;
1411 copy_pwd_changes (&ent->pwd, result, NULL, 0);
1415 if (use_nisplus)
1416 status = getpwent_next_nisplus_netgr (NULL, result, ent, buf,
1417 buffer, buflen, errnop);
1418 else
1419 status = getpwent_next_nis_netgr (NULL, result, ent, buf,
1420 buffer, buflen, errnop);
1421 if (status == NSS_STATUS_RETURN)
1422 continue;
1424 if (status == NSS_STATUS_SUCCESS && uid == result->pw_uid)
1425 return NSS_STATUS_SUCCESS;
1426 } while (status == NSS_STATUS_SUCCESS);
1427 continue;
1430 /* -user */
1431 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1432 && result->pw_name[1] != '@')
1434 blacklist_store_name (&result->pw_name[1], ent);
1435 continue;
1438 /* +user */
1439 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1440 && result->pw_name[1] != '@')
1442 enum nss_status status;
1444 /* Store the User in the blacklist for the "+" at the end of
1445 /etc/passwd */
1446 blacklist_store_name (&result->pw_name[1], ent);
1447 status = getpwnam_plususer (&result->pw_name[1], result, buffer,
1448 buflen, errnop);
1449 if (status == NSS_STATUS_SUCCESS && result->pw_uid == uid)
1450 break;
1451 else
1452 continue;
1455 /* +:... */
1456 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1458 enum nss_status status;
1460 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1461 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1462 break;
1463 else
1464 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1465 return NSS_STATUS_NOTFOUND;
1466 else
1467 return status;
1470 return NSS_STATUS_SUCCESS;
1473 enum nss_status
1474 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1475 char *buffer, size_t buflen, int *errnop)
1477 ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1478 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1479 enum nss_status status;
1481 __libc_lock_lock (lock);
1483 if (ni == NULL)
1485 __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1486 use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1489 __libc_lock_unlock (lock);
1491 status = internal_setpwent (&ent);
1492 if (status != NSS_STATUS_SUCCESS)
1493 return status;
1495 status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1497 internal_endpwent (&ent);
1499 return status;
1503 /* Support routines for remembering -@netgroup and -user entries.
1504 The names are stored in a single string with `|' as separator. */
1505 static void
1506 blacklist_store_name (const char *name, ent_t *ent)
1508 int namelen = strlen (name);
1509 char *tmp;
1511 /* first call, setup cache */
1512 if (ent->blacklist.size == 0)
1514 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1515 ent->blacklist.data = malloc (ent->blacklist.size);
1516 if (ent->blacklist.data == NULL)
1517 return;
1518 ent->blacklist.data[0] = '|';
1519 ent->blacklist.data[1] = '\0';
1520 ent->blacklist.current = 1;
1522 else
1524 if (in_blacklist (name, namelen, ent))
1525 return; /* no duplicates */
1527 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1529 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1530 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1531 if (tmp == NULL)
1533 free (ent->blacklist.data);
1534 ent->blacklist.size = 0;
1535 return;
1537 ent->blacklist.data = tmp;
1541 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1542 *tmp++ = '|';
1543 *tmp = '\0';
1544 ent->blacklist.current += namelen + 1;
1546 return;
1549 /* returns TRUE if ent->blacklist contains name, else FALSE */
1550 static bool_t
1551 in_blacklist (const char *name, int namelen, ent_t *ent)
1553 char buf[namelen + 3];
1554 char *cp;
1556 if (ent->blacklist.data == NULL)
1557 return FALSE;
1559 buf[0] = '|';
1560 cp = stpcpy (&buf[1], name);
1561 *cp++= '|';
1562 *cp = '\0';
1563 return strstr (ent->blacklist.data, buf) != NULL;