setgid to group "write" so that terminals need not be world writable
[unix-history] / usr / src / usr.bin / login / login.c.1
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
c89291e2 14static char sccsid[] = "@(#)login.c.1 5.13 (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
41#define WRITENAME "write" /* name of group to own ttys */
42#define WRITEGID write_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];
b4389814 94char *rhost;
86eb6c9e 95
88a01c09 96main(argc, argv)
3b8dd95e 97 char *argv[];
88a01c09
BJ
98{
99 register char *namep;
d3737d51 100 int pflag = 0, hflag = 0, t, f, c;
3b8dd95e 101 int invalid, quietlog;
f570e1ff 102 FILE *nlfd;
9479aa87 103 char *ttyn, *tty;
d3737d51
SL
104 int ldisc = 0, zero = 0, i;
105 char **envnew;
88a01c09 106
3b8dd95e
SL
107 signal(SIGALRM, timedout);
108 alarm(timeout);
88a01c09
BJ
109 signal(SIGQUIT, SIG_IGN);
110 signal(SIGINT, SIG_IGN);
3b8dd95e 111 setpriority(PRIO_PROCESS, 0, 0);
22d4760e 112 quota(Q_SETUID, 0, 0, 0);
3b8dd95e 113 /*
d3737d51 114 * -p is used by getty to tell login not to destroy the environment
3b8dd95e
SL
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 */
95a2c263 119 while (argc > 1) {
3b8dd95e 120 if (strcmp(argv[1], "-r") == 0) {
95a2c263
JB
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]);
3b8dd95e 127 SCPYN(utmp.ut_host, argv[2]);
95a2c263
JB
128 argc -= 2;
129 argv += 2;
130 continue;
b4389814 131 }
3b8dd95e 132 if (strcmp(argv[1], "-h") == 0 && getuid() == 0) {
95a2c263
JB
133 if (rflag || hflag) {
134 printf("Only one of -r and -h allowed\n");
135 exit(1);
136 }
d3737d51 137 hflag = 1;
3b8dd95e 138 SCPYN(utmp.ut_host, argv[2]);
95a2c263
JB
139 argc -= 2;
140 argv += 2;
141 continue;
b4389814 142 }
d3737d51
SL
143 if (strcmp(argv[1], "-p") == 0) {
144 argc--;
145 argv++;
146 pflag = 1;
95a2c263 147 continue;
d3737d51 148 }
95a2c263 149 break;
86eb6c9e 150 }
714accc5 151 ioctl(0, TIOCLSET, &zero);
c95ed2b2 152 ioctl(0, TIOCNXCL, 0);
4f8d3876
BJ
153 ioctl(0, FIONBIO, &zero);
154 ioctl(0, FIOASYNC, &zero);
714accc5 155 ioctl(0, TIOCGETP, &ttyb);
3b8dd95e
SL
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);
568353d4
MK
163 ttyb.sg_erase = CERASE;
164 ttyb.sg_kill = CKILL;
714accc5
SL
165 ioctl(0, TIOCSLTC, &ltc);
166 ioctl(0, TIOCSETC, &tc);
167 ioctl(0, TIOCSETP, &ttyb);
c32fb087 168 for (t = getdtablesize(); t > 2; t--)
88a01c09
BJ
169 close(t);
170 ttyn = ttyname(0);
6fc1474b 171 if (ttyn == (char *)0 || *ttyn == '\0')
88a01c09 172 ttyn = "/dev/tty??";
9479aa87
BJ
173 tty = rindex(ttyn, '/');
174 if (tty == NULL)
175 tty = ttyn;
176 else
177 tty++;
076ae92c 178 openlog("login", LOG_ODELAY, LOG_AUTH);
9479aa87 179 t = 0;
96aec705 180 invalid = FALSE;
f570e1ff
BJ
181 do {
182 ldisc = 0;
c95ed2b2 183 ioctl(0, TIOCSETD, &ldisc);
f570e1ff 184 SCPYN(utmp.ut_name, "");
3b8dd95e
SL
185 /*
186 * Name specified, take it.
187 */
188 if (argc > 1) {
f570e1ff
BJ
189 SCPYN(utmp.ut_name, argv[1]);
190 argc = 0;
191 }
3b8dd95e
SL
192 /*
193 * If remote login take given name,
194 * otherwise prompt user for something.
195 */
96aec705 196 if (rflag && !invalid)
3479a16a 197 SCPYN(utmp.ut_name, lusername);
a8fb897a 198 else
3b8dd95e 199 getloginname(&utmp);
96aec705 200 invalid = FALSE;
f570e1ff
BJ
201 if (!strcmp(pwd->pw_shell, "/bin/csh")) {
202 ldisc = NTTYDISC;
203 ioctl(0, TIOCSETD, &ldisc);
204 }
3b8dd95e
SL
205 /*
206 * If no remote login authentication and
207 * a password exists for this user, prompt
208 * for one and verify it.
209 */
95a2c263 210 if (usererr == -1 && *pwd->pw_passwd != '\0') {
3b8dd95e
SL
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;
f570e1ff 219 }
3b8dd95e
SL
220 /*
221 * If user not super-user, check for logins disabled.
222 */
f570e1ff 223 if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) {
f570e1ff
BJ
224 while ((c = getc(nlfd)) != EOF)
225 putchar(c);
226 fflush(stdout);
227 sleep(5);
228 exit(0);
229 }
3b8dd95e
SL
230 /*
231 * If valid so far and root is logging in,
232 * see if root logins on this terminal are permitted.
233 */
9479aa87 234 if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) {
df9d9536
KM
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);
f570e1ff
BJ
242 invalid = TRUE;
243 }
244 if (invalid) {
88a01c09 245 printf("Login incorrect\n");
9479aa87 246 if (++t >= 5) {
df9d9536
KM
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);
9479aa87 256 ioctl(0, TIOCHPCL, (struct sgttyb *) 0);
d3737d51 257 close(0), close(1), close(2);
9479aa87
BJ
258 sleep(10);
259 exit(1);
260 }
88a01c09 261 }
f570e1ff
BJ
262 if (*pwd->pw_shell == '\0')
263 pwd->pw_shell = "/bin/sh";
f570e1ff
BJ
264 if (chdir(pwd->pw_dir) < 0 && !invalid ) {
265 if (chdir("/") < 0) {
266 printf("No directory!\n");
267 invalid = TRUE;
268 } else {
3b8dd95e
SL
269 printf("No directory! %s\n",
270 "Logging in with home=/");
f570e1ff
BJ
271 pwd->pw_dir = "/";
272 }
88a01c09 273 }
3b8dd95e
SL
274 /*
275 * Remote login invalid must have been because
276 * of a restriction of some sort, no extra chances.
277 */
95a2c263 278 if (!usererr && invalid)
86eb6c9e 279 exit(1);
f570e1ff 280 } while (invalid);
3b8dd95e
SL
281/* committed to login turn off timeout */
282 alarm(0);
88a01c09 283
790c9c8b 284 if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
22d4760e
SL
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
6821e9c5 292 perror("quota (Q_SETUID)");
22d4760e
SL
293 sleep(5);
294 exit(0);
295 }
88a01c09
BJ
296 time(&utmp.ut_time);
297 t = ttyslot();
9479aa87 298 if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) {
88a01c09 299 lseek(f, (long)(t*sizeof(utmp)), 0);
9479aa87 300 SCPYN(utmp.ut_line, tty);
88a01c09
BJ
301 write(f, (char *)&utmp, sizeof(utmp));
302 close(f);
303 }
9479aa87 304 if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) {
88a01c09
BJ
305 write(f, (char *)&utmp, sizeof(utmp));
306 close(f);
307 }
9479aa87
BJ
308 quietlog = access(qlog, F_OK) == 0;
309 if ((f = open(lastlog, O_RDWR)) >= 0) {
f570e1ff
BJ
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 &&
3b8dd95e
SL
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);
f570e1ff
BJ
323 }
324 lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
325 time(&ll.ll_time);
9479aa87 326 SCPYN(ll.ll_line, tty);
3b8dd95e 327 SCPYN(ll.ll_host, utmp.ut_host);
f570e1ff
BJ
328 write(f, (char *) &ll, sizeof ll);
329 close(f);
330 }
c89291e2 331 chown(ttyn, pwd->pw_uid, WRITEGID);
c32fb087 332 if (!hflag && !rflag) /* XXX */
d3737d51 333 ioctl(0, TIOCSWINSZ, &win);
c89291e2 334 chmod(ttyn, 0620);
88a01c09 335 setgid(pwd->pw_gid);
e5321f7b
KM
336 strncpy(name, utmp.ut_name, NMAX);
337 name[NMAX] = '\0';
b1198826 338 initgroups(name, pwd->pw_gid);
22d4760e 339 quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
88a01c09 340 setuid(pwd->pw_uid);
d3737d51
SL
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
bed177dc
MK
355 setenv("HOME=", pwd->pw_dir, 1);
356 setenv("SHELL=", pwd->pw_shell, 1);
d3737d51
SL
357 if (term[0] == '\0')
358 strncpy(term, stypeof(tty), sizeof(term));
bed177dc
MK
359 setenv("TERM=", term, 0);
360 setenv("USER=", pwd->pw_name, 1);
361 setenv("PATH=", ":/usr/ucb:/bin:/usr/bin", 0);
d3737d51 362
88a01c09
BJ
363 if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
364 namep = pwd->pw_shell;
365 else
366 namep++;
367 strcat(minusnam, namep);
9479aa87 368 if (tty[sizeof("tty")-1] == 'd')
d3737d51
SL
369 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
370 if (pwd->pw_uid == 0)
df9d9536
KM
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);
4f8d3876 376 if (!quietlog) {
6821e9c5 377 struct stat st;
d3737d51 378
f570e1ff
BJ
379 showmotd();
380 strcat(maildir, pwd->pw_name);
6821e9c5
S
381 if (stat(maildir, &st) == 0 && st.st_size != 0)
382 printf("You have %smail.\n",
d3737d51 383 (st.st_mtime > st.st_atime) ? "new " : "");
f570e1ff 384 }
3b8dd95e 385 signal(SIGALRM, SIG_DFL);
88a01c09
BJ
386 signal(SIGQUIT, SIG_DFL);
387 signal(SIGINT, SIG_DFL);
5f87416f 388 signal(SIGTSTP, SIG_IGN);
88a01c09 389 execlp(pwd->pw_shell, minusnam, 0);
f570e1ff 390 perror(pwd->pw_shell);
88a01c09
BJ
391 printf("No shell\n");
392 exit(0);
393}
394
3b8dd95e
SL
395getloginname(up)
396 register struct utmp *up;
397{
398 register char *namep;
5a786176 399 char c;
3b8dd95e 400
3b8dd95e 401 while (up->ut_name[0] == '\0') {
d910ab7f 402 namep = up->ut_name;
5a786176 403 printf("login: ");
3b8dd95e
SL
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 }
d910ab7f
EW
413 strncpy(lusername, up->ut_name, NMAX);
414 lusername[NMAX] = 0;
d910ab7f 415 if ((pwd = getpwnam(lusername)) == NULL)
3b8dd95e 416 pwd = &nouser;
3b8dd95e
SL
417}
418
419timedout()
420{
421
422 printf("Login timed out after %d seconds\n", timeout);
423 exit(0);
424}
425
88a01c09
BJ
426int stopmotd;
427catch()
428{
1886582e 429
88a01c09
BJ
430 signal(SIGINT, SIG_IGN);
431 stopmotd++;
432}
433
f570e1ff 434rootterm(tty)
1886582e 435 char *tty;
f570e1ff 436{
9479aa87
BJ
437 register struct ttyent *t;
438
439 if ((t = getttynam(tty)) != NULL) {
440 if (t->ty_status & TTY_SECURE)
441 return (1);
f570e1ff 442 }
9479aa87 443 return (0);
f570e1ff
BJ
444}
445
88a01c09
BJ
446showmotd()
447{
448 FILE *mf;
449 register c;
450
451 signal(SIGINT, catch);
9479aa87 452 if ((mf = fopen("/etc/motd", "r")) != NULL) {
f570e1ff 453 while ((c = getc(mf)) != EOF && stopmotd == 0)
88a01c09
BJ
454 putchar(c);
455 fclose(mf);
456 }
457 signal(SIGINT, SIG_IGN);
458}
459
f570e1ff 460#undef UNKNOWN
88a01c09
BJ
461#define UNKNOWN "su"
462
463char *
464stypeof(ttyid)
3b8dd95e 465 char *ttyid;
88a01c09 466{
9479aa87 467 register struct ttyent *t;
88a01c09 468
9479aa87 469 if (ttyid == NULL || (t = getttynam(ttyid)) == NULL)
88a01c09 470 return (UNKNOWN);
9479aa87 471 return (t->ty_type);
88a01c09 472}
86eb6c9e 473
3b8dd95e
SL
474doremotelogin(host)
475 char *host;
476{
3b8dd95e
SL
477 getstr(rusername, sizeof (rusername), "remuser");
478 getstr(lusername, sizeof (lusername), "locuser");
d3737d51 479 getstr(term, sizeof(term), "Terminal type");
4cf9fc9e
SL
480 if (getuid()) {
481 pwd = &nouser;
95a2c263 482 return(-1);
4cf9fc9e 483 }
3b8dd95e 484 pwd = getpwnam(lusername);
4cf9fc9e
SL
485 if (pwd == NULL) {
486 pwd = &nouser;
95a2c263 487 return(-1);
3b8dd95e 488 }
95a2c263 489 return(ruserok(host, (pwd->pw_uid == 0), rusername, lusername));
3b8dd95e
SL
490}
491
86eb6c9e
BJ
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}
4f8d3876 509
3b8dd95e
SL
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{
d3737d51
SL
519 register char *cp = index(term, '/'), **cpp;
520 char *speed;
3b8dd95e
SL
521
522 if (cp) {
d3737d51
SL
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;
3b8dd95e
SL
531 break;
532 }
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 */
bed177dc 544setenv(var, value, clobber)
d3737d51
SL
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 */
bed177dc
MK
555 if (!clobber)
556 return;
d3737d51
SL
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}
c89291e2
KM
573
574write_gid()
575{
576 struct group *getgrnam(), *gr;
577 int gid = 0;
578
579 gr = getgrnam(WRITENAME);
580 if (gr != (struct group *) 0)
581 gid = gr->gr_gid;
582
583 endgrent();
584
585 return gid;
586}