1 /* $NetBSD: netgroup_mkdb.c,v 1.18 2009/10/21 01:07:47 snj 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.18 2009/10/21 01:07:47 snj 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 break; 250 251 default: 252 abort(); 253 break; 254 } 255 break; 256 257 case _NG_NAME: 258 e = NEW(struct nentry); 259 e->n_type = _NG_NAME; 260 e->n_name = name; 261 e->n_next = NULL; 262 e->n_size = size; 263 if (tail == NULL) 264 head = tail = e; 265 else { 266 tail->n_next = e; 267 tail = e; 268 } 269 break; 270 271 case _NG_GROUP: 272 if (tail == NULL) { 273 char fmt[BUFSIZ]; 274 _ng_print(fmt, sizeof(fmt), ng); 275 errx(1, "no netgroup key for %s", fmt); 276 } 277 else { 278 e = NEW(struct nentry); 279 e->n_type = _NG_GROUP; 280 e->n_group = ng; 281 e->n_next = NULL; 282 e->n_size = size; 283 tail->n_next = e; 284 tail = e; 285 } 286 break; 287 288 case _NG_ERROR: 289 errx(1, "Fatal error at `%s'", p); 290 break; 291 292 default: 293 abort(); 294 break; 295 } 296 } 297 } 298 (void)fclose(fp); 299 return db; 300 } 301 302 303 /* 304 * ng_insert(): Insert named key into the database, and return its associated 305 * string database 306 */ 307 static DB * 308 ng_insert(DB *db, const char *name) 309 { 310 DB *xdb = NULL; 311 DBT key, data; 312 313 key.data = __UNCONST(name); 314 key.size = strlen(name) + 1; 315 316 switch ((db->get)(db, &key, &data, 0)) { 317 case 0: 318 (void)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 *db, DB *udb, struct nentry *fe, char *name, size_t s, 360 StringList *ss) 361 { 362 DBT key, data; 363 struct nentry *e; 364 struct netgroup *ng; 365 char *p; 366 DB *xdb; 367 368 if (sl_find(ss, fe->n_name) != NULL) { 369 _ng_cycle(name, ss); 370 return; 371 } 372 sl_add(ss, fe->n_name); 373 374 for (e = fe->n_next; e != NULL; e = e->n_next) 375 switch (e->n_type) { 376 case _NG_GROUP: 377 if (!dups) 378 sl_delete(ss, fe->n_name, 0); 379 ng = e->n_group; 380 p = _ng_makekey(*((char **)(((char *) ng) + s)), 381 ng->ng_domain, e->n_size); 382 xdb = ng_insert(udb, p); 383 key.data = (u_char *)name; 384 key.size = strlen(name) + 1; 385 data.data = NULL; 386 data.size = 0; 387 switch ((xdb->put)(xdb, &key, &data, R_NOOVERWRITE)) { 388 case 0: 389 case 1: 390 break; 391 392 case -1: 393 err(1, "db put `%s'", name); 394 return; 395 396 default: 397 abort(); 398 break; 399 } 400 free(p); 401 break; 402 403 case _NG_NAME: 404 key.data = (u_char *) e->n_name; 405 key.size = strlen(e->n_name) + 1; 406 switch ((db->get)(db, &key, &data, 0)) { 407 struct nentry *rfe; 408 case 0: 409 (void)memcpy(&rfe, data.data, sizeof(rfe)); 410 ng_reventry(db, udb, rfe, name, s, ss); 411 break; 412 413 case 1: 414 break; 415 416 case -1: 417 err(1, "db get `%s'", e->n_name); 418 return; 419 420 default: 421 abort(); 422 return; 423 } 424 break; 425 426 default: 427 abort(); 428 break; 429 } 430 } 431 432 433 /* 434 * ng_reverse(): Reverse the database 435 */ 436 static DB * 437 ng_reverse(DB *db, size_t s) 438 { 439 int pos; 440 StringList *sl; 441 DBT key, data; 442 struct nentry *fe; 443 DB *udb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, 444 DB_HASH, NULL); 445 446 if (udb == NULL) 447 err(1, "dbopen"); 448 449 for (pos = R_FIRST;; pos = R_NEXT) 450 switch ((db->seq)(db, &key, &data, pos)) { 451 case 0: 452 sl = sl_init(); 453 (void)memcpy(&fe, data.data, sizeof(fe)); 454 ng_reventry(db, udb, fe, (char *) key.data, s, sl); 455 sl_free(sl, 0); 456 break; 457 458 case 1: 459 return udb; 460 461 case -1: 462 err(1, "seq"); 463 return udb; 464 } 465 466 return udb; 467 } 468 469 470 /* 471 * ng_print(): Pretty print a netgroup entry 472 */ 473 static void 474 ng_print(struct nentry *e, struct string *str) 475 { 476 char *ptr; 477 478 if (e->n_next == NULL) { 479 str_append(str, "", ' '); 480 return; 481 } 482 483 ptr = emalloc(e->n_size); 484 485 for (e = e->n_next; e != NULL; e = e->n_next) { 486 switch (e->n_type) { 487 case _NG_NAME: 488 (void)snprintf(ptr, e->n_size, "%s", e->n_name); 489 break; 490 491 case _NG_GROUP: 492 (void)snprintf(ptr, e->n_size, "(%s,%s,%s)", 493 NG_EMPTY(e->n_group->ng_host), 494 NG_EMPTY(e->n_group->ng_user), 495 NG_EMPTY(e->n_group->ng_domain)); 496 break; 497 498 default: 499 errx(1, "Internal error: Bad netgroup type"); 500 break; 501 } 502 str_append(str, ptr, ' '); 503 } 504 free(ptr); 505 } 506 507 508 /* 509 * ng_rprint(): Pretty print all reverse netgroup mappings in the given entry 510 */ 511 static void 512 ng_rprint(DB *db, struct string *str) 513 { 514 int pos; 515 DBT key, data; 516 517 for (pos = R_FIRST;; pos = R_NEXT) 518 switch ((db->seq)(db, &key, &data, pos)) { 519 case 0: 520 str_append(str, (char *)key.data, ','); 521 break; 522 523 case 1: 524 return; 525 526 default: 527 err(1, "seq"); 528 break; 529 } 530 } 531 532 533 #ifdef DEBUG_NG 534 /* 535 * ng_dump(): Pretty print all netgroups in the given database 536 */ 537 static void 538 ng_dump(DB *db) 539 { 540 int pos; 541 DBT key, data; 542 struct nentry *e; 543 struct string buf; 544 545 for (pos = R_FIRST;; pos = R_NEXT) 546 switch ((db->seq)(db, &key, &data, pos)) { 547 case 0: 548 (void)memcpy(&e, data.data, sizeof(e)); 549 str_init(&buf); 550 assert(e->n_type == _NG_NAME); 551 552 ng_print(e, &buf); 553 (void)fprintf(stderr, "%s\t%s\n", e->n_name, 554 buf.s_str ? buf.s_str : ""); 555 str_free(&buf); 556 break; 557 558 case 1: 559 return; 560 561 default: 562 err(1, "seq"); 563 return; 564 } 565 } 566 567 568 /* 569 * ng_rdump(): Pretty print all reverse mappings in the given database 570 */ 571 static void 572 ng_rdump(DB *db) 573 { 574 int pos; 575 DBT key, data; 576 DB *xdb; 577 struct string buf; 578 579 for (pos = R_FIRST;; pos = R_NEXT) 580 switch ((db->seq)(db, &key, &data, pos)) { 581 case 0: 582 (void)memcpy(&xdb, data.data, sizeof(xdb)); 583 str_init(&buf); 584 ng_rprint(xdb, &buf); 585 (void)fprintf(stderr, "%s\t%s\n", 586 (char *)key.data, buf.s_str ? buf.s_str : ""); 587 str_free(&buf); 588 break; 589 590 case 1: 591 return; 592 593 default: 594 err(1, "seq"); 595 return; 596 } 597 } 598 #endif /* DEBUG_NG */ 599 600 601 /* 602 * ng_write(): Dump the database into a file. 603 */ 604 static void 605 ng_write(DB *odb, DB *idb, int k) 606 { 607 int pos; 608 DBT key, data; 609 struct nentry *e; 610 struct string skey, sdata; 611 612 for (pos = R_FIRST;; pos = R_NEXT) 613 switch ((idb->seq)(idb, &key, &data, pos)) { 614 case 0: 615 memcpy(&e, data.data, sizeof(e)); 616 str_init(&skey); 617 str_init(&sdata); 618 assert(e->n_type == _NG_NAME); 619 620 str_prepend(&skey, e->n_name, k); 621 ng_print(e, &sdata); 622 key.data = (u_char *) skey.s_str; 623 key.size = skey.s_len + 1; 624 data.data = (u_char *) sdata.s_str; 625 data.size = sdata.s_len + 1; 626 627 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) { 628 case 0: 629 break; 630 631 case -1: 632 err(1, "put"); 633 break; 634 635 case 1: 636 default: 637 abort(); 638 break; 639 } 640 641 str_free(&skey); 642 str_free(&sdata); 643 break; 644 645 case 1: 646 return; 647 648 default: 649 err(1, "seq"); 650 return; 651 } 652 } 653 654 655 /* 656 * ng_rwrite(): Write the database 657 */ 658 static void 659 ng_rwrite(DB *odb, DB *idb, int k) 660 { 661 int pos; 662 DBT key, data; 663 DB *xdb; 664 struct string skey, sdata; 665 666 for (pos = R_FIRST;; pos = R_NEXT) 667 switch ((idb->seq)(idb, &key, &data, pos)) { 668 case 0: 669 memcpy(&xdb, data.data, sizeof(xdb)); 670 str_init(&skey); 671 str_init(&sdata); 672 673 str_prepend(&skey, (char *) key.data, k); 674 ng_rprint(xdb, &sdata); 675 key.data = (u_char *) skey.s_str; 676 key.size = skey.s_len + 1; 677 data.data = (u_char *) sdata.s_str; 678 data.size = sdata.s_len + 1; 679 680 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) { 681 case 0: 682 break; 683 684 case -1: 685 err(1, "put"); 686 break; 687 688 case 1: 689 default: 690 abort(); 691 break; 692 } 693 694 str_free(&skey); 695 str_free(&sdata); 696 break; 697 698 case 1: 699 return; 700 701 default: 702 err(1, "seq"); 703 return; 704 } 705 } 706 707 708 /* 709 * usage(): Print usage message and exit 710 */ 711 static void 712 usage(void) 713 { 714 715 (void)fprintf(stderr, "Usage: %s [-D] [-o db] [<file>]\n", 716 getprogname()); 717 exit(1); 718 } 719