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 2004 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 loadmaster_files(mastermap, defopts, stack, stkptr) 323 char *mastermap; 324 char *defopts; 325 char **stack, ***stkptr; 326 { 327 FILE *fp; 328 int done = 0; 329 char *line, *dir, *map, *opts; 330 char linebuf[LINESZ]; 331 char lineq[LINESZ]; 332 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 333 334 335 if ((fp = file_open(mastermap, fname, stack, stkptr)) == NULL) 336 return (__NSW_UNAVAIL); 337 338 while ((line = get_line(fp, fname, linebuf, 339 sizeof (linebuf))) != NULL) { 340 unquote(line, lineq); 341 if (macro_expand("", line, lineq, LINESZ)) { 342 syslog(LOG_ERR, 343 "map %s: line too long (max %d chars)", 344 mastermap, LINESZ - 1); 345 continue; 346 } 347 dir = line; 348 while (*dir && isspace(*dir)) 349 dir++; 350 if (*dir == '\0') 351 continue; 352 map = dir; 353 354 while (*map && !isspace(*map)) map++; 355 if (*map) 356 *map++ = '\0'; 357 358 if (*dir == '+') { 359 opts = map; 360 while (*opts && isspace(*opts)) 361 opts++; 362 if (*opts != '-') 363 opts = defopts; 364 else 365 opts++; 366 /* 367 * Check for no embedded blanks. 368 */ 369 if (strcspn(opts, " ") == strlen(opts)) { 370 dir++; 371 (void) loadmaster_map(dir, opts, stack, stkptr); 372 } else { 373 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); 374 continue; 375 } 376 377 } else { 378 while (*map && isspace(*map)) 379 map++; 380 if (*map == '\0') 381 continue; 382 opts = map; 383 while (*opts && !isspace(*opts)) 384 opts++; 385 if (*opts) { 386 *opts++ = '\0'; 387 while (*opts && isspace(*opts)) 388 opts++; 389 } 390 if (*opts != '-') 391 opts = defopts; 392 else 393 opts++; 394 /* 395 * Check for no embedded blanks. 396 */ 397 if (strcspn(opts, " ") == strlen(opts)) { 398 dirinit(dir, map, opts, 0, stack, stkptr); 399 } else { 400 pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); 401 continue; 402 } 403 } 404 done++; 405 } 406 407 (void) stack_op(POP, (char *)NULL, stack, stkptr); 408 (void) fclose(fp); 409 410 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 411 } 412 413 loaddirect_files(map, local_map, opts, stack, stkptr) 414 char *map, *local_map, *opts; 415 char **stack, ***stkptr; 416 { 417 FILE *fp; 418 int done = 0; 419 char *line, *p1, *p2; 420 char linebuf[LINESZ]; 421 char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ 422 423 if ((fp = file_open(map, fname, stack, stkptr)) == NULL) 424 return (__NSW_UNAVAIL); 425 426 while ((line = get_line(fp, fname, linebuf, 427 sizeof (linebuf))) != NULL) { 428 p1 = line; 429 while (*p1 && isspace(*p1)) 430 p1++; 431 if (*p1 == '\0') 432 continue; 433 p2 = p1; 434 while (*p2 && !isspace(*p2)) 435 p2++; 436 *p2 = '\0'; 437 if (*p1 == '+') { 438 p1++; 439 (void) loaddirect_map(p1, local_map, opts, stack, 440 stkptr); 441 } else { 442 dirinit(p1, local_map, opts, 1, stack, stkptr); 443 } 444 done++; 445 } 446 447 (void) stack_op(POP, (char *)NULL, stack, stkptr); 448 (void) fclose(fp); 449 450 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 451 } 452 453 /* 454 * This procedure opens the file and pushes it onto the 455 * the stack. Only if a file is opened successfully, is 456 * it pushed onto the stack 457 */ 458 static FILE * 459 file_open(map, fname, stack, stkptr) 460 char *map, *fname; 461 char **stack, ***stkptr; 462 { 463 FILE *fp; 464 465 if (*map != '/') { 466 /* prepend an "/etc" */ 467 (void) strcpy(fname, "/etc/"); 468 (void) strcat(fname, map); 469 } else 470 (void) strcpy(fname, map); 471 472 fp = fopen(fname, "r"); 473 474 if (fp != NULL) { 475 if (!stack_op(PUSH, fname, stack, stkptr)) { 476 (void) fclose(fp); 477 return (NULL); 478 } 479 } 480 return (fp); 481 } 482 483 /* 484 * reimplemnted to be MT-HOT. 485 */ 486 int 487 stack_op(op, name, stack, stkptr) 488 int op; 489 char *name; 490 char **stack, ***stkptr; 491 { 492 char **ptr = NULL; 493 char **stk_top = &stack[STACKSIZ - 1]; 494 495 /* 496 * the stackptr points to the next empty slot 497 * for PUSH: put the element and increment stkptr 498 * for POP: decrement stkptr and free 499 */ 500 501 switch (op) { 502 case INIT: 503 for (ptr = stack; ptr != stk_top; ptr++) 504 *ptr = (char *)NULL; 505 *stkptr = stack; 506 return (1); 507 case ERASE: 508 for (ptr = stack; ptr != stk_top; ptr++) 509 if (*ptr) { 510 if (trace > 1) 511 trace_prt(1, " ERASE %s\n", *ptr); 512 free (*ptr); 513 *ptr = (char *)NULL; 514 } 515 *stkptr = stack; 516 return (1); 517 case PUSH: 518 if (*stkptr == stk_top) 519 return (0); 520 for (ptr = stack; ptr != *stkptr; ptr++) 521 if (*ptr && (strcmp(*ptr, name) == 0)) { 522 return (0); 523 } 524 if (trace > 1) 525 trace_prt(1, " PUSH %s\n", name); 526 if ((**stkptr = strdup(name)) == NULL) { 527 syslog(LOG_ERR, "stack_op: Memory alloc failed : %m"); 528 return (0); 529 } 530 (*stkptr)++; 531 return (1); 532 case POP: 533 if (*stkptr != stack) 534 (*stkptr)--; 535 else 536 syslog(LOG_ERR, "Attempt to pop empty stack\n"); 537 538 if (*stkptr && **stkptr) { 539 if (trace > 1) 540 trace_prt(1, " POP %s\n", **stkptr); 541 free (**stkptr); 542 **stkptr = (char *)NULL; 543 } 544 return (1); 545 default: 546 return (0); 547 } 548 } 549 550 #define READ_EXECOUT_ARGS 3 551 552 /* 553 * read_execout(char *key, char **lp, char *fname, char *line, int linesz) 554 * A simpler, multithreaded implementation of popen(). Used due to 555 * non multithreaded implementation of popen() (it calls vfork()) and a 556 * significant bug in execl(). 557 * Returns 0 on OK or -1 on error. 558 */ 559 static int 560 read_execout(char *key, char **lp, char *fname, char *line, int linesz) 561 { 562 int p[2]; 563 int status = 0; 564 int child_pid; 565 char *args[READ_EXECOUT_ARGS]; 566 FILE *fp0; 567 568 if (pipe(p) < 0) { 569 syslog(LOG_ERR, "read_execout: Cannot create pipe"); 570 return (-1); 571 } 572 573 /* setup args for execv */ 574 if (((args[0] = strdup(fname)) == NULL) || 575 ((args[1] = strdup(key)) == NULL)) { 576 if (args[0] != NULL) 577 free(args[0]); 578 syslog(LOG_ERR, "read_execout: Memory allocation failed"); 579 return (-1); 580 } 581 args[2] = NULL; 582 583 if (trace > 3) 584 trace_prt(1, "\tread_execout: forking .....\n"); 585 586 switch ((child_pid = fork1())) { 587 case -1: 588 syslog(LOG_ERR, "read_execout: Cannot fork"); 589 return (-1); 590 case 0: 591 /* 592 * Child 593 */ 594 close(p[0]); 595 close(1); 596 if (fcntl(p[1], F_DUPFD, 1) != 1) { 597 syslog(LOG_ERR, 598 "read_execout: dup of stdout failed"); 599 _exit(-1); 600 } 601 close(p[1]); 602 execv(fname, &args[0]); 603 _exit(-1); 604 default: 605 /* 606 * Parent 607 */ 608 close(p[1]); 609 610 /* 611 * wait for child to complete. Note we read after the 612 * child exits to guarantee a full pipe. 613 */ 614 while (waitpid(child_pid, &status, 0) < 0) { 615 /* if waitpid fails with EINTR, restart */ 616 if (errno != EINTR) { 617 status = -1; 618 break; 619 } 620 } 621 if (status != -1) { 622 if ((fp0 = fdopen(p[0], "r")) != NULL) { 623 *lp = get_line(fp0, fname, line, linesz); 624 fclose(fp0); 625 } else { 626 close(p[0]); 627 status = -1; 628 } 629 } else { 630 close(p[0]); 631 } 632 633 /* free args */ 634 free(args[0]); 635 free(args[1]); 636 637 if (trace > 3) 638 trace_prt(1, "\tread_execout: map=%s key=%s line=%s\n", 639 fname, key, line); 640 641 return (status); 642 } 643 } 644