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