1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ns_files.c 24 * 25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <syslog.h> 34 #include <string.h> 35 #include <ctype.h> 36 #include <nsswitch.h> 37 #include <sys/stat.h> 38 #include <sys/param.h> 39 #include <rpc/rpc.h> 40 #include <rpcsvc/nfs_prot.h> 41 #include <thread.h> 42 #include <assert.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <unistd.h> 46 #include <synch.h> 47 #include <sys/types.h> 48 #include <sys/wait.h> 49 #include "automount.h" 50 51 static int read_execout(char *key, char **lp, char *fname, char *line, 52 int linesz); 53 static FILE *file_open(char *, char *, char **, char ***); 54 55 /* 56 * Initialize the stack 57 */ 58 void 59 init_files(char **stack, char ***stkptr) 60 { 61 /* 62 * The call is bogus for automountd since the stack is 63 * is more appropriately initialized in the thread-private 64 * routines 65 */ 66 if (stack == NULL && stkptr == NULL) 67 return; 68 (void) stack_op(INIT, NULL, stack, stkptr); 69 } 70 71 int 72 getmapent_files(key, mapname, ml, stack, stkptr, iswildcard, isrestricted) 73 char *key; 74 char *mapname; 75 struct mapline *ml; 76 char **stack, ***stkptr; 77 bool_t *iswildcard; 78 bool_t isrestricted; 79 { 80 int nserr; 81 FILE *fp; 82 char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; 83 char linebuf[LINESZ], lineqbuf[LINESZ]; 84 char *lp, *lq; 85 struct stat stbuf; 86 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 87 int syntaxok = 1; 88 89 if (iswildcard) 90 *iswildcard = FALSE; 91 if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { 92 nserr = __NSW_UNAVAIL; 93 goto done; 94 } 95 96 if (stat(fname, &stbuf) < 0) { 97 nserr = __NSW_UNAVAIL; 98 goto done; 99 } 100 101 /* 102 * If the file has its execute bit on then 103 * assume it's an executable map. 104 * Execute it and pass the key as an argument. 105 * Expect to get a map entry on the stdout. 106 * Ignore the "x" bit on restricted maps. 107 */ 108 if (!isrestricted && (stbuf.st_mode & S_IXUSR)) { 109 int rc; 110 111 if (trace > 1) { 112 trace_prt(1, 113 "\tExecutable map: map=%s key=%s\n", 114 fname, key); 115 } 116 117 rc = read_execout(key, &lp, fname, ml->linebuf, LINESZ); 118 119 if (rc != 0) { 120 nserr = __NSW_UNAVAIL; 121 goto done; 122 } 123 124 if (lp == NULL || strlen(ml->linebuf) == 0) { 125 nserr = __NSW_NOTFOUND; 126 goto done; 127 } 128 129 unquote(ml->linebuf, ml->lineqbuf); 130 nserr = __NSW_SUCCESS; 131 goto done; 132 } 133 134 135 /* 136 * It's just a normal map file. 137 * Search for the entry with the required key. 138 */ 139 for (;;) { 140 lp = get_line(fp, fname, linebuf, sizeof (linebuf)); 141 if (lp == NULL) { 142 nserr = __NSW_NOTFOUND; 143 goto done; 144 } 145 if (verbose && syntaxok && isspace(*(uchar_t *)lp)) { 146 syntaxok = 0; 147 syslog(LOG_ERR, 148 "leading space in map entry \"%s\" in %s", 149 lp, mapname); 150 } 151 lq = lineqbuf; 152 unquote(lp, lq); 153 if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word)) 154 == -1) || (word[0] == '\0')) 155 continue; 156 if (strcmp(word, key) == 0) 157 break; 158 if (word[0] == '*' && word[1] == '\0') { 159 if (iswildcard) 160 *iswildcard = TRUE; 161 break; 162 } 163 if (word[0] == '+') { 164 nserr = getmapent(key, word+1, ml, stack, stkptr, 165 iswildcard, isrestricted); 166 if (nserr == __NSW_SUCCESS) 167 goto done; 168 continue; 169 } 170 171 /* 172 * sanity check each map entry key against 173 * the lookup key as the map is searched. 174 */ 175 if (verbose && syntaxok) { /* sanity check entry */ 176 if (*key == '/') { 177 if (*word != '/') { 178 syntaxok = 0; 179 syslog(LOG_ERR, 180 "bad key \"%s\" in direct map %s\n", 181 word, mapname); 182 } 183 } else { 184 if (strchr(word, '/')) { 185 syntaxok = 0; 186 syslog(LOG_ERR, 187 "bad key \"%s\" in indirect map %s\n", 188 word, mapname); 189 } 190 } 191 } 192 } 193 194 (void) strcpy(ml->linebuf, lp); 195 (void) strcpy(ml->lineqbuf, lq); 196 nserr = __NSW_SUCCESS; 197 done: 198 if (fp) { 199 (void) stack_op(POP, (char *)NULL, stack, stkptr); 200 (void) fclose(fp); 201 } 202 203 204 return (nserr); 205 } 206 207 int 208 getmapkeys_files(mapname, list, error, cache_time, stack, stkptr) 209 char *mapname; 210 struct dir_entry **list; 211 int *error; 212 int *cache_time; 213 char **stack, ***stkptr; 214 { 215 FILE *fp = NULL; 216 char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; 217 char linebuf[LINESZ], lineqbuf[LINESZ]; 218 char *lp, *lq; 219 struct stat stbuf; 220 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 221 int syntaxok = 1; 222 int nserr; 223 struct dir_entry *last = NULL; 224 225 if (trace > 1) 226 trace_prt(1, "getmapkeys_files %s\n", mapname); 227 228 *cache_time = RDDIR_CACHE_TIME; 229 if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { 230 *error = ENOENT; 231 nserr = __NSW_UNAVAIL; 232 goto done; 233 } 234 if (fseek(fp, 0L, SEEK_SET) == -1) { 235 *error = ENOENT; 236 nserr = __NSW_UNAVAIL; 237 goto done; 238 } 239 240 if (stat(fname, &stbuf) < 0) { 241 *error = ENOENT; 242 nserr = __NSW_UNAVAIL; 243 goto done; 244 } 245 246 /* 247 * If the file has its execute bit on then 248 * assume it's an executable map. 249 * I don't know how to list executable maps, return 250 * an empty map. 251 */ 252 if (stbuf.st_mode & S_IXUSR) { 253 *error = 0; 254 nserr = __NSW_SUCCESS; 255 goto done; 256 } 257 /* 258 * It's just a normal map file. 259 * List entries one line at a time. 260 */ 261 for (;;) { 262 lp = get_line(fp, fname, linebuf, sizeof (linebuf)); 263 if (lp == NULL) { 264 nserr = __NSW_SUCCESS; 265 goto done; 266 } 267 if (syntaxok && isspace(*(uchar_t *)lp)) { 268 syntaxok = 0; 269 syslog(LOG_ERR, 270 "leading space in map entry \"%s\" in %s", 271 lp, mapname); 272 } 273 lq = lineqbuf; 274 unquote(lp, lq); 275 if ((getword(word, wordq, &lp, &lq, ' ', MAXFILENAMELEN) 276 == -1) || (word[0] == '\0')) 277 continue; 278 /* 279 * Wildcard entries should be ignored and this should be 280 * the last entry read to corroborate the search through 281 * files, i.e., search for key until a wildcard is reached. 282 */ 283 if (word[0] == '*' && word[1] == '\0') 284 break; 285 if (word[0] == '+') { 286 /* 287 * Name switch here 288 */ 289 getmapkeys(word+1, list, error, cache_time, 290 stack, stkptr, 0); 291 /* 292 * the list may have been updated, therefore 293 * our 'last' may no longer be valid 294 */ 295 last = NULL; 296 continue; 297 } 298 299 if (add_dir_entry(word, list, &last) != 0) { 300 *error = ENOMEM; 301 goto done; 302 } 303 assert(last != NULL); 304 } 305 306 nserr = __NSW_SUCCESS; 307 done: 308 if (fp) { 309 (void) stack_op(POP, (char *)NULL, stack, stkptr); 310 (void) fclose(fp); 311 } 312 313 if (*list != NULL) { 314 /* 315 * list of entries found 316 */ 317 *error = 0; 318 } 319 return (nserr); 320 } 321 322 int 323 loadmaster_files(mastermap, defopts, stack, stkptr) 324 char *mastermap; 325 char *defopts; 326 char **stack, ***stkptr; 327 { 328 FILE *fp; 329 int done = 0; 330 char *line, *dir, *map, *opts; 331 char linebuf[LINESZ]; 332 char lineq[LINESZ]; 333 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 334 335 336 if ((fp = file_open(mastermap, fname, stack, stkptr)) == NULL) 337 return (__NSW_UNAVAIL); 338 339 while ((line = get_line(fp, fname, linebuf, 340 sizeof (linebuf))) != NULL) { 341 unquote(line, lineq); 342 if (macro_expand("", line, lineq, LINESZ)) { 343 syslog(LOG_ERR, 344 "map %s: line too long (max %d chars)", 345 mastermap, LINESZ - 1); 346 continue; 347 } 348 dir = line; 349 while (*dir && isspace(*dir)) 350 dir++; 351 if (*dir == '\0') 352 continue; 353 map = dir; 354 355 while (*map && !isspace(*map)) map++; 356 if (*map) 357 *map++ = '\0'; 358 359 if (*dir == '+') { 360 opts = map; 361 while (*opts && isspace(*opts)) 362 opts++; 363 if (*opts != '-') 364 opts = defopts; 365 else 366 opts++; 367 /* 368 * Check for no embedded blanks. 369 */ 370 if (strcspn(opts, " ") == strlen(opts)) { 371 dir++; 372 (void) loadmaster_map(dir, opts, stack, stkptr); 373 } else { 374 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); 375 continue; 376 } 377 378 } else { 379 while (*map && isspace(*map)) 380 map++; 381 if (*map == '\0') 382 continue; 383 opts = map; 384 while (*opts && !isspace(*opts)) 385 opts++; 386 if (*opts) { 387 *opts++ = '\0'; 388 while (*opts && isspace(*opts)) 389 opts++; 390 } 391 if (*opts != '-') 392 opts = defopts; 393 else 394 opts++; 395 /* 396 * Check for no embedded blanks. 397 */ 398 if (strcspn(opts, " ") == strlen(opts)) { 399 dirinit(dir, map, opts, 0, stack, stkptr); 400 } else { 401 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); 402 continue; 403 } 404 } 405 done++; 406 } 407 408 (void) stack_op(POP, (char *)NULL, stack, stkptr); 409 (void) fclose(fp); 410 411 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 412 } 413 414 int 415 loaddirect_files(map, local_map, opts, stack, stkptr) 416 char *map, *local_map, *opts; 417 char **stack, ***stkptr; 418 { 419 FILE *fp; 420 int done = 0; 421 char *line, *p1, *p2; 422 char linebuf[LINESZ]; 423 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 424 425 if ((fp = file_open(map, fname, stack, stkptr)) == NULL) 426 return (__NSW_UNAVAIL); 427 428 while ((line = get_line(fp, fname, linebuf, 429 sizeof (linebuf))) != NULL) { 430 p1 = line; 431 while (*p1 && isspace(*p1)) 432 p1++; 433 if (*p1 == '\0') 434 continue; 435 p2 = p1; 436 while (*p2 && !isspace(*p2)) 437 p2++; 438 *p2 = '\0'; 439 if (*p1 == '+') { 440 p1++; 441 (void) loaddirect_map(p1, local_map, opts, stack, 442 stkptr); 443 } else { 444 dirinit(p1, local_map, opts, 1, stack, stkptr); 445 } 446 done++; 447 } 448 449 (void) stack_op(POP, (char *)NULL, stack, stkptr); 450 (void) fclose(fp); 451 452 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 453 } 454 455 /* 456 * This procedure opens the file and pushes it onto the 457 * the stack. Only if a file is opened successfully, is 458 * it pushed onto the stack 459 */ 460 static FILE * 461 file_open(map, fname, stack, stkptr) 462 char *map, *fname; 463 char **stack, ***stkptr; 464 { 465 FILE *fp; 466 467 if (*map != '/') { 468 /* prepend an "/etc" */ 469 (void) strcpy(fname, "/etc/"); 470 (void) strcat(fname, map); 471 } else 472 (void) strcpy(fname, map); 473 474 fp = fopen(fname, "r"); 475 476 if (fp != NULL) { 477 if (!stack_op(PUSH, fname, stack, stkptr)) { 478 (void) fclose(fp); 479 return (NULL); 480 } 481 } 482 return (fp); 483 } 484 485 /* 486 * reimplemnted to be MT-HOT. 487 */ 488 int 489 stack_op(op, name, stack, stkptr) 490 int op; 491 char *name; 492 char **stack, ***stkptr; 493 { 494 char **ptr = NULL; 495 char **stk_top = &stack[STACKSIZ - 1]; 496 497 /* 498 * the stackptr points to the next empty slot 499 * for PUSH: put the element and increment stkptr 500 * for POP: decrement stkptr and free 501 */ 502 503 switch (op) { 504 case INIT: 505 for (ptr = stack; ptr != stk_top; ptr++) 506 *ptr = (char *)NULL; 507 *stkptr = stack; 508 return (1); 509 case ERASE: 510 for (ptr = stack; ptr != stk_top; ptr++) 511 if (*ptr) { 512 if (trace > 1) 513 trace_prt(1, " ERASE %s\n", *ptr); 514 free (*ptr); 515 *ptr = (char *)NULL; 516 } 517 *stkptr = stack; 518 return (1); 519 case PUSH: 520 if (*stkptr == stk_top) 521 return (0); 522 for (ptr = stack; ptr != *stkptr; ptr++) 523 if (*ptr && (strcmp(*ptr, name) == 0)) { 524 return (0); 525 } 526 if (trace > 1) 527 trace_prt(1, " PUSH %s\n", name); 528 if ((**stkptr = strdup(name)) == NULL) { 529 syslog(LOG_ERR, "stack_op: Memory alloc failed : %m"); 530 return (0); 531 } 532 (*stkptr)++; 533 return (1); 534 case POP: 535 if (*stkptr != stack) 536 (*stkptr)--; 537 else 538 syslog(LOG_ERR, "Attempt to pop empty stack\n"); 539 540 if (*stkptr && **stkptr) { 541 if (trace > 1) 542 trace_prt(1, " POP %s\n", **stkptr); 543 free (**stkptr); 544 **stkptr = (char *)NULL; 545 } 546 return (1); 547 default: 548 return (0); 549 } 550 } 551 552 #define READ_EXECOUT_ARGS 3 553 554 /* 555 * read_execout(char *key, char **lp, char *fname, char *line, int linesz) 556 * A simpler, multithreaded implementation of popen(). Used due to 557 * non multithreaded implementation of popen() (it calls vfork()) and a 558 * significant bug in execl(). 559 * Returns 0 on OK or -1 on error. 560 */ 561 static int 562 read_execout(char *key, char **lp, char *fname, char *line, int linesz) 563 { 564 int p[2]; 565 int status = 0; 566 int child_pid; 567 char *args[READ_EXECOUT_ARGS]; 568 FILE *fp0; 569 570 if (pipe(p) < 0) { 571 syslog(LOG_ERR, "read_execout: Cannot create pipe"); 572 return (-1); 573 } 574 575 /* setup args for execv */ 576 if (((args[0] = strdup(fname)) == NULL) || 577 ((args[1] = strdup(key)) == NULL)) { 578 if (args[0] != NULL) 579 free(args[0]); 580 syslog(LOG_ERR, "read_execout: Memory allocation failed"); 581 return (-1); 582 } 583 args[2] = NULL; 584 585 if (trace > 3) 586 trace_prt(1, "\tread_execout: forking .....\n"); 587 588 switch ((child_pid = fork1())) { 589 case -1: 590 syslog(LOG_ERR, "read_execout: Cannot fork"); 591 return (-1); 592 case 0: 593 /* 594 * Child 595 */ 596 close(p[0]); 597 close(1); 598 if (fcntl(p[1], F_DUPFD, 1) != 1) { 599 syslog(LOG_ERR, 600 "read_execout: dup of stdout failed"); 601 _exit(-1); 602 } 603 close(p[1]); 604 execv(fname, &args[0]); 605 _exit(-1); 606 default: 607 /* 608 * Parent 609 */ 610 close(p[1]); 611 612 /* 613 * wait for child to complete. Note we read after the 614 * child exits to guarantee a full pipe. 615 */ 616 while (waitpid(child_pid, &status, 0) < 0) { 617 /* if waitpid fails with EINTR, restart */ 618 if (errno != EINTR) { 619 status = -1; 620 break; 621 } 622 } 623 if (status != -1) { 624 if ((fp0 = fdopen(p[0], "r")) != NULL) { 625 *lp = get_line(fp0, fname, line, linesz); 626 fclose(fp0); 627 } else { 628 close(p[0]); 629 status = -1; 630 } 631 } else { 632 close(p[0]); 633 } 634 635 /* free args */ 636 free(args[0]); 637 free(args[1]); 638 639 if (trace > 3) 640 trace_prt(1, "\tread_execout: map=%s key=%s line=%s\n", 641 fname, key, line); 642 643 return (status); 644 } 645 } 646