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