Home | History | Annotate | Download | only in sh
      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 /*
     23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 /*
     32  * UNIX shell
     33  */
     34 
     35 #include	"defs.h"
     36 #include	"sym.h"
     37 #include	"timeout.h"
     38 #include	<stdio.h>
     39 #include	<sys/types.h>
     40 #include	<sys/stat.h>
     41 #include	<sys/wait.h>
     42 #include	"dup.h"
     43 #include	"sh_policy.h"
     44 
     45 #ifdef RES
     46 #include	<sgtty.h>
     47 #endif
     48 
     49 pid_t mypid, mypgid, mysid;
     50 
     51 static BOOL	beenhere = FALSE;
     52 unsigned char	tmpout[TMPOUTSZ];
     53 struct fileblk	stdfile;
     54 struct fileblk *standin = &stdfile;
     55 int mailchk = 0;
     56 
     57 static unsigned char	*mailp;
     58 static long	*mod_time = 0;
     59 static BOOL login_shell = FALSE;
     60 
     61 #if vax
     62 char **execargs = (char **)(0x7ffffffc);
     63 #endif
     64 
     65 #if pdp11
     66 char **execargs = (char **)(-2);
     67 #endif
     68 
     69 
     70 static void	exfile();
     71 extern unsigned char 	*simple();
     72 static void Ldup(int, int);
     73 void settmp(void);
     74 void chkmail(void);
     75 void setmail(unsigned char *);
     76 
     77 int
     78 main(int c, char *v[], char *e[])
     79 {
     80 	int		rflag = ttyflg;
     81 	int		rsflag = 1;	/* local restricted flag */
     82 	unsigned char	*flagc = flagadr;
     83 	struct namnod	*n;
     84 
     85 	mypid = getpid();
     86 	mypgid = getpgid(mypid);
     87 	mysid = getsid(mypid);
     88 
     89 	/*
     90 	 * Do locale processing only if /usr is mounted.
     91 	 */
     92 	localedir_exists = (access(localedir, F_OK) == 0);
     93 
     94 	/*
     95 	 * initialize storage allocation
     96 	 */
     97 
     98 	if (stakbot == 0) {
     99 	addblok((unsigned)0);
    100 	}
    101 
    102 	/*
    103 	 * If the first character of the last path element of v[0] is "-"
    104 	 * (ex. -sh, or /bin/-sh), this is a login shell
    105 	 */
    106 	if (*simple(v[0]) == '-') {
    107 		signal(SIGXCPU, SIG_DFL);
    108 		signal(SIGXFSZ, SIG_DFL);
    109 
    110 		/*
    111 		 * As the previous comment states, this is a login shell.
    112 		 * Therefore, we set the login_shell flag to explicitly
    113 		 * indicate this condition.
    114 		 */
    115 		login_shell = TRUE;
    116 	}
    117 
    118 	stdsigs();
    119 
    120 	/*
    121 	 * set names from userenv
    122 	 */
    123 
    124 	setup_env();
    125 
    126 	/*
    127 	 * LC_MESSAGES is set here so that early error messages will
    128 	 * come out in the right style.
    129 	 * Note that LC_CTYPE is done later on and is *not*
    130 	 * taken from the previous environ
    131 	 */
    132 
    133 	/*
    134 	 * Do locale processing only if /usr is mounted.
    135 	 */
    136 	if (localedir_exists)
    137 		(void) setlocale(LC_ALL, "");
    138 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
    139 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
    140 #endif
    141 	(void) textdomain(TEXT_DOMAIN);
    142 
    143 	/*
    144 	 * This is a profile shell if the simple name of argv[0] is
    145 	 * pfsh or -pfsh
    146 	 */
    147 	if (c > 0 && (eq("pfsh", simple(*v)) || eq("-pfsh", simple(*v)))) {
    148 		flags |= pfshflg;
    149 		secpolicy_init();
    150 	}
    151 
    152 	/*
    153 	 * 'rsflag' is zero if SHELL variable is
    154 	 *  set in environment and
    155 	 *  the simple file part of the value.
    156 	 *  is rsh
    157 	 */
    158 	if (n = findnam("SHELL")) {
    159 		if (eq("rsh", simple(n->namval)))
    160 			rsflag = 0;
    161 	}
    162 
    163 	/*
    164 	 * a shell is also restricted if the simple name of argv(0) is
    165 	 * rsh or -rsh in its simple name
    166 	 */
    167 
    168 #ifndef RES
    169 
    170 	if (c > 0 && (eq("rsh", simple(*v)) || eq("-rsh", simple(*v))))
    171 		rflag = 0;
    172 
    173 #endif
    174 
    175 	if (eq("jsh", simple(*v)) || eq("-jsh", simple(*v)))
    176 		flags |= monitorflg;
    177 
    178 	hcreate();
    179 	set_dotpath();
    180 
    181 
    182 	/*
    183 	 * look for options
    184 	 * dolc is $#
    185 	 */
    186 	dolc = options(c, v);
    187 
    188 	if (dolc < 2) {
    189 		flags |= stdflg;
    190 		{
    191 
    192 			while (*flagc)
    193 				flagc++;
    194 			*flagc++ = STDFLG;
    195 			*flagc = 0;
    196 		}
    197 	}
    198 	if ((flags & stdflg) == 0)
    199 		dolc--;
    200 
    201 	if ((flags & privflg) == 0) {
    202 		uid_t euid;
    203 		gid_t egid;
    204 		uid_t ruid;
    205 		gid_t rgid;
    206 
    207 		/*
    208 		 * Determine all of the user's id #'s for this process and
    209 		 * then decide if this shell is being entered as a result
    210 		 * of a fork/exec.
    211 		 * If the effective uid/gid do NOT match and the euid/egid
    212 		 * is < 100 and the egid is NOT 1, reset the uid and gid to
    213 		 * the user originally calling this process.
    214 		 */
    215 		euid = geteuid();
    216 		ruid = getuid();
    217 		egid = getegid();
    218 		rgid = getgid();
    219 		if ((euid != ruid) && (euid < 100))
    220 			setuid(ruid);   /* reset the uid to the orig user */
    221 		if ((egid != rgid) && ((egid < 100) && (egid != 1)))
    222 			setgid(rgid);   /* reset the gid to the orig user */
    223 	}
    224 
    225 	dolv = (unsigned char **)v + c - dolc;
    226 	dolc--;
    227 
    228 	/*
    229 	 * return here for shell file execution
    230 	 * but not for parenthesis subshells
    231 	 */
    232 	if (setjmp(subshell)) {
    233 		freejobs();
    234 		flags |= subsh;
    235 	}
    236 
    237 	/*
    238 	 * number of positional parameters
    239 	 */
    240 	replace(&cmdadr, dolv[0]);	/* cmdadr is $0 */
    241 
    242 	/*
    243 	 * set pidname '$$'
    244 	 */
    245 	assnum(&pidadr, (long)mypid);
    246 
    247 	/*
    248 	 * set up temp file names
    249 	 */
    250 	settmp();
    251 
    252 	/*
    253 	 * default internal field separators
    254 	 * Do not allow importing of IFS from parent shell.
    255 	 * setup_env() may have set anything from parent shell to IFS.
    256 	 * Always set the default ifs to IFS.
    257 	 */
    258 	assign(&ifsnod, (unsigned char *)sptbnl);
    259 
    260 	dfault(&mchknod, MAILCHECK);
    261 	mailchk = stoi(mchknod.namval);
    262 
    263 	/* initialize OPTIND for getopt */
    264 
    265 	n = lookup("OPTIND");
    266 	assign(n, (unsigned char *)"1");
    267 	/*
    268 	 * make sure that option parsing starts
    269 	 * at first character
    270 	 */
    271 	_sp = 1;
    272 
    273 	if ((beenhere++) == FALSE)	/* ? profile */
    274 	{
    275 		if ((login_shell == TRUE) && (flags & privflg) == 0) {
    276 
    277 			/* system profile */
    278 
    279 #ifndef RES
    280 
    281 			if ((input = pathopen(nullstr, sysprofile)) >= 0)
    282 				exfile(rflag);		/* file exists */
    283 
    284 #endif
    285 			/* user profile */
    286 
    287 			if ((input = pathopen(homenod.namval, profile)) >= 0) {
    288 				exfile(rflag);
    289 				flags &= ~ttyflg;
    290 			}
    291 		}
    292 		if (rsflag == 0 || rflag == 0) {
    293 			if ((flags & rshflg) == 0) {
    294 				while (*flagc)
    295 					flagc++;
    296 				*flagc++ = 'r';
    297 				*flagc = '\0';
    298 			}
    299 			flags |= rshflg;
    300 		}
    301 
    302 		/*
    303 		 * open input file if specified
    304 		 */
    305 		if (comdiv) {
    306 			estabf(comdiv);
    307 			input = -1;
    308 		}
    309 		else
    310 		{
    311 			if (flags & stdflg) {
    312 				input = 0;
    313 			} else {
    314 			/*
    315 			 * If the command file specified by 'cmdadr'
    316 			 * doesn't exist, chkopen() will fail calling
    317 			 * exitsh(). If this is a login shell and
    318 			 * the $HOME/.profile file does not exist, the
    319 			 * above statement "flags &= ~ttyflg" does not
    320 			 * get executed and this makes exitsh() call
    321 			 * longjmp() instead of exiting. longjmp() will
    322 			 * return to the location specified by the last
    323 			 * active jmpbuffer, which is the one set up in
    324 			 * the function exfile() called after the system
    325 			 * profile file is executed (see lines above).
    326 			 * This would cause an infinite loop, because
    327 			 * chkopen() will continue to fail and exitsh()
    328 			 * to call longjmp(). To make exitsh() exit instead
    329 			 * of calling longjmp(), we then set the flag forcexit
    330 			 * at this stage.
    331 			 */
    332 
    333 				flags |= forcexit;
    334 				input = chkopen(cmdadr, 0);
    335 				flags &= ~forcexit;
    336 			}
    337 
    338 #ifdef ACCT
    339 			if (input != 0)
    340 				preacct(cmdadr);
    341 #endif
    342 			comdiv--;
    343 		}
    344 	}
    345 #ifdef pdp11
    346 	else
    347 		*execargs = (char *)dolv;	/* for `ps' cmd */
    348 #endif
    349 
    350 
    351 	exfile(0);
    352 	done(0);
    353 }
    354 
    355 static void
    356 exfile(int prof)
    357 {
    358 	time_t	mailtime = 0;	/* Must not be a register variable */
    359 	time_t 	curtime = 0;
    360 
    361 	/*
    362 	 * move input
    363 	 */
    364 	if (input > 0) {
    365 		Ldup(input, INIO);
    366 		input = INIO;
    367 	}
    368 
    369 
    370 	setmode(prof);
    371 
    372 	if (setjmp(errshell) && prof) {
    373 		close(input);
    374 		(void) endjobs(0);
    375 		return;
    376 	}
    377 	/*
    378 	 * error return here
    379 	 */
    380 
    381 	loopcnt = peekc = peekn = 0;
    382 	fndef = 0;
    383 	nohash = 0;
    384 	iopend = 0;
    385 
    386 	if (input >= 0)
    387 		initf(input);
    388 	/*
    389 	 * command loop
    390 	 */
    391 	for (;;) {
    392 		tdystak(0);
    393 		stakchk();	/* may reduce sbrk */
    394 		exitset();
    395 
    396 		if ((flags & prompt) && standin->fstak == 0 && !eof) {
    397 
    398 			if (mailp) {
    399 				time(&curtime);
    400 
    401 				if ((curtime - mailtime) >= mailchk) {
    402 					chkmail();
    403 					mailtime = curtime;
    404 				}
    405 			}
    406 
    407 			/* necessary to print jobs in a timely manner */
    408 			if (trapnote & TRAPSET)
    409 				chktrap();
    410 
    411 			prs(ps1nod.namval);
    412 
    413 #ifdef TIME_OUT
    414 			alarm(TIMEOUT);
    415 #endif
    416 
    417 		}
    418 
    419 		trapnote = 0;
    420 		peekc = readwc();
    421 		if (eof) {
    422 			if (endjobs(JOB_STOPPED))
    423 				return;
    424 			eof = 0;
    425 		}
    426 
    427 #ifdef TIME_OUT
    428 		alarm(0);
    429 #endif
    430 
    431 		{
    432 			struct trenod *t;
    433 			t = cmd(NL, MTFLG);
    434 			if (t == NULL && flags & ttyflg)
    435 				freejobs();
    436 			else
    437 				execute(t, 0, eflag);
    438 		}
    439 
    440 		eof |= (flags & oneflg);
    441 
    442 	}
    443 }
    444 
    445 void
    446 chkpr(void)
    447 {
    448 	if ((flags & prompt) && standin->fstak == 0)
    449 		prs(ps2nod.namval);
    450 }
    451 
    452 void
    453 settmp(void)
    454 {
    455 	int len;
    456 	serial = 0;
    457 	if ((len = snprintf((char *)tmpout, TMPOUTSZ, "/tmp/sh%u", mypid)) >=
    458 	    TMPOUTSZ) {
    459 		/*
    460 		 * TMPOUTSZ should be big enough, but if it isn't,
    461 		 * we'll at least try to create tmp files with
    462 		 * a truncated tmpfile name at tmpout.
    463 		 */
    464 		tmpout_offset = TMPOUTSZ - 1;
    465 	} else {
    466 		tmpout_offset = len;
    467 	}
    468 }
    469 
    470 static void
    471 Ldup(int fa, int fb)
    472 {
    473 #ifdef RES
    474 
    475 	dup(fa | DUPFLG, fb);
    476 	close(fa);
    477 	ioctl(fb, FIOCLEX, 0);
    478 
    479 #else
    480 
    481 	if (fa >= 0) {
    482 		if (fa != fb) {
    483 			close(fb);
    484 			fcntl(fa, 0, fb); /* normal dup */
    485 			close(fa);
    486 		}
    487 		fcntl(fb, 2, 1);	/* autoclose for fb */
    488 	}
    489 
    490 #endif
    491 }
    492 
    493 void
    494 chkmail(void)
    495 {
    496 	unsigned char 	*s = mailp;
    497 	unsigned char	*save;
    498 
    499 	long	*ptr = mod_time;
    500 	unsigned char	*start;
    501 	BOOL	flg;
    502 	struct stat	statb;
    503 
    504 	while (*s) {
    505 		start = s;
    506 		save = 0;
    507 		flg = 0;
    508 
    509 		while (*s) {
    510 			if (*s != COLON) {
    511 				if (*s == '%' && save == 0)
    512 					save = s;
    513 
    514 				s++;
    515 			} else {
    516 				flg = 1;
    517 				*s = 0;
    518 			}
    519 		}
    520 
    521 		if (save)
    522 			*save = 0;
    523 
    524 		if (*start && stat((const char *)start, &statb) >= 0) {
    525 			if (statb.st_size && *ptr &&
    526 			    statb.st_mtime != *ptr) {
    527 				if (save) {
    528 					prs(save+1);
    529 					newline();
    530 				}
    531 				else
    532 					prs(_gettext(mailmsg));
    533 			}
    534 			*ptr = statb.st_mtime;
    535 		} else if (*ptr == 0)
    536 			*ptr = 1;
    537 
    538 		if (save)
    539 			*save = '%';
    540 
    541 		if (flg)
    542 			*s++ = COLON;
    543 
    544 		ptr++;
    545 	}
    546 }
    547 
    548 void
    549 setmail(unsigned char *mailpath)
    550 {
    551 	unsigned char	*s = mailpath;
    552 	int 		cnt = 1;
    553 
    554 	long	*ptr;
    555 
    556 	free(mod_time);
    557 	if (mailp = mailpath) {
    558 		while (*s) {
    559 			if (*s == COLON)
    560 				cnt += 1;
    561 
    562 			s++;
    563 		}
    564 
    565 		ptr = mod_time = (long *)alloc(sizeof (long) * cnt);
    566 
    567 		while (cnt) {
    568 			*ptr = 0;
    569 			ptr++;
    570 			cnt--;
    571 		}
    572 	}
    573 }
    574 
    575 void
    576 setmode(int prof)
    577 {
    578 	/*
    579 	 * decide whether interactive
    580 	 */
    581 
    582 	if ((flags & intflg) ||
    583 	    ((flags&oneflg) == 0 &&
    584 	    isatty(output) &&
    585 	    isatty(input)))
    586 
    587 	{
    588 		dfault(&ps1nod, (geteuid() ? stdprompt : supprompt));
    589 		dfault(&ps2nod, readmsg);
    590 		flags |= ttyflg | prompt;
    591 		if (mailpnod.namflg != N_DEFAULT)
    592 			setmail(mailpnod.namval);
    593 		else
    594 			setmail(mailnod.namval);
    595 		startjobs();
    596 	}
    597 	else
    598 	{
    599 		flags |= prof;
    600 		flags &= ~prompt;
    601 	}
    602 }
    603 
    604 /*
    605  * A generic call back routine to output error messages from the
    606  * policy backing functions called by pfsh.
    607  *
    608  * msg must contain '\n' if a new line is to be printed.
    609  */
    610 void
    611 secpolicy_print(int level, const char *msg)
    612 {
    613 	switch (level) {
    614 	case SECPOLICY_WARN:
    615 	default:
    616 		prs(_gettext(msg));
    617 		return;
    618 	case SECPOLICY_ERROR:
    619 		error(msg);
    620 		break;
    621 	}
    622 }
    623