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 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 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 * 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 * 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 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 * 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 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 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 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 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 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 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 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