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