Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libcr / gen / getnetgrent.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * $FreeBSD: src/lib/libc/gen/getnetgrent.c,v 1.26 1999/11/04 04:16:27 ache Exp $
37 */
38
39#if defined(LIBC_SCCS) && !defined(lint)
40static char sccsid[] = "@(#)getnetgrent.c 8.2 (Berkeley) 4/27/95";
41#endif /* LIBC_SCCS and not lint */
42
43#include <stdio.h>
44#include <strings.h>
45#include <stdlib.h>
46#include <unistd.h>
47
48#ifdef YP
49/*
50 * Notes:
51 * We want to be able to use NIS netgroups properly while retaining
52 * the ability to use a local /etc/netgroup file. Unfortunately, you
53 * can't really do both at the same time - at least, not efficiently.
54 * NetBSD deals with this problem by creating a netgroup database
55 * using Berkeley DB (just like the password database) that allows
56 * for lookups using netgroup, netgroup.byuser or netgroup.byhost
57 * searches. This is a neat idea, but I don't have time to implement
58 * something like that now. (I think ultimately it would be nice
59 * if we DB-fied the group and netgroup stuff all in one shot, but
60 * for now I'm satisfied just to have something that works well
61 * without requiring massive code changes.)
62 *
63 * Therefore, to still permit the use of the local file and maintain
64 * optimum NIS performance, we allow for the following conditions:
65 *
66 * - If /etc/netgroup does not exist and NIS is turned on, we use
67 * NIS netgroups only.
68 *
69 * - If /etc/netgroup exists but is empty, we use NIS netgroups
70 * only.
71 *
72 * - If /etc/netgroup exists and contains _only_ a '+', we use
73 * NIS netgroups only.
74 *
75 * - If /etc/netgroup exists, contains locally defined netgroups
76 * and a '+', we use a mixture of NIS and the local entries.
77 * This method should return the same NIS data as just using
78 * NIS alone, but it will be slower if the NIS netgroup database
79 * is large (innetgr() in particular will suffer since extra
80 * processing has to be done in order to determine memberships
81 * using just the raw netgroup data).
82 *
83 * - If /etc/netgroup exists and contains only locally defined
84 * netgroup entries, we use just those local entries and ignore
85 * NIS (this is the original, pre-NIS behavior).
86 */
87
88#include <rpc/rpc.h>
89#include <rpcsvc/yp_prot.h>
90#include <rpcsvc/ypclnt.h>
91#include <sys/types.h>
92#include <sys/stat.h>
93#include <sys/param.h>
94#include <sys/errno.h>
95static char *_netgr_yp_domain;
96int _use_only_yp;
97static int _netgr_yp_enabled;
98static int _yp_innetgr;
99#endif
100
101#ifndef _PATH_NETGROUP
102#define _PATH_NETGROUP "/etc/netgroup"
103#endif
104
105/*
106 * Static Variables and functions used by setnetgrent(), getnetgrent() and
107 * endnetgrent().
108 * There are two linked lists:
109 * - linelist is just used by setnetgrent() to parse the net group file via.
110 * parse_netgrp()
111 * - netgrp is the list of entries for the current netgroup
112 */
113struct linelist {
114 struct linelist *l_next; /* Chain ptr. */
115 int l_parsed; /* Flag for cycles */
116 char *l_groupname; /* Name of netgroup */
117 char *l_line; /* Netgroup entrie(s) to be parsed */
118};
119
120struct netgrp {
121 struct netgrp *ng_next; /* Chain ptr */
122 char *ng_str[3]; /* Field pointers, see below */
123};
124#define NG_HOST 0 /* Host name */
125#define NG_USER 1 /* User name */
126#define NG_DOM 2 /* and Domain name */
127
128static struct linelist *linehead = (struct linelist *)0;
129static struct netgrp *nextgrp = (struct netgrp *)0;
130static struct {
131 struct netgrp *gr;
132 char *grname;
133} grouphead = {
134 (struct netgrp *)0,
135 (char *)0,
136};
137static FILE *netf = (FILE *)0;
138static int parse_netgrp();
139static struct linelist *read_for_group();
140void setnetgrent(), endnetgrent();
141int getnetgrent(), innetgr();
142
143#define LINSIZ 1024 /* Length of netgroup file line */
144
145/*
146 * setnetgrent()
147 * Parse the netgroup file looking for the netgroup and build the list
148 * of netgrp structures. Let parse_netgrp() and read_for_group() do
149 * most of the work.
150 */
151void
152setnetgrent(group)
153 char *group;
154{
155#ifdef YP
156 struct stat _yp_statp;
157 char _yp_plus;
158#endif
159
160 /* Sanity check */
161
162 if (group == NULL || !strlen(group))
163 return;
164
165 if (grouphead.gr == (struct netgrp *)0 ||
166 strcmp(group, grouphead.grname)) {
167 endnetgrent();
168#ifdef YP
169 /* Presumed guilty until proven innocent. */
170 _use_only_yp = 0;
171 /*
172 * If /etc/netgroup doesn't exist or is empty,
173 * use NIS exclusively.
174 */
175 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) &&
176 errno == ENOENT) || _yp_statp.st_size == 0)
177 _use_only_yp = _netgr_yp_enabled = 1;
178 if ((netf = fopen(_PATH_NETGROUP,"r")) != NULL ||_use_only_yp){
179 /*
180 * Icky: grab the first character of the netgroup file
181 * and turn on NIS if it's a '+'. rewind the stream
182 * afterwards so we don't goof up read_for_group() later.
183 */
184 if (netf) {
185 fscanf(netf, "%c", &_yp_plus);
186 rewind(netf);
187 if (_yp_plus == '+')
188 _use_only_yp = _netgr_yp_enabled = 1;
189 }
190 /*
191 * If we were called specifically for an innetgr()
192 * lookup and we're in NIS-only mode, short-circuit
193 * parse_netgroup() and cut directly to the chase.
194 */
195 if (_use_only_yp && _yp_innetgr) {
196 /* dohw! */
197 if (netf != NULL)
198 fclose(netf);
199 return;
200 }
201#else
202 if (netf = fopen(_PATH_NETGROUP, "r")) {
203#endif
204 if (parse_netgrp(group))
205 endnetgrent();
206 else {
207 grouphead.grname = (char *)
208 malloc(strlen(group) + 1);
209 strcpy(grouphead.grname, group);
210 }
211 if (netf)
212 fclose(netf);
213 }
214 }
215 nextgrp = grouphead.gr;
216}
217
218/*
219 * Get the next netgroup off the list.
220 */
221int
222getnetgrent(hostp, userp, domp)
223 char **hostp, **userp, **domp;
224{
225#ifdef YP
226 _yp_innetgr = 0;
227#endif
228
229 if (nextgrp) {
230 *hostp = nextgrp->ng_str[NG_HOST];
231 *userp = nextgrp->ng_str[NG_USER];
232 *domp = nextgrp->ng_str[NG_DOM];
233 nextgrp = nextgrp->ng_next;
234 return (1);
235 }
236 return (0);
237}
238
239/*
240 * endnetgrent() - cleanup
241 */
242void
243endnetgrent()
244{
245 register struct linelist *lp, *olp;
246 register struct netgrp *gp, *ogp;
247
248 lp = linehead;
249 while (lp) {
250 olp = lp;
251 lp = lp->l_next;
252 free(olp->l_groupname);
253 free(olp->l_line);
254 free((char *)olp);
255 }
256 linehead = (struct linelist *)0;
257 if (grouphead.grname) {
258 free(grouphead.grname);
259 grouphead.grname = (char *)0;
260 }
261 gp = grouphead.gr;
262 while (gp) {
263 ogp = gp;
264 gp = gp->ng_next;
265 if (ogp->ng_str[NG_HOST])
266 free(ogp->ng_str[NG_HOST]);
267 if (ogp->ng_str[NG_USER])
268 free(ogp->ng_str[NG_USER]);
269 if (ogp->ng_str[NG_DOM])
270 free(ogp->ng_str[NG_DOM]);
271 free((char *)ogp);
272 }
273 grouphead.gr = (struct netgrp *)0;
274#ifdef YP
275 _netgr_yp_enabled = 0;
276#endif
277}
278
279#ifdef YP
280static int _listmatch(list, group, len)
281 char *list, *group;
282 int len;
283{
284 char *ptr = list, *cptr;
285 int glen = strlen(group);
286
287 /* skip possible leading whitespace */
288 while(isspace((unsigned char)*ptr))
289 ptr++;
290
291 while (ptr < list + len) {
292 cptr = ptr;
293 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr))
294 ptr++;
295 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr))
296 return(1);
297 while(*ptr == ',' || isspace((unsigned char)*ptr))
298 ptr++;
299 }
300
301 return(0);
302}
303
304static int _buildkey(key, str, dom, rotation)
305char *key, *str, *dom;
306int *rotation;
307{
308 (*rotation)++;
309 if (*rotation > 4)
310 return(0);
311 switch(*rotation) {
312 case(1): sprintf((char *)key, "%s.%s", str, dom ? dom : "*");
313 break;
314 case(2): sprintf((char *)key, "%s.*", str);
315 break;
316 case(3): sprintf((char *)key, "*.%s", dom ? dom : "*");
317 break;
318 case(4): sprintf((char *)key, "*.*");
319 break;
320 }
321 return(1);
322}
323#endif
324
325/*
326 * Search for a match in a netgroup.
327 */
328int
329innetgr(group, host, user, dom)
330 const char *group, *host, *user, *dom;
331{
332 char *hst, *usr, *dm;
333#ifdef YP
334 char *result;
335 int resultlen;
336 int rv;
337#endif
338 /* Sanity check */
339
340 if (group == NULL || !strlen(group))
341 return (0);
342
343#ifdef YP
344 _yp_innetgr = 1;
345#endif
346 setnetgrent(group);
347#ifdef YP
348 _yp_innetgr = 0;
349 /*
350 * If we're in NIS-only mode, do the search using
351 * NIS 'reverse netgroup' lookups.
352 */
353 if (_use_only_yp) {
354 char _key[MAXHOSTNAMELEN];
355 int rot = 0, y = 0;
356
357 if(yp_get_default_domain(&_netgr_yp_domain))
358 return(0);
359 while(_buildkey(_key, user ? user : host, dom, &rot)) {
360 y = yp_match(_netgr_yp_domain, user? "netgroup.byuser":
361 "netgroup.byhost", _key, strlen(_key), &result,
362 &resultlen);
363 if (y) {
364 /*
365 * If we get an error other than 'no
366 * such key in map' then something is
367 * wrong and we should stop the search.
368 */
369 if (y != YPERR_KEY)
370 break;
371 } else {
372 rv = _listmatch(result, group, resultlen);
373 free(result);
374 if (rv)
375 return(1);
376 else
377 return(0);
378 }
379 }
380 /*
381 * Couldn't match using NIS-exclusive mode. If the error
382 * was YPERR_MAP, then the failure happened because there
383 * was no netgroup.byhost or netgroup.byuser map. The odds
384 * are we are talking to an Sun NIS+ server in YP emulation
385 * mode; if this is the case, then we have to do the check
386 * the 'old-fashioned' way by grovelling through the netgroup
387 * map and resolving memberships on the fly.
388 */
389 if (y != YPERR_MAP)
390 return(0);
391 }
392
393 setnetgrent(group);
394#endif /* YP */
395
396 while (getnetgrent(&hst, &usr, &dm))
397 if ((host == NULL || hst == NULL || !strcmp(host, hst)) &&
398 (user == NULL || usr == NULL || !strcmp(user, usr)) &&
399 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) {
400 endnetgrent();
401 return (1);
402 }
403 endnetgrent();
404 return (0);
405}
406
407/*
408 * Parse the netgroup file setting up the linked lists.
409 */
410static int
411parse_netgrp(group)
412 char *group;
413{
414 register char *spos, *epos;
415 register int len, strpos;
416#ifdef DEBUG
417 register int fields;
418#endif
419 char *pos, *gpos;
420 struct netgrp *grp;
421 struct linelist *lp = linehead;
422
423 /*
424 * First, see if the line has already been read in.
425 */
426 while (lp) {
427 if (!strcmp(group, lp->l_groupname))
428 break;
429 lp = lp->l_next;
430 }
431 if (lp == (struct linelist *)0 &&
432 (lp = read_for_group(group)) == (struct linelist *)0)
433 return (1);
434 if (lp->l_parsed) {
435#ifdef DEBUG
436 /*
437 * This error message is largely superflous since the
438 * code handles the error condition sucessfully, and
439 * spewing it out from inside libc can actually hose
440 * certain programs.
441 */
442 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
443#endif
444 return (1);
445 } else
446 lp->l_parsed = 1;
447 pos = lp->l_line;
448 /* Watch for null pointer dereferences, dammit! */
449 while (pos != NULL && *pos != '\0') {
450 if (*pos == '(') {
451 grp = (struct netgrp *)malloc(sizeof (struct netgrp));
452 bzero((char *)grp, sizeof (struct netgrp));
453 grp->ng_next = grouphead.gr;
454 grouphead.gr = grp;
455 pos++;
456 gpos = strsep(&pos, ")");
457#ifdef DEBUG
458 fields = 0;
459#endif
460 for (strpos = 0; strpos < 3; strpos++) {
461 if ((spos = strsep(&gpos, ","))) {
462#ifdef DEBUG
463 fields++;
464#endif
465 while (*spos == ' ' || *spos == '\t')
466 spos++;
467 if ((epos = strpbrk(spos, " \t"))) {
468 *epos = '\0';
469 len = epos - spos;
470 } else
471 len = strlen(spos);
472 if (len > 0) {
473 grp->ng_str[strpos] = (char *)
474 malloc(len + 1);
475 bcopy(spos, grp->ng_str[strpos],
476 len + 1);
477 }
478 } else {
479 /*
480 * All other systems I've tested
481 * return NULL for empty netgroup
482 * fields. It's up to user programs
483 * to handle the NULLs appropriately.
484 */
485 grp->ng_str[strpos] = NULL;
486 }
487 }
488#ifdef DEBUG
489 /*
490 * Note: on other platforms, malformed netgroup
491 * entries are not normally flagged. While we
492 * can catch bad entries and report them, we should
493 * stay silent by default for compatibility's sake.
494 */
495 if (fields < 3)
496 fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
497 grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST],
498 grp->ng_str[NG_USER] == NULL ? "" : ",",
499 grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER],
500 grp->ng_str[NG_DOM] == NULL ? "" : ",",
501 grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM],
502 lp->l_groupname);
503#endif
504 } else {
505 spos = strsep(&pos, ", \t");
506 if (parse_netgrp(spos))
507 continue;
508 }
509 if (pos == NULL)
510 break;
511 while (*pos == ' ' || *pos == ',' || *pos == '\t')
512 pos++;
513 }
514 return (0);
515}
516
517/*
518 * Read the netgroup file and save lines until the line for the netgroup
519 * is found. Return 1 if eof is encountered.
520 */
521static struct linelist *
522read_for_group(group)
523 char *group;
524{
525 register char *pos, *spos, *linep, *olinep;
526 register int len, olen;
527 int cont;
528 struct linelist *lp;
529 char line[LINSIZ + 2];
530#ifdef YP
531 char *result;
532 int resultlen;
533
534 while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) {
535 if (_netgr_yp_enabled) {
536 if(!_netgr_yp_domain)
537 if(yp_get_default_domain(&_netgr_yp_domain))
538 continue;
539 if (yp_match(_netgr_yp_domain, "netgroup", group,
540 strlen(group), &result, &resultlen)) {
541 free(result);
542 if (_use_only_yp)
543 return ((struct linelist *)0);
544 else {
545 _netgr_yp_enabled = 0;
546 continue;
547 }
548 }
549 snprintf(line, LINSIZ, "%s %s", group, result);
550 free(result);
551 }
552#else
553 while (fgets(line, LINSIZ, netf) != NULL) {
554#endif
555 pos = (char *)&line;
556#ifdef YP
557 if (*pos == '+') {
558 _netgr_yp_enabled = 1;
559 continue;
560 }
561#endif
562 if (*pos == '#')
563 continue;
564 while (*pos == ' ' || *pos == '\t')
565 pos++;
566 spos = pos;
567 while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
568 *pos != '\0')
569 pos++;
570 len = pos - spos;
571 while (*pos == ' ' || *pos == '\t')
572 pos++;
573 if (*pos != '\n' && *pos != '\0') {
574 lp = (struct linelist *)malloc(sizeof (*lp));
575 lp->l_parsed = 0;
576 lp->l_groupname = (char *)malloc(len + 1);
577 bcopy(spos, lp->l_groupname, len);
578 *(lp->l_groupname + len) = '\0';
579 len = strlen(pos);
580 olen = 0;
581
582 /*
583 * Loop around handling line continuations.
584 */
585 do {
586 if (*(pos + len - 1) == '\n')
587 len--;
588 if (*(pos + len - 1) == '\\') {
589 len--;
590 cont = 1;
591 } else
592 cont = 0;
593 if (len > 0) {
594 linep = (char *)malloc(olen + len + 1);
595 if (olen > 0) {
596 bcopy(olinep, linep, olen);
597 free(olinep);
598 }
599 bcopy(pos, linep + olen, len);
600 olen += len;
601 *(linep + olen) = '\0';
602 olinep = linep;
603 }
604 if (cont) {
605 if (fgets(line, LINSIZ, netf)) {
606 pos = line;
607 len = strlen(pos);
608 } else
609 cont = 0;
610 }
611 } while (cont);
612 lp->l_line = linep;
613 lp->l_next = linehead;
614 linehead = lp;
615
616 /*
617 * If this is the one we wanted, we are done.
618 */
619 if (!strcmp(lp->l_groupname, group))
620 return (lp);
621 }
622 }
623#ifdef YP
624 /*
625 * Yucky. The recursive nature of this whole mess might require
626 * us to make more than one pass through the netgroup file.
627 * This might be best left outside the #ifdef YP, but YP is
628 * defined by default anyway, so I'll leave it like this
629 * until I know better.
630 */
631 rewind(netf);
632#endif
633 return ((struct linelist *)0);
634}