87afce0b16550096c42426649f7478d796a9c710
[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 -------------------------------------------------*/