xref: /illumos-gate/usr/src/lib/libnsl/saf/doconfig.c (revision 7c478bd9)
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  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <ulimit.h>
39 #include <wait.h>
40 #include <sys/types.h>
41 #include <rpc/trace.h>
42 #include <sys/stat.h>
43 #include <stropts.h>
44 #include <ctype.h>
45 #include <sys/conf.h>
46 #include <errno.h>
47 #include <signal.h>
48 #include "sac.h"
49 
50 #if defined(sparc)
51 #define	_STAT _stat
52 #define	_FSTAT _fstat
53 #else  /* !sparc */
54 #define	_STAT stat
55 #define	_FSTAT fstat
56 #endif /* sparc */
57 
58 extern int	_stat(const char *, struct stat *);
59 extern int	_fstat(int, struct stat *);
60 
61 #define	COMMENT	'#'
62 #define	NOWAIT	0
63 #define	WAIT	1
64 
65 extern char	**_environ;
66 
67 static char	*eatwhite();
68 static int	doassign();
69 static int	dopush();
70 static int	dopop();
71 static int	dorun();
72 
73 /*
74  * doconfig - the configuration script interpreter, if all is ok,
75  *	      return 0.  If there is a "system" error, return -1.
76  *	      If there is an error performing a command, or there
77  *	      is a syntax error, return the line number in error.
78  *
79  *	args:	fd - file descriptor to push and pop from
80  *		script - name of the configuration script
81  *		rflag - restriction flag to determine what "commands"
82  *			can be run
83  */
84 
85 int
86 doconfig(int fd, char *script, long rflag)
87 {
88 	int line;		/* line counter */
89 	struct stat statbuf;	/* place for stat */
90 	FILE *fp;		/* file pointer for config script */
91 	char buf[BUFSIZ + 1];	/* scratch buffer */
92 	char *bp;		/* scratch pointer */
93 	char *p;		/* scratch pointer */
94 
95 	/* if the script does not exist, then there is nothing to do */
96 	trace3(TR_doconfig, 0, fd, rflag);
97 	if (_STAT(script, &statbuf) < 0) {
98 		trace1(TR_doconfig, 1);
99 		return (0);
100 	}
101 
102 	fp = fopen(script, "r");
103 	if (fp == NULL) {
104 		trace1(TR_doconfig, 1);
105 		return (-1);
106 	}
107 
108 	line = 0;
109 	while (fgets(buf, BUFSIZ, fp)) {
110 		line++;
111 		p = strchr(buf, '\n');
112 		/* if no \n, then line is too long */
113 		if (p == NULL) {
114 			(void) fclose(fp);
115 			trace1(TR_doconfig, 1);
116 			return (line);
117 		}
118 		*p = '\0';
119 
120 		/* remove comments */
121 		p = strchr(buf, COMMENT);
122 		if (p)
123 			*p = '\0';
124 
125 		/* remove leading whitespace */
126 		bp = eatwhite(buf);
127 		/* see if anything is left */
128 		if (*bp == '\0')
129 			continue;
130 
131 		/* remove trailing whitespace */
132 		p = &buf[strlen(buf) - 1];
133 		while (*p && isspace(*p))
134 			*p-- = '\0';
135 
136 		/* get the command */
137 		p = bp;
138 		while (*p && !isspace(*p))
139 			p++;
140 		if (*p)
141 			*p++ = '\0';
142 		/* skip any whitespace here too (between command and args) */
143 		p = eatwhite(p);
144 
145 		if (!strcmp(bp, "assign")) {
146 			if ((rflag & NOASSIGN) || doassign(p)) {
147 				(void) fclose(fp);
148 				trace1(TR_doconfig, 1);
149 				return (line);
150 			}
151 		} else if (!strcmp(bp, "push")) {
152 			if (dopush(fd, p)) {
153 				(void) fclose(fp);
154 				trace1(TR_doconfig, 1);
155 				return (line);
156 			}
157 		} else if (!strcmp(bp, "pop")) {
158 			if (dopop(fd, p)) {
159 				(void) fclose(fp);
160 				trace1(TR_doconfig, 1);
161 				return (line);
162 			}
163 		} else if (!strcmp(bp, "run")) {
164 			if ((rflag & NORUN) || dorun(p, NOWAIT)) {
165 				(void) fclose(fp);
166 				trace1(TR_doconfig, 1);
167 				return (line);
168 			}
169 		} else if (!strcmp(bp, "runwait")) {
170 			if ((rflag & NORUN) || dorun(p, WAIT)) {
171 				(void) fclose(fp);
172 				trace1(TR_doconfig, 1);
173 				return (line);
174 			}
175 		} else {
176 			/* unknown command */
177 			(void) fclose(fp);
178 			trace1(TR_doconfig, 1);
179 			return (line);
180 		}
181 	}
182 	if (!feof(fp)) {
183 		(void) fclose(fp);
184 		trace1(TR_doconfig, 1);
185 		return (-1);
186 	} else {
187 		(void) fclose(fp);
188 		trace1(TR_doconfig, 1);
189 		return (0);
190 	}
191 }
192 
193 
194 /*
195  * doassign - handle an `assign' command
196  *
197  *	args:	p - assignment string
198  */
199 
200 
201 static int
202 doassign(char *p)
203 {
204 	char *var;		/* environment variable to be assigned */
205 	char val[BUFSIZ];	/* and the value to be assigned to it */
206 	char scratch[BUFSIZ];	/* scratch buffer */
207 	char delim;		/* delimiter char seen (for quoted strings ) */
208 	char *tp;		/* scratch pointer */
209 
210 	trace1(TR_doassign, 0);
211 	if (*p == '\0') {
212 		trace1(TR_doassign, 1);
213 		return (-1);
214 	}
215 	var = p;
216 	/* skip first token, but stop if we see a '=' */
217 	while (*p && !isspace(*p) && (*p != '='))
218 		p++;
219 
220 	/* if we found end of string, it's an error */
221 	if (*p == '\0') {
222 		trace1(TR_doassign, 1);
223 		return (-1);
224 	}
225 
226 	/* if we found a space, look for the '=', otherwise it's an error */
227 	if (isspace(*p)) {
228 		*p++ = '\0';
229 		while (*p && isspace(*p))
230 			p++;
231 		if (*p == '\0') {
232 			trace1(TR_doassign, 1);
233 			return (-1);
234 		}
235 		if (*p == '=')
236 			p++;
237 		else {
238 			trace1(TR_doassign, 1);
239 			return (-1);
240 		}
241 	} else {
242 		/* skip over '=' */
243 		*p = '\0';
244 		p++;
245 	}
246 
247 	/* skip over any whitespace */
248 	p = eatwhite(p);
249 	if (*p == '\'' || *p == '"') {
250 		/* handle quoted values */
251 		delim = *p++;
252 		tp = val;
253 		for (;;) {
254 			if (*p == '\0') {
255 				trace1(TR_doassign, 1);
256 				return (-1);
257 			} else if (*p == delim) {
258 				if (*(p - 1) != '\\')
259 					break;
260 				else
261 					*(tp - 1) = *p++;
262 			} else
263 				*tp++ = *p++;
264 		}
265 		*tp = '\0';
266 		/* these assignments make the comment below true (values of tp and p */
267 		tp = ++p;
268 		p = val;
269 	} else {
270 		tp = p;
271 		/* look for end of token */
272 		while (*tp && !isspace(*tp))
273 			tp++;
274 	}
275 
276 /*
277  * at this point, p points to the value, and tp points to the
278  * end of the token.  check to make sure there is no garbage on
279  * the end of the line
280  */
281 
282 	if (*tp) {
283 		trace1(TR_doassign, 1);
284 		return (-1);
285 	}
286 	sprintf(scratch, "%s=%s", var, p);
287 	/* note: need to malloc fresh space so putenv works */
288 	tp = malloc(strlen(scratch) + 1);
289 	if (tp == NULL) {
290 		trace1(TR_doassign, 1);
291 		return (-1);
292 	}
293 	strcpy(tp, scratch);
294 	if (putenv(tp)) {
295 		trace1(TR_doassign, 1);
296 		return (-1);
297 	} else {
298 		trace1(TR_doassign, 1);
299 		return (0);
300 	}
301 }
302 
303 
304 /*
305  * dopush - handle a `push' command
306  *
307  *	args:	fd - file descriptor to push on
308  *		p - list of modules to push
309  */
310 
311 
312 static int
313 dopush(int fd, char *p)
314 {
315 	char *tp;	/* scratch pointer */
316 	int i;		/* scratch variable */
317 	int npush;	/* count # of modules pushed */
318 
319 	trace2(TR_dopush, 0, fd);
320 	if (*p == '\0') {
321 		trace1(TR_dopush, 1);
322 		return (-1);
323 	}
324 	npush = 0;
325 	for (;;) {
326 		if (*p == '\0') {	/* found end of line */
327 			trace1(TR_dopush, 1);
328 			return (0);
329 		}
330 		p = eatwhite(p);
331 		if (*p == '\0') {
332 			trace1(TR_dopush, 1);
333 			return (-1);
334 		}
335 		tp = p;
336 		while (*tp && !isspace(*tp) && (*tp != ','))
337 			tp++;
338 		if (*tp)
339 			*tp++ = '\0';
340 		if (ioctl(fd, I_PUSH, p) < 0) {
341 
342 /*
343  * try to pop all that we've done, if pop fails it doesn't matter because
344  * nothing can be done anyhow
345  */
346 
347 			for (i = 0; i < npush; ++i)
348 				ioctl(fd, I_POP, 0);
349 			trace1(TR_dopush, 1);
350 			return (-1);
351 		} else {
352 			/* count the number of modules we've pushed */
353 			npush++;
354 			p = tp;
355 		}
356 	}
357 }
358 
359 
360 /*
361  * dopop - handle a `pop' command
362  *
363  *	args:	fd - file descriptor to pop from
364  *		p - name of module to pop to or ALL (null means pop top only)
365  */
366 
367 
368 static int
369 dopop(int fd, char *p)
370 {
371 	char *modp;		/* module name from argument to pop */
372 	char buf[FMNAMESZ + 1];	/* scratch buffer */
373 
374 	trace2(TR_dopop, 0, fd);
375 	if (*p == '\0') {
376 		/* just a pop with no args */
377 		if (ioctl(fd, I_POP, 0) < 0) {
378 			trace1(TR_dopop, 1);
379 			return (-1);
380 		} else {
381 			trace1(TR_dopop, 1);
382 			return (0);
383 		}
384 	}
385 
386 	/* skip any whitespace in between */
387 	p = eatwhite(p);
388 	modp = p;
389 	/* find end of module name */
390 	while (*p && !isspace(*p))
391 		p++;
392 
393 	if (*p) {	/* if not end of line, extra junk on line */
394 		trace1(TR_dopop, 1);
395 		return (-1);
396 	}
397 	if (!strcmp(modp, "ALL")) {
398 		/* it's the magic name, pop them all */
399 		while (ioctl(fd, I_POP, 0) == 0)
400 			;
401 		/* After all popped, we'll get an EINVAL, which is expected */
402 		if (errno != EINVAL) {
403 			trace1(TR_dopop, 1);
404 			return (-1);
405 		} else {
406 			trace1(TR_dopop, 1);
407 			return (0);
408 		}
409 	} else {
410 		/* check to see if the named module is on the stream */
411 		if (ioctl(fd, I_FIND, modp) != 1) {
412 			trace1(TR_dopop, 1);
413 			return (-1);
414 		}
415 
416 		/* pop them until the right one is on top */
417 		for (;;) {
418 			if (ioctl(fd, I_LOOK, buf) < 0) {
419 				trace1(TR_dopop, 1);
420 				return (-1);
421 			}
422 			if (!strcmp(modp, buf)) {
423 				trace1(TR_dopop, 1);
424 				/* we're done */
425 				return (0);
426 			}
427 			if (ioctl(fd, I_POP, 0) < 0) {
428 				trace1(TR_dopop, 1);
429 				return (-1);
430 			}
431 		}
432 	}
433 }
434 
435 
436 /*
437  * dorun - handle a `run' command
438  *
439  *	args:	p - command line to run
440  *		waitflag - flag indicating whether a wait should be done
441  */
442 
443 
444 static int
445 dorun(char *p, int waitflg)
446 {
447 	char *tp;		/* scratch pointer */
448 	char *ep;		/* scratch pointer (end of token) */
449 	int nfiles;		/* # of possibly open files */
450 	int i;			/* scratch variable */
451 	char savech;		/* hold area */
452 	int status;		/* return status from wait */
453 	pid_t pid;		/* pid of child proc */
454 	pid_t rpid;		/* returned pid from wait */
455 	void (*func)();		/* return from signal */
456 
457 	trace2(TR_dorun, 0, waitflg);
458 	if (*p == '\0') {
459 		trace1(TR_dorun, 1);
460 		return (-1);
461 	}
462 
463 /*
464  * get first token
465  */
466 
467 	for (tp = p; *tp && !isspace(*tp); ++tp)
468 		;
469 	savech = '\0';
470 	if (*tp) {
471 		savech = *tp;
472 		*tp = '\0';
473 	}
474 
475 /*
476  * look for built-in's
477  */
478 
479 	if (!strcmp(p, "cd")) {
480 		*tp = savech;
481 		tp = eatwhite(tp);
482 		if (*tp == '\0')
483 			/* if nothing there, try to cd to $HOME */
484 			tp = getenv("HOME");
485 		if (chdir(tp) < 0) {
486 			trace1(TR_dorun, 1);
487 			return (-1);
488 		}
489 	} else if (!strcmp(p, "ulimit")) {
490 		*tp = savech;
491 		tp = eatwhite(tp);
492 		/* must have an argument */
493 		if (*tp == '\0') {
494 			trace1(TR_dorun, 1);
495 			return (-1);
496 		}
497 		/* make sure nothing appears on line after arg */
498 		for (ep = tp; *ep && !isspace(*ep); ++ep)
499 			;
500 		ep = eatwhite(ep);
501 		if (*ep) {
502 			trace1(TR_dorun, 1);
503 			return (-1);
504 		}
505 		if (!isdigit(*tp)) {
506 			trace1(TR_dorun, 1);
507 			return (-1);
508 		}
509 
510 		if (ulimit(2, atoi(tp)) < 0) {
511 			trace1(TR_dorun, 1);
512 			return (-1);
513 		}
514 	} else if (!strcmp(p, "umask")) {
515 		*tp = savech;
516 		tp = eatwhite(tp);
517 		/* must have an argument */
518 		if (*tp == '\0') {
519 			trace1(TR_dorun, 1);
520 			return (-1);
521 		}
522 		/* make sure nothing appears on line after arg */
523 		for (ep = tp; *ep && !isspace(*ep); ++ep)
524 			;
525 		ep = eatwhite(ep);
526 		if (*ep) {
527 			trace1(TR_dorun, 1);
528 			return (-1);
529 		}
530 		if (!isdigit(*tp)) {
531 			trace1(TR_dorun, 1);
532 			return (-1);
533 		}
534 		(void) umask(strtol(tp, NULL, 8));
535 	} else {
536 		/* not a built-in */
537 		*tp = savech;
538 		func = signal(SIGCLD, SIG_DFL);
539 		if ((pid = fork()) < 0) {
540 			signal(SIGCLD, func);
541 			trace1(TR_dorun, 1);
542 			return (-1);
543 		} else if (pid) {
544 			if (waitflg == WAIT) {
545 				status = 0;
546 				rpid = -1;
547 				while (rpid != pid)
548 					rpid = wait(&status);
549 				if (status) {
550 					/* child failed */
551 					signal(SIGCLD, func);
552 					trace1(TR_dorun, 1);
553 					return (-1);
554 				}
555 			}
556 			signal(SIGCLD, func);
557 		} else {
558 			/* set IFS for security */
559 			(void) putenv("IFS=\" \"");
560 			/*
561 			 * need to close all files to prevent unauthorized
562 			 * access in the children.  Setup stdin, stdout,
563 			 * and stderr to /dev/null.
564 			 */
565 			nfiles = ulimit(4, 0);
566 			closefrom(0);
567 			/* stdin */
568 			if (open("/dev/null", O_RDWR) != 0) {
569 				trace1(TR_dorun, 1);
570 				return (-1);
571 			}
572 			/* stdout */
573 			if (dup(0) != 1) {
574 				trace1(TR_dorun, 1);
575 				return (-1);
576 			}
577 			/* stderr */
578 			if (dup(0) != 2) {
579 				trace1(TR_dorun, 1);
580 				return (-1);
581 			}
582 			execle("/usr/bin/sh", "sh", "-c", p, 0, _environ);
583 			/* if we get here, there is a problem - remember that
584 			   this is the child */
585 			trace1(TR_dorun, 1);
586 			exit(1);
587 		}
588 	}
589 	trace1(TR_dorun, 1);
590 	return (0);
591 }
592 
593 
594 /*
595  * eatwhite - swallow any leading whitespace, return pointer to first
596  *	      non-white space character or to terminating null character
597  *	      if nothing else is there
598  *
599  *	args:	p - string to parse
600  */
601 
602 static char *
603 eatwhite(char *p)
604 {
605 	trace1(TR_eatwhite, 0);
606 	while (*p && isspace(*p))
607 		p++;
608 	trace1(TR_eatwhite, 1);
609 	return (p);
610 }
611