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