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