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