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