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