opielogin.c revision 73569
1/* opielogin.c: The infamous /bin/login 2 3%%% portions-copyright-cmetz-96 4Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights 5Reserved. The Inner Net License Version 2 applies to these portions of 6the software. 7You should have received a copy of the license with this software. If 8you didn't get a copy, you may request one from <license@inner.net>. 9 10Portions of this software are Copyright 1995 by Randall Atkinson and Dan 11McDonald, All Rights Reserved. All Rights under this copyright are assigned 12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13License Agreement applies to this software. 14 15 History: 16 17 Modified by cmetz for OPIE 2.32. Partially handle environment 18 variables on the command line (a better implementation is 19 coming soon). Handle failure to issue a challenge more 20 gracefully. 21 Modified by cmetz for OPIE 2.31. Use _PATH_NOLOGIN. Move Solaris 22 drain bamage kluge after rflag check; it breaks rlogin. 23 Use TCSAFLUSH instead of TCSANOW (except where it flushes 24 data we need). Sleep before kluging for Solaris. 25 Modified by cmetz for OPIE 2.3. Process login environment files. 26 Made logindevperm/fbtab handling more generic. Kluge around 27 Solaris drain bamage differently (maybe better?). Maybe 28 allow cleartext logins even when opiechallenge() fails. 29 Changed the conditions on when time.h and sys/time.h are 30 included. Send debug info to syslog. Use opielogin() instead 31 of dealing with utmp/setlogin() here. 32 Modified by cmetz for OPIE 2.22. Call setlogin(). Decreased default 33 timeout to two minutes. Use opiereadpass() flags to get 34 around Solaris drain bamage. 35 Modified by cmetz for OPIE 2.21. Took the sizeof() the wrong thing. 36 Modified by cmetz for OPIE 2.2. Changed prompts to ask for OTP 37 response where appropriate. Simple though small speed-up. 38 Don't allow cleartext if echo on. Don't try to clear 39 non-blocking I/O. Use opiereadpass(). Don't mess with 40 termios (as much, at least) -- that's opiereadpass()'s 41 job. Change opiereadpass() calls to add echo arg. Fixed 42 CONTROL macro. Don't modify argv (at least, unless 43 we have a reason to). Allow user in if ruserok() says 44 so. Removed useless strings (I don't think that 45 removing the ucb copyright one is a problem -- please 46 let me know if I'm wrong). Use FUNCTION declaration et 47 al. Moved definition of TRUE here. Ifdef around more 48 headers. Make everything static. Removed support for 49 omitting domain name if same domain -- it generally 50 didn't work and it would be a big portability problem. 51 Use opiereadpass() in getloginname() and then post- 52 process. Added code to grab hpux time zone from 53 /etc/src.sh. Renamed MAIL_DIR to PATH_MAIL. Removed 54 dupe catchexit and extraneous closelog. openlog() as 55 soon as possible because SunOS syslog is broken. 56 Don't print an extra blank line before a new Response 57 prompt. 58 Modified at NRL for OPIE 2.2. Changed strip_crlf to stripcrlf. 59 Do opiebackspace() on entries. 60 Modified at NRL for OPIE 2.1. Since we don't seem to use the 61 result of opiechallenge() anymore, discard it. Changed 62 BSD4_3 to HAVE_GETTTYNAM. Other symbol changes for 63 autoconf. Removed obselete usage comment. Removed 64 des_crypt.h. File renamed to opielogin.c. Added bletch 65 for setpriority. Added slash between MAIL_DIR and name. 66 Modified at NRL for OPIE 2.02. Flush stdio after printing login 67 prompt. Fixed Solaris shadow password problem introduced 68 in OPIE 2.01 (the shadow password structure is spwd, not 69 spasswd). 70 Modified at NRL for OPIE 2.01. Changed password lookup handling 71 to use a static structure to avoid problems with drain- 72 bamaged shadow password packages. Make sure to close 73 syslog by function to avoid problems with drain bamaged 74 syslog implementations. Log a few interesting errors. 75 Modified at NRL for OPIE 2.0. 76 Modified at Bellcore for the Bellcore S/Key Version 1 software 77 distribution. 78 Originally from BSD. 79*/ 80/* 81 * Portions of this software are 82 * Copyright (c) 1980,1987 Regents of the University of California. 83 * All rights reserved. The Berkeley software License Agreement 84 * specifies the terms and conditions for redistribution. 85 */ 86 87#include "opie_cfg.h" /* OPIE: defines symbols for filenames & pathnames */ 88#if HAVE_SYS_PARAM_H 89#include <sys/param.h> 90#endif /* HAVE_SYS_PARAM_H */ 91#include <sys/stat.h> 92#include <sys/types.h> 93 94#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H 95#include <sys/resource.h> 96#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ 97 98#if TIME_WITH_SYS_TIME 99# include <sys/time.h> 100# include <time.h> 101#else /* TIME_WITH_SYS_TIME */ 102#if HAVE_SYS_TIME_H 103#include <sys/time.h> 104#else /* HAVE_SYS_TIME_H */ 105#include <time.h> 106#endif /* HAVE_SYS_TIME_H */ 107#endif /* TIME_WITH_SYS_TIME */ 108 109#if HAVE_SYS_FILE_H 110#include <sys/file.h> 111#endif /* HAVE_SYS_FILE_H */ 112#include <signal.h> 113#if HAVE_PWD_H 114#include <pwd.h> /* POSIX Password routines */ 115#endif /* HAVE_PWD_H */ 116#include <stdio.h> 117#include <errno.h> 118#if HAVE_UNISTD_H 119#include <unistd.h> /* Basic POSIX macros and functions */ 120#endif /* HAVE_UNISTD_H */ 121#include <termios.h> /* POSIX terminal I/O */ 122#if HAVE_STRING_H 123#include <string.h> /* ANSI C string functions */ 124#endif /* HAVE_STRING_H */ 125#include <fcntl.h> /* File I/O functions */ 126#include <syslog.h> 127#include <grp.h> 128#include <netdb.h> 129#include <netinet/in.h> /* contains types needed for next include file */ 130#include <arpa/inet.h> /* Inet addr<-->ascii functions */ 131#if HAVE_STDLIB_H 132#include <stdlib.h> 133#endif /* HAVE_STDLIB_H */ 134#if HAVE_SYS_SELECT_H 135#include <sys/select.h> 136#endif /* HAVE_SYS_SELECT_H */ 137 138#ifdef QUOTA 139#include <sys/quota.h> 140#endif 141 142#if HAVE_GETTTYNAM 143#include <sys/ioctl.h> /* non-portable routines used only a few places */ 144#include <ttyent.h> 145#endif /* HAVE_GETTTYNAM */ 146 147#include "opie.h" 148 149#define TTYGID(gid) tty_gid(gid) /* gid that owns all ttys */ 150 151#define NMAX 32 152#define HMAX 256 153 154#if HAVE_LASTLOG_H 155#include <lastlog.h> 156#endif /* HAVE_LASTLOG_H */ 157 158static int rflag = 0; 159static int usererr = -1; 160static int stopmotd; 161static char rusername[NMAX + 1]; 162static char name[NMAX + 1] = ""; 163static char minusnam[16] = "-"; 164static char *envinit[1]; /* now set by setenv calls */ 165static char term[64] = "\0"; /* important to initialise to a NULL string */ 166static char host[HMAX + 1] = "\0"; 167static struct passwd nouser; 168static struct passwd thisuser; 169 170#if HAVE_SHADOW_H 171#include <shadow.h> 172#endif /* HAVE_SHADOW_H */ 173 174static char *ttyprompt; 175 176#ifdef PERMSFILE 177extern char *home; 178#endif /* PERMSFILE */ 179 180static struct termios attr; 181 182extern int errno; 183 184static int ouroptind; 185static char *ouroptarg; 186 187#if HAVE_LASTLOG_H 188#ifndef _PATH_LASTLOG 189#define _PATH_LASTLOG "/var/adm/lastlog" 190#endif /* _PATH_LASTLOG */ 191 192static char lastlog[] = _PATH_LASTLOG; 193#endif /* HAVE_LASTLOG_H */ 194 195/* 196 * The "timeout" variable bounds the time given to login. 197 * We initialize it here for safety and so that it can be 198 * patched on machines where the default value is not appropriate. 199 */ 200static int timeout = 120; 201 202static void getstr __P((char *, int, char *)); 203 204#if HAVE_CRYPT_H 205#include <crypt.h> 206#endif /* HAVE_CRYPT_H */ 207 208#undef TRUE 209#define TRUE -1 210 211#ifdef TIOCSWINSZ 212/* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is 213available on BSDish systems and at least Solaris 2.x, but portability to 214other systems is questionable. Use within this source code module is 215protected by suitable defines. 216 217I'd be interested in hearing about a more portable approach. rja */ 218 219static struct winsize win = {0, 0, 0, 0}; 220#endif 221 222 223/*------------------ BEGIN REAL CODE --------------------------------*/ 224 225/* We allow the malloc()s to potentially leak data out because we can 226only call this routine about four times in the lifetime of this process 227and the kernel will free all heap memory when we exit or exec. */ 228static int lookupuser FUNCTION_NOARGS 229{ 230 struct passwd *pwd; 231#if HAVE_SHADOW 232 struct spwd *spwd; 233#endif /* HAVE_SHADOW */ 234 235 memcpy(&thisuser, &nouser, sizeof(thisuser)); 236 237 if (!(pwd = getpwnam(name))) 238 return -1; 239 240 thisuser.pw_uid = pwd->pw_uid; 241 thisuser.pw_gid = pwd->pw_gid; 242 243 if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1))) 244 goto lookupuserbad; 245 strcpy(thisuser.pw_name, pwd->pw_name); 246 247 if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1))) 248 goto lookupuserbad; 249 strcpy(thisuser.pw_dir, pwd->pw_dir); 250 251 if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1))) 252 goto lookupuserbad; 253 strcpy(thisuser.pw_shell, pwd->pw_shell); 254 255#if HAVE_SHADOW 256 if (!(spwd = getspnam(name))) 257 goto lookupuserbad; 258 259 pwd->pw_passwd = spwd->sp_pwdp; 260 261 endspent(); 262#endif /* HAVE_SHADOW */ 263 264 if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1))) 265 goto lookupuserbad; 266 strcpy(thisuser.pw_passwd, pwd->pw_passwd); 267 268 endpwent(); 269 270 return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#')); 271 272lookupuserbad: 273 memcpy(&thisuser, &nouser, sizeof(thisuser)); 274 return -1; 275} 276 277static VOIDRET getloginname FUNCTION_NOARGS 278{ 279 register char *namep; 280 char c, d; 281 int flags; 282 static int first = 1; 283 284 memset(name, 0, sizeof(name)); 285 286 d = 0; 287 while (name[0] == '\0') { 288 flags = 1; 289 if (ttyprompt) { 290 if (first) { 291 flags = 4; 292 first--; 293 } else 294 printf("%s", ttyprompt); 295 } else 296 printf("login: "); 297 fflush(stdout); 298 if (++d == 3) 299 exit(0); 300 if (!opiereadpass(name, sizeof(name)-1, flags)) { 301 syslog(LOG_CRIT, "End-of-file (or other error?) on stdin!"); 302 exit(0); 303 } 304 for (namep = name; *namep; namep++) { 305 if (c == ' ') 306 c = '_'; 307 } 308 } 309} 310 311static VOIDRET timedout FUNCTION((i), int i) 312{ 313 /* input variable declared just to keep the compiler quiet */ 314 printf("Login timed out after %d seconds\n", timeout); 315 syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout); 316 exit(0); 317} 318 319#if !HAVE_MOTD_IN_PROFILE 320static VOIDRET catch FUNCTION((i), int i) 321{ 322 /* the input variable is declared to keep the compiler quiet */ 323 signal(SIGINT, SIG_IGN); 324 stopmotd++; 325} 326#endif /* !HAVE_MOTD_IN_PROFILE */ 327 328static VOIDRET catchexit FUNCTION_NOARGS 329{ 330 int i; 331 tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr); 332 putchar('\n'); 333 closelog(); 334 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) 335 close(i); 336} 337 338static int rootterm FUNCTION((ttyn), char *ttyn) 339{ 340#if HAVE_GETTTYNAM 341/* The getttynam() call and the ttyent structure first appeared in 4.3 BSD and 342are not portable to System V systems such as Solaris 2.x. or modern versions 343of IRIX rja */ 344 register struct ttyent *t; 345 char *tty; 346 347 tty = strrchr(ttyn, '/'); 348 349 if (tty == NULL) 350 tty = ttyn; 351 else 352 tty++; 353 354 if ((t = getttynam(tty)) != NULL) 355 return (t->ty_status & TTY_SECURE); 356 357 return (1); /* when in doubt, allow root logins */ 358 359#elif HAVE_ETC_DEFAULT_LOGIN 360 361 FILE *filno; 362 char line[128]; 363 char *next, *next2; 364 365/* SVR4 only permits two security modes for root logins: 1) only from CONSOLE, 366if the string "CONSOLE=/dev/console" exists and is not commented out with "#" 367characters, or 2) from anywhere. 368 369So we open /etc/default/login file grab the file contents one line at a time 370verify that the line being tested isn't commented out check for the substring 371"CONSOLE" and decide whether to permit this attempted root login/su. */ 372 373 if ((filno = fopen("/etc/default/login", "r")) != NULL) { 374 while (fgets(line, 128, filno) != NULL) { 375 next = line; 376 377 if ((line[0] != '#') && (next = strstr(line, "CONSOLE"))) { 378 next += 7; /* get past the string "CONSOLE" */ 379 380 while (*next && (*next == ' ') || (*next == '\t')) 381 next++; 382 383 if (*(next++) != '=') 384 break; /* some weird character, get next line */ 385 386 next2 = next; 387 while (*next2 && (*next2 != '\t') && (*next2 != ' ') && 388 (*next2 != '\n')) 389 next2++; 390 *next2 = 0; 391 392 return !strcmp(ttyn, next); /* Allow the login if and only if the 393 user's terminal line matches the 394 setting for CONSOLE */ 395 } 396 } /* end while another line could be obtained */ 397 } /* end if could open file */ 398 return (1); /* when no CONSOLE line exists, root can login from anywhere */ 399#elif HAVE_SECURETTY 400 { 401 FILE *f; 402 char buffer[1024], *c; 403 int rc = 0; 404 405 if (!(f = fopen("/etc/securetty", "r"))) 406 return 1; 407 408 if (c = strstr(ttyn, "/dev/")) 409 ttyn += 5; 410 411 if (c = strrchr(ttyn, '/')) 412 ttyn = ++c; 413 414 while (fgets(buffer, sizeof(buffer), f)) { 415 if (c = strrchr(buffer, '\n')) 416 *c = 0; 417 418 if (!(c = strrchr(buffer, '/'))) 419 c = buffer; 420 else 421 c++; 422 423 if (!strcmp(c, ttyn)) 424 rc = 1; 425 }; 426 427 fclose(f); 428 return rc; 429 } 430#else 431 return (1); /* when in doubt, allow root logins */ 432#endif 433} 434 435static int doremotelogin FUNCTION((host), char *host) 436{ 437 int rc; 438 439 getstr(rusername, sizeof(rusername), "remuser"); 440 getstr(name, sizeof(name), "locuser"); 441 getstr(term, sizeof(term), "Terminal type"); 442 if (getuid()) { 443 memcpy(&thisuser, &nouser, sizeof(thisuser)); 444 syslog(LOG_ERR, "getuid() failed"); 445 return (-1); 446 } 447 if (lookupuser()) { 448 syslog(LOG_ERR, "lookup failed for user %s", name); 449 return (-1); 450 } 451 rc = ruserok(host, !thisuser.pw_uid, rusername, name); 452 if (rc == -1) { 453 syslog(LOG_ERR, 454 "ruserok failed, host=%s, uid=%d, remote username=%s, local username=%s", 455 host, thisuser.pw_uid, rusername, name); 456 } 457 return rc; 458} 459 460 461static VOIDRET getstr FUNCTION((buf, cnt, err), char *buf AND int cnt AND char *err) 462{ 463 char c; 464 465 do { 466 if (read(0, &c, 1) != 1) 467 exit(1); 468 if (--cnt < 0) { 469 printf("%s too long\r\n", err); 470 syslog(LOG_CRIT, "%s too long", err); 471 exit(1); 472 } 473 *buf++ = c; 474 } 475 while ((c != 0) && (c != '~')); 476} 477 478struct speed_xlat { 479 char *c; 480 int i; 481} speeds[] = { 482 483#ifdef B0 484 { 485 "0", B0 486 }, 487#endif /* B0 */ 488#ifdef B50 489 { 490 "50", B50 491 }, 492#endif /* B50 */ 493#ifdef B75 494 { 495 "75", B75 496 }, 497#endif /* B75 */ 498#ifdef B110 499 { 500 "110", B110 501 }, 502#endif /* B110 */ 503#ifdef B134 504 { 505 "134", B134 506 }, 507#endif /* B134 */ 508#ifdef B150 509 { 510 "150", B150 511 }, 512#endif /* B150 */ 513#ifdef B200 514 { 515 "200", B200 516 }, 517#endif /* B200 */ 518#ifdef B300 519 { 520 "300", B300 521 }, 522#endif /* B300 */ 523#ifdef B600 524 { 525 "600", B600 526 }, 527#endif /* B600 */ 528#ifdef B1200 529 { 530 "1200", B1200 531 }, 532#endif /* B1200 */ 533#ifdef B1800 534 { 535 "1800", B1800 536 }, 537#endif /* B1800 */ 538#ifdef B2400 539 { 540 "2400", B2400 541 }, 542#endif /* B2400 */ 543#ifdef B4800 544 { 545 "4800", B4800 546 }, 547#endif /* B4800 */ 548#ifdef B7200 549 { 550 "7200", B7200 551 }, 552#endif /* B7200 */ 553#ifdef B9600 554 { 555 "9600", B9600 556 }, 557#endif /* B9600 */ 558#ifdef B14400 559 { 560 "14400", B14400 561 }, 562#endif /* B14400 */ 563#ifdef B19200 564 { 565 "19200", B19200 566 }, 567#endif /* B19200 */ 568#ifdef B28800 569 { 570 "28800", B28800 571 }, 572#endif /* B28800 */ 573#ifdef B38400 574 { 575 "38400", B38400 576 }, 577#endif /* B38400 */ 578#ifdef B57600 579 { 580 "57600", B57600 581 }, 582#endif /* B57600 */ 583#ifdef B115200 584 { 585 "115200", B115200 586 }, 587#endif /* B115200 */ 588#ifdef B230400 589 { 590 "230400", B230400 591 }, 592#endif /* 230400 */ 593 { 594 NULL, 0 595 } 596}; 597 598static VOIDRET doremoteterm FUNCTION((term), char *term) 599{ 600 register char *cp = strchr(term, '/'); 601 char *speed; 602 struct speed_xlat *x; 603 604 if (cp) { 605 *cp++ = '\0'; 606 speed = cp; 607 cp = strchr(speed, '/'); 608 if (cp) 609 *cp++ = '\0'; 610 for (x = speeds; x->c != NULL; x++) 611 if (strcmp(x->c, speed) == 0) { 612 cfsetispeed(&attr, x->i); 613 cfsetospeed(&attr, x->i); 614 break; 615 } 616 } 617} 618 619static int tty_gid FUNCTION((default_gid), int default_gid) 620{ 621 struct group *gr; 622 int gid = default_gid; 623 624 gr = getgrnam(TTYGRPNAME); 625 if (gr != (struct group *) 0) 626 gid = gr->gr_gid; 627 endgrent(); 628 return (gid); 629} 630 631int main FUNCTION((argc, argv), int argc AND char *argv[]) 632{ 633 extern char **environ; 634 register char *namep; 635 struct opie opie; 636 637 int invalid, quietlog; 638 FILE *nlfd; 639 char *tty, host[256]; 640 int pflag = 0, hflag = 0, fflag = 0; 641 int t, c; 642 int i; 643 char *p; 644 char opieprompt[OPIE_CHALLENGE_MAX + 1]; 645 int af_pwok; 646 int authsok; 647 char *pp; 648 char buf[256]; 649 int uid; 650 int opiepassed; 651 652#ifndef DEBUG 653 if (geteuid()) { 654 fprintf(stderr, "This program requires super-user priveleges.\n"); 655 exit(1); 656 } 657#endif /* DEBUG */ 658 659 for (t = sysconf(_SC_OPEN_MAX); t > 2; t--) 660 close(t); 661 662 openlog("login", LOG_ODELAY, LOG_AUTH); 663 664 /* initialisation */ 665 host[0] = '\0'; 666 opieprompt[0] = '\0'; 667 668 if (p = getenv("TERM")) { 669#ifdef DEBUG 670 syslog(LOG_DEBUG, "environment TERM=%s", p); 671#endif /* DEBUG */ 672 strncpy(term, p, sizeof(term)); 673 }; 674 675 memset(&nouser, 0, sizeof(nouser)); 676 nouser.pw_uid = -1; 677 nouser.pw_gid = -1; 678 nouser.pw_passwd = "#nope"; 679 nouser.pw_name = nouser.pw_gecos = nouser.pw_dir = nouser.pw_shell = ""; 680 681#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H 682 setpriority(PRIO_PROCESS, 0, 0); 683#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ 684 685 signal(SIGALRM, timedout); 686 alarm(timeout); 687 signal(SIGQUIT, SIG_IGN); 688 signal(SIGINT, SIG_IGN); 689 690#if DOTTYPROMPT 691 ttyprompt = (char *) getenv("TTYPROMPT"); 692#endif /* TTYPROMPT */ 693 694#ifdef QUOTA 695 quota(Q_SETUID, 0, 0, 0); 696#endif 697 698#ifdef DEBUG 699 { 700 int foo; 701 702 syslog(LOG_DEBUG, "my args are: (argc=%d)", foo = argc); 703 while (--foo) 704 syslog(LOG_DEBUG, "%d: %s", foo, argv[foo]); 705 } 706#endif /* DEBUG */ 707 708/* Implement our own getopt()-like functionality, but do so in a much more 709 strict manner to prevent security problems. */ 710 for (ouroptind = 1; ouroptind < argc; ouroptind++) { 711 if (!argv[ouroptind]) 712 continue; 713 714 if (argv[ouroptind][0] == '-') { 715 char *c = argv[ouroptind] + 1; 716 717 while(*c) { 718 switch(*(c++)) { 719 case 'd': 720 if (*c || (++ouroptind == argc)) 721 exit(1); 722 723/* The '-d' option is apparently a performance hack to get around 724 ttyname() being slow. The potential does exist for it to be used 725 for malice, and it does not seem to be strictly necessary, so we 726 will just eat it. */ 727 break; 728 729 case 'r': 730 if (rflag || hflag || fflag) { 731 fprintf(stderr, "Other options not allowed with -r\n"); 732 exit(1); 733 } 734 735 if (*c || (++ouroptind == argc)) 736 exit(1); 737 738 if (!(ouroptarg = argv[ouroptind])) 739 exit(1); 740 741 rflag = -1; 742 if (!doremotelogin(ouroptarg)) 743 rflag = 1; 744 745 strncpy(host, ouroptarg, sizeof(host)); 746 break; 747 748 case 'h': 749 if (!getuid()) { 750 if (rflag || hflag || fflag) { 751 fprintf(stderr, "Other options not allowed with -h\n"); 752 exit(1); 753 } 754 hflag = 1; 755 756 if (*c || (++ouroptind == argc)) 757 exit(1); 758 759 if (!(ouroptarg = argv[ouroptind])) 760 exit(1); 761 762 strncpy(host, ouroptarg, sizeof(host)); 763 } 764 break; 765 766 case 'f': 767 if (rflag) { 768 fprintf(stderr, "Only one of -r and -f allowed\n"); 769 exit(1); 770 } 771 fflag = 1; 772 773 if (*c || (++ouroptind == argc)) 774 exit(1); 775 776 if (!(ouroptarg = argv[ouroptind])) 777 exit(1); 778 779 strncpy(name, ouroptarg, sizeof(name)); 780 break; 781 case 'p': 782 pflag = 1; 783 break; 784 }; 785 }; 786 continue; 787 }; 788 789 if (strchr(argv[ouroptind], '=')) { 790 if (!strncmp(argv[ouroptind], "TERM=", 5)) { 791 strncpy(term, &(argv[ouroptind][5]), sizeof(term)); 792 term[sizeof(term) - 1] = 0; 793#ifdef DEBUG 794 syslog(LOG_DEBUG, "passed TERM=%s, ouroptind = %d", term, ouroptind); 795#endif /* DEBUG */ 796 } else { 797#ifdef DEBUG 798 syslog(LOG_DEBUG, "eating %s, ouroptind = %d", argv[ouroptind], ouroptind); 799#endif /* DEBUG */ 800 }; 801 continue; 802 }; 803 804 strncpy(name, argv[ouroptind], sizeof(name)); 805 }; 806 807#ifdef TIOCNXCL 808 /* BSDism: not sure how to rewrite for POSIX. rja */ 809 ioctl(0, TIOCNXCL, 0); /* set non-exclusive use of tty */ 810#endif 811 812 /* get original termio attributes */ 813 if (tcgetattr(STDIN_FILENO, &attr) != 0) 814 return (-1); 815 816/* If talking to an rlogin process, propagate the terminal type and baud rate 817 across the network. */ 818 if (rflag) 819 doremoteterm(term); 820 else { 821 struct termios termios; 822 fd_set fds; 823 struct timeval timeval; 824 825 memset(&timeval, 0, sizeof(struct timeval)); 826 827 FD_ZERO(&fds); 828 FD_SET(0, &fds); 829 830#if HAVE_USLEEP 831 usleep(1); 832#endif /* HAVE_USLEEP */ 833 834 if (select(1, &fds, NULL, NULL, &timeval)) { 835#ifdef DEBUG 836 syslog(LOG_DEBUG, "reading user name from tty buffer"); 837#endif /* DEBUG */ 838 839 if (tcgetattr(0, &termios)) { 840#ifdef DEBUG 841 syslog(LOG_DEBUG, "tcgetattr(0, &termios) failed"); 842#endif /* DEBUG */ 843 exit(1); 844 } 845 846 termios.c_lflag &= ~ECHO; 847 848 if (tcsetattr(0, TCSANOW, &termios)) { 849#ifdef DEBUG 850 syslog(LOG_DEBUG, "tcsetattr(0, &termios) failed"); 851#endif /* DEBUG */ 852 exit(1); 853 } 854 855 if ((i = read(0, name, sizeof(name)-1)) > 0) 856 name[i] = 0; 857 if ((p = strchr(name, '\r'))) 858 *p = 0; 859 if ((p = strchr(name, '\n'))) 860 *p = 0; 861 } 862 } 863 864/* Force termios portable control characters to the system default values as 865specified in termios.h. This should help the one-time password login feel the 866same as the vendor-supplied login. Common extensions are also set for 867completeness, but these are set within appropriate defines for portability. */ 868 869#define CONTROL(x) (x - 64) 870 871#ifdef VEOF 872#ifdef CEOF 873 attr.c_cc[VEOF] = CEOF; 874#else /* CEOF */ 875 attr.c_cc[VEOF] = CONTROL('D'); 876#endif /* CEOF */ 877#endif /* VEOF */ 878#ifdef VEOL 879#ifdef CEOL 880 attr.c_cc[VEOL] = CEOL; 881#else /* CEOL */ 882 attr.c_cc[VEOL] = CONTROL('J'); 883#endif /* CEOL */ 884#endif /* VEOL */ 885#ifdef VERASE 886#ifdef CERASE 887 attr.c_cc[VERASE] = CERASE; 888#else /* CERASE */ 889 attr.c_cc[VERASE] = CONTROL('H'); 890#endif /* CERASE */ 891#endif /* VERASE */ 892#ifdef VINTR 893#ifdef CINTR 894 attr.c_cc[VINTR] = CINTR; 895#else /* CINTR */ 896 attr.c_cc[VINTR] = CONTROL('C'); 897#endif /* CINTR */ 898#endif /* VINTR */ 899#ifdef VKILL 900#ifdef CKILL 901 attr.c_cc[VKILL] = CKILL; 902#else /* CKILL */ 903 attr.c_cc[VKILL] = CONTROL('U'); 904#endif /* CKILL */ 905#endif /* VKILL */ 906#ifdef VQUIT 907#ifdef CQUIT 908 attr.c_cc[VQUIT] = CQUIT; 909#else /* CQUIT */ 910 attr.c_cc[VQUIT] = CONTROL('\\'); 911#endif /* CQUIT */ 912#endif /* VQUIT */ 913#ifdef VSUSP 914#ifdef CSUSP 915 attr.c_cc[VSUSP] = CSUSP; 916#else /* CSUSP */ 917 attr.c_cc[VSUSP] = CONTROL('Z'); 918#endif /* CSUSP */ 919#endif /* VSUSP */ 920#ifdef VSTOP 921#ifdef CSTOP 922 attr.c_cc[VSTOP] = CSTOP; 923#else /* CSTOP */ 924 attr.c_cc[VSTOP] = CONTROL('S'); 925#endif /* CSTOP */ 926#endif /* VSTOP */ 927#ifdef VSTART 928#ifdef CSTART 929 attr.c_cc[VSTART] = CSTART; 930#else /* CSTART */ 931 attr.c_cc[VSTART] = CONTROL('Q'); 932#endif /* CSTART */ 933#endif /* VSTART */ 934#ifdef VDSUSP 935#ifdef CDSUSP 936 attr.c_cc[VDSUSP] = CDSUSP; 937#else /* CDSUSP */ 938 attr.c_cc[VDSUSP] = 0; 939#endif /* CDSUSP */ 940#endif /* VDSUSP */ 941#ifdef VEOL2 942#ifdef CEOL2 943 attr.c_cc[VEOL2] = CEOL2; 944#else /* CEOL2 */ 945 attr.c_cc[VEOL2] = 0; 946#endif /* CEOL2 */ 947#endif /* VEOL2 */ 948#ifdef VREPRINT 949#ifdef CRPRNT 950 attr.c_cc[VREPRINT] = CRPRNT; 951#else /* CRPRNT */ 952 attr.c_cc[VREPRINT] = 0; 953#endif /* CRPRNT */ 954#endif /* VREPRINT */ 955#ifdef VWERASE 956#ifdef CWERASE 957 attr.c_cc[VWERASE] = CWERASE; 958#else /* CWERASE */ 959 attr.c_cc[VWERASE] = 0; 960#endif /* CWERASE */ 961#endif /* VWERASE */ 962#ifdef VLNEXT 963#ifdef CLNEXT 964 attr.c_cc[VLNEXT] = CLNEXT; 965#else /* CLNEXT */ 966 attr.c_cc[VLNEXT] = 0; 967#endif /* CLNEXT */ 968#endif /* VLNEXT */ 969 970 attr.c_lflag |= ICANON; /* enable canonical input processing */ 971 attr.c_lflag &= ~ISIG; /* disable INTR, QUIT,& SUSP signals */ 972 attr.c_lflag |= (ECHO | ECHOE); /* enable echo and erase */ 973#ifdef ONLCR 974 /* POSIX does not specify any output processing flags, but the usage below 975 is SVID compliant and is generally portable to modern versions of UNIX. */ 976 attr.c_oflag |= ONLCR; /* map CR to CRNL on output */ 977#endif 978#ifdef ICRNL 979 attr.c_iflag |= ICRNL; 980#endif /* ICRNL */ 981 982 attr.c_oflag |= OPOST; 983 attr.c_lflag |= ICANON; /* enable canonical input */ 984 attr.c_lflag |= ECHO; 985 attr.c_lflag |= ECHOE; /* enable ERASE character */ 986 attr.c_lflag |= ECHOK; /* enable KILL to delete line */ 987 attr.c_cflag |= HUPCL; /* hangup on close */ 988 989 /* Set revised termio attributes */ 990 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr)) 991 return (-1); 992 993 atexit(catchexit); 994 995 tty = ttyname(0); 996 997 if (tty == (char *) 0 || *tty == '\0') 998 tty = "UNKNOWN"; /* was: "/dev/tty??" */ 999 1000#if HAVE_SETVBUF && defined(_IONBF) 1001#if SETVBUF_REVERSED 1002 setvbuf(stdout, _IONBF, NULL, 0); 1003 setvbuf(stderr, _IONBF, NULL, 0); 1004#else /* SETVBUF_REVERSED */ 1005 setvbuf(stdout, NULL, _IONBF, 0); 1006 setvbuf(stderr, NULL, _IONBF, 0); 1007#endif /* SETVBUF_REVERSED */ 1008#endif /* HAVE_SETVBUF && defined(_IONBF) */ 1009 1010#ifdef DEBUG 1011 syslog(LOG_DEBUG, "tty = %s", tty); 1012#endif /* DEBUG */ 1013 1014#ifdef HAVE_LOGIN_ENVFILE 1015 { 1016 FILE *f; 1017 1018 if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) { 1019 char line[128], *c, *c2; 1020 1021 while(fgets(line, sizeof(line)-1, f)) { 1022 c = line; 1023 while(*c && (isalnum(*c) || (*c == '_'))) c++; 1024 if (*c == '=') { 1025 *(c++) = 0; 1026 if (c2 = strchr(c, ';')) 1027 *c2 = 0; 1028 if (c2 = strchr(c, '\n')) 1029 *c2 = 0; 1030 if (c2 = strchr(c, ' ')) 1031 continue; 1032 if (c2 = strchr(c, '\t')) 1033 continue; 1034 if (!strcmp(line, "TZ")) 1035 continue; 1036 if (setenv(line, c, 1) < 0) { 1037 fprintf(stderr, "setenv() failed -- environment full?\n"); 1038 break; 1039 } 1040 } 1041 } 1042 fclose(f); 1043 } 1044 } 1045#endif /* HAVE_LOGIN_ENVFILE */ 1046 1047 t = 0; 1048 invalid = TRUE; 1049 af_pwok = opieaccessfile(host); 1050 1051 if (name[0]) 1052 if (name[0] == '-') { 1053 fprintf(stderr, "User names can't start with '-'.\n"); 1054 syslog(LOG_AUTH, "Attempt to use invalid username: %s.", name); 1055 exit(1); 1056 } else 1057 invalid = lookupuser(); 1058 1059 do { 1060 /* If remote login take given name, otherwise prompt user for something. */ 1061 if (invalid && !name[0]) { 1062 getloginname(); 1063 invalid = lookupuser(); 1064 authsok = 0; 1065 } 1066#ifdef DEBUG 1067 syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d", name, strlen(name), name[0]); 1068#endif /* DEBUG */ 1069 1070 if (fflag) { 1071 uid = getuid(); 1072 1073 if (uid != 0 && uid != thisuser.pw_uid) 1074 fflag = 0; 1075 /* Disallow automatic login for root. */ 1076 if (thisuser.pw_uid == 0) 1077 fflag = 0; 1078 } 1079 if (feof(stdin)) 1080 exit(0); 1081 1082 /* If no remote login authentication and a password exists for this user, 1083 prompt for and verify a password. */ 1084 if (!fflag && (rflag < 1) && *thisuser.pw_passwd) { 1085#ifdef DEBUG 1086 syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d\n", name, strlen(name), name[0]); 1087#endif /* DEBUG */ 1088 1089 /* Attempt a one-time password challenge */ 1090 i = opiechallenge(&opie, name, opieprompt); 1091 1092 if ((i < 0) || (i > 1)) { 1093 syslog(LOG_ERR, "error: opiechallenge() returned %d, errno=%d!\n", i, errno); 1094 } else { 1095 printf("%s\n", opieprompt); 1096 authsok |= 1; 1097 } 1098 1099 if (!memcmp(&thisuser, &nouser, sizeof(thisuser))) 1100 if (host[0]) 1101 syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s.", 1102 name, tty, host); 1103 else 1104 syslog(LOG_WARNING, "Invalid login attempt for %s on %s.", 1105 name, tty); 1106 1107 if (af_pwok && opiealways(thisuser.pw_dir)) 1108 authsok |= 2; 1109 1110#if DEBUG 1111 syslog(LOG_DEBUG, "af_pwok = %d, authsok = %d", af_pwok, authsok); 1112#endif /* DEBUG */ 1113 1114 if (!authsok) 1115 syslog(LOG_ERR, "no authentication methods are available for %s!", name); 1116 1117#if NEW_PROMPTS 1118 if ((authsok & 1) || !authsok) 1119 printf("Response"); 1120 if (((authsok & 3) == 3) || !authsok) 1121 printf(" or "); 1122 if ((authsok & 2) || !authsok) 1123 printf("Password"); 1124 printf(": "); 1125 fflush(stdout); 1126 if (!opiereadpass(buf, sizeof(buf), !(authsok & 2))) 1127 invalid = TRUE; 1128#else /* NEW_PROMPTS */ 1129 if (!(authsok & 1) && authsok) 1130 printf("(OTP response required)\n"); 1131 printf("Password:"); 1132 fflush(stdout); 1133 if (!opiereadpass(buf, sizeof(buf), 0)) 1134 invalid = TRUE; 1135#endif /* NEW_PROMPTS */ 1136 1137 if (!buf[0] && (authsok & 1)) { 1138 authsok &= ~2; 1139 /* Null line entered, so display appropriate prompt & flush current 1140 data. */ 1141#if NEW_PROMPTS 1142 printf("Response: "); 1143#else /* NEW_PROMPTS */ 1144 printf(" (echo on)\nPassword:"); 1145#endif /* NEW_PROMPTS */ 1146 if (!opiereadpass(buf, sizeof(buf), 1)) 1147 invalid = TRUE; 1148 } 1149 1150 if (authsok & 1) { 1151 i = opiegetsequence(&opie); 1152 opiepassed = !opieverify(&opie, buf); 1153 1154#ifdef DEBUG 1155 syslog(LOG_DEBUG, "opiepassed = %d", opiepassed); 1156#endif /* DEBUG */ 1157 } 1158 1159 if (!invalid) { 1160 if ((authsok & 1) && opiepassed) { 1161 if (i < 10) { 1162 printf("Warning: Re-initialize your OTP information"); 1163 if (i < 5) 1164 printf(" NOW!"); 1165 printf("\n"); 1166 } 1167 } else { 1168 if (authsok & 2) { 1169 pp = crypt(buf, thisuser.pw_passwd); 1170 invalid = strcmp(pp, thisuser.pw_passwd); 1171 } else 1172 invalid = TRUE; 1173 } 1174 } 1175 } 1176 1177 /* If user not super-user, check for logins disabled. */ 1178 if (thisuser.pw_uid) { 1179 if (nlfd = fopen(_PATH_NOLOGIN, "r")) { 1180 while ((c = getc(nlfd)) != EOF) 1181 putchar(c); 1182 fflush(stdout); 1183 sleep(5); 1184 exit(0); 1185 } 1186 } 1187 /* If valid so far and root is logging in, see if root logins on this 1188 terminal are permitted. */ 1189 if (!invalid && !thisuser.pw_uid && !rootterm(tty)) { 1190 if (host[0]) 1191 syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s FROM %.*s", 1192 tty, HMAX, host); 1193 else 1194 syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s", tty); 1195 invalid = TRUE; 1196 } 1197 /* If invalid, then log failure attempt data to appropriate system 1198 logfiles and close the connection. */ 1199 if (invalid) { 1200 printf("Login incorrect\n"); 1201 if (host[0]) 1202 syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s, %.*s", 1203 tty, HMAX, host, sizeof(name), name); 1204 else 1205 syslog(LOG_ERR, "LOGIN FAILURE ON %s, %.*s", 1206 tty, sizeof(name), name); 1207 if (++t >= 5) 1208 exit(1); 1209 } 1210 if (*thisuser.pw_shell == '\0') 1211 thisuser.pw_shell = "/bin/sh"; 1212 if ((chdir(thisuser.pw_dir) < 0) && !invalid) { 1213 if (chdir("/") < 0) { 1214 printf("No directory!\n"); 1215 invalid = TRUE; 1216 } else { 1217 printf("No directory! %s\n", "Logging in with HOME=/"); 1218 strcpy(thisuser.pw_dir, "/"); 1219 } 1220 } 1221 /* Remote login invalid must have been because of a restriction of some 1222 sort, no extra chances. */ 1223 if (invalid) { 1224 if (!usererr) 1225 exit(1); 1226 name[0] = 0; 1227 } 1228 } 1229 while (invalid); 1230 /* Committed to login -- turn off timeout */ 1231 alarm(0); 1232 1233#ifdef QUOTA 1234 if (quota(Q_SETUID, thisuser.pw_uid, 0, 0) < 0 && errno != EINVAL) { 1235 if (errno == EUSERS) 1236 printf("%s.\n%s.\n", "Too many users logged on already", 1237 "Try again later"); 1238 else 1239 if (errno == EPROCLIM) 1240 printf("You have too many processes running.\n"); 1241 else 1242 perror("quota (Q_SETUID)"); 1243 sleep(5); 1244 exit(0); 1245 } 1246#endif 1247 1248 if (opielogin(tty, name, host)) 1249 syslog(LOG_ERR, "can't record login: tty %s, name %s, host %s", tty, name, host); 1250 1251 quietlog = !access(QUIET_LOGIN_FILE, F_OK); 1252 1253#if HAVE_LASTLOG_H 1254 { 1255 int f; 1256 1257 if ((f = open(lastlog, O_RDWR)) >= 0) { 1258 struct lastlog ll; 1259 1260 lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0); 1261 1262 if ((sizeof(ll) == read(f, (char *) &ll, sizeof(ll))) && 1263 (ll.ll_time != 0) && (!quietlog)) { 1264 printf("Last login: %.*s ", 1265 24 - 5, (char *) ctime(&ll.ll_time)); 1266 if (*ll.ll_host != '\0') 1267 printf("from %.*s\n", sizeof(ll.ll_host), ll.ll_host); 1268 else 1269 printf("on %.*s\n", sizeof(ll.ll_line), ll.ll_line); 1270 } 1271 lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0); 1272 1273 time(&ll.ll_time); 1274 strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 1275 strncpy(ll.ll_host, host, sizeof(ll.ll_host)); 1276 write(f, (char *) &ll, sizeof ll); 1277 close(f); 1278 } 1279 } 1280#endif /* HAVE_LASTLOG_H */ 1281 1282 chown(tty, thisuser.pw_uid, TTYGID(thisuser.pw_gid)); 1283 1284#ifdef TIOCSWINSZ 1285/* POSIX does not specify any interface to set/get window sizes, so this is 1286not portable. It should work on most recent BSDish systems and the defines 1287should protect it on older System Vish systems. It does work under Solaris 12882.4, though it isn't clear how many other SVR4 systems support it. I'd be 1289interested in hearing of a more portable approach. rja */ 1290 if (!hflag && !rflag) 1291 ioctl(0, TIOCSWINSZ, &win); /* set window size to 0,0,0,0 */ 1292#endif 1293 1294 chmod(tty, 0622); 1295 setgid(thisuser.pw_gid); 1296 initgroups(name, thisuser.pw_gid); 1297 1298#ifdef QUOTA 1299 quota(Q_DOWARN, thisuser.pw_uid, (dev_t) - 1, 0); 1300#endif 1301 1302#ifdef PERMSFILE 1303 home = thisuser.pw_dir; 1304 permsfile(name, tty, thisuser.pw_uid, thisuser.pw_gid); 1305 fflush(stderr); 1306#endif /* PERMSFILE */ 1307 1308 setuid(thisuser.pw_uid); 1309 1310 /* destroy environment unless user has asked to preserve it */ 1311 if (!pflag) 1312 environ = envinit; 1313 setenv("HOME", thisuser.pw_dir, 1); 1314 setenv("SHELL", thisuser.pw_shell, 1); 1315 if (!term[0]) { 1316#if HAVE_GETTTYNAM 1317/* 1318 * The getttynam() call and the ttyent structure first appeared in 4.3 BSD. 1319 * They are not portable to System V systems such as Solaris 2.x. 1320 * rja 1321 */ 1322 register struct ttyent *t; 1323 register char *c; 1324 1325 if (c = strrchr(tty, '/')) 1326 c++; 1327 else 1328 c = tty; 1329 1330 if (t = getttynam(c)) 1331 strncpy(term, t->ty_type, sizeof(term)); 1332 else 1333#endif /* HAVE_GETTTYNAM */ 1334 strcpy(term, "unknown"); 1335 } 1336 1337 setenv("USER", name, 1); 1338 setenv("LOGNAME", name, 1); 1339 setenv("PATH", DEFAULT_PATH, 0); 1340 if (term[0]) { 1341#ifdef DEBUG 1342 syslog(LOG_DEBUG, "setting TERM=%s", term); 1343#endif /* DEBUG */ 1344 setenv("TERM", term, 1); 1345 } 1346 1347#ifdef HAVE_LOGIN_ENVFILE 1348 { 1349 FILE *f; 1350 1351 if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) { 1352 char line[128], *c, *c2; 1353 1354 while(fgets(line, sizeof(line)-1, f)) { 1355 c = line; 1356 while(*c && (isalnum(*c) || (*c == '_'))) c++; 1357 if (*c == '=') { 1358 *(c++) = 0; 1359 if (c2 = strchr(c, ';')) 1360 *c2 = 0; 1361 if (c2 = strchr(c, '\n')) 1362 *c2 = 0; 1363 if (c2 = strchr(c, ' ')) 1364 continue; 1365 if (c2 = strchr(c, '\t')) 1366 continue; 1367 if (setenv(line, c, 0) < 0) { 1368 fprintf(stderr, "setenv() failed -- environment full?\n"); 1369 break; 1370 } 1371 } 1372 } 1373 fclose(f); 1374 } 1375 } 1376#endif /* HAVE_LOGIN_ENVFILE */ 1377 1378 if ((namep = strrchr(thisuser.pw_shell, '/')) == NULL) 1379 namep = thisuser.pw_shell; 1380 else 1381 namep++; 1382 strcat(minusnam, namep); 1383 if (tty[sizeof("tty") - 1] == 'd') 1384 syslog(LOG_INFO, "DIALUP %s, %s", tty, name); 1385 if (!thisuser.pw_uid) 1386 if (host[0]) 1387 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", tty, HMAX, host); 1388 else 1389 syslog(LOG_NOTICE, "ROOT LOGIN %s", tty); 1390#if !HAVE_MOTD_IN_PROFILE 1391 if (!quietlog) { 1392 FILE *mf; 1393 register c; 1394 1395 signal(SIGINT, catch); 1396 if ((mf = fopen(MOTD_FILE, "r")) != NULL) { 1397 while ((c = getc(mf)) != EOF && !stopmotd) 1398 putchar(c); 1399 fclose(mf); 1400 } 1401 signal(SIGINT, SIG_IGN); 1402 } 1403#endif /* !HAVE_MOTD_IN_PROFILE */ 1404#if !HAVE_MAILCHECK_IN_PROFILE 1405 if (!quietlog) { 1406 struct stat st; 1407 char buf[128]; 1408 int len; 1409 1410 strncpy(buf, PATH_MAIL, sizeof(buf) - 2); 1411 buf[sizeof(buf) - 2] = 0; 1412 1413 len = strlen(buf); 1414 if (*(buf + len - 1) != '/') { 1415 *(buf + len) = '/'; 1416 *(buf + len + 1) = 0; 1417 } 1418 1419 strcat(buf, name); 1420#if DEBUG 1421 syslog(LOG_DEBUG, "statting %s", buf); 1422#endif /* DEBUG */ 1423 if (!stat(buf, &st) && st.st_size) 1424 printf("You have %smail.\n", 1425 (st.st_mtime > st.st_atime) ? "new " : ""); 1426 } 1427#endif /* !HAVE_MAILCHECK_IN_PROFILE */ 1428 signal(SIGALRM, SIG_DFL); 1429 signal(SIGQUIT, SIG_DFL); 1430 signal(SIGINT, SIG_DFL); 1431 signal(SIGTSTP, SIG_IGN); 1432 1433 attr.c_lflag |= (ISIG | IEXTEN); 1434 1435 catchexit(); 1436 execlp(thisuser.pw_shell, minusnam, 0); 1437 perror(thisuser.pw_shell); 1438 printf("No shell\n"); 1439 exit(0); 1440} 1441