Update.
[glibc.git] / nis / nss_compat / compat-pwd.c
blob89a6a7013e14ff6505066754d4e091c2dba6c982
1 /* Copyright (C) 1996-1999,2001,2002,2003,2004 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 Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <netdb.h>
24 #include <nss.h>
25 #include <nsswitch.h>
26 #include <pwd.h>
27 #include <stdio_ext.h>
28 #include <string.h>
29 #include <rpc/types.h>
30 #include <rpcsvc/ypclnt.h>
31 #include <bits/libc-lock.h>
33 #include "netgroup.h"
35 static service_user *ni;
36 static enum nss_status (*nss_setpwent) (int stayopen);
37 static enum nss_status (*nss_getpwnam_r) (const char *name,
38 struct passwd * pwd, char *buffer,
39 size_t buflen, int *errnop);
40 static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd,
41 char *buffer, size_t buflen,
42 int *errnop);
43 static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer,
44 size_t buflen, int *errnop);
45 static enum nss_status (*nss_endpwent) (void);
47 /* Get the declaration of the parser function. */
48 #define ENTNAME pwent
49 #define STRUCTURE passwd
50 #define EXTERN_PARSER
51 #include <nss/nss_files/files-parse.c>
53 /* Structure for remembering -@netgroup and -user members ... */
54 #define BLACKLIST_INITIAL_SIZE 512
55 #define BLACKLIST_INCREMENT 256
56 struct blacklist_t
58 char *data;
59 int current;
60 int size;
63 struct ent_t
65 bool_t netgroup;
66 bool_t first;
67 bool_t files;
68 FILE *stream;
69 struct blacklist_t blacklist;
70 struct passwd pwd;
71 struct __netgrent netgrdata;
73 typedef struct ent_t ent_t;
75 static ent_t ext_ent = {0, 0, TRUE, NULL, {NULL, 0, 0},
76 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
78 /* Protect global state against multiple changers. */
79 __libc_lock_define_initialized (static, lock)
81 /* Prototypes for local functions. */
82 static void blacklist_store_name (const char *, ent_t *);
83 static int in_blacklist (const char *, int, ent_t *);
85 /* Initialize the NSS interface/functions. The calling function must
86 hold the lock. */
87 static void
88 init_nss_interface (void)
90 if (__nss_database_lookup ("passwd_compat", NULL, "nis", &ni) >= 0)
92 nss_setpwent = __nss_lookup_function (ni, "setpwent");
93 nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r");
94 nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r");
95 nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r");
96 nss_endpwent = __nss_lookup_function (ni, "endpwent");
100 static void
101 give_pwd_free (struct passwd *pwd)
103 if (pwd->pw_name != NULL)
104 free (pwd->pw_name);
105 if (pwd->pw_passwd != NULL)
106 free (pwd->pw_passwd);
107 if (pwd->pw_gecos != NULL)
108 free (pwd->pw_gecos);
109 if (pwd->pw_dir != NULL)
110 free (pwd->pw_dir);
111 if (pwd->pw_shell != NULL)
112 free (pwd->pw_shell);
114 memset (pwd, '\0', sizeof (struct passwd));
117 static size_t
118 pwd_need_buflen (struct passwd *pwd)
120 size_t len = 0;
122 if (pwd->pw_passwd != NULL)
123 len += strlen (pwd->pw_passwd) + 1;
125 if (pwd->pw_gecos != NULL)
126 len += strlen (pwd->pw_gecos) + 1;
128 if (pwd->pw_dir != NULL)
129 len += strlen (pwd->pw_dir) + 1;
131 if (pwd->pw_shell != NULL)
132 len += strlen (pwd->pw_shell) + 1;
134 return len;
137 static void
138 copy_pwd_changes (struct passwd *dest, struct passwd *src,
139 char *buffer, size_t buflen)
141 if (src->pw_passwd != NULL && strlen (src->pw_passwd))
143 if (buffer == NULL)
144 dest->pw_passwd = strdup (src->pw_passwd);
145 else if (dest->pw_passwd &&
146 strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
147 strcpy (dest->pw_passwd, src->pw_passwd);
148 else
150 dest->pw_passwd = buffer;
151 strcpy (dest->pw_passwd, src->pw_passwd);
152 buffer += strlen (dest->pw_passwd) + 1;
153 buflen = buflen - (strlen (dest->pw_passwd) + 1);
157 if (src->pw_gecos != NULL && strlen (src->pw_gecos))
159 if (buffer == NULL)
160 dest->pw_gecos = strdup (src->pw_gecos);
161 else if (dest->pw_gecos &&
162 strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
163 strcpy (dest->pw_gecos, src->pw_gecos);
164 else
166 dest->pw_gecos = buffer;
167 strcpy (dest->pw_gecos, src->pw_gecos);
168 buffer += strlen (dest->pw_gecos) + 1;
169 buflen = buflen - (strlen (dest->pw_gecos) + 1);
172 if (src->pw_dir != NULL && strlen (src->pw_dir))
174 if (buffer == NULL)
175 dest->pw_dir = strdup (src->pw_dir);
176 else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir))
177 strcpy (dest->pw_dir, src->pw_dir);
178 else
180 dest->pw_dir = buffer;
181 strcpy (dest->pw_dir, src->pw_dir);
182 buffer += strlen (dest->pw_dir) + 1;
183 buflen = buflen - (strlen (dest->pw_dir) + 1);
187 if (src->pw_shell != NULL && strlen (src->pw_shell))
189 if (buffer == NULL)
190 dest->pw_shell = strdup (src->pw_shell);
191 else if (dest->pw_shell &&
192 strlen (dest->pw_shell) >= strlen (src->pw_shell))
193 strcpy (dest->pw_shell, src->pw_shell);
194 else
196 dest->pw_shell = buffer;
197 strcpy (dest->pw_shell, src->pw_shell);
198 buffer += strlen (dest->pw_shell) + 1;
199 buflen = buflen - (strlen (dest->pw_shell) + 1);
204 static enum nss_status
205 internal_setpwent (ent_t *ent, int stayopen)
207 enum nss_status status = NSS_STATUS_SUCCESS;
209 ent->first = ent->netgroup = FALSE;
210 ent->files = TRUE;
212 /* If something was left over free it. */
213 if (ent->netgroup)
214 __internal_endnetgrent (&ent->netgrdata);
216 if (ent->blacklist.data != NULL)
218 ent->blacklist.current = 1;
219 ent->blacklist.data[0] = '|';
220 ent->blacklist.data[1] = '\0';
222 else
223 ent->blacklist.current = 0;
225 if (ent->stream == NULL)
227 ent->stream = fopen ("/etc/passwd", "rm");
229 if (ent->stream == NULL)
230 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
231 else
233 /* We have to make sure the file is `closed on exec'. */
234 int result, flags;
236 result = flags = fcntl (fileno_unlocked (ent->stream), F_GETFD, 0);
237 if (result >= 0)
239 flags |= FD_CLOEXEC;
240 result = fcntl (fileno_unlocked (ent->stream), F_SETFD, flags);
242 if (result < 0)
244 /* Something went wrong. Close the stream and return a
245 failure. */
246 fclose (ent->stream);
247 ent->stream = NULL;
248 status = NSS_STATUS_UNAVAIL;
250 else
251 /* We take care of locking ourself. */
252 __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
255 else
256 rewind (ent->stream);
258 give_pwd_free (&ent->pwd);
260 if (status == NSS_STATUS_SUCCESS && nss_setpwent)
261 return nss_setpwent (stayopen);
263 return status;
267 enum nss_status
268 _nss_compat_setpwent (int stayopen)
270 enum nss_status result;
272 __libc_lock_lock (lock);
274 if (ni == NULL)
275 init_nss_interface ();
277 result = internal_setpwent (&ext_ent, stayopen);
279 __libc_lock_unlock (lock);
281 return result;
285 static enum nss_status
286 internal_endpwent (ent_t *ent)
288 if (nss_endpwent)
289 nss_endpwent ();
291 if (ent->stream != NULL)
293 fclose (ent->stream);
294 ent->stream = NULL;
297 if (ent->netgroup)
298 __internal_endnetgrent (&ent->netgrdata);
300 ent->first = ent->netgroup = FALSE;
302 if (ent->blacklist.data != NULL)
304 ent->blacklist.current = 1;
305 ent->blacklist.data[0] = '|';
306 ent->blacklist.data[1] = '\0';
308 else
309 ent->blacklist.current = 0;
311 give_pwd_free (&ent->pwd);
313 return NSS_STATUS_SUCCESS;
316 enum nss_status
317 _nss_compat_endpwent (void)
319 enum nss_status result;
321 __libc_lock_lock (lock);
323 result = internal_endpwent (&ext_ent);
325 __libc_lock_unlock (lock);
327 return result;
331 static enum nss_status
332 getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent,
333 char *group, char *buffer, size_t buflen,
334 int *errnop)
336 char *curdomain, *host, *user, *domain, *p2;
337 int status;
338 size_t p2len;
340 /* Leave function if NSS module does not support getpwnam_r,
341 we need this function here. */
342 if (!nss_getpwnam_r)
343 return NSS_STATUS_UNAVAIL;
345 if (yp_get_default_domain (&curdomain) != YPERR_SUCCESS)
347 ent->netgroup = FALSE;
348 ent->first = FALSE;
349 give_pwd_free (&ent->pwd);
350 return NSS_STATUS_UNAVAIL;
353 if (ent->first == TRUE)
355 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
356 __internal_setnetgrent (group, &ent->netgrdata);
357 ent->first = FALSE;
360 while (1)
362 char *saved_cursor;
364 saved_cursor = ent->netgrdata.cursor;
365 status = __internal_getnetgrent_r (&host, &user, &domain,
366 &ent->netgrdata, buffer, buflen,
367 errnop);
368 if (status != 1)
370 __internal_endnetgrent (&ent->netgrdata);
371 ent->netgroup = 0;
372 give_pwd_free (&ent->pwd);
373 return NSS_STATUS_RETURN;
376 if (user == NULL || user[0] == '-')
377 continue;
379 if (domain != NULL && strcmp (curdomain, domain) != 0)
380 continue;
382 /* If name != NULL, we are called from getpwnam. */
383 if (name != NULL)
384 if (strcmp (user, name) != 0)
385 continue;
387 p2len = pwd_need_buflen (&ent->pwd);
388 if (p2len > buflen)
390 *errnop = ERANGE;
391 return NSS_STATUS_TRYAGAIN;
393 p2 = buffer + (buflen - p2len);
394 buflen -= p2len;
396 if (nss_getpwnam_r (user, result, buffer, buflen, errnop) !=
397 NSS_STATUS_SUCCESS)
398 continue;
400 if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent))
402 /* Store the User in the blacklist for possible the "+" at the
403 end of /etc/passwd */
404 blacklist_store_name (result->pw_name, ent);
405 copy_pwd_changes (result, &ent->pwd, p2, p2len);
406 break;
410 return NSS_STATUS_SUCCESS;
413 /* get the next user from NSS (+ entry) */
414 static enum nss_status
415 getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer,
416 size_t buflen, int *errnop)
418 enum nss_status status;
419 char *p2;
420 size_t p2len;
422 /* Return if NSS module does not support getpwent_r. */
423 if (!nss_getpwent_r)
424 return NSS_STATUS_UNAVAIL;
426 p2len = pwd_need_buflen (&ent->pwd);
427 if (p2len > buflen)
429 *errnop = ERANGE;
430 return NSS_STATUS_TRYAGAIN;
432 p2 = buffer + (buflen - p2len);
433 buflen -= p2len;
435 if (ent->first)
436 ent->first = FALSE;
440 if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) !=
441 NSS_STATUS_SUCCESS)
442 return status;
444 while (in_blacklist (result->pw_name, strlen (result->pw_name), ent));
446 copy_pwd_changes (result, &ent->pwd, p2, p2len);
448 return NSS_STATUS_SUCCESS;
451 /* This function handle the +user entrys in /etc/passwd */
452 static enum nss_status
453 getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
454 char *buffer, size_t buflen, int *errnop)
456 struct passwd pwd;
457 char *p;
458 size_t plen;
460 if (!nss_getpwnam_r)
461 return NSS_STATUS_UNAVAIL;
463 memset (&pwd, '\0', sizeof (struct passwd));
465 copy_pwd_changes (&pwd, result, NULL, 0);
467 plen = pwd_need_buflen (&pwd);
468 if (plen > buflen)
470 *errnop = ERANGE;
471 return NSS_STATUS_TRYAGAIN;
473 p = buffer + (buflen - plen);
474 buflen -= plen;
476 if (nss_getpwnam_r (name, result, buffer, buflen, errnop) !=
477 NSS_STATUS_SUCCESS)
478 return NSS_STATUS_NOTFOUND;
480 if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
481 return NSS_STATUS_NOTFOUND;
483 copy_pwd_changes (result, &pwd, p, plen);
484 give_pwd_free (&pwd);
485 /* We found the entry. */
486 return NSS_STATUS_SUCCESS;
489 static enum nss_status
490 getpwent_next_file (struct passwd *result, ent_t *ent,
491 char *buffer, size_t buflen, int *errnop)
493 struct parser_data *data = (void *) buffer;
494 while (1)
496 fpos_t pos;
497 char *p;
498 int parse_res;
502 /* We need at least 3 characters for one line. */
503 if (__builtin_expect (buflen < 3, 0))
505 erange:
506 *errnop = ERANGE;
507 return NSS_STATUS_TRYAGAIN;
510 fgetpos (ent->stream, &pos);
511 buffer[buflen - 1] = '\xff';
512 p = fgets_unlocked (buffer, buflen, ent->stream);
513 if (p == NULL && feof_unlocked (ent->stream))
514 return NSS_STATUS_NOTFOUND;
516 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
518 erange_reset:
519 fsetpos (ent->stream, &pos);
520 goto erange;
523 /* Terminate the line for any case. */
524 buffer[buflen - 1] = '\0';
526 /* Skip leading blanks. */
527 while (isspace (*p))
528 ++p;
530 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
531 /* Parse the line. If it is invalid, loop to
532 get the next line of the file to parse. */
533 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
534 errnop)));
536 if (__builtin_expect (parse_res == -1, 0))
537 /* The parser ran out of space. */
538 goto erange_reset;
540 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
541 /* This is a real entry. */
542 break;
544 /* -@netgroup */
545 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
546 && result->pw_name[2] != '\0')
548 /* XXX Do not use fixed length buffer. */
549 char buf2[1024];
550 char *user, *host, *domain;
551 struct __netgrent netgrdata;
553 bzero (&netgrdata, sizeof (struct __netgrent));
554 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
555 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
556 buf2, sizeof (buf2), errnop))
558 if (user != NULL && user[0] != '-')
559 blacklist_store_name (user, ent);
561 __internal_endnetgrent (&netgrdata);
562 continue;
565 /* +@netgroup */
566 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
567 && result->pw_name[2] != '\0')
569 enum nss_status status;
571 ent->netgroup = TRUE;
572 ent->first = TRUE;
573 copy_pwd_changes (&ent->pwd, result, NULL, 0);
575 status = getpwent_next_nss_netgr (NULL, result, ent,
576 &result->pw_name[2],
577 buffer, buflen, errnop);
578 if (status == NSS_STATUS_RETURN)
579 continue;
580 else
581 return status;
584 /* -user */
585 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
586 && result->pw_name[1] != '@')
588 blacklist_store_name (&result->pw_name[1], ent);
589 continue;
592 /* +user */
593 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
594 && result->pw_name[1] != '@')
596 size_t len = strlen (result->pw_name);
597 char buf[len];
598 enum nss_status status;
600 /* Store the User in the blacklist for the "+" at the end of
601 /etc/passwd */
602 memcpy (buf, &result->pw_name[1], len);
603 status = getpwnam_plususer (&result->pw_name[1], result, ent,
604 buffer, buflen, errnop);
605 blacklist_store_name (buf, ent);
607 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
608 break;
609 else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
610 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
611 continue;
612 else
614 if (status == NSS_STATUS_TRYAGAIN)
616 /* The parser ran out of space */
617 fsetpos (ent->stream, &pos);
618 *errnop = ERANGE;
620 return status;
624 /* +:... */
625 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
627 ent->files = FALSE;
628 ent->first = TRUE;
629 copy_pwd_changes (&ent->pwd, result, NULL, 0);
631 return getpwent_next_nss (result, ent, buffer, buflen, errnop);
635 return NSS_STATUS_SUCCESS;
639 static enum nss_status
640 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
641 size_t buflen, int *errnop)
643 if (ent->netgroup)
645 enum nss_status status;
647 /* We are searching members in a netgroup */
648 /* Since this is not the first call, we don't need the group name */
649 status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen,
650 errnop);
651 if (status == NSS_STATUS_RETURN)
652 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
653 else
654 return status;
656 else if (ent->files)
657 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
658 else
659 return getpwent_next_nss (pw, ent, buffer, buflen, errnop);
663 enum nss_status
664 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
665 int *errnop)
667 enum nss_status result = NSS_STATUS_SUCCESS;
669 __libc_lock_lock (lock);
671 /* Be prepared that the setpwent function was not called before. */
672 if (ni == NULL)
673 init_nss_interface ();
675 if (ext_ent.stream == NULL)
676 result = internal_setpwent (&ext_ent, 1);
678 if (result == NSS_STATUS_SUCCESS)
679 result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
681 __libc_lock_unlock (lock);
683 return result;
686 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
687 static enum nss_status
688 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
689 char *buffer, size_t buflen, int *errnop)
691 struct parser_data *data = (void *) buffer;
693 while (1)
695 fpos_t pos;
696 char *p;
697 int parse_res;
701 /* We need at least 3 characters for one line. */
702 if (__builtin_expect (buflen < 3, 0))
704 erange:
705 *errnop = ERANGE;
706 return NSS_STATUS_TRYAGAIN;
709 fgetpos (ent->stream, &pos);
710 buffer[buflen - 1] = '\xff';
711 p = fgets_unlocked (buffer, buflen, ent->stream);
712 if (p == NULL && feof_unlocked (ent->stream))
714 return NSS_STATUS_NOTFOUND;
716 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
718 erange_reset:
719 fsetpos (ent->stream, &pos);
720 goto erange;
723 /* Terminate the line for any case. */
724 buffer[buflen - 1] = '\0';
726 /* Skip leading blanks. */
727 while (isspace (*p))
728 ++p;
730 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
731 /* Parse the line. If it is invalid, loop to
732 get the next line of the file to parse. */
733 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
734 errnop)));
736 if (__builtin_expect (parse_res == -1, 0))
737 /* The parser ran out of space. */
738 goto erange_reset;
740 /* This is a real entry. */
741 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
743 if (strcmp (result->pw_name, name) == 0)
744 return NSS_STATUS_SUCCESS;
745 else
746 continue;
749 /* -@netgroup */
750 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
751 && result->pw_name[2] != '\0')
753 if (innetgr (&result->pw_name[2], NULL, name, NULL))
754 return NSS_STATUS_NOTFOUND;
755 continue;
758 /* +@netgroup */
759 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
760 && result->pw_name[2] != '\0')
762 enum nss_status status;
764 if (innetgr (&result->pw_name[2], NULL, name, NULL))
766 status = getpwnam_plususer (name, result, ent, buffer,
767 buflen, errnop);
769 if (status == NSS_STATUS_RETURN)
770 continue;
772 return status;
774 continue;
777 /* -user */
778 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
779 && result->pw_name[1] != '@')
781 if (strcmp (&result->pw_name[1], name) == 0)
782 return NSS_STATUS_NOTFOUND;
783 else
784 continue;
787 /* +user */
788 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
789 && result->pw_name[1] != '@')
791 if (strcmp (name, &result->pw_name[1]) == 0)
793 enum nss_status status;
795 status = getpwnam_plususer (name, result, ent, buffer, buflen,
796 errnop);
797 if (status == NSS_STATUS_RETURN)
798 /* We couldn't parse the entry */
799 return NSS_STATUS_NOTFOUND;
800 else
801 return status;
805 /* +:... */
806 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
808 enum nss_status status;
810 status = getpwnam_plususer (name, result, ent,
811 buffer, buflen, errnop);
812 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
813 break;
814 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
815 return NSS_STATUS_NOTFOUND;
816 else
817 return status;
820 return NSS_STATUS_SUCCESS;
823 enum nss_status
824 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
825 char *buffer, size_t buflen, int *errnop)
827 enum nss_status result;
828 ent_t ent = {0, 0, TRUE, NULL, {NULL, 0, 0},
829 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
831 if (name[0] == '-' || name[0] == '+')
832 return NSS_STATUS_NOTFOUND;
834 __libc_lock_lock (lock);
836 if (ni == NULL)
837 init_nss_interface ();
839 __libc_lock_unlock (lock);
841 result = internal_setpwent (&ent, 0);
843 if (result == NSS_STATUS_SUCCESS)
844 result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
846 internal_endpwent (&ent);
848 return result;
851 /* This function handle the + entry in /etc/passwd for getpwuid */
852 static enum nss_status
853 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
854 size_t buflen, int *errnop)
856 struct passwd pwd;
857 char *p;
858 size_t plen;
860 if (!nss_getpwuid_r)
861 return NSS_STATUS_UNAVAIL;
863 memset (&pwd, '\0', sizeof (struct passwd));
865 copy_pwd_changes (&pwd, result, NULL, 0);
867 plen = pwd_need_buflen (&pwd);
868 if (plen > buflen)
870 *errnop = ERANGE;
871 return NSS_STATUS_TRYAGAIN;
873 p = buffer + (buflen - plen);
874 buflen -= plen;
876 if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) ==
877 NSS_STATUS_SUCCESS)
879 copy_pwd_changes (result, &pwd, p, plen);
880 give_pwd_free (&pwd);
881 /* We found the entry. */
882 return NSS_STATUS_SUCCESS;
884 else
886 /* Give buffer the old len back */
887 buflen += plen;
888 give_pwd_free (&pwd);
890 return NSS_STATUS_RETURN;
893 /* Searches in /etc/passwd and the NSS subsystem for a special user id */
894 static enum nss_status
895 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
896 char *buffer, size_t buflen, int *errnop)
898 struct parser_data *data = (void *) buffer;
900 while (1)
902 fpos_t pos;
903 char *p;
904 int parse_res;
908 /* We need at least 3 characters for one line. */
909 if (__builtin_expect (buflen < 3, 0))
911 erange:
912 *errnop = ERANGE;
913 return NSS_STATUS_TRYAGAIN;
916 fgetpos (ent->stream, &pos);
917 buffer[buflen - 1] = '\xff';
918 p = fgets_unlocked (buffer, buflen, ent->stream);
919 if (p == NULL && feof_unlocked (ent->stream))
920 return NSS_STATUS_NOTFOUND;
922 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
924 erange_reset:
925 fsetpos (ent->stream, &pos);
926 goto erange;
929 /* Terminate the line for any case. */
930 buffer[buflen - 1] = '\0';
932 /* Skip leading blanks. */
933 while (isspace (*p))
934 ++p;
936 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
937 /* Parse the line. If it is invalid, loop to
938 get the next line of the file to parse. */
939 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
940 errnop)));
942 if (__builtin_expect (parse_res == -1, 0))
943 /* The parser ran out of space. */
944 goto erange_reset;
946 /* This is a real entry. */
947 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
949 if (result->pw_uid == uid)
950 return NSS_STATUS_SUCCESS;
951 else
952 continue;
955 /* -@netgroup */
956 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
957 && result->pw_name[2] != '\0')
959 /* -1, because we remove first two character of pw_name. */
960 size_t len = strlen (result->pw_name) - 1;
961 char buf[len];
962 enum nss_status status;
964 memcpy (buf, &result->pw_name[2], len);
966 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
967 if (status == NSS_STATUS_SUCCESS &&
968 innetgr (buf, NULL, result->pw_name, NULL))
969 return NSS_STATUS_NOTFOUND;
971 continue;
974 /* +@netgroup */
975 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
976 && result->pw_name[2] != '\0')
978 /* -1, because we remove first two characters of pw_name. */
979 size_t len = strlen (result->pw_name) - 1;
980 char buf[len];
981 enum nss_status status;
983 memcpy (buf, &result->pw_name[2], len);
985 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
987 if (status == NSS_STATUS_RETURN)
988 continue;
990 if (status == NSS_STATUS_SUCCESS)
992 if (innetgr (buf, NULL, result->pw_name, NULL))
993 return NSS_STATUS_SUCCESS;
995 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
996 return NSS_STATUS_NOTFOUND;
997 else
998 return status;
1000 continue;
1003 /* -user */
1004 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1005 && result->pw_name[1] != '@')
1007 size_t len = strlen (result->pw_name);
1008 char buf[len];
1009 enum nss_status status;
1011 memcpy (buf, &result->pw_name[1], len);
1013 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1014 if (status == NSS_STATUS_SUCCESS &&
1015 innetgr (buf, NULL, result->pw_name, NULL))
1016 return NSS_STATUS_NOTFOUND;
1017 continue;
1020 /* +user */
1021 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1022 && result->pw_name[1] != '@')
1024 size_t len = strlen (result->pw_name);
1025 char buf[len];
1026 enum nss_status status;
1028 memcpy (buf, &result->pw_name[1], len);
1030 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1032 if (status == NSS_STATUS_RETURN)
1033 continue;
1035 if (status == NSS_STATUS_SUCCESS)
1037 if (strcmp (buf, result->pw_name) == 0)
1038 return NSS_STATUS_SUCCESS;
1040 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1041 return NSS_STATUS_NOTFOUND;
1042 else
1043 return status;
1045 continue;
1048 /* +:... */
1049 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1051 enum nss_status status;
1053 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1054 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1055 break;
1056 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1057 return NSS_STATUS_NOTFOUND;
1058 else
1059 return status;
1062 return NSS_STATUS_SUCCESS;
1065 enum nss_status
1066 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1067 char *buffer, size_t buflen, int *errnop)
1069 enum nss_status result;
1070 ent_t ent = {0, 0, TRUE, NULL, {NULL, 0, 0},
1071 {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1073 __libc_lock_lock (lock);
1075 if (ni == NULL)
1076 init_nss_interface ();
1078 __libc_lock_unlock (lock);
1080 result = internal_setpwent (&ent, 0);
1082 if (result == NSS_STATUS_SUCCESS)
1083 result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1085 internal_endpwent (&ent);
1087 return result;
1091 /* Support routines for remembering -@netgroup and -user entries.
1092 The names are stored in a single string with `|' as separator. */
1093 static void
1094 blacklist_store_name (const char *name, ent_t *ent)
1096 int namelen = strlen (name);
1097 char *tmp;
1099 /* first call, setup cache */
1100 if (ent->blacklist.size == 0)
1102 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1103 ent->blacklist.data = malloc (ent->blacklist.size);
1104 if (ent->blacklist.data == NULL)
1105 return;
1106 ent->blacklist.data[0] = '|';
1107 ent->blacklist.data[1] = '\0';
1108 ent->blacklist.current = 1;
1110 else
1112 if (in_blacklist (name, namelen, ent))
1113 return; /* no duplicates */
1115 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1117 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1118 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1119 if (tmp == NULL)
1121 free (ent->blacklist.data);
1122 ent->blacklist.size = 0;
1123 return;
1125 ent->blacklist.data = tmp;
1129 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1130 *tmp++ = '|';
1131 *tmp = '\0';
1132 ent->blacklist.current += namelen + 1;
1134 return;
1137 /* returns TRUE if ent->blacklist contains name, else FALSE */
1138 static bool_t
1139 in_blacklist (const char *name, int namelen, ent_t *ent)
1141 char buf[namelen + 3];
1142 char *cp;
1144 if (ent->blacklist.data == NULL)
1145 return FALSE;
1147 buf[0] = '|';
1148 cp = stpcpy (&buf[1], name);
1149 *cp++ = '|';
1150 *cp = '\0';
1151 return strstr (ent->blacklist.data, buf) != NULL;