chiark / gitweb /
Small tweaks. Support no-network configuration option, and rearrange
[become] / src / netg.c
1 /* -*-c-*-
2  *
3  * $Id: netg.c,v 1.3 1998/01/12 16:46:17 mdw Exp $
4  *
5  * A local database of netgroups
6  *
7  * (c) 1998 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: netg.c,v $
32  * Revision 1.3  1998/01/12 16:46:17  mdw
33  * Fix copyright date.
34  *
35  * Revision 1.2  1997/08/20  16:19:11  mdw
36  * Patch memory leak.  Replace `name_reinit' by `name_end' for more sensible
37  * restart.  Don't try to trace when tracing's turned off.
38  *
39  * Revision 1.1  1997/08/07 09:45:00  mdw
40  * New source file added to maintain a netgroups database.
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 /* --- ANSI headers --- */
47
48 #include <ctype.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 /* --- Unix headers --- */
54
55 #include "config.h"
56
57 #include <sys/types.h>
58
59 #ifdef HAVE_YP
60 #  include <rpc/rpc.h>
61 #  include <rpcsvc/ypclnt.h>
62 #  include <rpcsvc/yp_prot.h>
63 #endif
64
65 #include <netinet/in.h>
66
67 #include <arpa/inet.h>
68
69 #include <pwd.h>
70 #include <netdb.h>
71 #include <unistd.h>
72
73 /* --- Local headers --- */
74
75 #include "become.h"
76 #include "config.h"
77 #include "netg.h"
78 #include "sym.h"
79 #include "userdb.h"
80 #include "utils.h"
81
82 /*----- Type definitions --------------------------------------------------*/
83
84 /* --- Quick discussion --- *
85  *
86  * I've just noticed: netgroups are horrible.  They form a directed graph
87  * which is really horrible; I'll have to try and turn it into something
88  * more sensible (which will essentially involve cutting cycles).
89  *
90  * The structure looks a little bit like a good ol' List (see Knuth 1 or
91  * any decent Lisp manual), but with more information in the cons cells.
92  */
93
94 /* --- @netg__cons@ --- */
95
96 typedef struct netg__cons {
97   unsigned f;
98   union {
99     struct netg__cons *cons;
100     struct netg__atom *atom;
101   } car;
102   struct netg__cons *cdr;
103 } netg__cons;
104
105 enum {
106   f_cons = 1,                           /* The @car@ is a cons cell */
107   f_visit = 2,                          /* Currently threaded on this cell */
108   f_uncycled = 4                        /* Cycles removed from here on in */
109 };
110
111 /* --- @netg__atom@ --- */
112
113 typedef struct netg__atom {
114   char *n;                              /* Unresolved netgroup reference */
115   char *h;                              /* Matched hostname */
116   char *u;                              /* Matched user name */
117   char *d;                              /* Matched domain name */
118 } netg__atom;
119
120 /* --- @netg__sym@ --- */
121
122 typedef struct netg__sym {
123   sym_base _base;
124   netg__cons *cons;
125 } netg__sym;
126
127 /* --- Token types for the netgroup parser --- */
128
129 enum {
130   tok_string = 256,
131   tok_eof
132 };
133
134 /*----- Static variables --------------------------------------------------*/
135
136 static sym_table netg__table;           /* Netgroup table */
137 static sym_iter netg__iter;             /* Iterator object for users */
138
139 /*----- Main code ---------------------------------------------------------*/
140
141 /* --- @netg__lex@ --- *
142  *
143  * Arguments:   @const char **p@ = pointer to next unscanned character
144  *              @char *q@ = pointer to output buffer
145  *
146  * Returns:     Token type (either character code or a magic number).
147  *
148  * Use:         Lexes a netgroups line into tokens.
149  */
150
151 static int netg__lex(char **p, char *q)
152 {
153   /* --- Skip any leading whitespace --- */
154
155   while (isspace((unsigned char)**p))
156     (*p)++;
157
158   /* --- Now work out what we've got --- */
159
160   if (**p == 0)
161     return (tok_eof);
162   if (**p == '(' || **p == ')' || **p == ',')
163     return (*(*p)++);
164   do
165     *q++ = *(*p)++;
166   while (**p != 0 && **p != '(' && **p != ')' &&
167          **p != ',' && !isspace((unsigned char)**p));
168   *q++ = 0;
169   return (tok_string);
170 }
171
172 /* --- @netg__foreach@ --- *
173  *
174  * Arguments:   @int st@ = YP protocol-level status code
175  *              @char *k@ = pointer to string containing the key
176  *              @int ksz@ = length of the key string
177  *              @char *v@ = pointer to string containing the value
178  *              @int vsz@ = length of the value string
179  *              @char *data@ = pointer to my data information
180  *
181  * Returns:     Zero to continue, nonzero for no more entries.
182  *
183  * Use:         Handles each incoming netgroup, attaching it to the table.
184  */
185
186 static int netg__foreach(int st, char *k, int ksz,
187                          char *v, int vsz, char *data)
188 {
189   char *kc, *vc;
190   unsigned f;
191   netg__sym *sng;
192   netg__cons *c, **link;
193   char *p;
194   int t;
195
196   /* --- If something is amiss, then quit now --- */
197
198   if (st != YP_TRUE)
199     return (-1);
200
201   /* --- Ignore empty lines from the original file --- */
202
203   if (!ksz || !vsz)
204     return (0);
205
206   /* --- Build my own trashable copies of the key and value --- *
207    *
208    * Note the oddness when I copy the value string.  The extra byte at the
209    * beginning allows me to use the same area of memory as an output buffer
210    * for the lexer.  It must be big enough; the lexer doesn't back up; and
211    * that extra byte gives me somewhere to put a terminating null byte.
212    */
213
214   kc = xmalloc(ksz + 1);
215   memcpy(kc, k, ksz);
216   kc[ksz] = 0;
217
218   vc = xmalloc(vsz + 2);
219   memcpy(vc + 1, v, vsz);
220   vc[vsz + 1] = 0;
221
222   T( trace(TRACE_DEBUG, "debug: netgroup `%s': `%s'", kc, vc + 1); )
223
224   /* --- Allocate a symbol in my table --- */
225
226   sng = sym_find(&netg__table, kc, -1, sizeof(*sng), &f);
227   if (!f)
228     sng->cons = 0;
229
230   /* --- Run to the end of the list --- */
231
232   for (link = &sng->cons; *link; link = &((*link)->cdr))
233     ;
234
235   /* --- Now start the tricky bit --- *
236    *
237    * I have to start parsing the netgroup value string.  Oh, well, it
238    * could be worse.
239    *
240    * The parser is written so as to avoid saying things more often than
241    * necessary.  This tends to involve @goto@s.  You've been warned.
242    */
243
244   p = vc + 1;
245   t = netg__lex(&p, vc);
246
247   for (;;) {
248
249     /* --- Start with a fresh cons cell, with an empty atom attached --- */
250
251     c = xmalloc(sizeof(*c));
252     c->car.atom = xmalloc(sizeof(*c->car.atom));
253
254     /* --- Restart here after an error --- *
255      *
256      * If I restart here, I can avoid freeing the cons cell reallocating
257      * it, which is a little silly.
258      */
259
260   duff_restart:
261     c->car.atom->n = c->car.atom->h = c->car.atom->u = c->car.atom->d = 0;
262     c->f = 0;
263     c->cdr = 0;
264
265     /* --- Handle end-of-line --- */
266
267     if (t == tok_eof)
268       break;
269
270     /* --- Handle a netgroup reference --- */
271
272     if (t == tok_string) {
273       T( trace(TRACE_DEBUG, "debug: add reference to `%s'", vc); )
274       c->car.atom->n = xstrdup(vc);
275       *link = c;
276       link = &c->cdr;
277       t = netg__lex(&p, vc);
278       continue;
279     }
280
281     /* --- Parse our merry way through the host--user--domain triple --- */
282
283     if (t != '(')
284       goto duff;
285     t = netg__lex(&p, vc);
286
287     if (t == tok_string) {
288       T( trace(TRACE_DEBUG, "debug: add host `%s'", vc); )
289       c->car.atom->h = xstrdup(vc);
290       t = netg__lex(&p, vc);
291     }
292
293     if (t != ',')
294       goto duff_paren;
295     t = netg__lex(&p, vc);
296
297     if (t == tok_string) {
298       T( trace(TRACE_DEBUG, "debug: add user `%s'", vc); )
299       c->car.atom->u = xstrdup(vc);
300       t = netg__lex(&p, vc);
301     }
302
303     if (t != ',')
304       goto duff_paren;
305     t = netg__lex(&p, vc);
306
307     if (t == tok_string) {
308       T( trace(TRACE_DEBUG, "debug: add domain `%s'", vc); )
309       c->car.atom->d = xstrdup(vc);
310       t = netg__lex(&p, vc);
311     }
312
313     if (t != ')')
314       goto duff_paren;
315     t = netg__lex(&p, vc);
316
317     /* --- Finished that, so insert this cons cell into the list --- */
318
319     *link = c;
320     link = &c->cdr;
321     continue;
322
323     /* --- Tidy up during scanning of a triple --- *
324      *
325      * I'll search for the closing paren, and hope that I won't miss out too
326      * much.
327      */
328
329   duff_paren:
330     while (t != tok_eof && t != ')')
331       t = netg__lex(&p, vc);
332
333     /* --- Other syntax oddnesses come out here --- *
334      *
335      * Snarf the token which caused the error.
336      */
337
338   duff:
339     moan("syntax error in netgroups line for `%s'", kc);
340     if (c->car.atom->n) free(c->car.atom->n);
341     if (c->car.atom->h) free(c->car.atom->h);
342     if (c->car.atom->u) free(c->car.atom->u);
343     if (c->car.atom->d) free(c->car.atom->d);
344     t = netg__lex(&p, vc);
345     goto duff_restart;
346   }
347
348   free(c->car.atom);
349   free(c);
350   free(kc);
351   free(vc);
352   return (0);
353 }
354
355 /* --- @netg__dumpGroup@ ---  *
356  *
357  * Arguments:   @netg__cons *c@ = pointer to a list head
358  *              @int lev@ = indentation level
359  *
360  * Returns:     ---
361  *
362  * Use:         Dumps the netgroup given.
363  */
364
365 #ifdef TRACING
366
367 static void netg__dumpGroup(netg__cons *c, int lev)
368 {
369   netg__cons *cc;
370
371   if (!c)
372     return;
373
374   /* --- Check for a cycle --- */
375
376   if (c->f & f_visit) {
377     trace(TRACE_DEBUG, "debug: %*scycle!", lev * 2, "");
378     return;
379   }
380
381   /* --- Dump the netgroup --- */
382
383   c->f |= f_visit;
384
385   for (cc = c; cc; cc = cc->cdr) {
386     if (cc->f & f_cons) {
387       trace(TRACE_DEBUG, "debug: %*ssubnetgroup...", lev * 2, "");
388       netg__dumpGroup(cc->car.cons, lev + 1);
389     } else if (cc->car.atom->n) {
390       trace(TRACE_DEBUG, "debug: %*sunresolved subgroup `%s'",
391             lev * 2, "", cc->car.atom->n);
392     } else {
393       trace(TRACE_DEBUG, "debug: %*s(%s, %s, %s)", lev * 2, "",
394             cc->car.atom->h ? cc->car.atom->h : "<all-hosts>",
395             cc->car.atom->u ? cc->car.atom->u : "<all-users>",
396             cc->car.atom->d ? cc->car.atom->d : "<all-domains>");
397     }
398   }
399
400   c->f &= ~f_visit;
401 }
402
403 #endif
404
405 /* --- @netg__dump@ --- *
406  *
407  * Arguments:   ---
408  *
409  * Returns:     ---
410  *
411  * Use:         Dumps the netgroups table.
412  */
413
414 #ifdef TRACING
415
416 static void netg__dump(void)
417 {
418   sym_iter i;
419   netg__sym *sng;
420
421   trace(TRACE_DEBUG, "debug: dumping netgroups file");
422   for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
423     trace(TRACE_DEBUG, "debug: netgroup `%s'...", sng->_base.name);
424     sng->cons->f &= ~f_visit;
425     netg__dumpGroup(sng->cons, 1);
426   }
427 }
428
429 #endif
430
431 /* --- @netg_iterate@, @netg_iterate_r@ --- *
432  *
433  * Arguments:   @netg_iter *i@ = pointer to a netgroup iterator object
434  *
435  * Returns:     ---
436  *
437  * Use:         Starts iterating over the netgroups.
438  */
439
440 void netg_iterate(void) { netg_iterate_r(&netg__iter); }
441 void netg_iterate_r(netg_iter *i) { sym_createIter(i, &netg__table); }
442
443 /* --- @netg_next@, @netg_next_r@ --- *
444  *
445  * Arguments:   @netg_iter *i@ = pointer to a netgroup iterator object
446  *
447  * Returns:     An opaque pointer to the next item, or null.
448  *
449  * Use:         Returns the next netgroup.
450  */
451
452 netg *netg_next(void) { return (netg_next_r(&netg__iter)); }
453 netg *netg_next_r(netg_iter *i) { return (sym_next(i)); }
454
455 /* --- @netg_name@ --- *
456  *
457  * Arguments:   @netg *n@ = netgroup handle returned by @netg_next@.
458  *
459  * Returns:     A pointer to the name; you may not modify this string.
460  *
461  * Use:         Returns the name of a netgroup.
462  */
463
464 const char *netg_name(netg *n) { return (n->_base.name); }
465
466 /* --- @netg_scan@ --- *
467  *
468  * Arguments:   @netg *n@ = a netgroup handle returned by @netg_next@
469  *              @int (*proc)(netg *n, const char *host, const char *user,@
470  *                      @const char *domain, void *ctx)@ = function to call
471  *                              for each member.
472  *              @void *ctx@ = context pointer to pass to @proc@.
473  *
474  * Returns:     Zero if all went well, or the nonzero return value from
475  *              @proc@.
476  *
477  * Use:         Passes all the members of the netgroup to a given function.
478  *              The function is given the names, directly from the NIS
479  *              netgroup map, except that any empty entries are passed as
480  *              null pointers rather than empty strings.  You may not modify
481  *              any of the strings.  The enumeration function, @proc@, may
482  *              return nonzero to stop itself from being called any more;
483  *              if this happens, the value it returns becomes the result of
484  *              this function.  If all the items are enumerated OK, this
485  *              function returns zero.
486  */
487
488 static int netg__doScan(netg__cons *c,
489                         netg *n,
490                         int (*proc)(netg */*n*/, const char */*host*/,
491                                     const char */*user*/,
492                                     const char */*domain*/, void */*ctx*/),
493                         void *ctx)
494 {
495   int e;
496
497   while (c) {
498     if (c->f & f_cons)
499       e = netg__doScan(c->car.cons, n, proc, ctx);
500     else
501       e = proc(n, c->car.atom->h, c->car.atom->u, c->car.atom->d, ctx);
502     if (e)
503       return (e);
504     c = c->cdr;
505   }
506   return (0);
507 }
508
509 int netg_scan(netg *n,
510               int (*proc)(netg */*n*/, const char */*host*/,
511                           const char */*user*/, const char */*domain*/,
512                           void */*ctx*/),
513               void *ctx)
514 {
515   return (netg__doScan(n->cons, n, proc, ctx));
516 }
517
518 /* --- @netg__breakCycle@ --- *
519  *
520  * Arguments:   @netg__cons *c@ = pointer to a list
521  *
522  * Returns:     ---
523  *
524  * Use:         Scans the given list (recursively) and breaks any cycles it
525  *              finds.
526  */
527
528 static void netg__breakCycle(netg__cons *c)
529 {
530   netg__cons *cc;
531
532   if (!c || c->f & f_uncycled)
533     return;
534
535   c->f |= f_visit;
536   for (cc = c; cc; cc = cc->cdr) {
537     if (~cc->f & f_cons)
538       continue;
539     if (cc->car.cons->f & f_visit) {
540       T( trace(TRACE_DEBUG, "debug: cycle in netgroups"); )
541       cc->car.cons = 0;
542     } else
543       netg__breakCycle(cc->car.cons);
544   }
545   c->f &= ~f_visit;
546   c->f |= f_uncycled;
547 }
548
549 /* --- @netg_init@ --- *
550  *
551  * Arguments:   ---
552  *
553  * Returns:     ---
554  *
555  * Use:         Reads the netgroup database and turns it into something nice.
556  */
557
558 void netg_init(void)
559 {
560   char *ypdom;
561
562   /* --- Initialise my symbol table --- */
563
564   sym_createTable(&netg__table);
565
566   /* --- Bind myself unto a YP server --- */
567
568   if (yp_get_default_domain(&ypdom) ||
569       yp_bind(ypdom))
570     return;
571   
572   /* --- Now try to read all the netgroup entries --- */
573
574   {
575     static struct ypall_callback ncb = { netg__foreach, 0 };
576     yp_all(ypdom, "netgroup", &ncb);
577   }
578
579   /* --- Unbind from the server --- */
580
581   yp_unbind(ypdom);
582
583   /* --- Dump the table --- */
584
585   IF_TRACING(TRACE_DEBUG, netg__dump(); )
586
587   /* --- Now resolve all the remaining references --- */
588
589   {
590     sym_iter i;
591     netg__sym *sng, *ng;
592     netg__cons *c;
593     netg__atom *a;
594
595     for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
596       for (c = sng->cons; c; c = c->cdr) {
597         if ((c->f & f_cons) == 0 && c->car.atom->n) {
598           a = c->car.atom;
599           ng = sym_find(&netg__table, a->n, -1, 0, 0);
600           if (!ng) {
601             moan("undefined netgroup `%s' (ignored)", a->n);
602             c->car.atom = 0;
603           } else {
604             c->car.cons = ng->cons;
605             c->f |= f_cons;
606           }
607           free(a->n);
608           free(a);
609         }
610       }
611     }
612   }
613
614   /* --- Remove cycles in the netgroups table --- */
615
616   {
617     sym_iter i;
618     netg__sym *sng;
619
620     for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
621       sng->cons->f &= ~f_uncycled;
622     for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
623       netg__breakCycle(sng->cons);
624   }    
625
626   /* --- Dump the table again --- */
627
628   IF_TRACING(TRACE_DEBUG, netg__dump(); )
629 }
630
631 /* --- @netg_end@ --- *
632  *
633  * Arguments:   ---
634  *
635  * Returns:     ---
636  *
637  * Use:         Empties the netgroups database.
638  */
639
640 void netg_end(void)
641 {
642   sym_iter i;
643   netg__sym *sng;
644   netg__cons *c, *cc;
645
646   /* --- Remove all the old netgroups rubbish --- */
647
648   for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
649     c = sng->cons;
650     while (c) {
651       cc = c->cdr;
652       if (~c->f & f_cons) {
653         if (c->car.atom->n) free(c->car.atom->n);
654         if (c->car.atom->h) free(c->car.atom->h);
655         if (c->car.atom->u) free(c->car.atom->u);
656         if (c->car.atom->d) free(c->car.atom->d);
657         free(c->car.atom);
658       }
659       free(c);
660       c = cc;
661     }
662     sym_remove(&netg__table, sng);
663   }
664
665   sym_destroyTable(&netg__table);
666 }
667
668 /*----- Test driver -------------------------------------------------------*/
669
670 #ifdef TEST_RIG
671
672 int scanner(netg *n, const char *h, const char *u, const char *d, void *c)
673 {
674   fprintf(stderr, "  %s, %s, %s\n",
675           h ? h : "<any>", u ? u : "<any>", d ? d : "<any>");
676   return (0);
677 }
678
679 int main(void)
680 {
681   netg *n;
682   ego("netg-test");
683   traceon(stderr, TRACE_ALL);
684   netg_init();
685   for (netg_iterate(); (n = netg_next()) != 0; ) {
686     fprintf(stderr, "netgroup %s\n", netg_name(n));
687     netg_scan(n, scanner, 0);
688   }
689   return (0);
690 }
691
692 #endif
693
694 /*----- That's all, folks -------------------------------------------------*/