Missed an old include...
[become] / src / netg.c
CommitLineData
afce951b 1/* -*-c-*-
2 *
f60a3434 3 * $Id: netg.c,v 1.5 2003/10/12 00:14:55 mdw Exp $
afce951b 4 *
5 * A local database of netgroups
6 *
c758e654 7 * (c) 1998 EBI
afce951b 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 $
f60a3434 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 *
a340752b 36 * Revision 1.4 1998/04/23 13:24:49 mdw
37 * Switch to using the ypstuff interface to YP server.
38 *
c758e654 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
8e3b89ae 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 *
afce951b 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
afce951b 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
f60a3434 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
afce951b 81/* --- Local headers --- */
82
83#include "become.h"
84#include "config.h"
85#include "netg.h"
afce951b 86#include "userdb.h"
a340752b 87#include "ypstuff.h"
afce951b 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
103typedef 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
112enum {
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
120typedef 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
129typedef struct netg__sym {
130 sym_base _base;
131 netg__cons *cons;
132} netg__sym;
133
134/* --- Token types for the netgroup parser --- */
135
136enum {
137 tok_string = 256,
138 tok_eof
139};
140
141/*----- Static variables --------------------------------------------------*/
142
143static sym_table netg__table; /* Netgroup table */
144static 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
158static 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
193static 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
8e3b89ae 355 free(c->car.atom);
356 free(c);
afce951b 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
f60a3434 372#ifndef NTRACE
afce951b 373
374static 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
f60a3434 421#ifndef NTRACE
8e3b89ae 422
afce951b 423static void netg__dump(void)
424{
425 sym_iter i;
426 netg__sym *sng;
427
afce951b 428 trace(TRACE_DEBUG, "debug: dumping netgroups file");
f60a3434 429 for (sym_mkiter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
afce951b 430 trace(TRACE_DEBUG, "debug: netgroup `%s'...", sng->_base.name);
431 sng->cons->f &= ~f_visit;
432 netg__dumpGroup(sng->cons, 1);
433 }
afce951b 434}
435
8e3b89ae 436#endif
437
afce951b 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
447void netg_iterate(void) { netg_iterate_r(&netg__iter); }
f60a3434 448void netg_iterate_r(netg_iter *i) { sym_mkiter(i, &netg__table); }
afce951b 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
459netg *netg_next(void) { return (netg_next_r(&netg__iter)); }
460netg *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
471const 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
495static 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
516int 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
535static 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
565void netg_init(void)
566{
afce951b 567 /* --- Initialise my symbol table --- */
568
f60a3434 569 sym_create(&netg__table);
afce951b 570
571 /* --- Bind myself unto a YP server --- */
572
a340752b 573 ypstuff_bind();
574 if (!yp_domain)
afce951b 575 return;
576
577 /* --- Now try to read all the netgroup entries --- */
578
579 {
580 static struct ypall_callback ncb = { netg__foreach, 0 };
a340752b 581 yp_all(yp_domain, "netgroup", &ncb);
afce951b 582 }
583
afce951b 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
f60a3434 596 for (sym_mkiter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
afce951b 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
f60a3434 621 for (sym_mkiter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
afce951b 622 sng->cons->f &= ~f_uncycled;
f60a3434 623 for (sym_mkiter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
afce951b 624 netg__breakCycle(sng->cons);
625 }
626
627 /* --- Dump the table again --- */
628
629 IF_TRACING(TRACE_DEBUG, netg__dump(); )
630}
631
8e3b89ae 632/* --- @netg_end@ --- *
afce951b 633 *
634 * Arguments: ---
635 *
636 * Returns: ---
637 *
8e3b89ae 638 * Use: Empties the netgroups database.
afce951b 639 */
640
8e3b89ae 641void netg_end(void)
afce951b 642{
643 sym_iter i;
644 netg__sym *sng;
645 netg__cons *c, *cc;
646
647 /* --- Remove all the old netgroups rubbish --- */
648
f60a3434 649 for (sym_mkiter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
afce951b 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);
8e3b89ae 658 free(c->car.atom);
afce951b 659 }
660 free(c);
661 c = cc;
662 }
663 sym_remove(&netg__table, sng);
664 }
665
f60a3434 666 sym_destroy(&netg__table);
afce951b 667}
668
669/*----- Test driver -------------------------------------------------------*/
670
671#ifdef TEST_RIG
672
673int 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
680int 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 -------------------------------------------------*/