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