netgroup_mkdb.c revision 1.19 1 /* $NetBSD: netgroup_mkdb.c,v 1.19 2025/07/13 12:34:10 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1994 Christos Zoulas
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: netgroup_mkdb.c,v 1.19 2025/07/13 12:34:10 rillig Exp $");
31 #endif
32
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <stdlib.h>
37 #include <stddef.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <db.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <stringlist.h>
46 #define _NETGROUP_PRIVATE
47 #include <netgroup.h>
48 #include <assert.h>
49
50 #include "str.h"
51 #include "util.h"
52
53 #define DEBUG_NG
54
55 #define NEW(a) (a *) emalloc(sizeof(a))
56
57 struct nentry {
58 int n_type;
59 size_t n_size; /* Buffer size required for printing */
60 union {
61 char *_name;
62 struct netgroup *_group;
63 } _n;
64 #define n_name _n._name
65 #define n_group _n._group
66 struct nentry *n_next;
67 };
68
69
70 static void cleanup(void);
71 static DB *ng_insert(DB *, const char *);
72 static void ng_reventry(DB *, DB *, struct nentry *, char *,
73 size_t, StringList *);
74 static void ng_print(struct nentry *, struct string *);
75 static void ng_rprint(DB *, struct string *);
76 static DB *ng_reverse(DB *, size_t);
77 static DB *ng_load(const char *);
78 static void ng_write(DB *, DB *, int);
79 static void ng_rwrite(DB *, DB *, int);
80 static void usage(void) __dead;
81
82 #ifdef DEBUG_NG
83 static int debug = 0;
84 static void ng_dump(DB *);
85 static void ng_rdump(DB *);
86 #endif /* DEBUG_NG */
87 static int dups = 0;
88
89
90 static const char ng_empty[] = "";
91 #define NG_EMPTY(a) ((a) ? (a) : ng_empty)
92
93 static const char *dbname = _PATH_NETGROUP_DB;
94
95 int
96 main(int argc, char **argv)
97 {
98 DB *db, *ndb, *hdb, *udb;
99 int ch;
100 char buf[MAXPATHLEN];
101 const char *fname = _PATH_NETGROUP;
102
103
104 while ((ch = getopt(argc, argv, "dDo:")) != -1)
105 switch (ch) {
106 #ifdef DEBUG_NG
107 case 'd':
108 debug++;
109 break;
110 #endif
111 case 'o':
112 dbname = optarg;
113 break;
114
115 case 'D':
116 dups++;
117 break;
118
119 case '?':
120 default:
121 usage();
122 }
123
124 argc -= optind;
125 argv += optind;
126
127 if (argc == 1)
128 fname = *argv;
129 else if (argc > 1)
130 usage();
131
132 if (atexit(cleanup))
133 err(1, "Cannot install exit handler");
134
135 /* Read and parse the netgroup file */
136 ndb = ng_load(fname);
137 #ifdef DEBUG_NG
138 if (debug) {
139 (void)fprintf(stderr, "#### Database\n");
140 ng_dump(ndb);
141 }
142 #endif
143
144 /* Reverse the database by host */
145 hdb = ng_reverse(ndb, offsetof(struct netgroup, ng_host));
146 #ifdef DEBUG_NG
147 if (debug) {
148 (void)fprintf(stderr, "#### Reverse by host\n");
149 ng_rdump(hdb);
150 }
151 #endif
152
153 /* Reverse the database by user */
154 udb = ng_reverse(ndb, offsetof(struct netgroup, ng_user));
155 #ifdef DEBUG_NG
156 if (debug) {
157 (void)fprintf(stderr, "#### Reverse by user\n");
158 ng_rdump(udb);
159 }
160 #endif
161
162 (void)snprintf(buf, sizeof(buf), "%s.tmp", dbname);
163
164 db = dbopen(buf, O_RDWR | O_CREAT | O_EXCL,
165 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, NULL);
166 if (!db)
167 err(1, "%s", buf);
168
169 ng_write(db, ndb, _NG_KEYBYNAME);
170 ng_rwrite(db, udb, _NG_KEYBYUSER);
171 ng_rwrite(db, hdb, _NG_KEYBYHOST);
172
173 if ((db->close)(db))
174 err(1, "Error closing database");
175
176 if (rename(buf, dbname) == -1)
177 err(1, "Cannot rename `%s' to `%s'", buf, dbname);
178
179 return 0;
180 }
181
182
183 /*
184 * cleanup(): Remove temporary files upon exit
185 */
186 static void
187 cleanup(void)
188 {
189 char buf[MAXPATHLEN];
190 (void)snprintf(buf, sizeof(buf), "%s.tmp", dbname);
191 (void)unlink(buf);
192 }
193
194
195
196 /*
197 * ng_load(): Load the netgroup database from a file
198 */
199 static DB *
200 ng_load(const char *fname)
201 {
202 FILE *fp;
203 DB *db;
204 char *buf;
205 size_t size;
206 struct nentry *tail, *head, *e;
207 char *p, *name;
208 struct netgroup *ng;
209 DBT data, key;
210
211 /* Open the netgroup file */
212 if ((fp = fopen(fname, "r")) == NULL)
213 err(1, "%s", fname);
214
215 db = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL);
216
217 if (db == NULL)
218 err(1, "dbopen");
219
220 while ((buf = fparseln(fp, &size, NULL, NULL, 0)) != NULL) {
221 tail = head = NULL;
222 p = buf;
223
224 while (p != NULL) {
225 switch (_ng_parse(&p, &name, &ng)) {
226 case _NG_NONE:
227 /* done with this one */
228 p = NULL;
229 free(buf);
230 if (head == NULL)
231 break;
232
233 key.data = (u_char *)head->n_name;
234 key.size = strlen(head->n_name) + 1;
235 data.data = (u_char *)&head;
236 data.size = sizeof(head);
237 switch ((db->put)(db, &key, &data,
238 R_NOOVERWRITE)) {
239 case 0:
240 break;
241
242 case 1:
243 warnx("Duplicate entry netgroup `%s'",
244 head->n_name);
245 break;
246
247 case -1:
248 err(1, "put");
249
250 default:
251 abort();
252 }
253 break;
254
255 case _NG_NAME:
256 e = NEW(struct nentry);
257 e->n_type = _NG_NAME;
258 e->n_name = name;
259 e->n_next = NULL;
260 e->n_size = size;
261 if (tail == NULL)
262 head = tail = e;
263 else {
264 tail->n_next = e;
265 tail = e;
266 }
267 break;
268
269 case _NG_GROUP:
270 if (tail == NULL) {
271 char fmt[BUFSIZ];
272 _ng_print(fmt, sizeof(fmt), ng);
273 errx(1, "no netgroup key for %s", fmt);
274 }
275 else {
276 e = NEW(struct nentry);
277 e->n_type = _NG_GROUP;
278 e->n_group = ng;
279 e->n_next = NULL;
280 e->n_size = size;
281 tail->n_next = e;
282 tail = e;
283 }
284 break;
285
286 case _NG_ERROR:
287 errx(1, "Fatal error at `%s'", p);
288
289 default:
290 abort();
291 }
292 }
293 }
294 (void)fclose(fp);
295 return db;
296 }
297
298
299 /*
300 * ng_insert(): Insert named key into the database, and return its associated
301 * string database
302 */
303 static DB *
304 ng_insert(DB *db, const char *name)
305 {
306 DB *xdb = NULL;
307 DBT key, data;
308
309 key.data = __UNCONST(name);
310 key.size = strlen(name) + 1;
311
312 switch ((db->get)(db, &key, &data, 0)) {
313 case 0:
314 (void)memcpy(&xdb, data.data, sizeof(xdb));
315 break;
316
317 case 1:
318 xdb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL);
319 if (xdb == NULL)
320 err(1, "dbopen");
321
322 data.data = (u_char *)&xdb;
323 data.size = sizeof(xdb);
324 switch ((db->put)(db, &key, &data, R_NOOVERWRITE)) {
325 case 0:
326 break;
327
328 case -1:
329 err(1, "db put `%s'", name);
330
331 case 1:
332 default:
333 abort();
334 }
335 break;
336
337 case -1:
338 err(1, "db get `%s'", name);
339
340 default:
341 abort();
342 }
343
344 return xdb;
345 }
346
347
348 /*
349 * ng_reventry(): Recursively add all the netgroups to the group entry.
350 */
351 static void
352 ng_reventry(DB *db, DB *udb, struct nentry *fe, char *name, size_t s,
353 StringList *ss)
354 {
355 DBT key, data;
356 struct nentry *e;
357 struct netgroup *ng;
358 char *p;
359 DB *xdb;
360
361 if (sl_find(ss, fe->n_name) != NULL) {
362 _ng_cycle(name, ss);
363 return;
364 }
365 sl_add(ss, fe->n_name);
366
367 for (e = fe->n_next; e != NULL; e = e->n_next)
368 switch (e->n_type) {
369 case _NG_GROUP:
370 if (!dups)
371 sl_delete(ss, fe->n_name, 0);
372 ng = e->n_group;
373 p = _ng_makekey(*((char **)(((char *) ng) + s)),
374 ng->ng_domain, e->n_size);
375 xdb = ng_insert(udb, p);
376 key.data = (u_char *)name;
377 key.size = strlen(name) + 1;
378 data.data = NULL;
379 data.size = 0;
380 switch ((xdb->put)(xdb, &key, &data, R_NOOVERWRITE)) {
381 case 0:
382 case 1:
383 break;
384
385 case -1:
386 err(1, "db put `%s'", name);
387
388 default:
389 abort();
390 }
391 free(p);
392 break;
393
394 case _NG_NAME:
395 key.data = (u_char *) e->n_name;
396 key.size = strlen(e->n_name) + 1;
397 switch ((db->get)(db, &key, &data, 0)) {
398 struct nentry *rfe;
399 case 0:
400 (void)memcpy(&rfe, data.data, sizeof(rfe));
401 ng_reventry(db, udb, rfe, name, s, ss);
402 break;
403
404 case 1:
405 break;
406
407 case -1:
408 err(1, "db get `%s'", e->n_name);
409
410 default:
411 abort();
412 }
413 break;
414
415 default:
416 abort();
417 }
418 }
419
420
421 /*
422 * ng_reverse(): Reverse the database
423 */
424 static DB *
425 ng_reverse(DB *db, size_t s)
426 {
427 int pos;
428 StringList *sl;
429 DBT key, data;
430 struct nentry *fe;
431 DB *udb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0,
432 DB_HASH, NULL);
433
434 if (udb == NULL)
435 err(1, "dbopen");
436
437 for (pos = R_FIRST;; pos = R_NEXT)
438 switch ((db->seq)(db, &key, &data, pos)) {
439 case 0:
440 sl = sl_init();
441 (void)memcpy(&fe, data.data, sizeof(fe));
442 ng_reventry(db, udb, fe, (char *) key.data, s, sl);
443 sl_free(sl, 0);
444 break;
445
446 case 1:
447 return udb;
448
449 case -1:
450 err(1, "seq");
451 }
452 }
453
454
455 /*
456 * ng_print(): Pretty print a netgroup entry
457 */
458 static void
459 ng_print(struct nentry *e, struct string *str)
460 {
461 char *ptr;
462
463 if (e->n_next == NULL) {
464 str_append(str, "", ' ');
465 return;
466 }
467
468 ptr = emalloc(e->n_size);
469
470 for (e = e->n_next; e != NULL; e = e->n_next) {
471 switch (e->n_type) {
472 case _NG_NAME:
473 (void)snprintf(ptr, e->n_size, "%s", e->n_name);
474 break;
475
476 case _NG_GROUP:
477 (void)snprintf(ptr, e->n_size, "(%s,%s,%s)",
478 NG_EMPTY(e->n_group->ng_host),
479 NG_EMPTY(e->n_group->ng_user),
480 NG_EMPTY(e->n_group->ng_domain));
481 break;
482
483 default:
484 errx(1, "Internal error: Bad netgroup type");
485 }
486 str_append(str, ptr, ' ');
487 }
488 free(ptr);
489 }
490
491
492 /*
493 * ng_rprint(): Pretty print all reverse netgroup mappings in the given entry
494 */
495 static void
496 ng_rprint(DB *db, struct string *str)
497 {
498 int pos;
499 DBT key, data;
500
501 for (pos = R_FIRST;; pos = R_NEXT)
502 switch ((db->seq)(db, &key, &data, pos)) {
503 case 0:
504 str_append(str, (char *)key.data, ',');
505 break;
506
507 case 1:
508 return;
509
510 default:
511 err(1, "seq");
512 }
513 }
514
515
516 #ifdef DEBUG_NG
517 /*
518 * ng_dump(): Pretty print all netgroups in the given database
519 */
520 static void
521 ng_dump(DB *db)
522 {
523 int pos;
524 DBT key, data;
525 struct nentry *e;
526 struct string buf;
527
528 for (pos = R_FIRST;; pos = R_NEXT)
529 switch ((db->seq)(db, &key, &data, pos)) {
530 case 0:
531 (void)memcpy(&e, data.data, sizeof(e));
532 str_init(&buf);
533 assert(e->n_type == _NG_NAME);
534
535 ng_print(e, &buf);
536 (void)fprintf(stderr, "%s\t%s\n", e->n_name,
537 buf.s_str ? buf.s_str : "");
538 str_free(&buf);
539 break;
540
541 case 1:
542 return;
543
544 default:
545 err(1, "seq");
546 }
547 }
548
549
550 /*
551 * ng_rdump(): Pretty print all reverse mappings in the given database
552 */
553 static void
554 ng_rdump(DB *db)
555 {
556 int pos;
557 DBT key, data;
558 DB *xdb;
559 struct string buf;
560
561 for (pos = R_FIRST;; pos = R_NEXT)
562 switch ((db->seq)(db, &key, &data, pos)) {
563 case 0:
564 (void)memcpy(&xdb, data.data, sizeof(xdb));
565 str_init(&buf);
566 ng_rprint(xdb, &buf);
567 (void)fprintf(stderr, "%s\t%s\n",
568 (char *)key.data, buf.s_str ? buf.s_str : "");
569 str_free(&buf);
570 break;
571
572 case 1:
573 return;
574
575 default:
576 err(1, "seq");
577 }
578 }
579 #endif /* DEBUG_NG */
580
581
582 /*
583 * ng_write(): Dump the database into a file.
584 */
585 static void
586 ng_write(DB *odb, DB *idb, int k)
587 {
588 int pos;
589 DBT key, data;
590 struct nentry *e;
591 struct string skey, sdata;
592
593 for (pos = R_FIRST;; pos = R_NEXT)
594 switch ((idb->seq)(idb, &key, &data, pos)) {
595 case 0:
596 memcpy(&e, data.data, sizeof(e));
597 str_init(&skey);
598 str_init(&sdata);
599 assert(e->n_type == _NG_NAME);
600
601 str_prepend(&skey, e->n_name, k);
602 ng_print(e, &sdata);
603 key.data = (u_char *) skey.s_str;
604 key.size = skey.s_len + 1;
605 data.data = (u_char *) sdata.s_str;
606 data.size = sdata.s_len + 1;
607
608 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) {
609 case 0:
610 break;
611
612 case -1:
613 err(1, "put");
614
615 case 1:
616 default:
617 abort();
618 }
619
620 str_free(&skey);
621 str_free(&sdata);
622 break;
623
624 case 1:
625 return;
626
627 default:
628 err(1, "seq");
629 }
630 }
631
632
633 /*
634 * ng_rwrite(): Write the database
635 */
636 static void
637 ng_rwrite(DB *odb, DB *idb, int k)
638 {
639 int pos;
640 DBT key, data;
641 DB *xdb;
642 struct string skey, sdata;
643
644 for (pos = R_FIRST;; pos = R_NEXT)
645 switch ((idb->seq)(idb, &key, &data, pos)) {
646 case 0:
647 memcpy(&xdb, data.data, sizeof(xdb));
648 str_init(&skey);
649 str_init(&sdata);
650
651 str_prepend(&skey, (char *) key.data, k);
652 ng_rprint(xdb, &sdata);
653 key.data = (u_char *) skey.s_str;
654 key.size = skey.s_len + 1;
655 data.data = (u_char *) sdata.s_str;
656 data.size = sdata.s_len + 1;
657
658 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) {
659 case 0:
660 break;
661
662 case -1:
663 err(1, "put");
664
665 case 1:
666 default:
667 abort();
668 }
669
670 str_free(&skey);
671 str_free(&sdata);
672 break;
673
674 case 1:
675 return;
676
677 default:
678 err(1, "seq");
679 }
680 }
681
682
683 /*
684 * usage(): Print usage message and exit
685 */
686 static void
687 usage(void)
688 {
689
690 (void)fprintf(stderr, "Usage: %s [-D] [-o db] [<file>]\n",
691 getprogname());
692 exit(1);
693 }
694