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