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