1/* Copyright (C) 1996-2020 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 <https://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 <shadow.h>
26#include <stdio_ext.h>
27#include <string.h>
28#include <libc-lock.h>
29#include <kernel-features.h>
30#include <nss_files.h>
31
32#include "netgroup.h"
33#include "nisdomain.h"
34
35NSS_DECLARE_MODULE_FUNCTIONS (compat)
36
37static service_user *ni;
38static enum nss_status (*setspent_impl) (int stayopen);
39static enum nss_status (*getspnam_r_impl) (const char *name, struct spwd * sp,
40 char *buffer, size_t buflen,
41 int *errnop);
42static enum nss_status (*getspent_r_impl) (struct spwd * sp, char *buffer,
43 size_t buflen, int *errnop);
44static enum nss_status (*endspent_impl) (void);
45
46/* Get the declaration of the parser function. */
47#define ENTNAME spent
48#define STRUCTURE spwd
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 files;
66 bool first;
67 enum nss_status setent_status;
68 FILE *stream;
69 struct blacklist_t blacklist;
70 struct spwd pwd;
71 struct __netgrent netgrdata;
72};
73typedef struct ent_t ent_t;
74
75static ent_t ext_ent = { false, true, false, NSS_STATUS_SUCCESS, NULL,
76 { NULL, 0, 0},
77 { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
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 ("shadow_compat", "passwd_compat",
92 "nis", &ni) >= 0)
93 {
94 setspent_impl = __nss_lookup_function (ni, "setspent");
95 getspnam_r_impl = __nss_lookup_function (ni, "getspnam_r");
96 getspent_r_impl = __nss_lookup_function (ni, "getspent_r");
97 endspent_impl = __nss_lookup_function (ni, "endspent");
98 }
99}
100
101static void
102give_spwd_free (struct spwd *pwd)
103{
104 free (pwd->sp_namp);
105 free (pwd->sp_pwdp);
106
107 memset (pwd, '\0', sizeof (struct spwd));
108 pwd->sp_warn = -1;
109 pwd->sp_inact = -1;
110 pwd->sp_expire = -1;
111 pwd->sp_flag = ~0ul;
112}
113
114static int
115spwd_need_buflen (struct spwd *pwd)
116{
117 int len = 0;
118
119 if (pwd->sp_pwdp != NULL)
120 len += strlen (pwd->sp_pwdp) + 1;
121
122 return len;
123}
124
125static void
126copy_spwd_changes (struct spwd *dest, struct spwd *src,
127 char *buffer, size_t buflen)
128{
129 if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
130 {
131 if (buffer == NULL)
132 dest->sp_pwdp = strdup (src->sp_pwdp);
133 else if (dest->sp_pwdp
134 && strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp))
135 strcpy (dest->sp_pwdp, src->sp_pwdp);
136 else
137 {
138 dest->sp_pwdp = buffer;
139 strcpy (dest->sp_pwdp, src->sp_pwdp);
140 buffer += strlen (dest->sp_pwdp) + 1;
141 buflen = buflen - (strlen (dest->sp_pwdp) + 1);
142 }
143 }
144 if (src->sp_lstchg != 0)
145 dest->sp_lstchg = src->sp_lstchg;
146 if (src->sp_min != 0)
147 dest->sp_min = src->sp_min;
148 if (src->sp_max != 0)
149 dest->sp_max = src->sp_max;
150 if (src->sp_warn != -1)
151 dest->sp_warn = src->sp_warn;
152 if (src->sp_inact != -1)
153 dest->sp_inact = src->sp_inact;
154 if (src->sp_expire != -1)
155 dest->sp_expire = src->sp_expire;
156 if (src->sp_flag != ~0ul)
157 dest->sp_flag = src->sp_flag;
158}
159
160static enum nss_status
161internal_setspent (ent_t *ent, int stayopen, int needent)
162{
163 enum nss_status status = NSS_STATUS_SUCCESS;
164
165 ent->first = ent->netgroup = 0;
166 ent->files = true;
167
168 /* If something was left over free it. */
169 if (ent->netgroup)
170 __internal_endnetgrent (&ent->netgrdata);
171
172 if (ent->blacklist.data != NULL)
173 {
174 ent->blacklist.current = 1;
175 ent->blacklist.data[0] = '|';
176 ent->blacklist.data[1] = '\0';
177 }
178 else
179 ent->blacklist.current = 0;
180
181 if (ent->stream == NULL)
182 {
183 ent->stream = __nss_files_fopen ("/etc/shadow");
184
185 if (ent->stream == NULL)
186 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
187 }
188 else
189 rewind (ent->stream);
190
191 give_spwd_free (&ent->pwd);
192
193 if (needent && status == NSS_STATUS_SUCCESS && setspent_impl)
194 ent->setent_status = setspent_impl (stayopen);
195
196 return status;
197}
198
199
200enum nss_status
201_nss_compat_setspent (int stayopen)
202{
203 enum nss_status result;
204
205 __libc_lock_lock (lock);
206
207 if (ni == NULL)
208 init_nss_interface ();
209
210 result = internal_setspent (&ext_ent, stayopen, 1);
211
212 __libc_lock_unlock (lock);
213
214 return result;
215}
216
217
218static enum nss_status __attribute_warn_unused_result__
219internal_endspent (ent_t *ent)
220{
221 if (ent->stream != NULL)
222 {
223 fclose (ent->stream);
224 ent->stream = NULL;
225 }
226
227 if (ent->netgroup)
228 __internal_endnetgrent (&ent->netgrdata);
229
230 ent->first = ent->netgroup = false;
231 ent->files = true;
232
233 if (ent->blacklist.data != NULL)
234 {
235 ent->blacklist.current = 1;
236 ent->blacklist.data[0] = '|';
237 ent->blacklist.data[1] = '\0';
238 }
239 else
240 ent->blacklist.current = 0;
241
242 give_spwd_free (&ent->pwd);
243
244 return NSS_STATUS_SUCCESS;
245}
246
247/* Like internal_endspent, but preserve errno in all cases. */
248static void
249internal_endspent_noerror (ent_t *ent)
250{
251 int saved_errno = errno;
252 enum nss_status unused __attribute__ ((unused)) = internal_endspent (ent);
253 __set_errno (saved_errno);
254}
255
256enum nss_status
257_nss_compat_endspent (void)
258{
259 enum nss_status result;
260
261 __libc_lock_lock (lock);
262
263 if (endspent_impl)
264 endspent_impl ();
265
266 result = internal_endspent (&ext_ent);
267
268 __libc_lock_unlock (lock);
269
270 return result;
271}
272
273static enum nss_status
274getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent,
275 char *group, char *buffer, size_t buflen,
276 int *errnop)
277{
278 char *curdomain = NULL, *host, *user, *domain, *p2;
279 size_t p2len;
280
281 if (!getspnam_r_impl)
282 return NSS_STATUS_UNAVAIL;
283
284 /* If the setpwent call failed, say so. */
285 if (ent->setent_status != NSS_STATUS_SUCCESS)
286 return ent->setent_status;
287
288 if (ent->first)
289 {
290 memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
291 __internal_setnetgrent (group, &ent->netgrdata);
292 ent->first = false;
293 }
294
295 while (1)
296 {
297 enum nss_status status;
298
299 status = __internal_getnetgrent_r (&host, &user, &domain,
300 &ent->netgrdata, buffer, buflen,
301 errnop);
302 if (status != 1)
303 {
304 __internal_endnetgrent (&ent->netgrdata);
305 ent->netgroup = false;
306 give_spwd_free (&ent->pwd);
307 return NSS_STATUS_RETURN;
308 }
309
310 if (user == NULL || user[0] == '-')
311 continue;
312
313 if (domain != NULL)
314 {
315 if (curdomain == NULL
316 && __nss_get_default_domain (&curdomain) != 0)
317 {
318 __internal_endnetgrent (&ent->netgrdata);
319 ent->netgroup = false;
320 give_spwd_free (&ent->pwd);
321 return NSS_STATUS_UNAVAIL;
322 }
323 if (strcmp (curdomain, domain) != 0)
324 continue;
325 }
326
327 /* If name != NULL, we are called from getpwnam */
328 if (name != NULL)
329 if (strcmp (user, name) != 0)
330 continue;
331
332 p2len = spwd_need_buflen (&ent->pwd);
333 if (p2len > buflen)
334 {
335 *errnop = ERANGE;
336 return NSS_STATUS_TRYAGAIN;
337 }
338 p2 = buffer + (buflen - p2len);
339 buflen -= p2len;
340
341 if (getspnam_r_impl (user, result, buffer, buflen, errnop)
342 != NSS_STATUS_SUCCESS)
343 continue;
344
345 if (!in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
346 {
347 /* Store the User in the blacklist for possible the "+" at the
348 end of /etc/passwd */
349 blacklist_store_name (result->sp_namp, ent);
350 copy_spwd_changes (result, &ent->pwd, p2, p2len);
351 break;
352 }
353 }
354
355 return NSS_STATUS_SUCCESS;
356}
357
358
359static enum nss_status
360getspent_next_nss (struct spwd *result, ent_t *ent,
361 char *buffer, size_t buflen, int *errnop)
362{
363 enum nss_status status;
364 char *p2;
365 size_t p2len;
366
367 if (!getspent_r_impl)
368 return NSS_STATUS_UNAVAIL;
369
370 p2len = spwd_need_buflen (&ent->pwd);
371 if (p2len > buflen)
372 {
373 *errnop = ERANGE;
374 return NSS_STATUS_TRYAGAIN;
375 }
376 p2 = buffer + (buflen - p2len);
377 buflen -= p2len;
378 do
379 {
380 if ((status = getspent_r_impl (result, buffer, buflen, errnop))
381 != NSS_STATUS_SUCCESS)
382 return status;
383 }
384 while (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent));
385
386 copy_spwd_changes (result, &ent->pwd, p2, p2len);
387
388 return NSS_STATUS_SUCCESS;
389}
390
391
392/* This function handle the +user entrys in /etc/shadow */
393static enum nss_status
394getspnam_plususer (const char *name, struct spwd *result, ent_t *ent,
395 char *buffer, size_t buflen, int *errnop)
396{
397 if (!getspnam_r_impl)
398 return NSS_STATUS_UNAVAIL;
399
400 struct spwd pwd;
401 memset (&pwd, '\0', sizeof (struct spwd));
402 pwd.sp_warn = -1;
403 pwd.sp_inact = -1;
404 pwd.sp_expire = -1;
405 pwd.sp_flag = ~0ul;
406
407 copy_spwd_changes (&pwd, result, NULL, 0);
408
409 size_t plen = spwd_need_buflen (&pwd);
410 if (plen > buflen)
411 {
412 *errnop = ERANGE;
413 return NSS_STATUS_TRYAGAIN;
414 }
415 char *p = buffer + (buflen - plen);
416 buflen -= plen;
417
418 enum nss_status status = getspnam_r_impl (name, result, buffer, buflen,
419 errnop);
420 if (status != NSS_STATUS_SUCCESS)
421 return status;
422
423 if (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
424 return NSS_STATUS_NOTFOUND;
425
426 copy_spwd_changes (result, &pwd, p, plen);
427 give_spwd_free (&pwd);
428 /* We found the entry. */
429 return NSS_STATUS_SUCCESS;
430}
431
432
433static enum nss_status
434getspent_next_file (struct spwd *result, ent_t *ent,
435 char *buffer, size_t buflen, int *errnop)
436{
437 struct parser_data *data = (void *) buffer;
438 while (1)
439 {
440 fpos_t pos;
441 int parse_res = 0;
442 char *p;
443
444 do
445 {
446 /* We need at least 3 characters for one line. */
447 if (__glibc_unlikely (buflen < 3))
448 {
449 erange:
450 *errnop = ERANGE;
451 return NSS_STATUS_TRYAGAIN;
452 }
453
454 fgetpos (ent->stream, &pos);
455 buffer[buflen - 1] = '\xff';
456 p = fgets_unlocked (buffer, buflen, ent->stream);
457 if (p == NULL && feof_unlocked (ent->stream))
458 return NSS_STATUS_NOTFOUND;
459
460 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
461 {
462 erange_reset:
463 fsetpos (ent->stream, &pos);
464 goto erange;
465 }
466
467 /* Skip leading blanks. */
468 while (isspace (*p))
469 ++p;
470 }
471 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
472 /* Parse the line. If it is invalid, loop to
473 get the next line of the file to parse. */
474 || !(parse_res = _nss_files_parse_spent (p, result, data,
475 buflen, errnop)));
476
477 if (__glibc_unlikely (parse_res == -1))
478 /* The parser ran out of space. */
479 goto erange_reset;
480
481 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
482 /* This is a real entry. */
483 break;
484
485 /* -@netgroup */
486 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
487 && result->sp_namp[2] != '\0')
488 {
489 /* XXX Do not use fixed length buffers. */
490 char buf2[1024];
491 char *user, *host, *domain;
492 struct __netgrent netgrdata;
493
494 memset (&netgrdata, 0, sizeof (struct __netgrent));
495 __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
496 while (__internal_getnetgrent_r (&host, &user, &domain,
497 &netgrdata, buf2, sizeof (buf2),
498 errnop))
499 {
500 if (user != NULL && user[0] != '-')
501 blacklist_store_name (user, ent);
502 }
503 __internal_endnetgrent (&netgrdata);
504 continue;
505 }
506
507 /* +@netgroup */
508 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
509 && result->sp_namp[2] != '\0')
510 {
511 int status;
512
513 ent->netgroup = true;
514 ent->first = true;
515 copy_spwd_changes (&ent->pwd, result, NULL, 0);
516
517 status = getspent_next_nss_netgr (NULL, result, ent,
518 &result->sp_namp[2],
519 buffer, buflen, errnop);
520 if (status == NSS_STATUS_RETURN)
521 continue;
522 else
523 return status;
524 }
525
526 /* -user */
527 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
528 && result->sp_namp[1] != '@')
529 {
530 blacklist_store_name (&result->sp_namp[1], ent);
531 continue;
532 }
533
534 /* +user */
535 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
536 && result->sp_namp[1] != '@')
537 {
538 size_t len = strlen (result->sp_namp);
539 char buf[len];
540 enum nss_status status;
541
542 /* Store the User in the blacklist for the "+" at the end of
543 /etc/passwd */
544 memcpy (buf, &result->sp_namp[1], len);
545 status = getspnam_plususer (&result->sp_namp[1], result, ent,
546 buffer, buflen, errnop);
547 blacklist_store_name (buf, ent);
548
549 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
550 break;
551 /* We couldn't parse the entry */
552 else if (status == NSS_STATUS_RETURN
553 /* entry doesn't exist */
554 || status == NSS_STATUS_NOTFOUND)
555 continue;
556 else
557 {
558 if (status == NSS_STATUS_TRYAGAIN)
559 {
560 fsetpos (ent->stream, &pos);
561 *errnop = ERANGE;
562 }
563 return status;
564 }
565 }
566
567 /* +:... */
568 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
569 {
570 ent->files = false;
571 ent->first = true;
572 copy_spwd_changes (&ent->pwd, result, NULL, 0);
573
574 return getspent_next_nss (result, ent, buffer, buflen, errnop);
575 }
576 }
577
578 return NSS_STATUS_SUCCESS;
579}
580
581
582static enum nss_status
583internal_getspent_r (struct spwd *pw, ent_t *ent,
584 char *buffer, size_t buflen, int *errnop)
585{
586 if (ent->netgroup)
587 {
588 enum nss_status status;
589
590 /* We are searching members in a netgroup */
591 /* Since this is not the first call, we don't need the group name */
592 status = getspent_next_nss_netgr (NULL, pw, ent, NULL, buffer,
593 buflen, errnop);
594
595 if (status == NSS_STATUS_RETURN)
596 return getspent_next_file (pw, ent, buffer, buflen, errnop);
597 else
598 return status;
599 }
600 else if (ent->files)
601 return getspent_next_file (pw, ent, buffer, buflen, errnop);
602 else
603 return getspent_next_nss (pw, ent, buffer, buflen, errnop);
604}
605
606
607enum nss_status
608_nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen,
609 int *errnop)
610{
611 enum nss_status result = NSS_STATUS_SUCCESS;
612
613 __libc_lock_lock (lock);
614
615 /* Be prepared that the setpwent function was not called before. */
616 if (ni == NULL)
617 init_nss_interface ();
618
619 if (ext_ent.stream == NULL)
620 result = internal_setspent (&ext_ent, 1, 1);
621
622 if (result == NSS_STATUS_SUCCESS)
623 result = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop);
624
625 __libc_lock_unlock (lock);
626
627 return result;
628}
629
630
631/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
632static enum nss_status
633internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
634 char *buffer, size_t buflen, int *errnop)
635{
636 struct parser_data *data = (void *) buffer;
637
638 while (1)
639 {
640 fpos_t pos;
641 char *p;
642 int parse_res;
643
644 do
645 {
646 /* We need at least 3 characters for one line. */
647 if (__glibc_unlikely (buflen < 3))
648 {
649 erange:
650 *errnop = ERANGE;
651 return NSS_STATUS_TRYAGAIN;
652 }
653
654 fgetpos (ent->stream, &pos);
655 buffer[buflen - 1] = '\xff';
656 p = fgets_unlocked (buffer, buflen, ent->stream);
657 if (p == NULL && feof_unlocked (ent->stream))
658 return NSS_STATUS_NOTFOUND;
659
660 if (p == NULL || buffer[buflen - 1] != '\xff')
661 {
662 erange_reset:
663 fsetpos (ent->stream, &pos);
664 goto erange;
665 }
666
667 /* Terminate the line for any case. */
668 buffer[buflen - 1] = '\0';
669
670 /* Skip leading blanks. */
671 while (isspace (*p))
672 ++p;
673 }
674 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
675 /* Parse the line. If it is invalid, loop to
676 get the next line of the file to parse. */
677 || !(parse_res = _nss_files_parse_spent (p, result, data, buflen,
678 errnop)));
679
680 if (__glibc_unlikely (parse_res == -1))
681 /* The parser ran out of space. */
682 goto erange_reset;
683
684 /* This is a real entry. */
685 if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
686 {
687 if (strcmp (result->sp_namp, name) == 0)
688 return NSS_STATUS_SUCCESS;
689 else
690 continue;
691 }
692
693 /* -@netgroup */
694 /* If the loaded NSS module does not support this service, add
695 all users from a +@netgroup entry to the blacklist, too. */
696 if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
697 && result->sp_namp[2] != '\0')
698 {
699 if (innetgr (&result->sp_namp[2], NULL, name, NULL))
700 return NSS_STATUS_NOTFOUND;
701 continue;
702 }
703
704 /* +@netgroup */
705 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
706 && result->sp_namp[2] != '\0')
707 {
708 enum nss_status status;
709
710 if (innetgr (&result->sp_namp[2], NULL, name, NULL))
711 {
712 status = getspnam_plususer (name, result, ent, buffer,
713 buflen, errnop);
714
715 if (status == NSS_STATUS_RETURN)
716 continue;
717
718 return status;
719 }
720 continue;
721 }
722
723 /* -user */
724 if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
725 && result->sp_namp[1] != '@')
726 {
727 if (strcmp (&result->sp_namp[1], name) == 0)
728 return NSS_STATUS_NOTFOUND;
729 else
730 continue;
731 }
732
733 /* +user */
734 if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
735 && result->sp_namp[1] != '@')
736 {
737 if (strcmp (name, &result->sp_namp[1]) == 0)
738 {
739 enum nss_status status;
740
741 status = getspnam_plususer (name, result, ent,
742 buffer, buflen, errnop);
743
744 if (status == NSS_STATUS_RETURN)
745 /* We couldn't parse the entry */
746 return NSS_STATUS_NOTFOUND;
747 else
748 return status;
749 }
750 }
751
752 /* +:... */
753 if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
754 {
755 enum nss_status status;
756
757 status = getspnam_plususer (name, result, ent,
758 buffer, buflen, errnop);
759
760 if (status == NSS_STATUS_SUCCESS)
761 /* We found the entry. */
762 break;
763 else if (status == NSS_STATUS_RETURN)
764 /* We couldn't parse the entry */
765 return NSS_STATUS_NOTFOUND;
766 else
767 return status;
768 }
769 }
770 return NSS_STATUS_SUCCESS;
771}
772
773
774enum nss_status
775_nss_compat_getspnam_r (const char *name, struct spwd *pwd,
776 char *buffer, size_t buflen, int *errnop)
777{
778 enum nss_status result;
779 ent_t ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0},
780 { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
781
782 if (name[0] == '-' || name[0] == '+')
783 return NSS_STATUS_NOTFOUND;
784
785 __libc_lock_lock (lock);
786
787 if (ni == NULL)
788 init_nss_interface ();
789
790 __libc_lock_unlock (lock);
791
792 result = internal_setspent (&ent, 0, 0);
793
794 if (result == NSS_STATUS_SUCCESS)
795 result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
796
797 internal_endspent_noerror (&ent);
798
799 return result;
800}
801
802
803/* Support routines for remembering -@netgroup and -user entries.
804 The names are stored in a single string with `|' as separator. */
805static void
806blacklist_store_name (const char *name, ent_t *ent)
807{
808 int namelen = strlen (name);
809 char *tmp;
810
811 /* first call, setup cache */
812 if (ent->blacklist.size == 0)
813 {
814 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
815 ent->blacklist.data = malloc (ent->blacklist.size);
816 if (ent->blacklist.data == NULL)
817 return;
818 ent->blacklist.data[0] = '|';
819 ent->blacklist.data[1] = '\0';
820 ent->blacklist.current = 1;
821 }
822 else
823 {
824 if (in_blacklist (name, namelen, ent))
825 return; /* no duplicates */
826
827 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
828 {
829 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
830 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
831 if (tmp == NULL)
832 {
833 free (ent->blacklist.data);
834 ent->blacklist.size = 0;
835 return;
836 }
837 ent->blacklist.data = tmp;
838 }
839 }
840
841 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
842 *tmp++ = '|';
843 *tmp = '\0';
844 ent->blacklist.current += namelen + 1;
845
846 return;
847}
848
849
850/* Returns whether ent->blacklist contains name. */
851static bool
852in_blacklist (const char *name, int namelen, ent_t *ent)
853{
854 char buf[namelen + 3];
855 char *cp;
856
857 if (ent->blacklist.data == NULL)
858 return false;
859
860 buf[0] = '|';
861 cp = stpcpy (&buf[1], name);
862 *cp++ = '|';
863 *cp = '\0';
864 return strstr (ent->blacklist.data, buf) != NULL;
865}
866