Updated to fedora-glibc-20050627T0850
[glibc.git] / glibc-compat / nss_compat / compat-pwd.c
blob5857bf9f7810492e9bc5a808bbdf109d5fdb204b
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 <glibc-compat/include/pwd.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <glibc-compat/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 <nsswitch.h>
32 #include "netgroup.h"
34 /* Get the declaration of the parser function. */
35 #define ENTNAME pwent
36 #define STRUCTURE passwd
37 #define EXTERN_PARSER
38 #include "../nss_files/files-parse.c"
40 /* Structure for remembering -@netgroup and -user members ... */
41 #define BLACKLIST_INITIAL_SIZE 512
42 #define BLACKLIST_INCREMENT 256
43 struct blacklist_t
45 char *data;
46 int current;
47 int size;
50 struct ent_t
52 bool_t netgroup;
53 bool_t nis;
54 bool_t first;
55 char *oldkey;
56 int oldkeylen;
57 FILE *stream;
58 struct blacklist_t blacklist;
59 struct passwd pwd;
60 struct __netgrent netgrdata;
62 typedef struct ent_t ent_t;
64 static ent_t ext_ent = {0, 0, 0, NULL, 0, NULL, {NULL, 0, 0},
65 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
67 /* Protect global state against multiple changers. */
68 __libc_lock_define_initialized (static, lock)
70 /* Prototypes for local functions. */
71 static void blacklist_store_name (const char *, ent_t *);
72 static int in_blacklist (const char *, int, ent_t *);
74 static void
75 give_pwd_free (struct passwd *pwd)
77 if (pwd->pw_name != NULL)
78 free (pwd->pw_name);
79 if (pwd->pw_passwd != NULL)
80 free (pwd->pw_passwd);
81 if (pwd->pw_gecos != NULL)
82 free (pwd->pw_gecos);
83 if (pwd->pw_dir != NULL)
84 free (pwd->pw_dir);
85 if (pwd->pw_shell != NULL)
86 free (pwd->pw_shell);
88 memset (pwd, '\0', sizeof (struct passwd));
91 static size_t
92 pwd_need_buflen (struct passwd *pwd)
94 size_t len = 0;
96 if (pwd->pw_passwd != NULL)
97 len += strlen (pwd->pw_passwd) + 1;
99 if (pwd->pw_gecos != NULL)
100 len += strlen (pwd->pw_gecos) + 1;
102 if (pwd->pw_dir != NULL)
103 len += strlen (pwd->pw_dir) + 1;
105 if (pwd->pw_shell != NULL)
106 len += strlen (pwd->pw_shell) + 1;
108 return len;
111 static void
112 copy_pwd_changes (struct passwd *dest, struct passwd *src,
113 char *buffer, size_t buflen)
115 if (src->pw_passwd != NULL && strlen (src->pw_passwd))
117 if (buffer == NULL)
118 dest->pw_passwd = strdup (src->pw_passwd);
119 else if (dest->pw_passwd &&
120 strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
121 strcpy (dest->pw_passwd, src->pw_passwd);
122 else
124 dest->pw_passwd = buffer;
125 strcpy (dest->pw_passwd, src->pw_passwd);
126 buffer += strlen (dest->pw_passwd) + 1;
127 buflen = buflen - (strlen (dest->pw_passwd) + 1);
131 if (src->pw_gecos != NULL && strlen (src->pw_gecos))
133 if (buffer == NULL)
134 dest->pw_gecos = strdup (src->pw_gecos);
135 else if (dest->pw_gecos &&
136 strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
137 strcpy (dest->pw_gecos, src->pw_gecos);
138 else
140 dest->pw_gecos = buffer;
141 strcpy (dest->pw_gecos, src->pw_gecos);
142 buffer += strlen (dest->pw_gecos) + 1;
143 buflen = buflen - (strlen (dest->pw_gecos) + 1);
146 if (src->pw_dir != NULL && strlen (src->pw_dir))
148 if (buffer == NULL)
149 dest->pw_dir = strdup (src->pw_dir);
150 else if (dest->pw_dir &&
151 strlen (dest->pw_dir) >= strlen (src->pw_dir))
152 strcpy (dest->pw_dir, src->pw_dir);
153 else
155 dest->pw_dir = buffer;
156 strcpy (dest->pw_dir, src->pw_dir);
157 buffer += strlen (dest->pw_dir) + 1;
158 buflen = buflen - (strlen (dest->pw_dir) + 1);
162 if (src->pw_shell != NULL && strlen (src->pw_shell))
164 if (buffer == NULL)
165 dest->pw_shell = strdup (src->pw_shell);
166 else if (dest->pw_shell &&
167 strlen (dest->pw_shell) >= strlen (src->pw_shell))
168 strcpy (dest->pw_shell, src->pw_shell);
169 else
171 dest->pw_shell = buffer;
172 strcpy (dest->pw_shell, src->pw_shell);
173 buffer += strlen (dest->pw_shell) + 1;
174 buflen = buflen - (strlen (dest->pw_shell) + 1);
179 static enum nss_status
180 internal_setpwent (ent_t *ent)
182 enum nss_status status = NSS_STATUS_SUCCESS;
184 ent->nis = ent->first = ent->netgroup = 0;
186 /* If something was left over free it. */
187 if (ent->netgroup)
188 __internal_endnetgrent (&ent->netgrdata);
190 if (ent->oldkey != NULL)
192 free (ent->oldkey);
193 ent->oldkey = NULL;
194 ent->oldkeylen = 0;
197 if (ent->blacklist.data != NULL)
199 ent->blacklist.current = 1;
200 ent->blacklist.data[0] = '|';
201 ent->blacklist.data[1] = '\0';
203 else
204 ent->blacklist.current = 0;
206 if (ent->stream == NULL)
208 ent->stream = fopen ("/etc/passwd", "r");
210 if (ent->stream == NULL)
211 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
212 else
214 /* We have to make sure the file is `closed on exec'. */
215 int result, flags;
217 result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
218 if (result >= 0)
220 flags |= FD_CLOEXEC;
221 result = fcntl (fileno (ent->stream), F_SETFD, flags);
223 if (result < 0)
225 /* Something went wrong. Close the stream and return a
226 failure. */
227 fclose (ent->stream);
228 ent->stream = NULL;
229 status = NSS_STATUS_UNAVAIL;
233 else
234 rewind (ent->stream);
236 give_pwd_free (&ent->pwd);
238 return status;
242 enum nss_status
243 _nss_compat_setpwent (void)
245 enum nss_status result;
247 __libc_lock_lock (lock);
249 result = internal_setpwent (&ext_ent);
251 __libc_lock_unlock (lock);
253 return result;
257 static enum nss_status
258 internal_endpwent (ent_t *ent)
260 if (ent->stream != NULL)
262 fclose (ent->stream);
263 ent->stream = NULL;
266 if (ent->netgroup)
267 __internal_endnetgrent (&ent->netgrdata);
269 ent->nis = ent->first = ent->netgroup = 0;
271 if (ent->oldkey != NULL)
273 free (ent->oldkey);
274 ent->oldkey = NULL;
275 ent->oldkeylen = 0;
278 if (ent->blacklist.data != NULL)
280 ent->blacklist.current = 1;
281 ent->blacklist.data[0] = '|';
282 ent->blacklist.data[1] = '\0';
284 else
285 ent->blacklist.current = 0;
287 give_pwd_free (&ent->pwd);
289 return NSS_STATUS_SUCCESS;
292 enum nss_status
293 _nss_compat_endpwent (void)
295 enum nss_status result;
297 __libc_lock_lock (lock);
299 result = internal_endpwent (&ext_ent);
301 __libc_lock_unlock (lock);
303 return result;
306 static enum nss_status
307 getpwent_next_nis_netgr (const char *name, struct passwd *result, ent_t *ent,
308 char *group, char *buffer, size_t buflen)
310 struct parser_data *data = (void *) buffer;
311 char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
312 int status, outvallen;
313 size_t p2len;
315 if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
317 ent->netgroup = 0;
318 ent->first = 0;
319 give_pwd_free (&ent->pwd);
320 return NSS_STATUS_UNAVAIL;
323 if (ent->first == TRUE)
325 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
326 __internal_setnetgrent (group, &ent->netgrdata);
327 ent->first = FALSE;
330 while (1)
332 char *saved_cursor;
333 int parse_res;
335 saved_cursor = ent->netgrdata.cursor;
336 status = __internal_getnetgrent_r (&host, &user, &domain,
337 &ent->netgrdata, buffer, buflen,
338 &errno);
339 if (status != 1)
341 __internal_endnetgrent (&ent->netgrdata);
342 ent->netgroup = 0;
343 give_pwd_free (&ent->pwd);
344 return NSS_STATUS_RETURN;
347 if (user == NULL || user[0] == '-')
348 continue;
350 if (domain != NULL && strcmp (ypdomain, domain) != 0)
351 continue;
353 /* If name != NULL, we are called from getpwnam */
354 if (name != NULL)
355 if (strcmp (user, name) != 0)
356 continue;
358 if (yp_match (ypdomain, "passwd.byname", user,
359 strlen (user), &outval, &outvallen)
360 != YPERR_SUCCESS)
361 continue;
363 p2len = pwd_need_buflen (&ent->pwd);
364 if (p2len > buflen)
366 __set_errno (ERANGE);
367 return NSS_STATUS_TRYAGAIN;
369 p2 = buffer + (buflen - p2len);
370 buflen -= p2len;
371 p = strncpy (buffer, outval, buflen);
372 while (isspace (*p))
373 p++;
374 free (outval);
375 if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
377 ent->netgrdata.cursor = saved_cursor;
378 return NSS_STATUS_TRYAGAIN;
381 if (parse_res)
383 /* Store the User in the blacklist for the "+" at the end of
384 /etc/passwd */
385 blacklist_store_name (result->pw_name, ent);
386 copy_pwd_changes (result, &ent->pwd, p2, p2len);
387 break;
391 return NSS_STATUS_SUCCESS;
394 static enum nss_status
395 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
396 size_t buflen)
398 struct parser_data *data = (void *) buffer;
399 char *domain, *outkey, *outval, *p, *p2;
400 int outkeylen, outvallen, parse_res;
401 size_t p2len;
403 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
405 ent->nis = 0;
406 give_pwd_free (&ent->pwd);
407 return NSS_STATUS_UNAVAIL;
410 p2len = pwd_need_buflen (&ent->pwd);
411 if (p2len > buflen)
413 __set_errno (ERANGE);
414 return NSS_STATUS_TRYAGAIN;
416 p2 = buffer + (buflen - p2len);
417 buflen -= p2len;
420 bool_t saved_first;
421 char *saved_oldkey;
422 int saved_oldlen;
424 if (ent->first)
426 if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
427 &outval, &outvallen) != YPERR_SUCCESS)
429 ent->nis = 0;
430 give_pwd_free (&ent->pwd);
431 return NSS_STATUS_UNAVAIL;
434 saved_first = TRUE;
435 saved_oldkey = ent->oldkey;
436 saved_oldlen = ent->oldkeylen;
437 ent->oldkey = outkey;
438 ent->oldkeylen = outkeylen;
439 ent->first = FALSE;
441 else
443 if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
444 &outkey, &outkeylen, &outval, &outvallen)
445 != YPERR_SUCCESS)
447 ent->nis = 0;
448 give_pwd_free (&ent->pwd);
449 return NSS_STATUS_NOTFOUND;
452 saved_first = FALSE;
453 saved_oldkey = ent->oldkey;
454 saved_oldlen = ent->oldkeylen;
455 ent->oldkey = outkey;
456 ent->oldkeylen = outkeylen;
459 /* Copy the found data to our buffer */
460 p = strncpy (buffer, outval, buflen);
462 /* ...and free the data. */
463 free (outval);
465 while (isspace (*p))
466 ++p;
467 if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
469 free (ent->oldkey);
470 ent->oldkey = saved_oldkey;
471 ent->oldkeylen = saved_oldlen;
472 ent->first = saved_first;
473 __set_errno (ERANGE);
474 return NSS_STATUS_TRYAGAIN;
476 else
478 if (!saved_first)
479 free (saved_oldkey);
481 if (parse_res &&
482 in_blacklist (result->pw_name, strlen (result->pw_name), ent))
483 parse_res = 0;
485 while (!parse_res);
487 copy_pwd_changes (result, &ent->pwd, p2, p2len);
489 return NSS_STATUS_SUCCESS;
492 /* This function handle the +user entrys in /etc/passwd */
493 static enum nss_status
494 getpwnam_plususer (const char *name, struct passwd *result, char *buffer,
495 size_t buflen)
497 struct parser_data *data = (void *) buffer;
498 struct passwd pwd;
499 int parse_res;
500 char *p;
501 size_t plen;
502 char *domain, *outval, *ptr;
503 int outvallen;
505 memset (&pwd, '\0', sizeof (struct passwd));
507 copy_pwd_changes (&pwd, result, NULL, 0);
509 plen = pwd_need_buflen (&pwd);
510 if (plen > buflen)
512 __set_errno (ERANGE);
513 return NSS_STATUS_TRYAGAIN;
515 p = buffer + (buflen - plen);
516 buflen -= plen;
519 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
520 return NSS_STATUS_NOTFOUND;
522 if (yp_match (domain, "passwd.byname", name, strlen (name),
523 &outval, &outvallen) != YPERR_SUCCESS)
524 return NSS_STATUS_NOTFOUND;
525 ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
526 buflen : (size_t) outvallen);
527 buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
528 free (outval);
529 while (isspace (*ptr))
530 ptr++;
531 if ((parse_res = _nss_files_parse_pwent (ptr, result, data, buflen))
532 == -1)
534 __set_errno (ERANGE);
535 return NSS_STATUS_TRYAGAIN;
538 if (parse_res > 0)
540 copy_pwd_changes (result, &pwd, p, plen);
541 give_pwd_free (&pwd);
542 /* We found the entry. */
543 return NSS_STATUS_SUCCESS;
545 else
547 /* Give buffer the old len back */
548 buflen += plen;
549 give_pwd_free (&pwd);
551 return NSS_STATUS_RETURN;
554 /* get the next user from NIS+ (+ entry) */
555 static enum nss_status
556 getpwent_next_file (struct passwd *result, ent_t *ent,
557 char *buffer, size_t buflen)
559 struct parser_data *data = (void *) buffer;
560 while (1)
562 fpos_t pos;
563 char *p;
564 int parse_res;
568 fgetpos (ent->stream, &pos);
569 buffer[buflen - 1] = '\xff';
570 p = fgets (buffer, buflen, ent->stream);
571 if (p == NULL && feof (ent->stream))
572 return NSS_STATUS_NOTFOUND;
573 if (p == NULL || buffer[buflen - 1] != '\xff')
575 fsetpos (ent->stream, &pos);
576 __set_errno (ERANGE);
577 return NSS_STATUS_TRYAGAIN;
580 /* Terminate the line for any case. */
581 buffer[buflen - 1] = '\0';
583 /* Skip leading blanks. */
584 while (isspace (*p))
585 ++p;
587 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
588 /* Parse the line. If it is invalid, loop to
589 get the next line of the file to parse. */
590 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
592 if (parse_res == -1)
594 /* The parser ran out of space. */
595 fsetpos (ent->stream, &pos);
596 __set_errno (ERANGE);
597 return NSS_STATUS_TRYAGAIN;
600 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
601 /* This is a real entry. */
602 break;
604 /* -@netgroup */
605 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
606 && result->pw_name[2] != '\0')
608 char buf2[1024];
609 char *user, *host, *domain;
610 struct __netgrent netgrdata;
612 bzero (&netgrdata, sizeof (struct __netgrent));
613 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
614 while (__internal_getnetgrent_r (&host, &user, &domain,
615 &netgrdata, buf2, sizeof (buf2),
616 &errno))
618 if (user != NULL && user[0] != '-')
619 blacklist_store_name (user, ent);
621 __internal_endnetgrent (&netgrdata);
622 continue;
625 /* +@netgroup */
626 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
627 && result->pw_name[2] != '\0')
629 int status;
631 ent->netgroup = TRUE;
632 ent->first = TRUE;
633 copy_pwd_changes (&ent->pwd, result, NULL, 0);
635 status = getpwent_next_nis_netgr (NULL, result, ent,
636 &result->pw_name[2],
637 buffer, buflen);
638 if (status == NSS_STATUS_RETURN)
639 continue;
640 else
641 return status;
644 /* -user */
645 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
646 && result->pw_name[1] != '@')
648 blacklist_store_name (&result->pw_name[1], ent);
649 continue;
652 /* +user */
653 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
654 && result->pw_name[1] != '@')
656 enum nss_status status;
658 /* Store the User in the blacklist for the "+" at the end of
659 /etc/passwd */
660 blacklist_store_name (&result->pw_name[1], ent);
661 status = getpwnam_plususer (&result->pw_name[1], result, buffer,
662 buflen);
663 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
664 break;
665 else
666 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
667 continue;
668 else
669 return status;
672 /* +:... */
673 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
675 ent->nis = TRUE;
676 ent->first = TRUE;
677 copy_pwd_changes (&ent->pwd, result, NULL, 0);
679 return getpwent_next_nis (result, ent, buffer, buflen);
683 return NSS_STATUS_SUCCESS;
687 /* get the next user from NIS (+ entry) */
688 static enum nss_status
689 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
690 size_t buflen)
692 if (ent->netgroup)
694 int status;
696 /* We are searching members in a netgroup */
697 /* Since this is not the first call, we don't need the group name */
698 status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen);
699 if (status == NSS_STATUS_RETURN)
700 return getpwent_next_file (pw, ent, buffer, buflen);
701 else
702 return status;
704 else
705 if (ent->nis)
707 return getpwent_next_nis (pw, ent, buffer, buflen);
709 else
710 return getpwent_next_file (pw, ent, buffer, buflen);
713 enum nss_status
714 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen)
716 enum nss_status status = NSS_STATUS_SUCCESS;
718 __libc_lock_lock (lock);
720 /* Be prepared that the setpwent function was not called before. */
721 if (ext_ent.stream == NULL)
722 status = internal_setpwent (&ext_ent);
724 if (status == NSS_STATUS_SUCCESS)
725 status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen);
727 __libc_lock_unlock (lock);
729 return status;
732 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
733 static enum nss_status
734 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
735 char *buffer, size_t buflen)
737 struct parser_data *data = (void *) buffer;
739 while (1)
741 fpos_t pos;
742 char *p;
743 int parse_res;
747 fgetpos (ent->stream, &pos);
748 buffer[buflen - 1] = '\xff';
749 p = fgets (buffer, buflen, ent->stream);
750 if (p == NULL && feof (ent->stream))
751 return NSS_STATUS_NOTFOUND;
752 if (p == NULL || buffer[buflen - 1] != '\xff')
754 fsetpos (ent->stream, &pos);
755 __set_errno (ERANGE);
756 return NSS_STATUS_TRYAGAIN;
759 /* Terminate the line for any case. */
760 buffer[buflen - 1] = '\0';
762 /* Skip leading blanks. */
763 while (isspace (*p))
764 ++p;
766 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
767 /* Parse the line. If it is invalid, loop to
768 get the next line of the file to parse. */
769 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
771 if (parse_res == -1)
773 /* The parser ran out of space. */
774 fsetpos (ent->stream, &pos);
775 __set_errno (ERANGE);
776 return NSS_STATUS_TRYAGAIN;
779 /* This is a real entry. */
780 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
782 if (strcmp (result->pw_name, name) == 0)
783 return NSS_STATUS_SUCCESS;
784 else
785 continue;
788 /* -@netgroup */
789 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
790 && result->pw_name[2] != '\0')
792 char buf2[1024];
793 char *user, *host, *domain;
794 struct __netgrent netgrdata;
796 bzero (&netgrdata, sizeof (struct __netgrent));
797 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
798 while (__internal_getnetgrent_r (&host, &user, &domain,
799 &netgrdata, buf2, sizeof (buf2),
800 &errno))
802 if (user != NULL && user[0] != '-')
803 if (strcmp (user, name) == 0)
804 return NSS_STATUS_NOTFOUND;
806 __internal_endnetgrent (&netgrdata);
807 continue;
810 /* +@netgroup */
811 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
812 && result->pw_name[2] != '\0')
814 char buf[strlen (result->pw_name)];
815 int status;
817 strcpy (buf, &result->pw_name[2]);
818 ent->netgroup = TRUE;
819 ent->first = TRUE;
820 copy_pwd_changes (&ent->pwd, result, NULL, 0);
824 status = getpwent_next_nis_netgr (name, result, ent, buf,
825 buffer, buflen);
826 if (status == NSS_STATUS_RETURN)
827 continue;
829 if (status == NSS_STATUS_SUCCESS &&
830 strcmp (result->pw_name, name) == 0)
831 return NSS_STATUS_SUCCESS;
832 } while (status == NSS_STATUS_SUCCESS);
833 continue;
836 /* -user */
837 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
838 && result->pw_name[1] != '@')
840 if (strcmp (&result->pw_name[1], name) == 0)
841 return NSS_STATUS_NOTFOUND;
842 else
843 continue;
846 /* +user */
847 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
848 && result->pw_name[1] != '@')
850 if (strcmp (name, &result->pw_name[1]) == 0)
852 enum nss_status status;
854 status = getpwnam_plususer (name, result, buffer, buflen);
855 if (status == NSS_STATUS_RETURN)
856 /* We couldn't parse the entry */
857 return NSS_STATUS_NOTFOUND;
858 else
859 return status;
863 /* +:... */
864 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
866 enum nss_status status;
868 status = getpwnam_plususer (name, result, buffer, buflen);
869 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
870 break;
871 else
872 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
873 return NSS_STATUS_NOTFOUND;
874 else
875 return status;
878 return NSS_STATUS_SUCCESS;
881 enum nss_status
882 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
883 char *buffer, size_t buflen)
885 ent_t ent = {0, 0, 0, NULL, 0, NULL, {NULL, 0, 0},
886 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
887 enum nss_status status;
889 if (name[0] == '-' || name[0] == '+')
890 return NSS_STATUS_NOTFOUND;
892 status = internal_setpwent (&ent);
893 if (status != NSS_STATUS_SUCCESS)
894 return status;
896 status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen);
898 internal_endpwent (&ent);
900 return status;
903 /* This function handle the + entry in /etc/passwd for getpwuid */
904 static enum nss_status
905 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
906 size_t buflen)
908 struct parser_data *data = (void *) buffer;
909 struct passwd pwd;
910 int parse_res;
911 char *p;
912 size_t plen;
913 char buf[1024];
914 char *domain, *outval, *ptr;
915 int outvallen;
917 memset (&pwd, '\0', sizeof (struct passwd));
919 copy_pwd_changes (&pwd, result, NULL, 0);
921 plen = pwd_need_buflen (&pwd);
922 if (plen > buflen)
924 __set_errno (ERANGE);
925 return NSS_STATUS_TRYAGAIN;
927 p = buffer + (buflen - plen);
928 buflen -= plen;
931 if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
932 return NSS_STATUS_TRYAGAIN;
934 sprintf (buf, "%d", uid);
935 if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
936 &outval, &outvallen)
937 != YPERR_SUCCESS)
938 return NSS_STATUS_TRYAGAIN;
939 ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
940 buflen : (size_t) outvallen);
941 buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
942 free (outval);
943 while (isspace (*ptr))
944 ptr++;
945 if ((parse_res = _nss_files_parse_pwent (ptr, result, data, buflen))
946 == -1)
948 __set_errno (ERANGE);
949 return NSS_STATUS_TRYAGAIN;
952 if (parse_res > 0)
954 copy_pwd_changes (result, &pwd, p, plen);
955 give_pwd_free (&pwd);
956 /* We found the entry. */
957 return NSS_STATUS_SUCCESS;
959 else
961 /* Give buffer the old len back */
962 buflen += plen;
963 give_pwd_free (&pwd);
965 return NSS_STATUS_RETURN;
968 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
969 static enum nss_status
970 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
971 char *buffer, size_t buflen)
973 struct parser_data *data = (void *) buffer;
975 while (1)
977 fpos_t pos;
978 char *p;
979 int parse_res;
983 fgetpos (ent->stream, &pos);
984 buffer[buflen - 1] = '\xff';
985 p = fgets (buffer, buflen, ent->stream);
986 if (p == NULL && feof (ent->stream))
987 return NSS_STATUS_NOTFOUND;
988 if (p == NULL || buffer[buflen - 1] != '\xff')
990 fsetpos (ent->stream, &pos);
991 __set_errno (ERANGE);
992 return NSS_STATUS_TRYAGAIN;
995 /* Terminate the line for any case. */
996 buffer[buflen - 1] = '\0';
998 /* Skip leading blanks. */
999 while (isspace (*p))
1000 ++p;
1002 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
1003 /* Parse the line. If it is invalid, loop to
1004 get the next line of the file to parse. */
1005 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
1007 if (parse_res == -1)
1009 /* The parser ran out of space. */
1010 fsetpos (ent->stream, &pos);
1011 __set_errno (ERANGE);
1012 return NSS_STATUS_TRYAGAIN;
1015 /* This is a real entry. */
1016 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1018 if (result->pw_uid == uid)
1019 return NSS_STATUS_SUCCESS;
1020 else
1021 continue;
1024 /* -@netgroup */
1025 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1026 && result->pw_name[2] != '\0')
1028 char buf2[1024];
1029 char *user, *host, *domain;
1030 struct __netgrent netgrdata;
1032 bzero (&netgrdata, sizeof (struct __netgrent));
1033 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1034 while (__internal_getnetgrent_r (&host, &user, &domain,
1035 &netgrdata, buf2, sizeof (buf2),
1036 &errno))
1038 if (user != NULL && user[0] != '-')
1039 blacklist_store_name (user, ent);
1041 __internal_endnetgrent (&netgrdata);
1042 continue;
1045 /* +@netgroup */
1046 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1047 && result->pw_name[2] != '\0')
1049 char buf[strlen (result->pw_name)];
1050 int status;
1052 strcpy (buf, &result->pw_name[2]);
1053 ent->netgroup = TRUE;
1054 ent->first = TRUE;
1055 copy_pwd_changes (&ent->pwd, result, NULL, 0);
1059 status = getpwent_next_nis_netgr (NULL, result, ent, buf,
1060 buffer, buflen);
1061 if (status == NSS_STATUS_RETURN)
1062 continue;
1064 if (status == NSS_STATUS_SUCCESS && uid == result->pw_uid)
1065 return NSS_STATUS_SUCCESS;
1066 } while (status == NSS_STATUS_SUCCESS);
1067 continue;
1070 /* -user */
1071 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1072 && result->pw_name[1] != '@')
1074 blacklist_store_name (&result->pw_name[1], ent);
1075 continue;
1078 /* +user */
1079 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1080 && result->pw_name[1] != '@')
1082 enum nss_status status;
1084 /* Store the User in the blacklist for the "+" at the end of
1085 /etc/passwd */
1086 blacklist_store_name (&result->pw_name[1], ent);
1087 status = getpwnam_plususer (&result->pw_name[1], result, buffer,
1088 buflen);
1089 if (status == NSS_STATUS_SUCCESS && result->pw_uid == uid)
1090 break;
1091 else
1092 continue;
1095 /* +:... */
1096 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1098 enum nss_status status;
1100 status = getpwuid_plususer (uid, result, buffer, buflen);
1101 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1102 break;
1103 else
1104 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1105 return NSS_STATUS_NOTFOUND;
1106 else
1108 if (status == NSS_STATUS_TRYAGAIN)
1109 /* The parser ran out of space */
1110 fsetpos (ent->stream, &pos);
1111 return status;
1115 return NSS_STATUS_SUCCESS;
1118 enum nss_status
1119 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1120 char *buffer, size_t buflen)
1122 ent_t ent = {0, 0, 0, NULL, 0, NULL, {NULL, 0, 0},
1123 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1124 enum nss_status status;
1126 status = internal_setpwent (&ent);
1127 if (status != NSS_STATUS_SUCCESS)
1128 return status;
1130 status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen);
1132 internal_endpwent (&ent);
1134 return status;
1138 /* Support routines for remembering -@netgroup and -user entries.
1139 The names are stored in a single string with `|' as separator. */
1140 static void
1141 blacklist_store_name (const char *name, ent_t *ent)
1143 int namelen = strlen (name);
1144 char *tmp;
1146 /* first call, setup cache */
1147 if (ent->blacklist.size == 0)
1149 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1150 ent->blacklist.data = malloc (ent->blacklist.size);
1151 if (ent->blacklist.data == NULL)
1152 return;
1153 ent->blacklist.data[0] = '|';
1154 ent->blacklist.data[1] = '\0';
1155 ent->blacklist.current = 1;
1157 else
1159 if (in_blacklist (name, namelen, ent))
1160 return; /* no duplicates */
1162 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1164 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1165 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1166 if (tmp == NULL)
1168 free (ent->blacklist.data);
1169 ent->blacklist.size = 0;
1170 return;
1172 ent->blacklist.data = tmp;
1176 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1177 *tmp++ = '|';
1178 *tmp = '\0';
1179 ent->blacklist.current += namelen + 1;
1181 return;
1184 /* returns TRUE if ent->blacklist contains name, else FALSE */
1185 static bool_t
1186 in_blacklist (const char *name, int namelen, ent_t *ent)
1188 char buf[namelen + 3];
1189 char *cp;
1191 if (ent->blacklist.data == NULL)
1192 return FALSE;
1194 buf[0] = '|';
1195 cp = stpcpy (&buf[1], name);
1196 *cp++= '|';
1197 *cp = '\0';
1198 return strstr (ent->blacklist.data, buf) != NULL;