support_become_root: Enable file creation in user namespaces
[glibc.git] / nss / nss_compat / compat-pwd.c
blobb16eef5d44b09e45efc825b285757df7114b27f7
1 /* Copyright (C) 1996-2017 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, see
17 <http://www.gnu.org/licenses/>. */
19 #include <ctype.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <netdb.h>
23 #include <nss.h>
24 #include <nsswitch.h>
25 #include <pwd.h>
26 #include <stdio_ext.h>
27 #include <string.h>
28 #include <libc-lock.h>
29 #include <kernel-features.h>
31 #include "netgroup.h"
32 #include "nisdomain.h"
34 static service_user *ni;
35 static enum nss_status (*nss_setpwent) (int stayopen);
36 static enum nss_status (*nss_getpwnam_r) (const char *name,
37 struct passwd * pwd, char *buffer,
38 size_t buflen, int *errnop);
39 static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd,
40 char *buffer, size_t buflen,
41 int *errnop);
42 static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer,
43 size_t buflen, int *errnop);
44 static enum nss_status (*nss_endpwent) (void);
46 /* Get the declaration of the parser function. */
47 #define ENTNAME pwent
48 #define STRUCTURE passwd
49 #define EXTERN_PARSER
50 #include <nss/nss_files/files-parse.c>
52 /* Structure for remembering -@netgroup and -user members ... */
53 #define BLACKLIST_INITIAL_SIZE 512
54 #define BLACKLIST_INCREMENT 256
55 struct blacklist_t
57 char *data;
58 int current;
59 int size;
62 struct ent_t
64 bool netgroup;
65 bool first;
66 bool files;
67 enum nss_status setent_status;
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 = { false, false, true, NSS_STATUS_SUCCESS, NULL,
76 { NULL, 0, 0 },
77 { NULL, NULL, 0, 0, NULL, NULL, NULL }};
79 /* Protect global state against multiple changers. */
80 __libc_lock_define_initialized (static, lock)
82 /* Prototypes for local functions. */
83 static void blacklist_store_name (const char *, ent_t *);
84 static bool in_blacklist (const char *, int, ent_t *);
86 /* Initialize the NSS interface/functions. The calling function must
87 hold the lock. */
88 static void
89 init_nss_interface (void)
91 if (__nss_database_lookup ("passwd_compat", NULL, "nis", &ni) >= 0)
93 nss_setpwent = __nss_lookup_function (ni, "setpwent");
94 nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r");
95 nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r");
96 nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r");
97 nss_endpwent = __nss_lookup_function (ni, "endpwent");
101 static void
102 give_pwd_free (struct passwd *pwd)
104 free (pwd->pw_name);
105 free (pwd->pw_passwd);
106 free (pwd->pw_gecos);
107 free (pwd->pw_dir);
108 free (pwd->pw_shell);
110 memset (pwd, '\0', sizeof (struct passwd));
113 static size_t
114 pwd_need_buflen (struct passwd *pwd)
116 size_t len = 0;
118 if (pwd->pw_passwd != NULL)
119 len += strlen (pwd->pw_passwd) + 1;
121 if (pwd->pw_gecos != NULL)
122 len += strlen (pwd->pw_gecos) + 1;
124 if (pwd->pw_dir != NULL)
125 len += strlen (pwd->pw_dir) + 1;
127 if (pwd->pw_shell != NULL)
128 len += strlen (pwd->pw_shell) + 1;
130 return len;
133 static void
134 copy_pwd_changes (struct passwd *dest, struct passwd *src,
135 char *buffer, size_t buflen)
137 if (src->pw_passwd != NULL && strlen (src->pw_passwd))
139 if (buffer == NULL)
140 dest->pw_passwd = strdup (src->pw_passwd);
141 else if (dest->pw_passwd &&
142 strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
143 strcpy (dest->pw_passwd, src->pw_passwd);
144 else
146 dest->pw_passwd = buffer;
147 strcpy (dest->pw_passwd, src->pw_passwd);
148 buffer += strlen (dest->pw_passwd) + 1;
149 buflen = buflen - (strlen (dest->pw_passwd) + 1);
153 if (src->pw_gecos != NULL && strlen (src->pw_gecos))
155 if (buffer == NULL)
156 dest->pw_gecos = strdup (src->pw_gecos);
157 else if (dest->pw_gecos &&
158 strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
159 strcpy (dest->pw_gecos, src->pw_gecos);
160 else
162 dest->pw_gecos = buffer;
163 strcpy (dest->pw_gecos, src->pw_gecos);
164 buffer += strlen (dest->pw_gecos) + 1;
165 buflen = buflen - (strlen (dest->pw_gecos) + 1);
168 if (src->pw_dir != NULL && strlen (src->pw_dir))
170 if (buffer == NULL)
171 dest->pw_dir = strdup (src->pw_dir);
172 else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir))
173 strcpy (dest->pw_dir, src->pw_dir);
174 else
176 dest->pw_dir = buffer;
177 strcpy (dest->pw_dir, src->pw_dir);
178 buffer += strlen (dest->pw_dir) + 1;
179 buflen = buflen - (strlen (dest->pw_dir) + 1);
183 if (src->pw_shell != NULL && strlen (src->pw_shell))
185 if (buffer == NULL)
186 dest->pw_shell = strdup (src->pw_shell);
187 else if (dest->pw_shell &&
188 strlen (dest->pw_shell) >= strlen (src->pw_shell))
189 strcpy (dest->pw_shell, src->pw_shell);
190 else
192 dest->pw_shell = buffer;
193 strcpy (dest->pw_shell, src->pw_shell);
194 buffer += strlen (dest->pw_shell) + 1;
195 buflen = buflen - (strlen (dest->pw_shell) + 1);
200 static enum nss_status
201 internal_setpwent (ent_t *ent, int stayopen, int needent)
203 enum nss_status status = NSS_STATUS_SUCCESS;
205 ent->first = ent->netgroup = false;
206 ent->files = true;
207 ent->setent_status = NSS_STATUS_SUCCESS;
209 /* If something was left over free it. */
210 if (ent->netgroup)
211 __internal_endnetgrent (&ent->netgrdata);
213 if (ent->blacklist.data != NULL)
215 ent->blacklist.current = 1;
216 ent->blacklist.data[0] = '|';
217 ent->blacklist.data[1] = '\0';
219 else
220 ent->blacklist.current = 0;
222 if (ent->stream == NULL)
224 ent->stream = fopen ("/etc/passwd", "rme");
226 if (ent->stream == NULL)
227 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
228 else
229 /* We take care of locking ourself. */
230 __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
232 else
233 rewind (ent->stream);
235 give_pwd_free (&ent->pwd);
237 if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent)
238 ent->setent_status = nss_setpwent (stayopen);
240 return status;
244 enum nss_status
245 _nss_compat_setpwent (int stayopen)
247 enum nss_status result;
249 __libc_lock_lock (lock);
251 if (ni == NULL)
252 init_nss_interface ();
254 result = internal_setpwent (&ext_ent, stayopen, 1);
256 __libc_lock_unlock (lock);
258 return result;
262 static enum nss_status
263 internal_endpwent (ent_t *ent)
265 if (ent->stream != NULL)
267 fclose (ent->stream);
268 ent->stream = NULL;
271 if (ent->netgroup)
272 __internal_endnetgrent (&ent->netgrdata);
274 ent->first = ent->netgroup = false;
276 if (ent->blacklist.data != NULL)
278 ent->blacklist.current = 1;
279 ent->blacklist.data[0] = '|';
280 ent->blacklist.data[1] = '\0';
282 else
283 ent->blacklist.current = 0;
285 give_pwd_free (&ent->pwd);
287 return NSS_STATUS_SUCCESS;
290 enum nss_status
291 _nss_compat_endpwent (void)
293 enum nss_status result;
295 __libc_lock_lock (lock);
297 if (nss_endpwent)
298 nss_endpwent ();
300 result = internal_endpwent (&ext_ent);
302 __libc_lock_unlock (lock);
304 return result;
308 static enum nss_status
309 getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent,
310 char *group, char *buffer, size_t buflen,
311 int *errnop)
313 char *curdomain = NULL, *host, *user, *domain, *p2;
314 int status;
315 size_t p2len;
317 /* Leave function if NSS module does not support getpwnam_r,
318 we need this function here. */
319 if (!nss_getpwnam_r)
320 return NSS_STATUS_UNAVAIL;
322 if (ent->first)
324 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
325 __internal_setnetgrent (group, &ent->netgrdata);
326 ent->first = false;
329 while (1)
331 status = __internal_getnetgrent_r (&host, &user, &domain,
332 &ent->netgrdata, buffer, buflen,
333 errnop);
334 if (status != 1)
336 __internal_endnetgrent (&ent->netgrdata);
337 ent->netgroup = 0;
338 give_pwd_free (&ent->pwd);
339 return NSS_STATUS_RETURN;
342 if (user == NULL || user[0] == '-')
343 continue;
345 if (domain != NULL)
347 if (curdomain == NULL
348 && __nss_get_default_domain (&curdomain) != 0)
350 __internal_endnetgrent (&ent->netgrdata);
351 ent->netgroup = false;
352 give_pwd_free (&ent->pwd);
353 return NSS_STATUS_UNAVAIL;
355 if (strcmp (curdomain, domain) != 0)
356 continue;
359 /* If name != NULL, we are called from getpwnam. */
360 if (name != NULL)
361 if (strcmp (user, name) != 0)
362 continue;
364 p2len = pwd_need_buflen (&ent->pwd);
365 if (p2len > buflen)
367 *errnop = ERANGE;
368 return NSS_STATUS_TRYAGAIN;
370 p2 = buffer + (buflen - p2len);
371 buflen -= p2len;
373 if (nss_getpwnam_r (user, result, buffer, buflen, errnop) !=
374 NSS_STATUS_SUCCESS)
375 continue;
377 if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent))
379 /* Store the User in the blacklist for possible the "+" at the
380 end of /etc/passwd */
381 blacklist_store_name (result->pw_name, ent);
382 copy_pwd_changes (result, &ent->pwd, p2, p2len);
383 break;
387 return NSS_STATUS_SUCCESS;
390 /* get the next user from NSS (+ entry) */
391 static enum nss_status
392 getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer,
393 size_t buflen, int *errnop)
395 enum nss_status status;
396 char *p2;
397 size_t p2len;
399 /* Return if NSS module does not support getpwent_r. */
400 if (!nss_getpwent_r)
401 return NSS_STATUS_UNAVAIL;
403 /* If the setpwent call failed, say so. */
404 if (ent->setent_status != NSS_STATUS_SUCCESS)
405 return ent->setent_status;
407 p2len = pwd_need_buflen (&ent->pwd);
408 if (p2len > buflen)
410 *errnop = ERANGE;
411 return NSS_STATUS_TRYAGAIN;
413 p2 = buffer + (buflen - p2len);
414 buflen -= p2len;
416 if (ent->first)
417 ent->first = false;
421 if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) !=
422 NSS_STATUS_SUCCESS)
423 return status;
425 while (in_blacklist (result->pw_name, strlen (result->pw_name), ent));
427 copy_pwd_changes (result, &ent->pwd, p2, p2len);
429 return NSS_STATUS_SUCCESS;
432 /* This function handle the +user entrys in /etc/passwd */
433 static enum nss_status
434 getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
435 char *buffer, size_t buflen, int *errnop)
437 if (!nss_getpwnam_r)
438 return NSS_STATUS_UNAVAIL;
440 struct passwd pwd;
441 memset (&pwd, '\0', sizeof (struct passwd));
443 copy_pwd_changes (&pwd, result, NULL, 0);
445 size_t plen = pwd_need_buflen (&pwd);
446 if (plen > buflen)
448 *errnop = ERANGE;
449 return NSS_STATUS_TRYAGAIN;
451 char *p = buffer + (buflen - plen);
452 buflen -= plen;
454 enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen,
455 errnop);
456 if (status != NSS_STATUS_SUCCESS)
457 return status;
459 if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
460 return NSS_STATUS_NOTFOUND;
462 copy_pwd_changes (result, &pwd, p, plen);
463 give_pwd_free (&pwd);
464 /* We found the entry. */
465 return NSS_STATUS_SUCCESS;
468 static enum nss_status
469 getpwent_next_file (struct passwd *result, ent_t *ent,
470 char *buffer, size_t buflen, int *errnop)
472 struct parser_data *data = (void *) buffer;
473 while (1)
475 fpos_t pos;
476 char *p;
477 int parse_res;
481 /* We need at least 3 characters for one line. */
482 if (__glibc_unlikely (buflen < 3))
484 erange:
485 *errnop = ERANGE;
486 return NSS_STATUS_TRYAGAIN;
489 fgetpos (ent->stream, &pos);
490 buffer[buflen - 1] = '\xff';
491 p = fgets_unlocked (buffer, buflen, ent->stream);
492 if (p == NULL && feof_unlocked (ent->stream))
493 return NSS_STATUS_NOTFOUND;
495 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
497 erange_reset:
498 fsetpos (ent->stream, &pos);
499 goto erange;
502 /* Terminate the line for any case. */
503 buffer[buflen - 1] = '\0';
505 /* Skip leading blanks. */
506 while (isspace (*p))
507 ++p;
509 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
510 /* Parse the line. If it is invalid, loop to
511 get the next line of the file to parse. */
512 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
513 errnop)));
515 if (__glibc_unlikely (parse_res == -1))
516 /* The parser ran out of space. */
517 goto erange_reset;
519 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
520 /* This is a real entry. */
521 break;
523 /* -@netgroup */
524 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
525 && result->pw_name[2] != '\0')
527 /* XXX Do not use fixed length buffer. */
528 char buf2[1024];
529 char *user, *host, *domain;
530 struct __netgrent netgrdata;
532 memset (&netgrdata, 0, sizeof (struct __netgrent));
533 __internal_setnetgrent (&result->pw_name[2], &netgrdata);
534 while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
535 buf2, sizeof (buf2), errnop))
537 if (user != NULL && user[0] != '-')
538 blacklist_store_name (user, ent);
540 __internal_endnetgrent (&netgrdata);
541 continue;
544 /* +@netgroup */
545 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
546 && result->pw_name[2] != '\0')
548 enum nss_status status;
550 ent->netgroup = true;
551 ent->first = true;
552 copy_pwd_changes (&ent->pwd, result, NULL, 0);
554 status = getpwent_next_nss_netgr (NULL, result, ent,
555 &result->pw_name[2],
556 buffer, buflen, errnop);
557 if (status == NSS_STATUS_RETURN)
558 continue;
559 else
560 return status;
563 /* -user */
564 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
565 && result->pw_name[1] != '@')
567 blacklist_store_name (&result->pw_name[1], ent);
568 continue;
571 /* +user */
572 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
573 && result->pw_name[1] != '@')
575 size_t len = strlen (result->pw_name);
576 char buf[len];
577 enum nss_status status;
579 /* Store the User in the blacklist for the "+" at the end of
580 /etc/passwd */
581 memcpy (buf, &result->pw_name[1], len);
582 status = getpwnam_plususer (&result->pw_name[1], result, ent,
583 buffer, buflen, errnop);
584 blacklist_store_name (buf, ent);
586 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
587 break;
588 else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
589 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
590 continue;
591 else
593 if (status == NSS_STATUS_TRYAGAIN)
595 /* The parser ran out of space */
596 fsetpos (ent->stream, &pos);
597 *errnop = ERANGE;
599 return status;
603 /* +:... */
604 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
606 ent->files = false;
607 ent->first = true;
608 copy_pwd_changes (&ent->pwd, result, NULL, 0);
610 return getpwent_next_nss (result, ent, buffer, buflen, errnop);
614 return NSS_STATUS_SUCCESS;
618 static enum nss_status
619 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
620 size_t buflen, int *errnop)
622 if (ent->netgroup)
624 enum nss_status status;
626 /* We are searching members in a netgroup */
627 /* Since this is not the first call, we don't need the group name */
628 status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen,
629 errnop);
630 if (status == NSS_STATUS_RETURN)
631 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
632 else
633 return status;
635 else if (ent->files)
636 return getpwent_next_file (pw, ent, buffer, buflen, errnop);
637 else
638 return getpwent_next_nss (pw, ent, buffer, buflen, errnop);
642 enum nss_status
643 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
644 int *errnop)
646 enum nss_status result = NSS_STATUS_SUCCESS;
648 __libc_lock_lock (lock);
650 /* Be prepared that the setpwent function was not called before. */
651 if (ni == NULL)
652 init_nss_interface ();
654 if (ext_ent.stream == NULL)
655 result = internal_setpwent (&ext_ent, 1, 1);
657 if (result == NSS_STATUS_SUCCESS)
658 result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
660 __libc_lock_unlock (lock);
662 return result;
665 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
666 static enum nss_status
667 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
668 char *buffer, size_t buflen, int *errnop)
670 struct parser_data *data = (void *) buffer;
672 while (1)
674 fpos_t pos;
675 char *p;
676 int parse_res;
680 /* We need at least 3 characters for one line. */
681 if (__glibc_unlikely (buflen < 3))
683 erange:
684 *errnop = ERANGE;
685 return NSS_STATUS_TRYAGAIN;
688 fgetpos (ent->stream, &pos);
689 buffer[buflen - 1] = '\xff';
690 p = fgets_unlocked (buffer, buflen, ent->stream);
691 if (p == NULL && feof_unlocked (ent->stream))
693 return NSS_STATUS_NOTFOUND;
695 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
697 erange_reset:
698 fsetpos (ent->stream, &pos);
699 goto erange;
702 /* Terminate the line for any case. */
703 buffer[buflen - 1] = '\0';
705 /* Skip leading blanks. */
706 while (isspace (*p))
707 ++p;
709 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
710 /* Parse the line. If it is invalid, loop to
711 get the next line of the file to parse. */
712 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
713 errnop)));
715 if (__glibc_unlikely (parse_res == -1))
716 /* The parser ran out of space. */
717 goto erange_reset;
719 /* This is a real entry. */
720 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
722 if (strcmp (result->pw_name, name) == 0)
723 return NSS_STATUS_SUCCESS;
724 else
725 continue;
728 /* -@netgroup */
729 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
730 && result->pw_name[2] != '\0')
732 if (innetgr (&result->pw_name[2], NULL, name, NULL))
733 return NSS_STATUS_NOTFOUND;
734 continue;
737 /* +@netgroup */
738 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
739 && result->pw_name[2] != '\0')
741 enum nss_status status;
743 if (innetgr (&result->pw_name[2], NULL, name, NULL))
745 status = getpwnam_plususer (name, result, ent, buffer,
746 buflen, errnop);
748 if (status == NSS_STATUS_RETURN)
749 continue;
751 return status;
753 continue;
756 /* -user */
757 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
758 && result->pw_name[1] != '@')
760 if (strcmp (&result->pw_name[1], name) == 0)
761 return NSS_STATUS_NOTFOUND;
762 else
763 continue;
766 /* +user */
767 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
768 && result->pw_name[1] != '@')
770 if (strcmp (name, &result->pw_name[1]) == 0)
772 enum nss_status status;
774 status = getpwnam_plususer (name, result, ent, buffer, buflen,
775 errnop);
776 if (status == NSS_STATUS_RETURN)
777 /* We couldn't parse the entry */
778 return NSS_STATUS_NOTFOUND;
779 else
780 return status;
784 /* +:... */
785 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
787 enum nss_status status;
789 status = getpwnam_plususer (name, result, ent,
790 buffer, buflen, errnop);
791 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
792 break;
793 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
794 return NSS_STATUS_NOTFOUND;
795 else
796 return status;
799 return NSS_STATUS_SUCCESS;
802 enum nss_status
803 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
804 char *buffer, size_t buflen, int *errnop)
806 enum nss_status result;
807 ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
808 { NULL, NULL, 0, 0, NULL, NULL, NULL }};
810 if (name[0] == '-' || name[0] == '+')
811 return NSS_STATUS_NOTFOUND;
813 __libc_lock_lock (lock);
815 if (ni == NULL)
816 init_nss_interface ();
818 __libc_lock_unlock (lock);
820 result = internal_setpwent (&ent, 0, 0);
822 if (result == NSS_STATUS_SUCCESS)
823 result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
825 internal_endpwent (&ent);
827 return result;
830 /* This function handle the + entry in /etc/passwd for getpwuid */
831 static enum nss_status
832 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
833 size_t buflen, int *errnop)
835 struct passwd pwd;
836 char *p;
837 size_t plen;
839 if (!nss_getpwuid_r)
840 return NSS_STATUS_UNAVAIL;
842 memset (&pwd, '\0', sizeof (struct passwd));
844 copy_pwd_changes (&pwd, result, NULL, 0);
846 plen = pwd_need_buflen (&pwd);
847 if (plen > buflen)
849 *errnop = ERANGE;
850 return NSS_STATUS_TRYAGAIN;
852 p = buffer + (buflen - plen);
853 buflen -= plen;
855 if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) ==
856 NSS_STATUS_SUCCESS)
858 copy_pwd_changes (result, &pwd, p, plen);
859 give_pwd_free (&pwd);
860 /* We found the entry. */
861 return NSS_STATUS_SUCCESS;
863 else
865 /* Give buffer the old len back */
866 buflen += plen;
867 give_pwd_free (&pwd);
869 return NSS_STATUS_RETURN;
872 /* Searches in /etc/passwd and the NSS subsystem for a special user id */
873 static enum nss_status
874 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
875 char *buffer, size_t buflen, int *errnop)
877 struct parser_data *data = (void *) buffer;
879 while (1)
881 fpos_t pos;
882 char *p;
883 int parse_res;
887 /* We need at least 3 characters for one line. */
888 if (__glibc_unlikely (buflen < 3))
890 erange:
891 *errnop = ERANGE;
892 return NSS_STATUS_TRYAGAIN;
895 fgetpos (ent->stream, &pos);
896 buffer[buflen - 1] = '\xff';
897 p = fgets_unlocked (buffer, buflen, ent->stream);
898 if (p == NULL && feof_unlocked (ent->stream))
899 return NSS_STATUS_NOTFOUND;
901 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
903 erange_reset:
904 fsetpos (ent->stream, &pos);
905 goto erange;
908 /* Terminate the line for any case. */
909 buffer[buflen - 1] = '\0';
911 /* Skip leading blanks. */
912 while (isspace (*p))
913 ++p;
915 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
916 /* Parse the line. If it is invalid, loop to
917 get the next line of the file to parse. */
918 !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
919 errnop)));
921 if (__glibc_unlikely (parse_res == -1))
922 /* The parser ran out of space. */
923 goto erange_reset;
925 /* This is a real entry. */
926 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
928 if (result->pw_uid == uid)
929 return NSS_STATUS_SUCCESS;
930 else
931 continue;
934 /* -@netgroup */
935 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
936 && result->pw_name[2] != '\0')
938 /* -1, because we remove first two character of pw_name. */
939 size_t len = strlen (result->pw_name) - 1;
940 char buf[len];
941 enum nss_status status;
943 memcpy (buf, &result->pw_name[2], len);
945 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
946 if (status == NSS_STATUS_SUCCESS &&
947 innetgr (buf, NULL, result->pw_name, NULL))
948 return NSS_STATUS_NOTFOUND;
950 continue;
953 /* +@netgroup */
954 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
955 && result->pw_name[2] != '\0')
957 /* -1, because we remove first two characters of pw_name. */
958 size_t len = strlen (result->pw_name) - 1;
959 char buf[len];
960 enum nss_status status;
962 memcpy (buf, &result->pw_name[2], len);
964 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
966 if (status == NSS_STATUS_RETURN)
967 continue;
969 if (status == NSS_STATUS_SUCCESS)
971 if (innetgr (buf, NULL, result->pw_name, NULL))
972 return NSS_STATUS_SUCCESS;
974 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
975 return NSS_STATUS_NOTFOUND;
976 else
977 return status;
979 continue;
982 /* -user */
983 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
984 && result->pw_name[1] != '@')
986 size_t len = strlen (result->pw_name);
987 char buf[len];
988 enum nss_status status;
990 memcpy (buf, &result->pw_name[1], len);
992 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
993 if (status == NSS_STATUS_SUCCESS &&
994 innetgr (buf, NULL, result->pw_name, NULL))
995 return NSS_STATUS_NOTFOUND;
996 continue;
999 /* +user */
1000 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1001 && result->pw_name[1] != '@')
1003 size_t len = strlen (result->pw_name);
1004 char buf[len];
1005 enum nss_status status;
1007 memcpy (buf, &result->pw_name[1], len);
1009 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1011 if (status == NSS_STATUS_RETURN)
1012 continue;
1014 if (status == NSS_STATUS_SUCCESS)
1016 if (strcmp (buf, result->pw_name) == 0)
1017 return NSS_STATUS_SUCCESS;
1019 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1020 return NSS_STATUS_NOTFOUND;
1021 else
1022 return status;
1024 continue;
1027 /* +:... */
1028 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1030 enum nss_status status;
1032 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1033 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1034 break;
1035 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1036 return NSS_STATUS_NOTFOUND;
1037 else
1038 return status;
1041 return NSS_STATUS_SUCCESS;
1044 enum nss_status
1045 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1046 char *buffer, size_t buflen, int *errnop)
1048 enum nss_status result;
1049 ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
1050 { NULL, NULL, 0, 0, NULL, NULL, NULL }};
1052 __libc_lock_lock (lock);
1054 if (ni == NULL)
1055 init_nss_interface ();
1057 __libc_lock_unlock (lock);
1059 result = internal_setpwent (&ent, 0, 0);
1061 if (result == NSS_STATUS_SUCCESS)
1062 result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1064 internal_endpwent (&ent);
1066 return result;
1070 /* Support routines for remembering -@netgroup and -user entries.
1071 The names are stored in a single string with `|' as separator. */
1072 static void
1073 blacklist_store_name (const char *name, ent_t *ent)
1075 int namelen = strlen (name);
1076 char *tmp;
1078 /* first call, setup cache */
1079 if (ent->blacklist.size == 0)
1081 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1082 ent->blacklist.data = malloc (ent->blacklist.size);
1083 if (ent->blacklist.data == NULL)
1084 return;
1085 ent->blacklist.data[0] = '|';
1086 ent->blacklist.data[1] = '\0';
1087 ent->blacklist.current = 1;
1089 else
1091 if (in_blacklist (name, namelen, ent))
1092 return; /* no duplicates */
1094 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1096 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1097 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1098 if (tmp == NULL)
1100 free (ent->blacklist.data);
1101 ent->blacklist.size = 0;
1102 return;
1104 ent->blacklist.data = tmp;
1108 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1109 *tmp++ = '|';
1110 *tmp = '\0';
1111 ent->blacklist.current += namelen + 1;
1113 return;
1116 /* Returns whether ent->blacklist contains name. */
1117 static bool
1118 in_blacklist (const char *name, int namelen, ent_t *ent)
1120 char buf[namelen + 3];
1121 char *cp;
1123 if (ent->blacklist.data == NULL)
1124 return false;
1126 buf[0] = '|';
1127 cp = stpcpy (&buf[1], name);
1128 *cp++ = '|';
1129 *cp = '\0';
1130 return strstr (ent->blacklist.data, buf) != NULL;