1/* Copyright (C) 1996-2019 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.
4
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.
9
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.
14
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/>. */
18
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>
30
31#include "netgroup.h"
32#include "nisdomain.h"
33
34static service_user *ni;
35static enum nss_status (*nss_setpwent) (int stayopen);
36static enum nss_status (*nss_getpwnam_r) (const char *name,
37 struct passwd * pwd, char *buffer,
38 size_t buflen, int *errnop);
39static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd,
40 char *buffer, size_t buflen,
41 int *errnop);
42static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer,
43 size_t buflen, int *errnop);
44static enum nss_status (*nss_endpwent) (void);
45
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>
51
52/* Structure for remembering -@netgroup and -user members ... */
53#define BLACKLIST_INITIAL_SIZE 512
54#define BLACKLIST_INCREMENT 256
55struct blacklist_t
56{
57 char *data;
58 int current;
59 int size;
60};
61
62struct ent_t
63{
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;
72};
73typedef struct ent_t ent_t;
74
75static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL,
76 { NULL, 0, 0 },
77 { NULL, NULL, 0, 0, NULL, NULL, NULL }};
78
79/* Protect global state against multiple changers. */
80__libc_lock_define_initialized (static, lock)
81
82/* Prototypes for local functions. */
83static void blacklist_store_name (const char *, ent_t *);
84static bool in_blacklist (const char *, int, ent_t *);
85
86/* Initialize the NSS interface/functions. The calling function must
87 hold the lock. */
88static void
89init_nss_interface (void)
90{
91 if (__nss_database_lookup2 ("passwd_compat", NULL, "nis", &ni) >= 0)
92 {
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");
98 }
99}
100
101static void
102give_pwd_free (struct passwd *pwd)
103{
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);
109
110 memset (pwd, '\0', sizeof (struct passwd));
111}
112
113static size_t
114pwd_need_buflen (struct passwd *pwd)
115{
116 size_t len = 0;
117
118 if (pwd->pw_passwd != NULL)
119 len += strlen (pwd->pw_passwd) + 1;
120
121 if (pwd->pw_gecos != NULL)
122 len += strlen (pwd->pw_gecos) + 1;
123
124 if (pwd->pw_dir != NULL)
125 len += strlen (pwd->pw_dir) + 1;
126
127 if (pwd->pw_shell != NULL)
128 len += strlen (pwd->pw_shell) + 1;
129
130 return len;
131}
132
133static void
134copy_pwd_changes (struct passwd *dest, struct passwd *src,
135 char *buffer, size_t buflen)
136{
137 if (src->pw_passwd != NULL && strlen (src->pw_passwd))
138 {
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
145 {
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);
150 }
151 }
152
153 if (src->pw_gecos != NULL && strlen (src->pw_gecos))
154 {
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
161 {
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);
166 }
167 }
168 if (src->pw_dir != NULL && strlen (src->pw_dir))
169 {
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
175 {
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);
180 }
181 }
182
183 if (src->pw_shell != NULL && strlen (src->pw_shell))
184 {
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
191 {
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);
196 }
197 }
198}
199
200static enum nss_status
201internal_setpwent (ent_t *ent, int stayopen, int needent)
202{
203 enum nss_status status = NSS_STATUS_SUCCESS;
204
205 ent->first = ent->netgroup = false;
206 ent->files = true;
207 ent->setent_status = NSS_STATUS_SUCCESS;
208
209 /* If something was left over free it. */
210 if (ent->netgroup)
211 __internal_endnetgrent (&ent->netgrdata);
212
213 if (ent->blacklist.data != NULL)
214 {
215 ent->blacklist.current = 1;
216 ent->blacklist.data[0] = '|';
217 ent->blacklist.data[1] = '\0';
218 }
219 else
220 ent->blacklist.current = 0;
221
222 if (ent->stream == NULL)
223 {
224 ent->stream = fopen ("/etc/passwd", "rme");
225
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);
231 }
232 else
233 rewind (ent->stream);
234
235 give_pwd_free (&ent->pwd);
236
237 if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent)
238 ent->setent_status = nss_setpwent (stayopen);
239
240 return status;
241}
242
243
244enum nss_status
245_nss_compat_setpwent (int stayopen)
246{
247 enum nss_status result;
248
249 __libc_lock_lock (lock);
250
251 if (ni == NULL)
252 init_nss_interface ();
253
254 result = internal_setpwent (&ext_ent, stayopen, 1);
255
256 __libc_lock_unlock (lock);
257
258 return result;
259}
260
261
262static enum nss_status
263internal_endpwent (ent_t *ent)
264{
265 if (ent->stream != NULL)
266 {
267 fclose (ent->stream);
268 ent->stream = NULL;
269 }
270
271 if (ent->netgroup)
272 __internal_endnetgrent (&ent->netgrdata);
273
274 ent->first = ent->netgroup = false;
275
276 if (ent->blacklist.data != NULL)
277 {
278 ent->blacklist.current = 1;
279 ent->blacklist.data[0] = '|';
280 ent->blacklist.data[1] = '\0';
281 }
282 else
283 ent->blacklist.current = 0;
284
285 give_pwd_free (&ent->pwd);
286
287 return NSS_STATUS_SUCCESS;
288}
289
290enum nss_status
291_nss_compat_endpwent (void)
292{
293 enum nss_status result;
294
295 __libc_lock_lock (lock);
296
297 if (nss_endpwent)
298 nss_endpwent ();
299
300 result = internal_endpwent (&ext_ent);
301
302 __libc_lock_unlock (lock);
303
304 return result;
305}
306
307
308static enum nss_status
309getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent,
310 char *group, char *buffer, size_t buflen,
311 int *errnop)
312{
313 char *curdomain = NULL, *host, *user, *domain, *p2;
314 int status;
315 size_t p2len;
316
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;
321
322 if (ent->first)
323 {
324 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
325 __internal_setnetgrent (group, &ent->netgrdata);
326 ent->first = false;
327 }
328
329 while (1)
330 {
331 status = __internal_getnetgrent_r (&host, &user, &domain,
332 &ent->netgrdata, buffer, buflen,
333 errnop);
334 if (status != 1)
335 {
336 __internal_endnetgrent (&ent->netgrdata);
337 ent->netgroup = 0;
338 give_pwd_free (&ent->pwd);
339 return NSS_STATUS_RETURN;
340 }
341
342 if (user == NULL || user[0] == '-')
343 continue;
344
345 if (domain != NULL)
346 {
347 if (curdomain == NULL
348 && __nss_get_default_domain (&curdomain) != 0)
349 {
350 __internal_endnetgrent (&ent->netgrdata);
351 ent->netgroup = false;
352 give_pwd_free (&ent->pwd);
353 return NSS_STATUS_UNAVAIL;
354 }
355 if (strcmp (curdomain, domain) != 0)
356 continue;
357 }
358
359 /* If name != NULL, we are called from getpwnam. */
360 if (name != NULL)
361 if (strcmp (user, name) != 0)
362 continue;
363
364 p2len = pwd_need_buflen (&ent->pwd);
365 if (p2len > buflen)
366 {
367 *errnop = ERANGE;
368 return NSS_STATUS_TRYAGAIN;
369 }
370 p2 = buffer + (buflen - p2len);
371 buflen -= p2len;
372
373 if (nss_getpwnam_r (user, result, buffer, buflen, errnop)
374 != NSS_STATUS_SUCCESS)
375 continue;
376
377 if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent))
378 {
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;
384 }
385 }
386
387 return NSS_STATUS_SUCCESS;
388}
389
390/* get the next user from NSS (+ entry) */
391static enum nss_status
392getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer,
393 size_t buflen, int *errnop)
394{
395 enum nss_status status;
396 char *p2;
397 size_t p2len;
398
399 /* Return if NSS module does not support getpwent_r. */
400 if (!nss_getpwent_r)
401 return NSS_STATUS_UNAVAIL;
402
403 /* If the setpwent call failed, say so. */
404 if (ent->setent_status != NSS_STATUS_SUCCESS)
405 return ent->setent_status;
406
407 p2len = pwd_need_buflen (&ent->pwd);
408 if (p2len > buflen)
409 {
410 *errnop = ERANGE;
411 return NSS_STATUS_TRYAGAIN;
412 }
413 p2 = buffer + (buflen - p2len);
414 buflen -= p2len;
415
416 if (ent->first)
417 ent->first = false;
418
419 do
420 {
421 if ((status = nss_getpwent_r (result, buffer, buflen, errnop))
422 != NSS_STATUS_SUCCESS)
423 return status;
424 }
425 while (in_blacklist (result->pw_name, strlen (result->pw_name), ent));
426
427 copy_pwd_changes (result, &ent->pwd, p2, p2len);
428
429 return NSS_STATUS_SUCCESS;
430}
431
432/* This function handle the +user entrys in /etc/passwd */
433static enum nss_status
434getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
435 char *buffer, size_t buflen, int *errnop)
436{
437 if (!nss_getpwnam_r)
438 return NSS_STATUS_UNAVAIL;
439
440 struct passwd pwd;
441 memset (&pwd, '\0', sizeof (struct passwd));
442
443 copy_pwd_changes (&pwd, result, NULL, 0);
444
445 size_t plen = pwd_need_buflen (&pwd);
446 if (plen > buflen)
447 {
448 *errnop = ERANGE;
449 return NSS_STATUS_TRYAGAIN;
450 }
451 char *p = buffer + (buflen - plen);
452 buflen -= plen;
453
454 enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen,
455 errnop);
456 if (status != NSS_STATUS_SUCCESS)
457 return status;
458
459 if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
460 return NSS_STATUS_NOTFOUND;
461
462 copy_pwd_changes (result, &pwd, p, plen);
463 give_pwd_free (&pwd);
464 /* We found the entry. */
465 return NSS_STATUS_SUCCESS;
466}
467
468static enum nss_status
469getpwent_next_file (struct passwd *result, ent_t *ent,
470 char *buffer, size_t buflen, int *errnop)
471{
472 struct parser_data *data = (void *) buffer;
473 while (1)
474 {
475 fpos_t pos;
476 char *p;
477 int parse_res;
478
479 do
480 {
481 /* We need at least 3 characters for one line. */
482 if (__glibc_unlikely (buflen < 3))
483 {
484 erange:
485 *errnop = ERANGE;
486 return NSS_STATUS_TRYAGAIN;
487 }
488
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;
494
495 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
496 {
497 erange_reset:
498 fsetpos (ent->stream, &pos);
499 goto erange;
500 }
501
502 /* Terminate the line for any case. */
503 buffer[buflen - 1] = '\0';
504
505 /* Skip leading blanks. */
506 while (isspace (*p))
507 ++p;
508 }
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)));
514
515 if (__glibc_unlikely (parse_res == -1))
516 /* The parser ran out of space. */
517 goto erange_reset;
518
519 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
520 /* This is a real entry. */
521 break;
522
523 /* -@netgroup */
524 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
525 && result->pw_name[2] != '\0')
526 {
527 /* XXX Do not use fixed length buffer. */
528 char buf2[1024];
529 char *user, *host, *domain;
530 struct __netgrent netgrdata;
531
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))
536 {
537 if (user != NULL && user[0] != '-')
538 blacklist_store_name (user, ent);
539 }
540 __internal_endnetgrent (&netgrdata);
541 continue;
542 }
543
544 /* +@netgroup */
545 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
546 && result->pw_name[2] != '\0')
547 {
548 enum nss_status status;
549
550 ent->netgroup = true;
551 ent->first = true;
552 copy_pwd_changes (&ent->pwd, result, NULL, 0);
553
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;
561 }
562
563 /* -user */
564 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
565 && result->pw_name[1] != '@')
566 {
567 blacklist_store_name (&result->pw_name[1], ent);
568 continue;
569 }
570
571 /* +user */
572 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
573 && result->pw_name[1] != '@')
574 {
575 size_t len = strlen (result->pw_name);
576 char buf[len];
577 enum nss_status status;
578
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);
585
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
592 {
593 if (status == NSS_STATUS_TRYAGAIN)
594 {
595 /* The parser ran out of space */
596 fsetpos (ent->stream, &pos);
597 *errnop = ERANGE;
598 }
599 return status;
600 }
601 }
602
603 /* +:... */
604 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
605 {
606 ent->files = false;
607 ent->first = true;
608 copy_pwd_changes (&ent->pwd, result, NULL, 0);
609
610 return getpwent_next_nss (result, ent, buffer, buflen, errnop);
611 }
612 }
613
614 return NSS_STATUS_SUCCESS;
615}
616
617
618static enum nss_status
619internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
620 size_t buflen, int *errnop)
621{
622 if (ent->netgroup)
623 {
624 enum nss_status status;
625
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;
634 }
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);
639
640}
641
642enum nss_status
643_nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
644 int *errnop)
645{
646 enum nss_status result = NSS_STATUS_SUCCESS;
647
648 __libc_lock_lock (lock);
649
650 /* Be prepared that the setpwent function was not called before. */
651 if (ni == NULL)
652 init_nss_interface ();
653
654 if (ext_ent.stream == NULL)
655 result = internal_setpwent (&ext_ent, 1, 1);
656
657 if (result == NSS_STATUS_SUCCESS)
658 result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
659
660 __libc_lock_unlock (lock);
661
662 return result;
663}
664
665/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
666static enum nss_status
667internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
668 char *buffer, size_t buflen, int *errnop)
669{
670 struct parser_data *data = (void *) buffer;
671
672 while (1)
673 {
674 fpos_t pos;
675 char *p;
676 int parse_res;
677
678 do
679 {
680 /* We need at least 3 characters for one line. */
681 if (__glibc_unlikely (buflen < 3))
682 {
683 erange:
684 *errnop = ERANGE;
685 return NSS_STATUS_TRYAGAIN;
686 }
687
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))
692 {
693 return NSS_STATUS_NOTFOUND;
694 }
695 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
696 {
697 erange_reset:
698 fsetpos (ent->stream, &pos);
699 goto erange;
700 }
701
702 /* Terminate the line for any case. */
703 buffer[buflen - 1] = '\0';
704
705 /* Skip leading blanks. */
706 while (isspace (*p))
707 ++p;
708 }
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)));
714
715 if (__glibc_unlikely (parse_res == -1))
716 /* The parser ran out of space. */
717 goto erange_reset;
718
719 /* This is a real entry. */
720 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
721 {
722 if (strcmp (result->pw_name, name) == 0)
723 return NSS_STATUS_SUCCESS;
724 else
725 continue;
726 }
727
728 /* -@netgroup */
729 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
730 && result->pw_name[2] != '\0')
731 {
732 if (innetgr (&result->pw_name[2], NULL, name, NULL))
733 return NSS_STATUS_NOTFOUND;
734 continue;
735 }
736
737 /* +@netgroup */
738 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
739 && result->pw_name[2] != '\0')
740 {
741 enum nss_status status;
742
743 if (innetgr (&result->pw_name[2], NULL, name, NULL))
744 {
745 status = getpwnam_plususer (name, result, ent, buffer,
746 buflen, errnop);
747
748 if (status == NSS_STATUS_RETURN)
749 continue;
750
751 return status;
752 }
753 continue;
754 }
755
756 /* -user */
757 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
758 && result->pw_name[1] != '@')
759 {
760 if (strcmp (&result->pw_name[1], name) == 0)
761 return NSS_STATUS_NOTFOUND;
762 else
763 continue;
764 }
765
766 /* +user */
767 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
768 && result->pw_name[1] != '@')
769 {
770 if (strcmp (name, &result->pw_name[1]) == 0)
771 {
772 enum nss_status status;
773
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;
781 }
782 }
783
784 /* +:... */
785 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
786 {
787 enum nss_status status;
788
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;
797 }
798 }
799 return NSS_STATUS_SUCCESS;
800}
801
802enum nss_status
803_nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
804 char *buffer, size_t buflen, int *errnop)
805{
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 }};
809
810 if (name[0] == '-' || name[0] == '+')
811 return NSS_STATUS_NOTFOUND;
812
813 __libc_lock_lock (lock);
814
815 if (ni == NULL)
816 init_nss_interface ();
817
818 __libc_lock_unlock (lock);
819
820 result = internal_setpwent (&ent, 0, 0);
821
822 if (result == NSS_STATUS_SUCCESS)
823 result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
824
825 internal_endpwent (&ent);
826
827 return result;
828}
829
830/* This function handle the + entry in /etc/passwd for getpwuid */
831static enum nss_status
832getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
833 size_t buflen, int *errnop)
834{
835 struct passwd pwd;
836 char *p;
837 size_t plen;
838
839 if (!nss_getpwuid_r)
840 return NSS_STATUS_UNAVAIL;
841
842 memset (&pwd, '\0', sizeof (struct passwd));
843
844 copy_pwd_changes (&pwd, result, NULL, 0);
845
846 plen = pwd_need_buflen (&pwd);
847 if (plen > buflen)
848 {
849 *errnop = ERANGE;
850 return NSS_STATUS_TRYAGAIN;
851 }
852 p = buffer + (buflen - plen);
853 buflen -= plen;
854
855 if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) ==
856 NSS_STATUS_SUCCESS)
857 {
858 copy_pwd_changes (result, &pwd, p, plen);
859 give_pwd_free (&pwd);
860 /* We found the entry. */
861 return NSS_STATUS_SUCCESS;
862 }
863 else
864 {
865 /* Give buffer the old len back */
866 buflen += plen;
867 give_pwd_free (&pwd);
868 }
869 return NSS_STATUS_RETURN;
870}
871
872/* Searches in /etc/passwd and the NSS subsystem for a special user id */
873static enum nss_status
874internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
875 char *buffer, size_t buflen, int *errnop)
876{
877 struct parser_data *data = (void *) buffer;
878
879 while (1)
880 {
881 fpos_t pos;
882 char *p;
883 int parse_res;
884
885 do
886 {
887 /* We need at least 3 characters for one line. */
888 if (__glibc_unlikely (buflen < 3))
889 {
890 erange:
891 *errnop = ERANGE;
892 return NSS_STATUS_TRYAGAIN;
893 }
894
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;
900
901 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
902 {
903 erange_reset:
904 fsetpos (ent->stream, &pos);
905 goto erange;
906 }
907
908 /* Terminate the line for any case. */
909 buffer[buflen - 1] = '\0';
910
911 /* Skip leading blanks. */
912 while (isspace (*p))
913 ++p;
914 }
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)));
920
921 if (__glibc_unlikely (parse_res == -1))
922 /* The parser ran out of space. */
923 goto erange_reset;
924
925 /* This is a real entry. */
926 if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
927 {
928 if (result->pw_uid == uid)
929 return NSS_STATUS_SUCCESS;
930 else
931 continue;
932 }
933
934 /* -@netgroup */
935 if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
936 && result->pw_name[2] != '\0')
937 {
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;
942
943 memcpy (buf, &result->pw_name[2], len);
944
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;
949
950 continue;
951 }
952
953 /* +@netgroup */
954 if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
955 && result->pw_name[2] != '\0')
956 {
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;
961
962 memcpy (buf, &result->pw_name[2], len);
963
964 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
965
966 if (status == NSS_STATUS_RETURN)
967 continue;
968
969 if (status == NSS_STATUS_SUCCESS)
970 {
971 if (innetgr (buf, NULL, result->pw_name, NULL))
972 return NSS_STATUS_SUCCESS;
973 }
974 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
975 return NSS_STATUS_NOTFOUND;
976 else
977 return status;
978
979 continue;
980 }
981
982 /* -user */
983 if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
984 && result->pw_name[1] != '@')
985 {
986 size_t len = strlen (result->pw_name);
987 char buf[len];
988 enum nss_status status;
989
990 memcpy (buf, &result->pw_name[1], len);
991
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;
997 }
998
999 /* +user */
1000 if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1001 && result->pw_name[1] != '@')
1002 {
1003 size_t len = strlen (result->pw_name);
1004 char buf[len];
1005 enum nss_status status;
1006
1007 memcpy (buf, &result->pw_name[1], len);
1008
1009 status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1010
1011 if (status == NSS_STATUS_RETURN)
1012 continue;
1013
1014 if (status == NSS_STATUS_SUCCESS)
1015 {
1016 if (strcmp (buf, result->pw_name) == 0)
1017 return NSS_STATUS_SUCCESS;
1018 }
1019 else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1020 return NSS_STATUS_NOTFOUND;
1021 else
1022 return status;
1023
1024 continue;
1025 }
1026
1027 /* +:... */
1028 if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1029 {
1030 enum nss_status status;
1031
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;
1039 }
1040 }
1041 return NSS_STATUS_SUCCESS;
1042}
1043
1044enum nss_status
1045_nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1046 char *buffer, size_t buflen, int *errnop)
1047{
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 }};
1051
1052 __libc_lock_lock (lock);
1053
1054 if (ni == NULL)
1055 init_nss_interface ();
1056
1057 __libc_lock_unlock (lock);
1058
1059 result = internal_setpwent (&ent, 0, 0);
1060
1061 if (result == NSS_STATUS_SUCCESS)
1062 result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1063
1064 internal_endpwent (&ent);
1065
1066 return result;
1067}
1068
1069
1070/* Support routines for remembering -@netgroup and -user entries.
1071 The names are stored in a single string with `|' as separator. */
1072static void
1073blacklist_store_name (const char *name, ent_t *ent)
1074{
1075 int namelen = strlen (name);
1076 char *tmp;
1077
1078 /* first call, setup cache */
1079 if (ent->blacklist.size == 0)
1080 {
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;
1088 }
1089 else
1090 {
1091 if (in_blacklist (name, namelen, ent))
1092 return; /* no duplicates */
1093
1094 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1095 {
1096 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1097 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1098 if (tmp == NULL)
1099 {
1100 free (ent->blacklist.data);
1101 ent->blacklist.size = 0;
1102 return;
1103 }
1104 ent->blacklist.data = tmp;
1105 }
1106 }
1107
1108 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1109 *tmp++ = '|';
1110 *tmp = '\0';
1111 ent->blacklist.current += namelen + 1;
1112
1113 return;
1114}
1115
1116/* Returns whether ent->blacklist contains name. */
1117static bool
1118in_blacklist (const char *name, int namelen, ent_t *ent)
1119{
1120 char buf[namelen + 3];
1121 char *cp;
1122
1123 if (ent->blacklist.data == NULL)
1124 return false;
1125
1126 buf[0] = '|';
1127 cp = stpcpy (&buf[1], name);
1128 *cp++ = '|';
1129 *cp = '\0';
1130 return strstr (ent->blacklist.data, buf) != NULL;
1131}
1132