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