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