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 2005 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 <stdio.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/wait.h> 45 #include "dup.h" 46 #include "sh_policy.h" 47 48 #ifdef RES 49 #include <sgtty.h> 50 #endif 51 52 pid_t mypid, mypgid, mysid; 53 54 static BOOL beenhere = FALSE; 55 unsigned char tmpout[TMPOUTSZ]; 56 struct fileblk stdfile; 57 struct fileblk *standin = &stdfile; 58 int mailchk = 0; 59 60 static unsigned char *mailp; 61 static long *mod_time = 0; 62 static BOOL login_shell = FALSE; 63 64 #if vax 65 char **execargs = (char **)(0x7ffffffc); 66 #endif 67 68 #if pdp11 69 char **execargs = (char **)(-2); 70 #endif 71 72 73 static void exfile(); 74 extern unsigned char *simple(); 75 static void Ldup(int, int); 76 void settmp(void); 77 void chkmail(void); 78 void setmail(unsigned char *); 79 80 int 81 main(int c, char *v[], char *e[]) 82 { 83 int rflag = ttyflg; 84 int rsflag = 1; /* local restricted flag */ 85 unsigned char *flagc = flagadr; 86 struct namnod *n; 87 88 mypid = getpid(); 89 mypgid = getpgid(mypid); 90 mysid = getsid(mypid); 91 92 /* 93 * Do locale processing only if /usr is mounted. 94 */ 95 localedir_exists = (access(localedir, F_OK) == 0); 96 97 /* 98 * initialize storage allocation 99 */ 100 101 if (stakbot == 0) { 102 addblok((unsigned)0); 103 } 104 105 /* 106 * If the first character of the last path element of v[0] is "-" 107 * (ex. -sh, or /bin/-sh), this is a login shell 108 */ 109 if (*simple(v[0]) == '-') { 110 signal(SIGXCPU, SIG_DFL); 111 signal(SIGXFSZ, SIG_DFL); 112 113 /* 114 * As the previous comment states, this is a login shell. 115 * Therefore, we set the login_shell flag to explicitly 116 * indicate this condition. 117 */ 118 login_shell = TRUE; 119 } 120 121 stdsigs(); 122 123 /* 124 * set names from userenv 125 */ 126 127 setup_env(); 128 129 /* 130 * LC_MESSAGES is set here so that early error messages will 131 * come out in the right style. 132 * Note that LC_CTYPE is done later on and is *not* 133 * taken from the previous environ 134 */ 135 136 /* 137 * Do locale processing only if /usr is mounted. 138 */ 139 if (localedir_exists) 140 (void) setlocale(LC_ALL, ""); 141 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 142 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 143 #endif 144 (void) textdomain(TEXT_DOMAIN); 145 146 /* 147 * This is a profile shell if the simple name of argv[0] is 148 * pfsh or -pfsh 149 */ 150 if (c > 0 && (eq("pfsh", simple(*v)) || eq("-pfsh", simple(*v)))) { 151 flags |= pfshflg; 152 secpolicy_init(); 153 } 154 155 /* 156 * 'rsflag' is zero if SHELL variable is 157 * set in environment and 158 * the simple file part of the value. 159 * is rsh 160 */ 161 if (n = findnam("SHELL")) { 162 if (eq("rsh", simple(n->namval))) 163 rsflag = 0; 164 } 165 166 /* 167 * a shell is also restricted if the simple name of argv(0) is 168 * rsh or -rsh in its simple name 169 */ 170 171 #ifndef RES 172 173 if (c > 0 && (eq("rsh", simple(*v)) || eq("-rsh", simple(*v)))) 174 rflag = 0; 175 176 #endif 177 178 if (eq("jsh", simple(*v)) || eq("-jsh", simple(*v))) 179 flags |= monitorflg; 180 181 hcreate(); 182 set_dotpath(); 183 184 185 /* 186 * look for options 187 * dolc is $# 188 */ 189 dolc = options(c, v); 190 191 if (dolc < 2) { 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 uid_t euid; 206 gid_t egid; 207 uid_t ruid; 208 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, (unsigned char *)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, (unsigned char *)"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 exfile(rflag); 295 flags &= ~ttyflg; 296 } 297 } 298 if (rsflag == 0 || rflag == 0) { 299 if ((flags & rshflg) == 0) { 300 while (*flagc) 301 flagc++; 302 *flagc++ = 'r'; 303 *flagc = '\0'; 304 } 305 flags |= rshflg; 306 } 307 308 /* 309 * open input file if specified 310 */ 311 if (comdiv) { 312 estabf(comdiv); 313 input = -1; 314 } 315 else 316 { 317 if (flags & stdflg) { 318 input = 0; 319 } else { 320 /* 321 * If the command file specified by 'cmdadr' 322 * doesn't exist, chkopen() will fail calling 323 * exitsh(). If this is a login shell and 324 * the $HOME/.profile file does not exist, the 325 * above statement "flags &= ~ttyflg" does not 326 * get executed and this makes exitsh() call 327 * longjmp() instead of exiting. longjmp() will 328 * return to the location specified by the last 329 * active jmpbuffer, which is the one set up in 330 * the function exfile() called after the system 331 * profile file is executed (see lines above). 332 * This would cause an infinite loop, because 333 * chkopen() will continue to fail and exitsh() 334 * to call longjmp(). To make exitsh() exit instead 335 * of calling longjmp(), we then set the flag forcexit 336 * at this stage. 337 */ 338 339 flags |= forcexit; 340 input = chkopen(cmdadr, 0); 341 flags &= ~forcexit; 342 } 343 344 #ifdef ACCT 345 if (input != 0) 346 preacct(cmdadr); 347 #endif 348 comdiv--; 349 } 350 } 351 #ifdef pdp11 352 else 353 *execargs = (char *)dolv; /* for `ps' cmd */ 354 #endif 355 356 357 exfile(0); 358 done(0); 359 } 360 361 static void 362 exfile(int prof) 363 { 364 time_t mailtime = 0; /* Must not be a register variable */ 365 time_t curtime = 0; 366 367 /* 368 * move input 369 */ 370 if (input > 0) { 371 Ldup(input, INIO); 372 input = INIO; 373 } 374 375 376 setmode(prof); 377 378 if (setjmp(errshell) && prof) { 379 close(input); 380 (void) endjobs(0); 381 return; 382 } 383 /* 384 * error return here 385 */ 386 387 loopcnt = peekc = peekn = 0; 388 fndef = 0; 389 nohash = 0; 390 iopend = 0; 391 392 if (input >= 0) 393 initf(input); 394 /* 395 * command loop 396 */ 397 for (;;) { 398 tdystak(0); 399 stakchk(); /* may reduce sbrk */ 400 exitset(); 401 402 if ((flags & prompt) && standin->fstak == 0 && !eof) { 403 404 if (mailp) { 405 time(&curtime); 406 407 if ((curtime - mailtime) >= mailchk) { 408 chkmail(); 409 mailtime = curtime; 410 } 411 } 412 413 /* necessary to print jobs in a timely manner */ 414 if (trapnote & TRAPSET) 415 chktrap(); 416 417 prs(ps1nod.namval); 418 419 #ifdef TIME_OUT 420 alarm(TIMEOUT); 421 #endif 422 423 } 424 425 trapnote = 0; 426 peekc = readwc(); 427 if (eof) { 428 if (endjobs(JOB_STOPPED)) 429 return; 430 eof = 0; 431 } 432 433 #ifdef TIME_OUT 434 alarm(0); 435 #endif 436 437 { 438 struct trenod *t; 439 t = cmd(NL, MTFLG); 440 if (t == NULL && flags & ttyflg) 441 freejobs(); 442 else 443 execute(t, 0, eflag); 444 } 445 446 eof |= (flags & oneflg); 447 448 } 449 } 450 451 void 452 chkpr(void) 453 { 454 if ((flags & prompt) && standin->fstak == 0) 455 prs(ps2nod.namval); 456 } 457 458 void 459 settmp(void) 460 { 461 int len; 462 serial = 0; 463 if ((len = snprintf((char *)tmpout, TMPOUTSZ, "/tmp/sh%u", mypid)) >= 464 TMPOUTSZ) { 465 /* 466 * TMPOUTSZ should be big enough, but if it isn't, 467 * we'll at least try to create tmp files with 468 * a truncated tmpfile name at tmpout. 469 */ 470 tmpout_offset = TMPOUTSZ - 1; 471 } else { 472 tmpout_offset = len; 473 } 474 } 475 476 static void 477 Ldup(int fa, int fb) 478 { 479 #ifdef RES 480 481 dup(fa | DUPFLG, fb); 482 close(fa); 483 ioctl(fb, FIOCLEX, 0); 484 485 #else 486 487 if (fa >= 0) { 488 if (fa != fb) { 489 close(fb); 490 fcntl(fa, 0, fb); /* normal dup */ 491 close(fa); 492 } 493 fcntl(fb, 2, 1); /* autoclose for fb */ 494 } 495 496 #endif 497 } 498 499 void 500 chkmail(void) 501 { 502 unsigned char *s = mailp; 503 unsigned char *save; 504 505 long *ptr = mod_time; 506 unsigned char *start; 507 BOOL flg; 508 struct stat statb; 509 510 while (*s) { 511 start = s; 512 save = 0; 513 flg = 0; 514 515 while (*s) { 516 if (*s != COLON) { 517 if (*s == '%' && save == 0) 518 save = s; 519 520 s++; 521 } else { 522 flg = 1; 523 *s = 0; 524 } 525 } 526 527 if (save) 528 *save = 0; 529 530 if (*start && stat((const char *)start, &statb) >= 0) { 531 if (statb.st_size && *ptr && 532 statb.st_mtime != *ptr) { 533 if (save) { 534 prs(save+1); 535 newline(); 536 } 537 else 538 prs(mailmsg); 539 } 540 *ptr = statb.st_mtime; 541 } else if (*ptr == 0) 542 *ptr = 1; 543 544 if (save) 545 *save = '%'; 546 547 if (flg) 548 *s++ = COLON; 549 550 ptr++; 551 } 552 } 553 554 void 555 setmail(unsigned char *mailpath) 556 { 557 unsigned char *s = mailpath; 558 int cnt = 1; 559 560 long *ptr; 561 562 free(mod_time); 563 if (mailp = mailpath) { 564 while (*s) { 565 if (*s == COLON) 566 cnt += 1; 567 568 s++; 569 } 570 571 ptr = mod_time = (long *)alloc(sizeof (long) * cnt); 572 573 while (cnt) { 574 *ptr = 0; 575 ptr++; 576 cnt--; 577 } 578 } 579 } 580 581 void 582 setwidth() 583 { 584 unsigned char *name = lookup("LC_CTYPE")->namval; 585 if (!name || !*name) 586 name = lookup("LANG")->namval; 587 /* 588 * Do locale processing only if /usr is mounted. 589 */ 590 if (localedir_exists) { 591 if (!name || !*name) 592 (void) setlocale(LC_CTYPE, "C"); 593 else 594 (void) setlocale(LC_CTYPE, (const char *)name); 595 } 596 } 597 598 void 599 setmode(int prof) 600 { 601 /* 602 * decide whether interactive 603 */ 604 605 if ((flags & intflg) || 606 ((flags&oneflg) == 0 && 607 isatty(output) && 608 isatty(input))) 609 610 { 611 dfault(&ps1nod, (geteuid() ? stdprompt : supprompt)); 612 dfault(&ps2nod, readmsg); 613 flags |= ttyflg | prompt; 614 if (mailpnod.namflg != N_DEFAULT) 615 setmail(mailpnod.namval); 616 else 617 setmail(mailnod.namval); 618 startjobs(); 619 } 620 else 621 { 622 flags |= prof; 623 flags &= ~prompt; 624 } 625 } 626 627 /* 628 * A generic call back routine to output error messages from the 629 * policy backing functions called by pfsh. 630 * 631 * msg must contain '\n' if a new line is to be printed. 632 */ 633 void 634 secpolicy_print(int level, const char *msg) 635 { 636 switch (level) { 637 case SECPOLICY_WARN: 638 default: 639 prs(msg); /* prs() does gettext() */ 640 return; 641 case SECPOLICY_ERROR: 642 error(msg); 643 break; 644 } 645 } 646