1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include "namespace.h"
36
37 #include <ctype.h>
38 #include <errno.h>
39 #include <netdb.h>
40 #include <nsswitch.h>
41 #include <pthread.h>
42 #include <pthread_np.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #include "nss_tls.h"
49
50 #ifdef YP
51 /*
52 * Notes:
53 * We want to be able to use NIS netgroups properly while retaining
54 * the ability to use a local /etc/netgroup file. Unfortunately, you
55 * can't really do both at the same time - at least, not efficiently.
56 * NetBSD deals with this problem by creating a netgroup database
57 * using Berkeley DB (just like the password database) that allows
58 * for lookups using netgroup, netgroup.byuser or netgroup.byhost
59 * searches. This is a neat idea, but I don't have time to implement
60 * something like that now. (I think ultimately it would be nice
61 * if we DB-fied the group and netgroup stuff all in one shot, but
62 * for now I'm satisfied just to have something that works well
63 * without requiring massive code changes.)
64 *
65 * Therefore, to still permit the use of the local file and maintain
66 * optimum NIS performance, we allow for the following conditions:
67 *
68 * - If /etc/netgroup does not exist and NIS is turned on, we use
69 * NIS netgroups only.
70 *
71 * - If /etc/netgroup exists but is empty, we use NIS netgroups
72 * only.
73 *
74 * - If /etc/netgroup exists and contains _only_ a '+', we use
75 * NIS netgroups only.
76 *
77 * - If /etc/netgroup exists, contains locally defined netgroups
78 * and a '+', we use a mixture of NIS and the local entries.
79 * This method should return the same NIS data as just using
80 * NIS alone, but it will be slower if the NIS netgroup database
81 * is large (innetgr() in particular will suffer since extra
82 * processing has to be done in order to determine memberships
83 * using just the raw netgroup data).
84 *
85 * - If /etc/netgroup exists and contains only locally defined
86 * netgroup entries, we use just those local entries and ignore
87 * NIS (this is the original, pre-NIS behavior).
88 */
89
90 #include <rpc/rpc.h>
91 #include <rpcsvc/yp_prot.h>
92 #include <rpcsvc/ypclnt.h>
93 #include <sys/param.h>
94 #include <sys/stat.h>
95 #include <sys/errno.h>
96 static char *_netgr_yp_domain;
97 int _use_only_yp;
98 static int _netgr_yp_enabled;
99 static int _yp_innetgr;
100 #endif
101
102 #ifndef _PATH_NETGROUP
103 #define _PATH_NETGROUP "/etc/netgroup"
104 #endif
105
106 enum constants {
107 NGRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
108 NGRP_STORAGE_MAX = 1 << 20, /* 1 MByte */
109 };
110
111 static const ns_src defaultsrc[] = {
112 { NSSRC_COMPAT, NS_SUCCESS },
113 { NULL, 0 },
114 };
115
116 /*
117 * Static Variables and functions used by setnetgrent(), getnetgrent() and
118 * endnetgrent().
119 * There are two linked lists:
120 * - linelist is just used by setnetgrent() to parse the net group file via.
121 * parse_netgrp()
122 * - netgrp is the list of entries for the current netgroup
123 */
124 struct linelist {
125 struct linelist *l_next; /* Chain ptr. */
126 int l_parsed; /* Flag for cycles */
127 char *l_groupname; /* Name of netgroup */
128 char *l_line; /* Netgroup entrie(s) to be parsed */
129 };
130
131 struct netgrp {
132 struct netgrp *ng_next; /* Chain ptr */
133 char *ng_str[3]; /* Field pointers, see below */
134 };
135
136 struct netgr_state {
137 FILE *st_netf;
138 struct linelist *st_linehead;
139 struct netgrp *st_nextgrp;
140 struct netgrp *st_gr;
141 char *st_grname;
142 };
143
144 #define NG_HOST 0 /* Host name */
145 #define NG_USER 1 /* User name */
146 #define NG_DOM 2 /* and Domain name */
147
148 static void netgr_endstate(void *);
149 NSS_TLS_HANDLING(netgr);
150
151 static int files_endnetgrent(void *, void *, va_list);
152 static int files_getnetgrent_r(void *, void *, va_list);
153 static int files_setnetgrent(void *, void *, va_list);
154
155 static int compat_endnetgrent(void *, void *, va_list);
156 static int compat_innetgr(void *, void *, va_list);
157 static int compat_getnetgrent_r(void *, void *, va_list);
158 static int compat_setnetgrent(void *, void *, va_list);
159
160 static void _compat_clearstate(void);
161 static int _getnetgrent_r(char **, char **, char **, char *, size_t, int *,
162 struct netgr_state *);
163 static int _innetgr_fallback(void *, void *, const char *, const char *,
164 const char *, const char *);
165 static int innetgr_fallback(void *, void *, va_list);
166 static int parse_netgrp(const char *, struct netgr_state *, int);
167 static struct linelist *read_for_group(const char *, struct netgr_state *, int);
168
169 #define LINSIZ 1024 /* Length of netgroup file line */
170
171 static const ns_dtab getnetgrent_dtab[] = {
172 NS_FILES_CB(files_getnetgrent_r, NULL)
173 NS_COMPAT_CB(compat_getnetgrent_r, NULL)
174 { NULL, NULL, NULL },
175 };
176
177 static const ns_dtab setnetgrent_dtab[] = {
178 NS_FILES_CB(files_setnetgrent, NULL)
179 NS_COMPAT_CB(compat_setnetgrent, NULL)
180 { NULL, NULL, NULL },
181 };
182
183 static const ns_dtab endnetgrent_dtab[] = {
184 NS_FILES_CB(files_endnetgrent, NULL)
185 NS_COMPAT_CB(compat_endnetgrent, NULL)
186 { NULL, NULL, NULL },
187 };
188
189 static struct netgr_state compat_state;
190
191 static void
netgr_endstate(void * arg)192 netgr_endstate(void *arg)
193 {
194 struct linelist *lp, *olp;
195 struct netgrp *gp, *ogp;
196 struct netgr_state *st;
197
198 st = (struct netgr_state *)arg;
199 lp = st->st_linehead;
200 while (lp != NULL) {
201 olp = lp;
202 lp = lp->l_next;
203 free(olp->l_groupname);
204 free(olp->l_line);
205 free(olp);
206 }
207 st->st_linehead = NULL;
208 if (st->st_grname != NULL) {
209 free(st->st_grname);
210 st->st_grname = NULL;
211 }
212 gp = st->st_gr;
213 while (gp != NULL) {
214 ogp = gp;
215 gp = gp->ng_next;
216 free(ogp->ng_str[NG_HOST]);
217 free(ogp->ng_str[NG_USER]);
218 free(ogp->ng_str[NG_DOM]);
219 free(ogp);
220 }
221 st->st_gr = NULL;
222 st->st_nextgrp = NULL;
223 }
224
225 static int
files_getnetgrent_r(void * retval,void * mdata,va_list ap)226 files_getnetgrent_r(void *retval, void *mdata, va_list ap)
227 {
228 struct netgr_state *st;
229 char **hostp, **userp, **domp, *buf;
230 size_t bufsize;
231 int *errnop;
232
233 hostp = va_arg(ap, char **);
234 userp = va_arg(ap, char **);
235 domp = va_arg(ap, char **);
236 buf = va_arg(ap, char *);
237 bufsize = va_arg(ap, size_t);
238 errnop = va_arg(ap, int *);
239
240 if (netgr_getstate(&st) != 0)
241 return (NS_UNAVAIL);
242
243 return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop, st));
244 }
245
246 static int
files_setnetgrent(void * retval,void * mdata,va_list ap)247 files_setnetgrent(void *retval, void *mdata, va_list ap)
248 {
249 const ns_src src[] = {
250 { NSSRC_FILES, NS_SUCCESS },
251 { NULL, 0 },
252 };
253 struct netgr_state *st;
254 const char *group;
255 int rv;
256
257 group = va_arg(ap, const char *);
258
259 if (group == NULL || group[0] == '\0')
260 return (NS_RETURN);
261
262 rv = netgr_getstate(&st);
263 if (rv != 0)
264 return (NS_UNAVAIL);
265
266 if (st->st_gr == NULL || strcmp(group, st->st_grname) != 0) {
267 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP,
268 "endnetgrent", src);
269 if ((st->st_netf = fopen(_PATH_NETGROUP, "re")) != NULL) {
270 if (parse_netgrp(group, st, 0) != 0)
271 (void)_nsdispatch(NULL, endnetgrent_dtab,
272 NSDB_NETGROUP, "endnetgrent", src);
273 else
274 st->st_grname = strdup(group);
275 (void)fclose(st->st_netf);
276 st->st_netf = NULL;
277 }
278 }
279 st->st_nextgrp = st->st_gr;
280 return (st->st_grname != NULL ? NS_SUCCESS : NS_NOTFOUND);
281 }
282
283 static int
files_endnetgrent(void * retval,void * mdata,va_list ap)284 files_endnetgrent(void *retval, void *mdata, va_list ap)
285 {
286 struct netgr_state *st;
287
288 if (netgr_getstate(&st) != 0)
289 return (NS_UNAVAIL);
290 netgr_endstate(st);
291 return (NS_SUCCESS);
292 }
293
294 static int
compat_getnetgrent_r(void * retval,void * mdata,va_list ap)295 compat_getnetgrent_r(void *retval, void *mdata, va_list ap)
296 {
297 char **hostp, **userp, **domp, *buf;
298 size_t bufsize;
299 int *errnop;
300 #ifdef YP
301 _yp_innetgr = 0;
302 #endif
303
304 hostp = va_arg(ap, char **);
305 userp = va_arg(ap, char **);
306 domp = va_arg(ap, char **);
307 buf = va_arg(ap, char *);
308 bufsize = va_arg(ap, size_t);
309 errnop = va_arg(ap, int *);
310
311 return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop,
312 &compat_state));
313 }
314
315 /*
316 * compat_setnetgrent()
317 * Parse the netgroup file looking for the netgroup and build the list
318 * of netgrp structures. Let parse_netgrp() and read_for_group() do
319 * most of the work.
320 */
321 static int
compat_setnetgrent(void * retval,void * mdata,va_list ap)322 compat_setnetgrent(void *retval, void *mdata, va_list ap)
323 {
324 FILE *netf;
325 const char *group;
326 #ifdef YP
327 struct stat _yp_statp;
328 char _yp_plus;
329 #endif
330
331 group = va_arg(ap, const char *);
332
333 /* Sanity check */
334 if (group == NULL || !strlen(group))
335 return (NS_RETURN);
336
337 if (compat_state.st_gr == NULL ||
338 strcmp(group, compat_state.st_grname) != 0) {
339 _compat_clearstate();
340
341 #ifdef YP
342 /* Presumed guilty until proven innocent. */
343 _use_only_yp = 0;
344 /*
345 * If /etc/netgroup doesn't exist or is empty,
346 * use NIS exclusively.
347 */
348 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) &&
349 errno == ENOENT) || _yp_statp.st_size == 0)
350 _use_only_yp = _netgr_yp_enabled = 1;
351 if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){
352 compat_state.st_netf = netf;
353 /*
354 * Icky: grab the first character of the netgroup file
355 * and turn on NIS if it's a '+'. rewind the stream
356 * afterwards so we don't goof up read_for_group() later.
357 */
358 if (netf) {
359 fscanf(netf, "%c", &_yp_plus);
360 rewind(netf);
361 if (_yp_plus == '+')
362 _use_only_yp = _netgr_yp_enabled = 1;
363 }
364 /*
365 * If we were called specifically for an innetgr()
366 * lookup and we're in NIS-only mode, short-circuit
367 * parse_netgroup() and cut directly to the chase.
368 */
369 if (_use_only_yp && _yp_innetgr) {
370 /* dohw! */
371 if (netf != NULL)
372 fclose(netf);
373 return (NS_RETURN);
374 }
375 #else
376 if ((netf = fopen(_PATH_NETGROUP, "re"))) {
377 compat_state.st_netf = netf;
378 #endif
379 if (parse_netgrp(group, &compat_state, 1)) {
380 _compat_clearstate();
381 } else {
382 compat_state.st_grname = strdup(group);
383 }
384 if (netf)
385 fclose(netf);
386 }
387 }
388 compat_state.st_nextgrp = compat_state.st_gr;
389 return (NS_SUCCESS);
390 }
391
392 static void
393 _compat_clearstate(void)
394 {
395
396 #ifdef YP
397 _netgr_yp_enabled = 0;
398 #endif
399 netgr_endstate(&compat_state);
400 }
401
402 /*
403 * compat_endnetgrent() - cleanup
404 */
405 static int
406 compat_endnetgrent(void *retval, void *mdata, va_list ap)
407 {
408
409 _compat_clearstate();
410 return (NS_SUCCESS);
411 }
412
413 int
414 _getnetgrent_r(char **hostp, char **userp, char **domp, char *buf,
415 size_t bufsize, int *errnop, struct netgr_state *st)
416 {
417 char *p, *src;
418 size_t len;
419 int rv;
420
421 #define COPY_NG_ELEM(dstp, i) do { \
422 src = st->st_nextgrp->ng_str[(i)]; \
423 if (src == NULL) \
424 src = ""; \
425 len = strlcpy(p, src, bufsize); \
426 if (len >= bufsize) { \
427 *errnop = ERANGE; \
428 return (NS_RETURN); \
429 } \
430 *(dstp) = p; \
431 p += len + 1; \
432 bufsize -= len + 1; \
433 } while (0)
434
435 p = buf;
436 if (st->st_nextgrp != NULL) {
437 COPY_NG_ELEM(hostp, NG_HOST);
438 COPY_NG_ELEM(userp, NG_USER);
439 COPY_NG_ELEM(domp, NG_DOM);
440 st->st_nextgrp = st->st_nextgrp->ng_next;
441 rv = NS_SUCCESS;
442 } else {
443 rv = NS_NOTFOUND;
444 }
445 #undef COPY_NG_ELEM
446
447 return (rv);
448 }
449
450 #ifdef YP
451 static int
452 _listmatch(const char *list, const char *group, int len)
453 {
454 const char *ptr = list;
455 const char *cptr;
456 int glen = strlen(group);
457
458 /* skip possible leading whitespace */
459 while (isspace((unsigned char)*ptr))
460 ptr++;
461
462 while (ptr < list + len) {
463 cptr = ptr;
464 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr))
465 ptr++;
466 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr))
467 return (1);
468 while (*ptr == ',' || isspace((unsigned char)*ptr))
469 ptr++;
470 }
471
472 return (0);
473 }
474
475 static int
476 _revnetgr_lookup(char* lookupdom, char* map, const char* str,
477 const char* dom, const char* group)
478 {
479 int y, rv, rot;
480 char key[MAXHOSTNAMELEN];
481 char *result;
482 int resultlen;
483
484 for (rot = 0; ; rot++) {
485 switch (rot) {
486 case 0:
487 snprintf(key, MAXHOSTNAMELEN, "%s.%s", str,
488 dom ? dom : lookupdom);
489 break;
490 case 1:
491 snprintf(key, MAXHOSTNAMELEN, "%s.*", str);
492 break;
493 case 2:
494 snprintf(key, MAXHOSTNAMELEN, "*.%s",
495 dom ? dom : lookupdom);
496 break;
497 case 3:
498 snprintf(key, MAXHOSTNAMELEN, "*.*");
499 break;
500 default:
501 return (0);
502 }
503 y = yp_match(lookupdom, map, key, strlen(key), &result,
504 &resultlen);
505 if (y == 0) {
506 rv = _listmatch(result, group, resultlen);
507 free(result);
508 if (rv)
509 return (1);
510 } else if (y != YPERR_KEY) {
511 /*
512 * If we get an error other than 'no
513 * such key in map' then something is
514 * wrong and we should stop the search.
515 */
516 return (-1);
517 }
518 }
519 }
520 #endif
521
522 /*
523 * Search for a match in a netgroup.
524 */
525 static int
526 compat_innetgr(void *retval, void *mdata, va_list ap)
527 {
528 #ifdef YP
529 const ns_src src[] = {
530 { mdata, NS_SUCCESS },
531 { NULL, 0 },
532 };
533 #endif
534 const char *group, *host, *user, *dom;
535
536 group = va_arg(ap, const char *);
537 host = va_arg(ap, const char *);
538 user = va_arg(ap, const char *);
539 dom = va_arg(ap, const char *);
540
541 if (group == NULL || !strlen(group))
542 return (NS_RETURN);
543
544 #ifdef YP
545 _yp_innetgr = 1;
546 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
547 src, group);
548 _yp_innetgr = 0;
549 /*
550 * If we're in NIS-only mode, do the search using
551 * NIS 'reverse netgroup' lookups.
552 *
553 * What happens with 'reverse netgroup' lookups:
554 *
555 * 1) try 'reverse netgroup' lookup
556 * 1.a) if host is specified and user is null:
557 * look in netgroup.byhost
558 * (try host.domain, host.*, *.domain or *.*)
559 * if found, return yes
560 * 1.b) if user is specified and host is null:
561 * look in netgroup.byuser
562 * (try host.domain, host.*, *.domain or *.*)
563 * if found, return yes
564 * 1.c) if both host and user are specified,
565 * don't do 'reverse netgroup' lookup. It won't work.
566 * 1.d) if neither host ane user are specified (why?!?)
567 * don't do 'reverse netgroup' lookup either.
568 * 2) if domain is specified and 'reverse lookup' is done:
569 * 'reverse lookup' was authoritative. bye bye.
570 * 3) otherwise, too bad, try it the slow way.
571 */
572 if (_use_only_yp && (host == NULL) != (user == NULL)) {
573 int ret;
574 if(yp_get_default_domain(&_netgr_yp_domain))
575 return (NS_NOTFOUND);
576 ret = _revnetgr_lookup(_netgr_yp_domain,
577 host?"netgroup.byhost":"netgroup.byuser",
578 host?host:user, dom, group);
579 if (ret == 1) {
580 *(int *)retval = 1;
581 return (NS_SUCCESS);
582 } else if (ret == 0 && dom != NULL) {
583 *(int *)retval = 0;
584 return (NS_SUCCESS);
585 }
586 }
587 #endif /* YP */
588
589 return (_innetgr_fallback(retval, mdata, group, host, user, dom));
590 }
591
592 static int
593 _innetgr_fallback(void *retval, void *mdata, const char *group, const char *host,
594 const char *user, const char *dom)
595 {
596 const ns_src src[] = {
597 { mdata, NS_SUCCESS },
598 { NULL, 0 },
599 };
600 char *h, *u, *d;
601 char *buf;
602 size_t bufsize;
603 int rv, ret_errno;
604
605 if (group == NULL || group[0] == '\0')
606 return (NS_RETURN);
607
608 bufsize = NGRP_STORAGE_INITIAL;
609 buf = malloc(bufsize);
610 if (buf == NULL)
611 return (NS_UNAVAIL);
612
613 *(int *)retval = 0;
614
615 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
616 src, group);
617
618 for (;;) {
619 do {
620 ret_errno = 0;
621 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP,
622 "getnetgrent_r", src, &h, &u, &d, buf, bufsize,
623 &ret_errno);
624 if (rv != NS_SUCCESS && ret_errno == ERANGE) {
625 bufsize *= 2;
626 if (bufsize > NGRP_STORAGE_MAX ||
627 (buf = reallocf(buf, bufsize)) == NULL)
628 goto out;
629 }
630 } while (rv != NS_SUCCESS && ret_errno == ERANGE);
631
632 if (rv != NS_SUCCESS) {
633 if (rv == NS_NOTFOUND && ret_errno == 0)
634 rv = NS_SUCCESS;
635 break;
636 }
637
638 if ((host == NULL || h == NULL || strcmp(host, h) == 0) &&
639 (user == NULL || u == NULL || strcmp(user, u) == 0) &&
640 (dom == NULL || d == NULL || strcmp(dom, d) == 0)) {
641 *(int *)retval = 1;
642 break;
643 }
644 }
645
646 out:
647 free(buf);
648 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent",
649 src);
650 return (rv);
651 }
652
653 static int
654 innetgr_fallback(void *retval, void *mdata, va_list ap)
655 {
656 const char *group, *host, *user, *dom;
657
658 group = va_arg(ap, const char *);
659 host = va_arg(ap, const char *);
660 user = va_arg(ap, const char *);
661 dom = va_arg(ap, const char *);
662
663 return (_innetgr_fallback(retval, mdata, group, host, user, dom));
664 }
665
666 /*
667 * Parse the netgroup file setting up the linked lists.
668 */
669 static int
670 parse_netgrp(const char *group, struct netgr_state *st, int niscompat)
671 {
672 struct netgrp *grp;
673 struct linelist *lp = st->st_linehead;
674 char **ng;
675 char *epos, *gpos, *pos, *spos;
676 int freepos, len, strpos;
677 #ifdef DEBUG
678 int fields;
679 #endif
680
681 /*
682 * First, see if the line has already been read in.
683 */
684 while (lp) {
685 if (!strcmp(group, lp->l_groupname))
686 break;
687 lp = lp->l_next;
688 }
689 if (lp == NULL && (lp = read_for_group(group, st, niscompat)) == NULL)
690 return (1);
691 if (lp->l_parsed) {
692 #ifdef DEBUG
693 /*
694 * This error message is largely superflous since the
695 * code handles the error condition sucessfully, and
696 * spewing it out from inside libc can actually hose
697 * certain programs.
698 */
699 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
700 #endif
701 return (1);
702 } else
703 lp->l_parsed = 1;
704 pos = lp->l_line;
705 /* Watch for null pointer dereferences, dammit! */
706 while (pos != NULL && *pos != '\0') {
707 if (*pos == '(') {
708 grp = malloc(sizeof(*grp));
709 if (grp == NULL)
710 return (1);
711 ng = grp->ng_str;
712 bzero(grp, sizeof(*grp));
713 pos++;
714 gpos = strsep(&pos, ")");
715 #ifdef DEBUG
716 fields = 0;
717 #endif
718 for (strpos = 0; strpos < 3; strpos++) {
719 if ((spos = strsep(&gpos, ",")) == NULL) {
720 /*
721 * All other systems I've tested
722 * return NULL for empty netgroup
723 * fields. It's up to user programs
724 * to handle the NULLs appropriately.
725 */
726 ng[strpos] = NULL;
727 continue;
728 }
729 #ifdef DEBUG
730 fields++;
731 #endif
732 while (*spos == ' ' || *spos == '\t')
733 spos++;
734 if ((epos = strpbrk(spos, " \t"))) {
735 *epos = '\0';
736 len = epos - spos;
737 } else
738 len = strlen(spos);
739 if (len <= 0)
740 continue;
741 ng[strpos] = malloc(len + 1);
742 if (ng[strpos] == NULL) {
743 for (freepos = 0; freepos < strpos;
744 freepos++)
745 free(ng[freepos]);
746 free(grp);
747 return (1);
748 }
749 bcopy(spos, ng[strpos], len + 1);
750 }
751 grp->ng_next = st->st_gr;
752 st->st_gr = grp;
753 #ifdef DEBUG
754 /*
755 * Note: on other platforms, malformed netgroup
756 * entries are not normally flagged. While we
757 * can catch bad entries and report them, we should
758 * stay silent by default for compatibility's sake.
759 */
760 if (fields < 3) {
761 fprintf(stderr,
762 "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
763 ng[NG_HOST] == NULL ? "" : ng[NG_HOST],
764 ng[NG_USER] == NULL ? "" : ",",
765 ng[NG_USER] == NULL ? "" : ng[NG_USER],
766 ng[NG_DOM] == NULL ? "" : ",",
767 ng[NG_DOM] == NULL ? "" : ng[NG_DOM],
768 lp->l_groupname);
769 }
770 #endif
771 } else {
772 spos = strsep(&pos, ", \t");
773 if (parse_netgrp(spos, st, niscompat))
774 continue;
775 }
776 if (pos == NULL)
777 break;
778 while (*pos == ' ' || *pos == ',' || *pos == '\t')
779 pos++;
780 }
781 return (0);
782 }
783
784 /*
785 * Read the netgroup file and save lines until the line for the netgroup
786 * is found. Return 1 if eof is encountered.
787 */
788 static struct linelist *
789 read_for_group(const char *group, struct netgr_state *st, int niscompat)
790 {
791 char *linep, *olinep, *pos, *spos;
792 int len, olen;
793 int cont;
794 struct linelist *lp;
795 char line[LINSIZ + 2];
796 FILE *netf;
797 #ifdef YP
798 char *result;
799 int resultlen;
800 linep = NULL;
801
802 netf = st->st_netf;
803 while ((_netgr_yp_enabled && niscompat) ||
804 fgets(line, LINSIZ, netf) != NULL) {
805 if (_netgr_yp_enabled) {
806 if(!_netgr_yp_domain)
807 if(yp_get_default_domain(&_netgr_yp_domain))
808 continue;
809 if (yp_match(_netgr_yp_domain, "netgroup", group,
810 strlen(group), &result, &resultlen)) {
811 free(result);
812 if (_use_only_yp)
813 return ((struct linelist *)0);
814 else {
815 _netgr_yp_enabled = 0;
816 continue;
817 }
818 }
819 if (strlen(result) == 0) {
820 free(result);
821 return (NULL);
822 }
823 snprintf(line, LINSIZ, "%s %s", group, result);
824 free(result);
825 }
826 #else
827 linep = NULL;
828 while (fgets(line, LINSIZ, netf) != NULL) {
829 #endif
830 pos = (char *)&line;
831 #ifdef YP
832 if (niscompat && *pos == '+') {
833 _netgr_yp_enabled = 1;
834 continue;
835 }
836 #endif
837 if (*pos == '#')
838 continue;
839 while (*pos == ' ' || *pos == '\t')
840 pos++;
841 spos = pos;
842 while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
843 *pos != '\0')
844 pos++;
845 len = pos - spos;
846 while (*pos == ' ' || *pos == '\t')
847 pos++;
848 if (*pos != '\n' && *pos != '\0') {
849 lp = malloc(sizeof (*lp));
850 if (lp == NULL)
851 return (NULL);
852 lp->l_parsed = 0;
853 lp->l_groupname = malloc(len + 1);
854 if (lp->l_groupname == NULL) {
855 free(lp);
856 return (NULL);
857 }
858 bcopy(spos, lp->l_groupname, len);
859 *(lp->l_groupname + len) = '\0';
860 len = strlen(pos);
861 olen = 0;
862
863 /*
864 * Loop around handling line continuations.
865 */
866 do {
867 if (*(pos + len - 1) == '\n')
868 len--;
869 if (*(pos + len - 1) == '\\') {
870 len--;
871 cont = 1;
872 } else
873 cont = 0;
874 if (len > 0) {
875 linep = malloc(olen + len + 1);
876 if (linep == NULL) {
877 free(lp->l_groupname);
878 free(lp);
879 if (olen > 0)
880 free(olinep);
881 return (NULL);
882 }
883 if (olen > 0) {
884 bcopy(olinep, linep, olen);
885 free(olinep);
886 }
887 bcopy(pos, linep + olen, len);
888 olen += len;
889 *(linep + olen) = '\0';
890 olinep = linep;
891 }
892 if (cont) {
893 if (fgets(line, LINSIZ, netf)) {
894 pos = line;
895 len = strlen(pos);
896 } else
897 cont = 0;
898 }
899 } while (cont);
900 lp->l_line = linep;
901 lp->l_next = st->st_linehead;
902 st->st_linehead = lp;
903
904 /*
905 * If this is the one we wanted, we are done.
906 */
907 if (!strcmp(lp->l_groupname, group))
908 return (lp);
909 }
910 }
911 #ifdef YP
912 /*
913 * Yucky. The recursive nature of this whole mess might require
914 * us to make more than one pass through the netgroup file.
915 * This might be best left outside the #ifdef YP, but YP is
916 * defined by default anyway, so I'll leave it like this
917 * until I know better.
918 */
919 rewind(netf);
920 #endif
921 return (NULL);
922 }
923
924 int
925 getnetgrent_r(char **hostp, char **userp, char **domp, char *buf, size_t bufsize)
926 {
927 int rv, ret_errno;
928
929 ret_errno = 0;
930 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP, "getnetgrent_r",
931 defaultsrc, hostp, userp, domp, buf, bufsize, &ret_errno);
932 if (rv == NS_SUCCESS) {
933 return (1);
934 } else {
935 errno = ret_errno;
936 return (0);
937 }
938 }
939
940 int
941 getnetgrent(char **hostp, char **userp, char **domp)
942 {
943 static char *ngrp_storage;
944 static size_t ngrp_storage_size;
945 int ret_errno, rv;
946
947 if (ngrp_storage == NULL) {
948 ngrp_storage_size = NGRP_STORAGE_INITIAL;
949 ngrp_storage = malloc(ngrp_storage_size);
950 if (ngrp_storage == NULL)
951 return (0);
952 }
953
954 do {
955 ret_errno = 0;
956 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP,
957 "getnetgrent_r", defaultsrc, hostp, userp, domp,
958 ngrp_storage, ngrp_storage_size, &ret_errno);
959 if (rv != NS_SUCCESS && ret_errno == ERANGE) {
960 ngrp_storage_size *= 2;
961 if (ngrp_storage_size > NGRP_STORAGE_MAX) {
962 free(ngrp_storage);
963 ngrp_storage = NULL;
964 errno = ERANGE;
965 return (0);
966 }
967 ngrp_storage = reallocf(ngrp_storage,
968 ngrp_storage_size);
969 if (ngrp_storage == NULL)
970 return (0);
971 }
972 } while (rv != NS_SUCCESS && ret_errno == ERANGE);
973
974 if (rv == NS_SUCCESS) {
975 return (1);
976 } else {
977 errno = ret_errno;
978 return (0);
979 }
980 }
981
982 void
983 setnetgrent(const char *netgroup)
984 {
985
986 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
987 defaultsrc, netgroup);
988 }
989
990 void
991 endnetgrent(void)
992 {
993
994 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent",
995 defaultsrc);
996 }
997
998 int
999 innetgr(const char *netgroup, const char *host, const char *user,
1000 const char *domain)
1001 {
1002 static const ns_dtab dtab[] = {
1003 NS_COMPAT_CB(compat_innetgr, NULL)
1004 NS_FALLBACK_CB(innetgr_fallback)
1005 { NULL, NULL, NULL },
1006 };
1007 int result, rv;
1008
1009 rv = _nsdispatch(&result, dtab, NSDB_NETGROUP, "innetgr", defaultsrc,
1010 netgroup, host, user, domain);
1011 return (rv == NS_SUCCESS ? result : 0);
1012 }
1013