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