login.c revision 1.5 1 /*-
2 * Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University
3 * of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University of California.\n\
37 All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 static char sccsid[] = "@(#)login.c 5.78 (Berkeley) 6/29/92";
42 #endif /* not lint */
43
44 /*
45 * login [ name ]
46 * login -h hostname (for telnetd, etc.)
47 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
48 */
49
50 #include <sys/param.h>
51 #include <sys/stat.h>
52 #include <sys/time.h>
53 #include <sys/resource.h>
54 #include <sys/file.h>
55
56 #include <signal.h>
57 #include <ttyent.h>
58 #include <syslog.h>
59 #include <setjmp.h>
60 #include <tzfile.h>
61 #include <utmp.h>
62 #include <errno.h>
63 #include <grp.h>
64 #include <pwd.h>
65 #include <unistd.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include "pathnames.h"
70
71 void badlogin __P((char *));
72 void checknologin __P((void));
73 void dolastlog __P((int));
74 void getloginname __P((void));
75 void motd __P((void));
76 int rootterm __P((char *));
77 void sigint __P((int));
78 void sleepexit __P((int));
79 char *stypeof __P((char *));
80 void timedout __P((int));
81 #ifdef KERBEROS
82 int klogin __P((struct passwd *, char *, char *, char *));
83 #endif
84
85 #define TTYGRPNAME "tty" /* name of group to own ttys */
86
87 /*
88 * This bounds the time given to login. Not a define so it can
89 * be patched on machines where it's too small.
90 */
91 int timeout = 300;
92
93 #ifdef KERBEROS
94 int notickets = 1;
95 char *instance;
96 char *krbtkfile_env;
97 int authok;
98 #endif
99
100 struct passwd *pwd;
101 int failures;
102 char term[64], *envinit[1], *hostname, *username, *tty;
103
104 int
105 main(argc, argv)
106 int argc;
107 char *argv[];
108 {
109 extern char **environ;
110 register int ch;
111 register char *p;
112 struct group *gr;
113 struct stat st;
114 struct timeval tp;
115 struct utmp utmp;
116 int ask, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval, uid;
117 char *domain, *salt, *ttyn;
118 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
119 char localhost[MAXHOSTNAMELEN];
120
121 (void)signal(SIGALRM, timedout);
122 (void)alarm((u_int)timeout);
123 (void)signal(SIGQUIT, SIG_IGN);
124 (void)signal(SIGINT, SIG_IGN);
125 (void)setpriority(PRIO_PROCESS, 0, 0);
126
127 openlog("login", LOG_ODELAY, LOG_AUTH);
128
129 /*
130 * -p is used by getty to tell login not to destroy the environment
131 * -f is used to skip a second login authentication
132 * -h is used by other servers to pass the name of the remote
133 * host to login so that it may be placed in utmp and wtmp
134 */
135 domain = NULL;
136 if (gethostname(localhost, sizeof(localhost)) < 0)
137 syslog(LOG_ERR, "couldn't get local hostname: %m");
138 else
139 domain = index(localhost, '.');
140
141 fflag = hflag = pflag = 0;
142 uid = getuid();
143 while ((ch = getopt(argc, argv, "fh:p")) != EOF)
144 switch (ch) {
145 case 'f':
146 fflag = 1;
147 break;
148 case 'h':
149 if (uid) {
150 (void)fprintf(stderr,
151 "login: -h option: %s\n", strerror(EPERM));
152 exit(1);
153 }
154 hflag = 1;
155 if (domain && (p = index(optarg, '.')) &&
156 strcasecmp(p, domain) == 0)
157 *p = 0;
158 hostname = optarg;
159 break;
160 case 'p':
161 pflag = 1;
162 break;
163 case '?':
164 default:
165 if (!uid)
166 syslog(LOG_ERR, "invalid flag %c", ch);
167 (void)fprintf(stderr,
168 "usage: login [-fp] [-h hostname] [username]\n");
169 exit(1);
170 }
171 argc -= optind;
172 argv += optind;
173
174 if (*argv) {
175 username = *argv;
176 ask = 0;
177 } else
178 ask = 1;
179
180 for (cnt = getdtablesize(); cnt > 2; cnt--)
181 (void)close(cnt);
182
183 ttyn = ttyname(STDIN_FILENO);
184 if (ttyn == NULL || *ttyn == '\0') {
185 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
186 ttyn = tname;
187 }
188 if (tty = rindex(ttyn, '/'))
189 ++tty;
190 else
191 tty = ttyn;
192
193 for (cnt = 0;; ask = 1) {
194 if (ask) {
195 fflag = 0;
196 getloginname();
197 }
198 rootlogin = 0;
199 #ifdef KERBEROS
200 if ((instance = index(username, '.')) != NULL) {
201 if (strncmp(instance, ".root", 5) == 0)
202 rootlogin = 1;
203 *instance++ = '\0';
204 } else
205 instance = "";
206 #endif
207 if (strlen(username) > UT_NAMESIZE)
208 username[UT_NAMESIZE] = '\0';
209
210 /*
211 * Note if trying multiple user names; log failures for
212 * previous user name, but don't bother logging one failure
213 * for nonexistent name (mistyped username).
214 */
215 if (failures && strcmp(tbuf, username)) {
216 if (failures > (pwd ? 0 : 1))
217 badlogin(tbuf);
218 failures = 0;
219 }
220 (void)strcpy(tbuf, username);
221
222 if (pwd = getpwnam(username))
223 salt = pwd->pw_passwd;
224 else
225 salt = "xx";
226
227 /*
228 * if we have a valid account name, and it doesn't have a
229 * password, or the -f option was specified and the caller
230 * is root or the caller isn't changing their uid, don't
231 * authenticate.
232 */
233 if (pwd && (*pwd->pw_passwd == '\0' ||
234 fflag && (uid == 0 || uid == pwd->pw_uid)))
235 break;
236 fflag = 0;
237 if (pwd && pwd->pw_uid == 0)
238 rootlogin = 1;
239
240 (void)setpriority(PRIO_PROCESS, 0, -4);
241
242 p = getpass("Password:");
243
244 if (pwd) {
245 #ifdef KERBEROS
246 rval = klogin(pwd, instance, localhost, p);
247 if (rval == 0)
248 authok = 1;
249 else if (rval == 1)
250 rval = strcmp(crypt(p, salt), pwd->pw_passwd);
251 #else
252 rval = strcmp(crypt(p, salt), pwd->pw_passwd);
253 #endif
254 }
255 bzero(p, strlen(p));
256
257 (void)setpriority(PRIO_PROCESS, 0, 0);
258
259 /*
260 * If trying to log in as root without Kerberos,
261 * but with insecure terminal, refuse the login attempt.
262 */
263 #ifdef KERBEROS
264 if (authok == 0)
265 #endif
266 if (pwd && rootlogin && !rootterm(tty)) {
267 (void)fprintf(stderr,
268 "%s login refused on this terminal.\n",
269 pwd->pw_name);
270 if (hostname)
271 syslog(LOG_NOTICE,
272 "LOGIN %s REFUSED FROM %s ON TTY %s",
273 pwd->pw_name, hostname, tty);
274 else
275 syslog(LOG_NOTICE,
276 "LOGIN %s REFUSED ON TTY %s",
277 pwd->pw_name, tty);
278 continue;
279 }
280
281 if (pwd && !rval)
282 break;
283
284 (void)printf("Login incorrect\n");
285 failures++;
286 /* we allow 10 tries, but after 3 we start backing off */
287 if (++cnt > 3) {
288 if (cnt >= 10) {
289 badlogin(username);
290 sleepexit(1);
291 }
292 sleep((u_int)((cnt - 3) * 5));
293 }
294 }
295
296 /* committed to login -- turn off timeout */
297 (void)alarm((u_int)0);
298
299 endpwent();
300
301 /* if user not super-user, check for disabled logins */
302 if (!rootlogin)
303 checknologin();
304
305 if (chdir(pwd->pw_dir) < 0) {
306 (void)printf("No home directory %s!\n", pwd->pw_dir);
307 if (chdir("/"))
308 exit(0);
309 pwd->pw_dir = "/";
310 (void)printf("Logging in with home = \"/\".\n");
311 }
312
313 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
314
315 if (pwd->pw_change || pwd->pw_expire)
316 (void)gettimeofday(&tp, (struct timezone *)NULL);
317 if (pwd->pw_change)
318 if (tp.tv_sec >= pwd->pw_change) {
319 (void)printf("Sorry -- your password has expired.\n");
320 sleepexit(1);
321 } else if (pwd->pw_change - tp.tv_sec <
322 2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
323 (void)printf("Warning: your password expires on %s",
324 ctime(&pwd->pw_change));
325 if (pwd->pw_expire)
326 if (tp.tv_sec >= pwd->pw_expire) {
327 (void)printf("Sorry -- your account has expired.\n");
328 sleepexit(1);
329 } else if (pwd->pw_expire - tp.tv_sec <
330 2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
331 (void)printf("Warning: your account expires on %s",
332 ctime(&pwd->pw_expire));
333
334 /* Nothing else left to fail -- really log in. */
335 bzero((void *)&utmp, sizeof(utmp));
336 (void)time(&utmp.ut_time);
337 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
338 if (hostname)
339 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
340 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
341 login(&utmp);
342
343 dolastlog(quietlog);
344
345 (void)chown(ttyn, pwd->pw_uid,
346 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
347 (void)setgid(pwd->pw_gid);
348
349 initgroups(username, pwd->pw_gid);
350
351 if (*pwd->pw_shell == '\0')
352 pwd->pw_shell = _PATH_BSHELL;
353
354 /* Destroy environment unless user has requested its preservation. */
355 if (!pflag)
356 environ = envinit;
357 (void)setenv("HOME", pwd->pw_dir, 1);
358 (void)setenv("SHELL", pwd->pw_shell, 1);
359 if (term[0] == '\0')
360 (void)strncpy(term, stypeof(tty), sizeof(term));
361 (void)setenv("TERM", term, 0);
362 (void)setenv("LOGNAME", pwd->pw_name, 1);
363 (void)setenv("USER", pwd->pw_name, 1);
364 (void)setenv("PATH", _PATH_DEFPATH, 0);
365 #ifdef KERBEROS
366 if (krbtkfile_env)
367 (void)setenv("KRBTKFILE", krbtkfile_env, 1);
368 #endif
369
370 if (tty[sizeof("tty")-1] == 'd')
371 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
372
373 /* If fflag is on, assume caller/authenticator has logged root login. */
374 if (rootlogin && fflag == 0)
375 if (hostname)
376 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
377 username, tty, hostname);
378 else
379 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
380
381 #ifdef KERBEROS
382 if (!quietlog && notickets == 1)
383 (void)printf("Warning: no Kerberos tickets issued.\n");
384 #endif
385
386 if (!quietlog) {
387 (void)printf(
388 "Copyright (c) 1980,1983,1986,1988,1990,1991 The Regents of the University\n%s",
389 "of California. All rights reserved.\n\n");
390 motd();
391 (void)snprintf(tbuf,
392 sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
393 if (stat(tbuf, &st) == 0 && st.st_size != 0)
394 (void)printf("You have %smail.\n",
395 (st.st_mtime > st.st_atime) ? "new " : "");
396 }
397
398 (void)signal(SIGALRM, SIG_DFL);
399 (void)signal(SIGQUIT, SIG_DFL);
400 (void)signal(SIGINT, SIG_DFL);
401 (void)signal(SIGTSTP, SIG_IGN);
402
403 tbuf[0] = '-';
404 (void)strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ?
405 p + 1 : pwd->pw_shell);
406
407 if (setlogin(pwd->pw_name) < 0)
408 syslog(LOG_ERR, "setlogin() failure: %m");
409
410 /* Discard permissions last so can't get killed and drop core. */
411 if (rootlogin)
412 (void) setuid(0);
413 else
414 (void) setuid(pwd->pw_uid);
415
416 execlp(pwd->pw_shell, tbuf, 0);
417 (void)fprintf(stderr, "%s: %s\n", pwd->pw_shell, strerror(errno));
418 exit(1);
419 }
420
421 #ifdef KERBEROS
422 #define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */
423 #else
424 #define NBUFSIZ (UT_NAMESIZE + 1)
425 #endif
426
427 void
428 getloginname()
429 {
430 register int ch;
431 register char *p;
432 static char nbuf[NBUFSIZ];
433
434 for (;;) {
435 (void)printf("login: ");
436 for (p = nbuf; (ch = getchar()) != '\n'; ) {
437 if (ch == EOF) {
438 badlogin(username);
439 exit(0);
440 }
441 if (p < nbuf + (NBUFSIZ - 1))
442 *p++ = ch;
443 }
444 if (p > nbuf)
445 if (nbuf[0] == '-')
446 (void)fprintf(stderr,
447 "login names may not start with '-'.\n");
448 else {
449 *p = '\0';
450 username = nbuf;
451 break;
452 }
453 }
454 }
455
456 int
457 rootterm(ttyn)
458 char *ttyn;
459 {
460 struct ttyent *t;
461
462 return((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
463 }
464
465 jmp_buf motdinterrupt;
466
467 void
468 motd()
469 {
470 register int fd, nchars;
471 sig_t oldint;
472 char tbuf[8192];
473
474 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
475 return;
476 oldint = signal(SIGINT, sigint);
477 if (setjmp(motdinterrupt) == 0)
478 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
479 (void)write(fileno(stdout), tbuf, nchars);
480 (void)signal(SIGINT, oldint);
481 (void)close(fd);
482 }
483
484 /* ARGSUSED */
485 void
486 sigint(signo)
487 int signo;
488 {
489 longjmp(motdinterrupt, 1);
490 }
491
492 /* ARGSUSED */
493 void
494 timedout(signo)
495 int signo;
496 {
497 (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
498 exit(0);
499 }
500
501 void
502 checknologin()
503 {
504 register int fd, nchars;
505 char tbuf[8192];
506
507 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
508 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
509 (void)write(fileno(stdout), tbuf, nchars);
510 sleepexit(0);
511 }
512 }
513
514 void
515 dolastlog(quiet)
516 int quiet;
517 {
518 struct lastlog ll;
519 int fd;
520
521 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
522 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
523 if (!quiet) {
524 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
525 ll.ll_time != 0) {
526 (void)printf("Last login: %.*s ",
527 24-5, (char *)ctime(&ll.ll_time));
528 if (*ll.ll_host != '\0')
529 (void)printf("from %.*s\n",
530 (int)sizeof(ll.ll_host),
531 ll.ll_host);
532 else
533 (void)printf("on %.*s\n",
534 (int)sizeof(ll.ll_line),
535 ll.ll_line);
536 }
537 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
538 }
539 bzero((void *)&ll, sizeof(ll));
540 (void)time(&ll.ll_time);
541 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
542 if (hostname)
543 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
544 (void)write(fd, (char *)&ll, sizeof(ll));
545 (void)close(fd);
546 }
547 }
548
549 void
550 badlogin(name)
551 char *name;
552 {
553 if (failures == 0)
554 return;
555 if (hostname) {
556 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
557 failures, failures > 1 ? "S" : "", hostname);
558 syslog(LOG_AUTHPRIV|LOG_NOTICE,
559 "%d LOGIN FAILURE%s FROM %s, %s",
560 failures, failures > 1 ? "S" : "", hostname, name);
561 } else {
562 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
563 failures, failures > 1 ? "S" : "", tty);
564 syslog(LOG_AUTHPRIV|LOG_NOTICE,
565 "%d LOGIN FAILURE%s ON %s, %s",
566 failures, failures > 1 ? "S" : "", tty, name);
567 }
568 }
569
570 #undef UNKNOWN
571 #define UNKNOWN "su"
572
573 char *
574 stypeof(ttyid)
575 char *ttyid;
576 {
577 struct ttyent *t;
578
579 return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
580 }
581
582 void
583 sleepexit(eval)
584 int eval;
585 {
586 (void)sleep((u_int)5);
587 exit(eval);
588 }
589