1/* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7#ifndef lint 8char copyright[] = 9"@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11#endif not lint 12 13#ifndef lint 14static char sccsid[] = "@(#)login.c.1 5.15 (Berkeley) 04/12/86"; 15#endif not lint 16 17/* 18 * login [ name ] 19 * login -r hostname (for rlogind) 20 * login -h hostname (for telnetd, etc.) 21 */ 22 23#include <sys/param.h> 24#include <sys/quota.h> 25#include <sys/stat.h> 26#include <sys/time.h> 27#include <sys/resource.h> 28#include <sys/file.h> 29 30#include <sgtty.h> 31#include <utmp.h> 32#include <signal.h> 33#include <pwd.h> 34#include <stdio.h> 35#include <lastlog.h> 36#include <errno.h> 37#include <ttyent.h> 38#include <syslog.h> 39#include <grp.h> 40 41#define TTYGRPNAME "tty" /* name of group to own ttys */ 42#define TTYGID(gid) tty_gid(gid) /* gid that owns all ttys */ 43 44#define SCMPN(a, b) strncmp(a, b, sizeof(a)) 45#define SCPYN(a, b) strncpy(a, b, sizeof(a)) 46 47#define NMAX sizeof(utmp.ut_name) 48#define HMAX sizeof(utmp.ut_host) 49 50#define FALSE 0 51#define TRUE -1 52 53char nolog[] = "/etc/nologin"; 54char qlog[] = ".hushlogin"; 55char maildir[30] = "/usr/spool/mail/"; 56char lastlog[] = "/usr/adm/lastlog"; 57struct passwd nouser = {"", "nope", -1, -1, -1, "", "", "", "" }; 58struct sgttyb ttyb; 59struct utmp utmp; 60char minusnam[16] = "-"; 61char *envinit[] = { 0 }; /* now set by setenv calls */ 62/* 63 * This bounds the time given to login. We initialize it here 64 * so it can be patched on machines where it's too small. 65 */ 66int timeout = 60; 67 68char term[64]; 69 70struct passwd *pwd; 71char *strcat(), *rindex(), *index(), *malloc(), *realloc(); 72int timedout(); 73char *ttyname(); 74char *crypt(); 75char *getpass(); 76char *stypeof(); 77extern char **environ; 78extern int errno; 79 80struct tchars tc = { 81 CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK 82}; 83struct ltchars ltc = { 84 CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT 85}; 86 87struct winsize win = { 0, 0, 0, 0 }; 88 89int rflag; 90int usererr = -1; 91char rusername[NMAX+1], lusername[NMAX+1]; 92char rpassword[NMAX+1]; 93char name[NMAX+1]; 94char *rhost; 95 96main(argc, argv) 97 char *argv[]; 98{ 99 register char *namep; 100 int pflag = 0, hflag = 0, t, f, c; 101 int invalid, quietlog; 102 FILE *nlfd; 103 char *ttyn, *tty; 104 int ldisc = 0, zero = 0, i; 105 char **envnew; 106 107 signal(SIGALRM, timedout); 108 alarm(timeout); 109 signal(SIGQUIT, SIG_IGN); 110 signal(SIGINT, SIG_IGN); 111 setpriority(PRIO_PROCESS, 0, 0); 112 quota(Q_SETUID, 0, 0, 0); 113 /* 114 * -p is used by getty to tell login not to destroy the environment 115 * -r is used by rlogind to cause the autologin protocol; 116 * -h is used by other servers to pass the name of the 117 * remote host to login so that it may be placed in utmp and wtmp 118 */ 119 while (argc > 1) { 120 if (strcmp(argv[1], "-r") == 0) { 121 if (rflag || hflag) { 122 printf("Only one of -r and -h allowed\n"); 123 exit(1); 124 } 125 rflag = 1; 126 usererr = doremotelogin(argv[2]); 127 SCPYN(utmp.ut_host, argv[2]); 128 argc -= 2; 129 argv += 2; 130 continue; 131 } 132 if (strcmp(argv[1], "-h") == 0 && getuid() == 0) { 133 if (rflag || hflag) { 134 printf("Only one of -r and -h allowed\n"); 135 exit(1); 136 } 137 hflag = 1; 138 SCPYN(utmp.ut_host, argv[2]); 139 argc -= 2; 140 argv += 2; 141 continue; 142 } 143 if (strcmp(argv[1], "-p") == 0) { 144 argc--; 145 argv++; 146 pflag = 1; 147 continue; 148 } 149 break; 150 } 151 ioctl(0, TIOCLSET, &zero); 152 ioctl(0, TIOCNXCL, 0); 153 ioctl(0, FIONBIO, &zero); 154 ioctl(0, FIOASYNC, &zero); 155 ioctl(0, TIOCGETP, &ttyb); 156 /* 157 * If talking to an rlogin process, 158 * propagate the terminal type and 159 * baud rate across the network. 160 */ 161 if (rflag) 162 doremoteterm(term, &ttyb); 163 ttyb.sg_erase = CERASE; 164 ttyb.sg_kill = CKILL; 165 ioctl(0, TIOCSLTC, <c); 166 ioctl(0, TIOCSETC, &tc); 167 ioctl(0, TIOCSETP, &ttyb); 168 for (t = getdtablesize(); t > 2; t--) 169 close(t); 170 ttyn = ttyname(0); 171 if (ttyn == (char *)0 || *ttyn == '\0') 172 ttyn = "/dev/tty??"; 173 tty = rindex(ttyn, '/'); 174 if (tty == NULL) 175 tty = ttyn; 176 else 177 tty++; 178 openlog("login", LOG_ODELAY, LOG_AUTH); 179 t = 0; 180 invalid = FALSE; 181 do { 182 ldisc = 0; 183 ioctl(0, TIOCSETD, &ldisc); 184 SCPYN(utmp.ut_name, ""); 185 /* 186 * Name specified, take it. 187 */ 188 if (argc > 1) { 189 SCPYN(utmp.ut_name, argv[1]); 190 argc = 0; 191 } 192 /* 193 * If remote login take given name, 194 * otherwise prompt user for something. 195 */ 196 if (rflag && !invalid) 197 SCPYN(utmp.ut_name, lusername); 198 else 199 getloginname(&utmp); 200 invalid = FALSE; 201 if (!strcmp(pwd->pw_shell, "/bin/csh")) { 202 ldisc = NTTYDISC; 203 ioctl(0, TIOCSETD, &ldisc); 204 } 205 /* 206 * If no remote login authentication and 207 * a password exists for this user, prompt 208 * for one and verify it. 209 */ 210 if (usererr == -1 && *pwd->pw_passwd != '\0') { 211 char *pp; 212 213 setpriority(PRIO_PROCESS, 0, -4); 214 pp = getpass("Password:"); 215 namep = crypt(pp, pwd->pw_passwd); 216 setpriority(PRIO_PROCESS, 0, 0); 217 if (strcmp(namep, pwd->pw_passwd)) 218 invalid = TRUE; 219 } 220 /* 221 * If user not super-user, check for logins disabled. 222 */ 223 if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) { 224 while ((c = getc(nlfd)) != EOF) 225 putchar(c); 226 fflush(stdout); 227 sleep(5); 228 exit(0); 229 } 230 /* 231 * If valid so far and root is logging in, 232 * see if root logins on this terminal are permitted. 233 */ 234 if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) { 235 if (utmp.ut_host[0]) 236 syslog(LOG_CRIT, 237 "ROOT LOGIN REFUSED ON %s FROM %.*s", 238 tty, HMAX, utmp.ut_host); 239 else 240 syslog(LOG_CRIT, 241 "ROOT LOGIN REFUSED ON %s", tty); 242 invalid = TRUE; 243 } 244 if (invalid) { 245 printf("Login incorrect\n"); 246 if (++t >= 5) { 247 if (utmp.ut_host[0]) 248 syslog(LOG_CRIT, 249 "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s", 250 tty, HMAX, utmp.ut_host, 251 NMAX, utmp.ut_name); 252 else 253 syslog(LOG_CRIT, 254 "REPEATED LOGIN FAILURES ON %s, %.*s", 255 tty, NMAX, utmp.ut_name); 256 ioctl(0, TIOCHPCL, (struct sgttyb *) 0); 257 close(0), close(1), close(2); 258 sleep(10); 259 exit(1); 260 } 261 } 262 if (*pwd->pw_shell == '\0') 263 pwd->pw_shell = "/bin/sh"; 264 if (chdir(pwd->pw_dir) < 0 && !invalid ) { 265 if (chdir("/") < 0) { 266 printf("No directory!\n"); 267 invalid = TRUE; 268 } else { 269 printf("No directory! %s\n", 270 "Logging in with home=/"); 271 pwd->pw_dir = "/"; 272 } 273 } 274 /* 275 * Remote login invalid must have been because 276 * of a restriction of some sort, no extra chances. 277 */ 278 if (!usererr && invalid) 279 exit(1); 280 } while (invalid); 281/* committed to login turn off timeout */ 282 alarm(0); 283 284 if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) { 285 if (errno == EUSERS) 286 printf("%s.\n%s.\n", 287 "Too many users logged on already", 288 "Try again later"); 289 else if (errno == EPROCLIM) 290 printf("You have too many processes running.\n"); 291 else 292 perror("quota (Q_SETUID)"); 293 sleep(5); 294 exit(0); 295 } 296 time(&utmp.ut_time); 297 t = ttyslot(); 298 if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) { 299 lseek(f, (long)(t*sizeof(utmp)), 0); 300 SCPYN(utmp.ut_line, tty); 301 write(f, (char *)&utmp, sizeof(utmp)); 302 close(f); 303 } 304 if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) { 305 write(f, (char *)&utmp, sizeof(utmp)); 306 close(f); 307 } 308 quietlog = access(qlog, F_OK) == 0; 309 if ((f = open(lastlog, O_RDWR)) >= 0) { 310 struct lastlog ll; 311 312 lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0); 313 if (read(f, (char *) &ll, sizeof ll) == sizeof ll && 314 ll.ll_time != 0 && !quietlog) { 315 printf("Last login: %.*s ", 316 24-5, (char *)ctime(&ll.ll_time)); 317 if (*ll.ll_host != '\0') 318 printf("from %.*s\n", 319 sizeof (ll.ll_host), ll.ll_host); 320 else 321 printf("on %.*s\n", 322 sizeof (ll.ll_line), ll.ll_line); 323 } 324 lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0); 325 time(&ll.ll_time); 326 SCPYN(ll.ll_line, tty); 327 SCPYN(ll.ll_host, utmp.ut_host); 328 write(f, (char *) &ll, sizeof ll); 329 close(f); 330 } 331 chown(ttyn, pwd->pw_uid, TTYGID(pwd->pw_gid)); 332 if (!hflag && !rflag) /* XXX */ 333 ioctl(0, TIOCSWINSZ, &win); 334 chmod(ttyn, 0620); 335 setgid(pwd->pw_gid); 336 strncpy(name, utmp.ut_name, NMAX); 337 name[NMAX] = '\0'; 338 initgroups(name, pwd->pw_gid); 339 quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); 340 setuid(pwd->pw_uid); 341 /* destroy environment unless user has asked to preserve it */ 342 if (!pflag) 343 environ = envinit; 344 345 /* set up environment, this time without destruction */ 346 /* copy the environment before setenving */ 347 i = 0; 348 while (environ[i] != NULL) 349 i++; 350 envnew = (char **) malloc(sizeof (char *) * (i + 1)); 351 for (; i >= 0; i--) 352 envnew[i] = environ[i]; 353 environ = envnew; 354 355 setenv("HOME=", pwd->pw_dir, 1); 356 setenv("SHELL=", pwd->pw_shell, 1); 357 if (term[0] == '\0') 358 strncpy(term, stypeof(tty), sizeof(term)); 359 setenv("TERM=", term, 0); 360 setenv("USER=", pwd->pw_name, 1); 361 setenv("PATH=", ":/usr/ucb:/bin:/usr/bin", 0); 362 363 if ((namep = rindex(pwd->pw_shell, '/')) == NULL) 364 namep = pwd->pw_shell; 365 else 366 namep++; 367 strcat(minusnam, namep); 368 if (tty[sizeof("tty")-1] == 'd') 369 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 370 if (pwd->pw_uid == 0) 371 if (utmp.ut_host[0]) 372 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", 373 tty, HMAX, utmp.ut_host); 374 else 375 syslog(LOG_NOTICE, "ROOT LOGIN %s", tty); 376 if (!quietlog) { 377 struct stat st; 378 379 showmotd(); 380 strcat(maildir, pwd->pw_name); 381 if (stat(maildir, &st) == 0 && st.st_size != 0) 382 printf("You have %smail.\n", 383 (st.st_mtime > st.st_atime) ? "new " : ""); 384 } 385 signal(SIGALRM, SIG_DFL); 386 signal(SIGQUIT, SIG_DFL); 387 signal(SIGINT, SIG_DFL); 388 signal(SIGTSTP, SIG_IGN); 389 execlp(pwd->pw_shell, minusnam, 0); 390 perror(pwd->pw_shell); 391 printf("No shell\n"); 392 exit(0); 393} 394 395getloginname(up) 396 register struct utmp *up; 397{ 398 register char *namep; 399 char c; 400 401 while (up->ut_name[0] == '\0') { 402 namep = up->ut_name; 403 printf("login: "); 404 while ((c = getchar()) != '\n') { 405 if (c == ' ') 406 c = '_'; 407 if (c == EOF) 408 exit(0); 409 if (namep < up->ut_name+NMAX) 410 *namep++ = c; 411 } 412 } 413 strncpy(lusername, up->ut_name, NMAX); 414 lusername[NMAX] = 0; 415 if ((pwd = getpwnam(lusername)) == NULL) 416 pwd = &nouser; 417} 418 419timedout() 420{ 421 422 printf("Login timed out after %d seconds\n", timeout); 423 exit(0); 424} 425 426int stopmotd; 427catch() 428{ 429 430 signal(SIGINT, SIG_IGN); 431 stopmotd++; 432} 433 434rootterm(tty) 435 char *tty; 436{ 437 register struct ttyent *t; 438 439 if ((t = getttynam(tty)) != NULL) { 440 if (t->ty_status & TTY_SECURE) 441 return (1); 442 } 443 return (0); 444} 445 446showmotd() 447{ 448 FILE *mf; 449 register c; 450 451 signal(SIGINT, catch); 452 if ((mf = fopen("/etc/motd", "r")) != NULL) { 453 while ((c = getc(mf)) != EOF && stopmotd == 0) 454 putchar(c); 455 fclose(mf); 456 } 457 signal(SIGINT, SIG_IGN); 458} 459 460#undef UNKNOWN 461#define UNKNOWN "su" 462 463char * 464stypeof(ttyid) 465 char *ttyid; 466{ 467 register struct ttyent *t; 468 469 if (ttyid == NULL || (t = getttynam(ttyid)) == NULL) 470 return (UNKNOWN); 471 return (t->ty_type); 472} 473 474doremotelogin(host) 475 char *host; 476{ 477 getstr(rusername, sizeof (rusername), "remuser"); 478 getstr(lusername, sizeof (lusername), "locuser"); 479 getstr(term, sizeof(term), "Terminal type"); 480 if (getuid()) { 481 pwd = &nouser; 482 return(-1); 483 } 484 pwd = getpwnam(lusername); 485 if (pwd == NULL) { 486 pwd = &nouser; 487 return(-1); 488 } 489 return(ruserok(host, (pwd->pw_uid == 0), rusername, lusername)); 490} 491 492getstr(buf, cnt, err) 493 char *buf; 494 int cnt; 495 char *err; 496{ 497 char c; 498 499 do { 500 if (read(0, &c, 1) != 1) 501 exit(1); 502 if (--cnt < 0) { 503 printf("%s too long\r\n", err); 504 exit(1); 505 } 506 *buf++ = c; 507 } while (c != 0); 508} 509 510char *speeds[] = 511 { "0", "50", "75", "110", "134", "150", "200", "300", 512 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; 513#define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) 514 515doremoteterm(term, tp) 516 char *term; 517 struct sgttyb *tp; 518{ 519 register char *cp = index(term, '/'), **cpp; 520 char *speed; 521 522 if (cp) { 523 *cp++ = '\0'; 524 speed = cp; 525 cp = index(speed, '/'); 526 if (cp) 527 *cp++ = '\0'; 528 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) 529 if (strcmp(*cpp, speed) == 0) { 530 tp->sg_ispeed = tp->sg_ospeed = cpp-speeds; 531 break; 532 } 533 } 534 tp->sg_flags = ECHO|CRMOD|ANYP|XTABS; 535} 536 537/* 538 * Set the value of var to be arg in the Unix 4.2 BSD environment env. 539 * Var should end with '='. 540 * (bindings are of the form "var=value") 541 * This procedure assumes the memory for the first level of environ 542 * was allocated using malloc. 543 */ 544setenv(var, value, clobber) 545 char *var, *value; 546{ 547 extern char **environ; 548 int index = 0; 549 int varlen = strlen(var); 550 int vallen = strlen(value); 551 552 for (index = 0; environ[index] != NULL; index++) { 553 if (strncmp(environ[index], var, varlen) == 0) { 554 /* found it */ 555 if (!clobber) 556 return; 557 environ[index] = malloc(varlen + vallen + 1); 558 strcpy(environ[index], var); 559 strcat(environ[index], value); 560 return; 561 } 562 } 563 environ = (char **) realloc(environ, sizeof (char *) * (index + 2)); 564 if (environ == NULL) { 565 fprintf(stderr, "login: malloc out of memory\n"); 566 exit(1); 567 } 568 environ[index] = malloc(varlen + vallen + 1); 569 strcpy(environ[index], var); 570 strcat(environ[index], value); 571 environ[++index] = NULL; 572} 573 574tty_gid(default_gid) 575 int default_gid; 576{ 577 struct group *getgrnam(), *gr; 578 int gid = default_gid; 579 580 gr = getgrnam(TTYGRPNAME); 581 if (gr != (struct group *) 0) 582 gid = gr->gr_gid; 583 584 endgrent(); 585 586 return (gid); 587} 588