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