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