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