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