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