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