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