Deleted Added
full compact
login.c (87233) login.c (87628)
1/*-
2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 * The Regents of the University 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
1/*-
2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 * The Regents of the University 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#include <sys/cdefs.h>
35
36__FBSDID("$FreeBSD: head/usr.bin/login/login.c 87233 2001-12-02 20:54:57Z markm $");
37
34#if 0
38#ifndef lint
35#ifndef lint
39static const char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
36static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
40#endif
37#endif
38#endif
41
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/usr.bin/login/login.c 87628 2001-12-10 21:13:08Z dwmalone $");
42
42/*
43 * login [ name ]
44 * login -h hostname (for telnetd, etc.)
45 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
46 */
47
48#include <sys/copyright.h>
49#include <sys/param.h>
50#include <sys/file.h>
51#include <sys/socket.h>
52#include <sys/stat.h>
53#include <sys/time.h>
54#include <sys/resource.h>
55#include <sys/wait.h>
56#include <netinet/in.h>
57#include <arpa/inet.h>
58
59#include <err.h>
60#include <errno.h>
61#include <grp.h>
62#include <libutil.h>
63#include <login_cap.h>
64#include <netdb.h>
65#include <pwd.h>
66#include <setjmp.h>
67#include <signal.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <syslog.h>
72#include <ttyent.h>
73#include <unistd.h>
74#include <utmp.h>
75
76#ifndef NO_PAM
77#include <security/pam_appl.h>
78#include <security/pam_misc.h>
79#endif
80
81#include "login.h"
82#include "pathnames.h"
83
84/* wrapper for KAME-special getnameinfo() */
85#ifndef NI_WITHSCOPEID
86#define NI_WITHSCOPEID 0
87#endif
88
89static int auth_traditional __P((void));
90static void badlogin __P((char *));
91static void dolastlog __P((int));
92static void getloginname __P((void));
93static void motd __P((const char *));
94static void refused __P((const char *,const char *,int));
95static int rootterm __P((char *));
96static void sigint __P((int));
97static void sleepexit __P((int));
98static const char *stypeof __P((char *));
99static void timedout __P((int));
100static void usage __P((void));
101
102#ifndef NO_PAM
103static int auth_pam __P((void));
104static int export_pam_environment __P((void));
105static int ok_to_export __P((const char *));
106
107static pam_handle_t *pamh = NULL;
108static char **environ_pam;
109
110#define PAM_END { \
111 if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
112 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
113 if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
114 syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
115 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
116 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
117}
118#endif /* NO_PAM */
119
120#define TTYGRPNAME "tty" /* group to own ttys */
121#define DEFAULT_BACKOFF 3
122#define DEFAULT_RETRIES 10
123#define DEFAULT_PROMPT "login: "
124#define DEFAULT_PASSWD_PROMPT "Password:"
125#define INVALID_HOST "invalid hostname"
126#define UNKNOWN "su"
127#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
128#define NBUFSIZ UT_NAMESIZE + 64
129
130/*
131 * This bounds the time given to login. Not a define so it can
132 * be patched on machines where it's too small.
133 */
134u_int timeout = 300;
135
136/* Buffer for signal handling of timeout */
137jmp_buf timeout_buf;
138
139struct passwd *pwd;
140int failures;
141char *term, *envinit[1], *hostname, *passwd_prompt, *prompt, *tty, *username;
142char full_hostname[MAXHOSTNAMELEN];
143
144int
145main(argc, argv)
146 int argc;
147 char *argv[];
148{
149 struct group *gr;
150 struct stat st;
151 struct timeval tp;
152 struct utmp utmp;
153 int rootok, retries, backoff;
154 int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
155 time_t warntime;
156 uid_t uid, euid;
157 gid_t egid;
158 char *p, *ttyn;
159 char tbuf[MAXPATHLEN + 2];
160 char tname[sizeof(_PATH_TTY) + 10];
161 char *shell = NULL;
162 static char default_prompt[] = DEFAULT_PROMPT;
163 static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
164 static char invalid_host[] = INVALID_HOST;
165 login_cap_t *lc = NULL;
166#ifndef NO_PAM
167 pid_t pid;
168 int e;
169#endif /* NO_PAM */
170
171 (void)signal(SIGQUIT, SIG_IGN);
172 (void)signal(SIGINT, SIG_IGN);
173 (void)signal(SIGHUP, SIG_IGN);
174 if (setjmp(timeout_buf)) {
175 if (failures)
176 badlogin(tbuf);
177 (void)fprintf(stderr, "Login timed out after %d seconds\n",
178 timeout);
179 exit(0);
180 }
181 (void)signal(SIGALRM, timedout);
182 (void)alarm(timeout);
183 (void)setpriority(PRIO_PROCESS, 0, 0);
184
185 openlog("login", LOG_ODELAY, LOG_AUTH);
186
187 /*
188 * -p is used by getty to tell login not to destroy the environment
189 * -f is used to skip a second login authentication
190 * -h is used by other servers to pass the name of the remote
191 * host to login so that it may be placed in utmp and wtmp
192 */
193 *full_hostname = '\0';
194 term = NULL;
195
196 fflag = hflag = pflag = 0;
197 uid = getuid();
198 euid = geteuid();
199 egid = getegid();
200 while ((ch = getopt(argc, argv, "fh:p")) != -1)
201 switch (ch) {
202 case 'f':
203 fflag = 1;
204 break;
205 case 'h':
206 if (uid)
207 errx(1, "-h option: %s", strerror(EPERM));
208 hflag = 1;
209 if (strlcpy(full_hostname, optarg,
210 sizeof(full_hostname)) >= sizeof(full_hostname))
211 errx(1, "-h option: %s: exceeds maximum "
212 "hostname size", optarg);
213
214 trimdomain(optarg, UT_HOSTSIZE);
215
216 if (strlen(optarg) > UT_HOSTSIZE) {
217 struct addrinfo hints, *res;
218 int ga_err;
219
220 memset(&hints, 0, sizeof(hints));
221 hints.ai_family = AF_UNSPEC;
222 ga_err = getaddrinfo(optarg, NULL, &hints,
223 &res);
224 if (ga_err == 0) {
225 char hostbuf[MAXHOSTNAMELEN];
226
227 getnameinfo(res->ai_addr,
228 res->ai_addrlen,
229 hostbuf,
230 sizeof(hostbuf), NULL, 0,
231 NI_NUMERICHOST|
232 NI_WITHSCOPEID);
233 optarg = strdup(hostbuf);
234 if (optarg == NULL) {
235 syslog(LOG_NOTICE,
236 "strdup(): %m");
237 sleepexit(1);
238 }
239 } else
240 optarg = invalid_host;
241 if (res != NULL)
242 freeaddrinfo(res);
243 }
244 hostname = optarg;
245 break;
246 case 'p':
247 pflag = 1;
248 break;
249 case '?':
250 default:
251 if (!uid)
252 syslog(LOG_ERR, "invalid flag %c", ch);
253 usage();
254 }
255 argc -= optind;
256 argv += optind;
257
258 if (*argv) {
259 username = *argv;
260 ask = 0;
261 } else
262 ask = 1;
263
264 for (cnt = getdtablesize(); cnt > 2; cnt--)
265 (void)close(cnt);
266
267 ttyn = ttyname(STDIN_FILENO);
268 if (ttyn == NULL || *ttyn == '\0') {
269 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
270 ttyn = tname;
271 }
272 if ((tty = strrchr(ttyn, '/')) != NULL)
273 ++tty;
274 else
275 tty = ttyn;
276
277 /*
278 * Get "login-retries" & "login-backoff" from default class
279 */
280 lc = login_getclass(NULL);
281 prompt = login_getcapstr(lc, "prompt", default_prompt, default_prompt);
282 passwd_prompt = login_getcapstr(lc, "passwd_prompt",
283 default_passwd_prompt, default_passwd_prompt);
284 retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES,
285 DEFAULT_RETRIES);
286 backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF,
287 DEFAULT_BACKOFF);
288 login_close(lc);
289 lc = NULL;
290
291 for (cnt = 0;; ask = 1) {
292 if (ask) {
293 fflag = 0;
294 getloginname();
295 }
296 rootlogin = 0;
297 rootok = rootterm(tty); /* Default (auth may change) */
298
299 if (strlen(username) > UT_NAMESIZE)
300 username[UT_NAMESIZE] = '\0';
301
302 /*
303 * Note if trying multiple user names; log failures for
304 * previous user name, but don't bother logging one failure
305 * for nonexistent name (mistyped username).
306 */
307 if (failures && strcmp(tbuf, username)) {
308 if (failures > (pwd ? 0 : 1))
309 badlogin(tbuf);
310 }
311 (void)strlcpy(tbuf, username, sizeof(tbuf));
312
313 pwd = getpwnam(username);
314
315 /*
316 * if we have a valid account name, and it doesn't have a
317 * password, or the -f option was specified and the caller
318 * is root or the caller isn't changing their uid, don't
319 * authenticate.
320 */
321 if (pwd != NULL) {
322 if (pwd->pw_uid == 0)
323 rootlogin = 1;
324
325 if (fflag && (uid == (uid_t)0 ||
326 uid == (uid_t)pwd->pw_uid)) {
327 /* already authenticated */
328 break;
329 } else if (pwd->pw_passwd[0] == '\0') {
330 if (!rootlogin || rootok) {
331 /* pretend password okay */
332 rval = 0;
333 goto ttycheck;
334 }
335 }
336 }
337
338 fflag = 0;
339
340 (void)setpriority(PRIO_PROCESS, 0, -4);
341
342#ifndef NO_PAM
343 /*
344 * Try to authenticate using PAM. If a PAM system error
345 * occurs, perhaps because of a botched configuration,
346 * then fall back to using traditional Unix authentication.
347 */
348 if ((rval = auth_pam()) == -1)
349#endif /* NO_PAM */
350 rval = auth_traditional();
351
352 (void)setpriority(PRIO_PROCESS, 0, 0);
353
354 /*
355 * PAM authentication may have changed "pwd" to the
356 * entry for the template user. Check again to see if
357 * this is a root login after all.
358 */
359 if (pwd != NULL && pwd->pw_uid == 0)
360 rootlogin = 1;
361
362 ttycheck:
363 /*
364 * If trying to log in as root without Kerberos,
365 * but with insecure terminal, refuse the login attempt.
366 */
367 if (pwd && !rval) {
368 if (rootlogin && !rootok)
369 refused(NULL, "NOROOT", 0);
370 else /* valid password & authenticated */
371 break;
372 }
373
374 (void)printf("Login incorrect\n");
375 failures++;
376
377 /*
378 * we allow up to 'retry' (10) tries,
379 * but after 'backoff' (3) we start backing off
380 */
381 if (++cnt > backoff) {
382 if (cnt >= retries) {
383 badlogin(username);
384 sleepexit(1);
385 }
386 sleep((u_int)((cnt - backoff) * 5));
387 }
388 }
389
390 /* committed to login -- turn off timeout */
391 (void)alarm((u_int)0);
392 (void)signal(SIGHUP, SIG_DFL);
393
394 endpwent();
395
396 /*
397 * Establish the login class.
398 */
399 lc = login_getpwclass(pwd);
400
401 quietlog = login_getcapbool(lc, "hushlogin", 0);
402 /*
403 * Switching needed for NFS with root access disabled.
404 *
405 * XXX: This change fails to modify the additional groups for the
406 * process, and as such, may restrict rights normally granted
407 * through those groups.
408 */
409 (void)setegid(pwd->pw_gid);
410 (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
411 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
412 if (login_getcapbool(lc, "requirehome", 0))
413 refused("Home directory not available", "HOMEDIR", 1);
414 if (chdir("/") < 0)
415 refused("Cannot find root directory", "ROOTDIR", 1);
416 if (!quietlog || *pwd->pw_dir)
417 printf("No home directory.\nLogging in with home = \"/\".\n");
418 pwd->pw_dir = strdup("/");
419 if (pwd->pw_dir == NULL) {
420 syslog(LOG_NOTICE, "strdup(): %m");
421 sleepexit(1);
422 }
423 }
424 (void)seteuid(euid);
425 (void)setegid(egid);
426 if (!quietlog)
427 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
428
429 if (pwd->pw_change || pwd->pw_expire)
430 (void)gettimeofday(&tp, (struct timezone *)NULL);
431
432 warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
433 DEFAULT_WARN);
434
435 if (pwd->pw_expire) {
436 if (tp.tv_sec >= pwd->pw_expire) {
437 refused("Sorry -- your account has expired", "EXPIRED",
438 1);
439 } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
440 (void)printf("Warning: your account expires on %s",
441 ctime(&pwd->pw_expire));
442 }
443
444 if (lc != NULL) {
445 if (hostname) {
446 struct addrinfo hints, *res;
447 int ga_err;
448
449 memset(&hints, 0, sizeof(hints));
450 hints.ai_family = AF_UNSPEC;
451 ga_err = getaddrinfo(full_hostname, NULL, &hints,
452 &res);
453 if (ga_err == 0) {
454 char hostbuf[MAXHOSTNAMELEN];
455
456 getnameinfo(res->ai_addr, res->ai_addrlen,
457 hostbuf, sizeof(hostbuf), NULL, 0,
458 NI_NUMERICHOST|NI_WITHSCOPEID);
459 if ((optarg = strdup(hostbuf)) == NULL) {
460 syslog(LOG_NOTICE, "strdup(): %m");
461 sleepexit(1);
462 }
463 } else
464 optarg = NULL;
465 if (res != NULL)
466 freeaddrinfo(res);
467 if (!auth_hostok(lc, full_hostname, optarg))
468 refused("Permission denied", "HOST", 1);
469 }
470
471 if (!auth_ttyok(lc, tty))
472 refused("Permission denied", "TTY", 1);
473
474 if (!auth_timeok(lc, time(NULL)))
475 refused("Logins not available right now", "TIME", 1);
476 }
477 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
478 if (*pwd->pw_shell == '\0')
479 pwd->pw_shell = strdup(_PATH_BSHELL);
480 if (pwd->pw_shell == NULL) {
481 syslog(LOG_NOTICE, "strdup(): %m");
482 sleepexit(1);
483 }
484 if (*shell == '\0') /* Not overridden */
485 shell = pwd->pw_shell;
486 if ((shell = strdup(shell)) == NULL) {
487 syslog(LOG_NOTICE, "strdup(): %m");
488 sleepexit(1);
489 }
490
491#ifdef LOGIN_ACCESS
492 if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
493 refused("Permission denied", "ACCESS", 1);
494#endif /* LOGIN_ACCESS */
495
496 /* Nothing else left to fail -- really log in. */
497 memset((void *)&utmp, 0, sizeof(utmp));
498 (void)time(&utmp.ut_time);
499 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
500 if (hostname)
501 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
502 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
503 login(&utmp);
504
505 dolastlog(quietlog);
506
507 /*
508 * Set device protections, depending on what terminal the
509 * user is logged in. This feature is used on Suns to give
510 * console users better privacy.
511 */
512 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
513
514 /*
515 * Clear flags of the tty. None should be set, and when the
516 * user sets them otherwise, this can cause the chown to fail.
517 * Since it isn't clear that flags are useful on character
518 * devices, we just clear them.
519 */
520 if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
521 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
522 if (chown(ttyn, pwd->pw_uid,
523 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
524 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
525
526
527 /*
528 * Preserve TERM if it happens to be already set.
529 */
530 if ((term = getenv("TERM")) != NULL) {
531 if ((term = strdup(term)) == NULL) {
532 syslog(LOG_NOTICE,
533 "strdup(): %m");
534 sleepexit(1);
535 }
536 }
537
538 /*
539 * Exclude cons/vt/ptys only, assume dialup otherwise
540 * TODO: Make dialup tty determination a library call
541 * for consistency (finger etc.)
542 */
543 if (hostname==NULL && isdialuptty(tty))
544 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
545
546#ifdef LOGALL
547 /*
548 * Syslog each successful login, so we don't have to watch hundreds
549 * of wtmp or lastlogin files.
550 */
551 if (hostname)
552 syslog(LOG_INFO, "login from %s on %s as %s",
553 full_hostname, tty, pwd->pw_name);
554 else
555 syslog(LOG_INFO, "login on %s as %s",
556 tty, pwd->pw_name);
557#endif
558
559 /*
560 * If fflag is on, assume caller/authenticator has logged root login.
561 */
562 if (rootlogin && fflag == 0)
563 {
564 if (hostname)
565 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
566 username, tty, full_hostname);
567 else
568 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
569 username, tty);
570 }
571
572 /*
573 * Destroy environment unless user has requested its preservation.
574 * We need to do this before setusercontext() because that may
575 * set or reset some environment variables.
576 */
577 if (!pflag)
578 environ = envinit;
579
580 /*
581 * PAM modules might add supplementary groups during pam_setcred().
582 */
583 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
584 syslog(LOG_ERR, "setusercontext() failed - exiting");
585 exit(1);
586 }
587
588#ifndef NO_PAM
589 if (pamh) {
590 if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
591 syslog(LOG_ERR, "pam_open_session: %s",
592 pam_strerror(pamh, e));
593 } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
594 != PAM_SUCCESS) {
595 syslog(LOG_ERR, "pam_setcred: %s",
596 pam_strerror(pamh, e));
597 }
598
599 /*
600 * Add any environmental variables that the
601 * PAM modules may have set.
602 * Call *after* opening session!
603 */
604 if (pamh) {
605 environ_pam = pam_getenvlist(pamh);
606 if (environ_pam)
607 export_pam_environment();
608 }
609
610 /*
611 * We must fork() before setuid() because we need to call
612 * pam_close_session() as root.
613 */
614 pid = fork();
615 if (pid < 0) {
616 err(1, "fork");
617 PAM_END;
618 exit(0);
619 } else if (pid) {
620 /* parent - wait for child to finish, then cleanup
621 session */
622 wait(NULL);
623 PAM_END;
624 exit(0);
625 } else {
626 if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
627 syslog(LOG_ERR, "pam_end: %s",
628 pam_strerror(pamh, e));
629 }
630 }
631#endif /* NO_PAM */
632
633 /*
634 * We don't need to be root anymore, so
635 * set the user and session context
636 */
637 if (setlogin(username) != 0) {
638 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
639 exit(1);
640 }
641 if (setusercontext(lc, pwd, pwd->pw_uid,
642 LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
643 syslog(LOG_ERR, "setusercontext() failed - exiting");
644 exit(1);
645 }
646
647 (void)setenv("SHELL", pwd->pw_shell, 1);
648 (void)setenv("HOME", pwd->pw_dir, 1);
649 if (term != NULL && *term != '\0')
650 (void)setenv("TERM", term, 1); /* Preset overrides */
651 else {
652 (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */
653 }
654 (void)setenv("LOGNAME", username, 1);
655 (void)setenv("USER", username, 1);
656 (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
657
658 if (!quietlog) {
659 const char *cw;
660
661 cw = login_getcapstr(lc, "copyright", NULL, NULL);
662 if (cw != NULL && access(cw, F_OK) == 0)
663 motd(cw);
664 else
665 (void)printf("%s\n\t%s %s\n",
666 "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
667 "The Regents of the University of California. ",
668 "All rights reserved.");
669
670 (void)printf("\n");
671
672 cw = login_getcapstr(lc, "welcome", NULL, NULL);
673 if (cw == NULL || access(cw, F_OK) != 0)
674 cw = _PATH_MOTDFILE;
675 motd(cw);
676
677 if (login_getcapbool(lc, "nocheckmail", 0) == 0) {
678 /* $MAIL may have been set by class. */
679 cw = getenv("MAIL");
680 if (cw != NULL)
681 strlcpy(tbuf, cw, sizeof(tbuf));
682 else
683 snprintf(tbuf, sizeof(tbuf), "%s/%s",
684 _PATH_MAILDIR, pwd->pw_name);
685 if (stat(tbuf, &st) == 0 && st.st_size != 0)
686 (void)printf("You have %smail.\n",
687 (st.st_mtime > st.st_atime) ? "new " : "");
688 }
689 }
690
691 login_close(lc);
692
693 (void)signal(SIGALRM, SIG_DFL);
694 (void)signal(SIGQUIT, SIG_DFL);
695 (void)signal(SIGINT, SIG_DFL);
696 (void)signal(SIGTSTP, SIG_IGN);
697
698 /*
699 * Login shells have a leading '-' in front of argv[0]
700 */
701 if ((u_int)snprintf(tbuf, sizeof(tbuf), "-%s",
702 (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell) >=
703 sizeof(tbuf)) {
704 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
705 username);
706 errx(1, "shell exceeds maximum pathname size");
707 }
708
709 execlp(shell, tbuf, (char *)0);
710 err(1, "%s", shell);
711}
712
713static int
714auth_traditional()
715{
716 int rval;
717 char *p;
718 const char *ep;
719 const char *salt;
720
721 rval = 1;
722 salt = pwd != NULL ? pwd->pw_passwd : "xx";
723
724 p = getpass(passwd_prompt);
725 ep = crypt(p, salt);
726
727 if (pwd) {
728 if (!p[0] && pwd->pw_passwd[0])
729 ep = ":";
730 if (strcmp(ep, pwd->pw_passwd) == 0)
731 rval = 0;
732 }
733
734 /* clear entered password */
735 memset(p, 0, strlen(p));
736 return rval;
737}
738
739#ifndef NO_PAM
740/*
741 * Attempt to authenticate the user using PAM. Returns 0 if the user is
742 * authenticated, or 1 if not authenticated. If some sort of PAM system
743 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
744 * function returns -1. This can be used as an indication that we should
745 * fall back to a different authentication mechanism.
746 */
747static int
748auth_pam()
749{
750 const char *tmpl_user;
751 const void *item;
752 int rval;
753 int e;
754 static struct pam_conv conv = { misc_conv, NULL };
755
756 if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
757 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
758 return -1;
759 }
760 if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
761 syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
762 pam_strerror(pamh, e));
763 return -1;
764 }
765 if (hostname != NULL &&
766 (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
767 syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
768 pam_strerror(pamh, e));
769 return -1;
770 }
771 e = pam_authenticate(pamh, 0);
772 switch (e) {
773
774 case PAM_SUCCESS:
775 /*
776 * With PAM we support the concept of a "template"
777 * user. The user enters a login name which is
778 * authenticated by PAM, usually via a remote service
779 * such as RADIUS or TACACS+. If authentication
780 * succeeds, a different but related "template" name
781 * is used for setting the credentials, shell, and
782 * home directory. The name the user enters need only
783 * exist on the remote authentication server, but the
784 * template name must be present in the local password
785 * database.
786 *
787 * This is supported by two various mechanisms in the
788 * individual modules. However, from the application's
789 * point of view, the template user is always passed
790 * back as a changed value of the PAM_USER item.
791 */
792 if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
793 PAM_SUCCESS) {
794 tmpl_user = (const char *) item;
795 if (strcmp(username, tmpl_user) != 0)
796 pwd = getpwnam(tmpl_user);
797 } else
798 syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
799 pam_strerror(pamh, e));
800 rval = 0;
801 break;
802
803 case PAM_AUTH_ERR:
804 case PAM_USER_UNKNOWN:
805 case PAM_MAXTRIES:
806 rval = 1;
807 break;
808
809 default:
810 syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
811 rval = -1;
812 break;
813 }
814
815 if (rval == 0) {
816 e = pam_acct_mgmt(pamh, 0);
817 if (e == PAM_NEW_AUTHTOK_REQD) {
818 e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
819 if (e != PAM_SUCCESS) {
820 syslog(LOG_ERR, "pam_chauthtok: %s",
821 pam_strerror(pamh, e));
822 rval = 1;
823 }
824 } else if (e != PAM_SUCCESS) {
825 rval = 1;
826 }
827 }
828
829 if (rval != 0) {
830 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
831 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
832 }
833 pamh = NULL;
834 }
835 return rval;
836}
837
838static int
839export_pam_environment()
840{
841 char **pp;
842
843 for (pp = environ_pam; *pp != NULL; pp++) {
844 if (ok_to_export(*pp))
845 (void) putenv(*pp);
846 free(*pp);
847 }
848 return PAM_SUCCESS;
849}
850
851/*
852 * Sanity checks on PAM environmental variables:
853 * - Make sure there is an '=' in the string.
854 * - Make sure the string doesn't run on too long.
855 * - Do not export certain variables. This list was taken from the
856 * Solaris pam_putenv(3) man page.
857 */
858static int
859ok_to_export(s)
860 const char *s;
861{
862 static const char *noexport[] = {
863 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
864 "IFS", "PATH", NULL
865 };
866 const char **pp;
867 size_t n;
868
869 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
870 return 0;
871 if (strncmp(s, "LD_", 3) == 0)
872 return 0;
873 for (pp = noexport; *pp != NULL; pp++) {
874 n = strlen(*pp);
875 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
876 return 0;
877 }
878 return 1;
879}
880#endif /* NO_PAM */
881
882static void
883usage()
884{
885
886 (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
887 exit(1);
888}
889
890/*
891 * Allow for authentication style and/or kerberos instance
892 */
893
894void
895getloginname()
896{
897 int ch;
898 char *p;
899 static char nbuf[NBUFSIZ];
900
901 for (;;) {
902 (void)printf("%s", prompt);
903 for (p = nbuf; (ch = getchar()) != '\n'; ) {
904 if (ch == EOF) {
905 badlogin(username);
906 exit(0);
907 }
908 if (p < nbuf + (NBUFSIZ - 1))
909 *p++ = ch;
910 }
911 if (p > nbuf) {
912 if (nbuf[0] == '-')
913 (void)fprintf(stderr,
914 "login names may not start with '-'.\n");
915 else {
916 *p = '\0';
917 username = nbuf;
918 break;
919 }
920 }
921 }
922}
923
924int
925rootterm(ttyn)
926 char *ttyn;
927{
928 struct ttyent *t;
929
930 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
931}
932
933volatile int motdinterrupt;
934
935void
936sigint(signo)
937 int signo __unused;
938{
939 motdinterrupt = 1;
940}
941
942void
943motd(motdfile)
944 const char *motdfile;
945{
946 int fd, nchars;
947 sig_t oldint;
948 char tbuf[256];
949
950 if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
951 return;
952 motdinterrupt = 0;
953 oldint = signal(SIGINT, sigint);
954 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
955 (void)write(fileno(stdout), tbuf, nchars);
956 (void)signal(SIGINT, oldint);
957 (void)close(fd);
958}
959
960/* ARGSUSED */
961void
962timedout(signo)
963 int signo;
964{
965
966 longjmp(timeout_buf, signo);
967}
968
969
970void
971dolastlog(quiet)
972 int quiet;
973{
974 struct lastlog ll;
975 int fd;
976
977 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
978 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
979 if (!quiet) {
980 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
981 ll.ll_time != 0) {
982 (void)printf("Last login: %.*s ",
983 24-5, (char *)ctime(&ll.ll_time));
984 if (*ll.ll_host != '\0')
985 (void)printf("from %.*s\n",
986 (int)sizeof(ll.ll_host),
987 ll.ll_host);
988 else
989 (void)printf("on %.*s\n",
990 (int)sizeof(ll.ll_line),
991 ll.ll_line);
992 }
993 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
994 }
995 memset((void *)&ll, 0, sizeof(ll));
996 (void)time(&ll.ll_time);
997 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
998 if (hostname)
999 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1000 (void)write(fd, (char *)&ll, sizeof(ll));
1001 (void)close(fd);
1002 } else {
1003 syslog(LOG_ERR, "cannot open %s: %m", _PATH_LASTLOG);
1004 }
1005}
1006
1007void
1008badlogin(name)
1009 char *name;
1010{
1011
1012 if (failures == 0)
1013 return;
1014 if (hostname) {
1015 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1016 failures, failures > 1 ? "S" : "", full_hostname);
1017 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1018 "%d LOGIN FAILURE%s FROM %s, %s",
1019 failures, failures > 1 ? "S" : "", full_hostname, name);
1020 } else {
1021 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1022 failures, failures > 1 ? "S" : "", tty);
1023 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1024 "%d LOGIN FAILURE%s ON %s, %s",
1025 failures, failures > 1 ? "S" : "", tty, name);
1026 }
1027 failures = 0;
1028}
1029
1030const char *
1031stypeof(ttyid)
1032 char *ttyid;
1033{
1034 struct ttyent *t;
1035
1036 if (ttyid != NULL && *ttyid != '\0') {
1037 t = getttynam(ttyid);
1038 if (t != NULL && t->ty_type != NULL)
1039 return (t->ty_type);
1040 }
1041 return (UNKNOWN);
1042}
1043
1044void
1045refused(msg, rtype, lout)
1046 const char *msg;
1047 const char *rtype;
1048 int lout;
1049{
1050
1051 if (msg != NULL)
1052 printf("%s.\n", msg);
1053 if (hostname)
1054 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1055 pwd->pw_name, rtype, full_hostname, tty);
1056 else
1057 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1058 pwd->pw_name, rtype, tty);
1059 if (lout)
1060 sleepexit(1);
1061}
1062
1063void
1064sleepexit(eval)
1065 int eval;
1066{
1067
1068 (void)sleep(5);
1069 exit(eval);
1070}
43/*
44 * login [ name ]
45 * login -h hostname (for telnetd, etc.)
46 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
47 */
48
49#include <sys/copyright.h>
50#include <sys/param.h>
51#include <sys/file.h>
52#include <sys/socket.h>
53#include <sys/stat.h>
54#include <sys/time.h>
55#include <sys/resource.h>
56#include <sys/wait.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59
60#include <err.h>
61#include <errno.h>
62#include <grp.h>
63#include <libutil.h>
64#include <login_cap.h>
65#include <netdb.h>
66#include <pwd.h>
67#include <setjmp.h>
68#include <signal.h>
69#include <stdio.h>
70#include <stdlib.h>
71#include <string.h>
72#include <syslog.h>
73#include <ttyent.h>
74#include <unistd.h>
75#include <utmp.h>
76
77#ifndef NO_PAM
78#include <security/pam_appl.h>
79#include <security/pam_misc.h>
80#endif
81
82#include "login.h"
83#include "pathnames.h"
84
85/* wrapper for KAME-special getnameinfo() */
86#ifndef NI_WITHSCOPEID
87#define NI_WITHSCOPEID 0
88#endif
89
90static int auth_traditional __P((void));
91static void badlogin __P((char *));
92static void dolastlog __P((int));
93static void getloginname __P((void));
94static void motd __P((const char *));
95static void refused __P((const char *,const char *,int));
96static int rootterm __P((char *));
97static void sigint __P((int));
98static void sleepexit __P((int));
99static const char *stypeof __P((char *));
100static void timedout __P((int));
101static void usage __P((void));
102
103#ifndef NO_PAM
104static int auth_pam __P((void));
105static int export_pam_environment __P((void));
106static int ok_to_export __P((const char *));
107
108static pam_handle_t *pamh = NULL;
109static char **environ_pam;
110
111#define PAM_END { \
112 if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
113 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
114 if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
115 syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
116 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
117 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
118}
119#endif /* NO_PAM */
120
121#define TTYGRPNAME "tty" /* group to own ttys */
122#define DEFAULT_BACKOFF 3
123#define DEFAULT_RETRIES 10
124#define DEFAULT_PROMPT "login: "
125#define DEFAULT_PASSWD_PROMPT "Password:"
126#define INVALID_HOST "invalid hostname"
127#define UNKNOWN "su"
128#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
129#define NBUFSIZ UT_NAMESIZE + 64
130
131/*
132 * This bounds the time given to login. Not a define so it can
133 * be patched on machines where it's too small.
134 */
135u_int timeout = 300;
136
137/* Buffer for signal handling of timeout */
138jmp_buf timeout_buf;
139
140struct passwd *pwd;
141int failures;
142char *term, *envinit[1], *hostname, *passwd_prompt, *prompt, *tty, *username;
143char full_hostname[MAXHOSTNAMELEN];
144
145int
146main(argc, argv)
147 int argc;
148 char *argv[];
149{
150 struct group *gr;
151 struct stat st;
152 struct timeval tp;
153 struct utmp utmp;
154 int rootok, retries, backoff;
155 int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
156 time_t warntime;
157 uid_t uid, euid;
158 gid_t egid;
159 char *p, *ttyn;
160 char tbuf[MAXPATHLEN + 2];
161 char tname[sizeof(_PATH_TTY) + 10];
162 char *shell = NULL;
163 static char default_prompt[] = DEFAULT_PROMPT;
164 static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
165 static char invalid_host[] = INVALID_HOST;
166 login_cap_t *lc = NULL;
167#ifndef NO_PAM
168 pid_t pid;
169 int e;
170#endif /* NO_PAM */
171
172 (void)signal(SIGQUIT, SIG_IGN);
173 (void)signal(SIGINT, SIG_IGN);
174 (void)signal(SIGHUP, SIG_IGN);
175 if (setjmp(timeout_buf)) {
176 if (failures)
177 badlogin(tbuf);
178 (void)fprintf(stderr, "Login timed out after %d seconds\n",
179 timeout);
180 exit(0);
181 }
182 (void)signal(SIGALRM, timedout);
183 (void)alarm(timeout);
184 (void)setpriority(PRIO_PROCESS, 0, 0);
185
186 openlog("login", LOG_ODELAY, LOG_AUTH);
187
188 /*
189 * -p is used by getty to tell login not to destroy the environment
190 * -f is used to skip a second login authentication
191 * -h is used by other servers to pass the name of the remote
192 * host to login so that it may be placed in utmp and wtmp
193 */
194 *full_hostname = '\0';
195 term = NULL;
196
197 fflag = hflag = pflag = 0;
198 uid = getuid();
199 euid = geteuid();
200 egid = getegid();
201 while ((ch = getopt(argc, argv, "fh:p")) != -1)
202 switch (ch) {
203 case 'f':
204 fflag = 1;
205 break;
206 case 'h':
207 if (uid)
208 errx(1, "-h option: %s", strerror(EPERM));
209 hflag = 1;
210 if (strlcpy(full_hostname, optarg,
211 sizeof(full_hostname)) >= sizeof(full_hostname))
212 errx(1, "-h option: %s: exceeds maximum "
213 "hostname size", optarg);
214
215 trimdomain(optarg, UT_HOSTSIZE);
216
217 if (strlen(optarg) > UT_HOSTSIZE) {
218 struct addrinfo hints, *res;
219 int ga_err;
220
221 memset(&hints, 0, sizeof(hints));
222 hints.ai_family = AF_UNSPEC;
223 ga_err = getaddrinfo(optarg, NULL, &hints,
224 &res);
225 if (ga_err == 0) {
226 char hostbuf[MAXHOSTNAMELEN];
227
228 getnameinfo(res->ai_addr,
229 res->ai_addrlen,
230 hostbuf,
231 sizeof(hostbuf), NULL, 0,
232 NI_NUMERICHOST|
233 NI_WITHSCOPEID);
234 optarg = strdup(hostbuf);
235 if (optarg == NULL) {
236 syslog(LOG_NOTICE,
237 "strdup(): %m");
238 sleepexit(1);
239 }
240 } else
241 optarg = invalid_host;
242 if (res != NULL)
243 freeaddrinfo(res);
244 }
245 hostname = optarg;
246 break;
247 case 'p':
248 pflag = 1;
249 break;
250 case '?':
251 default:
252 if (!uid)
253 syslog(LOG_ERR, "invalid flag %c", ch);
254 usage();
255 }
256 argc -= optind;
257 argv += optind;
258
259 if (*argv) {
260 username = *argv;
261 ask = 0;
262 } else
263 ask = 1;
264
265 for (cnt = getdtablesize(); cnt > 2; cnt--)
266 (void)close(cnt);
267
268 ttyn = ttyname(STDIN_FILENO);
269 if (ttyn == NULL || *ttyn == '\0') {
270 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
271 ttyn = tname;
272 }
273 if ((tty = strrchr(ttyn, '/')) != NULL)
274 ++tty;
275 else
276 tty = ttyn;
277
278 /*
279 * Get "login-retries" & "login-backoff" from default class
280 */
281 lc = login_getclass(NULL);
282 prompt = login_getcapstr(lc, "prompt", default_prompt, default_prompt);
283 passwd_prompt = login_getcapstr(lc, "passwd_prompt",
284 default_passwd_prompt, default_passwd_prompt);
285 retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES,
286 DEFAULT_RETRIES);
287 backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF,
288 DEFAULT_BACKOFF);
289 login_close(lc);
290 lc = NULL;
291
292 for (cnt = 0;; ask = 1) {
293 if (ask) {
294 fflag = 0;
295 getloginname();
296 }
297 rootlogin = 0;
298 rootok = rootterm(tty); /* Default (auth may change) */
299
300 if (strlen(username) > UT_NAMESIZE)
301 username[UT_NAMESIZE] = '\0';
302
303 /*
304 * Note if trying multiple user names; log failures for
305 * previous user name, but don't bother logging one failure
306 * for nonexistent name (mistyped username).
307 */
308 if (failures && strcmp(tbuf, username)) {
309 if (failures > (pwd ? 0 : 1))
310 badlogin(tbuf);
311 }
312 (void)strlcpy(tbuf, username, sizeof(tbuf));
313
314 pwd = getpwnam(username);
315
316 /*
317 * if we have a valid account name, and it doesn't have a
318 * password, or the -f option was specified and the caller
319 * is root or the caller isn't changing their uid, don't
320 * authenticate.
321 */
322 if (pwd != NULL) {
323 if (pwd->pw_uid == 0)
324 rootlogin = 1;
325
326 if (fflag && (uid == (uid_t)0 ||
327 uid == (uid_t)pwd->pw_uid)) {
328 /* already authenticated */
329 break;
330 } else if (pwd->pw_passwd[0] == '\0') {
331 if (!rootlogin || rootok) {
332 /* pretend password okay */
333 rval = 0;
334 goto ttycheck;
335 }
336 }
337 }
338
339 fflag = 0;
340
341 (void)setpriority(PRIO_PROCESS, 0, -4);
342
343#ifndef NO_PAM
344 /*
345 * Try to authenticate using PAM. If a PAM system error
346 * occurs, perhaps because of a botched configuration,
347 * then fall back to using traditional Unix authentication.
348 */
349 if ((rval = auth_pam()) == -1)
350#endif /* NO_PAM */
351 rval = auth_traditional();
352
353 (void)setpriority(PRIO_PROCESS, 0, 0);
354
355 /*
356 * PAM authentication may have changed "pwd" to the
357 * entry for the template user. Check again to see if
358 * this is a root login after all.
359 */
360 if (pwd != NULL && pwd->pw_uid == 0)
361 rootlogin = 1;
362
363 ttycheck:
364 /*
365 * If trying to log in as root without Kerberos,
366 * but with insecure terminal, refuse the login attempt.
367 */
368 if (pwd && !rval) {
369 if (rootlogin && !rootok)
370 refused(NULL, "NOROOT", 0);
371 else /* valid password & authenticated */
372 break;
373 }
374
375 (void)printf("Login incorrect\n");
376 failures++;
377
378 /*
379 * we allow up to 'retry' (10) tries,
380 * but after 'backoff' (3) we start backing off
381 */
382 if (++cnt > backoff) {
383 if (cnt >= retries) {
384 badlogin(username);
385 sleepexit(1);
386 }
387 sleep((u_int)((cnt - backoff) * 5));
388 }
389 }
390
391 /* committed to login -- turn off timeout */
392 (void)alarm((u_int)0);
393 (void)signal(SIGHUP, SIG_DFL);
394
395 endpwent();
396
397 /*
398 * Establish the login class.
399 */
400 lc = login_getpwclass(pwd);
401
402 quietlog = login_getcapbool(lc, "hushlogin", 0);
403 /*
404 * Switching needed for NFS with root access disabled.
405 *
406 * XXX: This change fails to modify the additional groups for the
407 * process, and as such, may restrict rights normally granted
408 * through those groups.
409 */
410 (void)setegid(pwd->pw_gid);
411 (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
412 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
413 if (login_getcapbool(lc, "requirehome", 0))
414 refused("Home directory not available", "HOMEDIR", 1);
415 if (chdir("/") < 0)
416 refused("Cannot find root directory", "ROOTDIR", 1);
417 if (!quietlog || *pwd->pw_dir)
418 printf("No home directory.\nLogging in with home = \"/\".\n");
419 pwd->pw_dir = strdup("/");
420 if (pwd->pw_dir == NULL) {
421 syslog(LOG_NOTICE, "strdup(): %m");
422 sleepexit(1);
423 }
424 }
425 (void)seteuid(euid);
426 (void)setegid(egid);
427 if (!quietlog)
428 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
429
430 if (pwd->pw_change || pwd->pw_expire)
431 (void)gettimeofday(&tp, (struct timezone *)NULL);
432
433 warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
434 DEFAULT_WARN);
435
436 if (pwd->pw_expire) {
437 if (tp.tv_sec >= pwd->pw_expire) {
438 refused("Sorry -- your account has expired", "EXPIRED",
439 1);
440 } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
441 (void)printf("Warning: your account expires on %s",
442 ctime(&pwd->pw_expire));
443 }
444
445 if (lc != NULL) {
446 if (hostname) {
447 struct addrinfo hints, *res;
448 int ga_err;
449
450 memset(&hints, 0, sizeof(hints));
451 hints.ai_family = AF_UNSPEC;
452 ga_err = getaddrinfo(full_hostname, NULL, &hints,
453 &res);
454 if (ga_err == 0) {
455 char hostbuf[MAXHOSTNAMELEN];
456
457 getnameinfo(res->ai_addr, res->ai_addrlen,
458 hostbuf, sizeof(hostbuf), NULL, 0,
459 NI_NUMERICHOST|NI_WITHSCOPEID);
460 if ((optarg = strdup(hostbuf)) == NULL) {
461 syslog(LOG_NOTICE, "strdup(): %m");
462 sleepexit(1);
463 }
464 } else
465 optarg = NULL;
466 if (res != NULL)
467 freeaddrinfo(res);
468 if (!auth_hostok(lc, full_hostname, optarg))
469 refused("Permission denied", "HOST", 1);
470 }
471
472 if (!auth_ttyok(lc, tty))
473 refused("Permission denied", "TTY", 1);
474
475 if (!auth_timeok(lc, time(NULL)))
476 refused("Logins not available right now", "TIME", 1);
477 }
478 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
479 if (*pwd->pw_shell == '\0')
480 pwd->pw_shell = strdup(_PATH_BSHELL);
481 if (pwd->pw_shell == NULL) {
482 syslog(LOG_NOTICE, "strdup(): %m");
483 sleepexit(1);
484 }
485 if (*shell == '\0') /* Not overridden */
486 shell = pwd->pw_shell;
487 if ((shell = strdup(shell)) == NULL) {
488 syslog(LOG_NOTICE, "strdup(): %m");
489 sleepexit(1);
490 }
491
492#ifdef LOGIN_ACCESS
493 if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
494 refused("Permission denied", "ACCESS", 1);
495#endif /* LOGIN_ACCESS */
496
497 /* Nothing else left to fail -- really log in. */
498 memset((void *)&utmp, 0, sizeof(utmp));
499 (void)time(&utmp.ut_time);
500 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
501 if (hostname)
502 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
503 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
504 login(&utmp);
505
506 dolastlog(quietlog);
507
508 /*
509 * Set device protections, depending on what terminal the
510 * user is logged in. This feature is used on Suns to give
511 * console users better privacy.
512 */
513 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
514
515 /*
516 * Clear flags of the tty. None should be set, and when the
517 * user sets them otherwise, this can cause the chown to fail.
518 * Since it isn't clear that flags are useful on character
519 * devices, we just clear them.
520 */
521 if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
522 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
523 if (chown(ttyn, pwd->pw_uid,
524 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
525 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
526
527
528 /*
529 * Preserve TERM if it happens to be already set.
530 */
531 if ((term = getenv("TERM")) != NULL) {
532 if ((term = strdup(term)) == NULL) {
533 syslog(LOG_NOTICE,
534 "strdup(): %m");
535 sleepexit(1);
536 }
537 }
538
539 /*
540 * Exclude cons/vt/ptys only, assume dialup otherwise
541 * TODO: Make dialup tty determination a library call
542 * for consistency (finger etc.)
543 */
544 if (hostname==NULL && isdialuptty(tty))
545 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
546
547#ifdef LOGALL
548 /*
549 * Syslog each successful login, so we don't have to watch hundreds
550 * of wtmp or lastlogin files.
551 */
552 if (hostname)
553 syslog(LOG_INFO, "login from %s on %s as %s",
554 full_hostname, tty, pwd->pw_name);
555 else
556 syslog(LOG_INFO, "login on %s as %s",
557 tty, pwd->pw_name);
558#endif
559
560 /*
561 * If fflag is on, assume caller/authenticator has logged root login.
562 */
563 if (rootlogin && fflag == 0)
564 {
565 if (hostname)
566 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
567 username, tty, full_hostname);
568 else
569 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
570 username, tty);
571 }
572
573 /*
574 * Destroy environment unless user has requested its preservation.
575 * We need to do this before setusercontext() because that may
576 * set or reset some environment variables.
577 */
578 if (!pflag)
579 environ = envinit;
580
581 /*
582 * PAM modules might add supplementary groups during pam_setcred().
583 */
584 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
585 syslog(LOG_ERR, "setusercontext() failed - exiting");
586 exit(1);
587 }
588
589#ifndef NO_PAM
590 if (pamh) {
591 if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
592 syslog(LOG_ERR, "pam_open_session: %s",
593 pam_strerror(pamh, e));
594 } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
595 != PAM_SUCCESS) {
596 syslog(LOG_ERR, "pam_setcred: %s",
597 pam_strerror(pamh, e));
598 }
599
600 /*
601 * Add any environmental variables that the
602 * PAM modules may have set.
603 * Call *after* opening session!
604 */
605 if (pamh) {
606 environ_pam = pam_getenvlist(pamh);
607 if (environ_pam)
608 export_pam_environment();
609 }
610
611 /*
612 * We must fork() before setuid() because we need to call
613 * pam_close_session() as root.
614 */
615 pid = fork();
616 if (pid < 0) {
617 err(1, "fork");
618 PAM_END;
619 exit(0);
620 } else if (pid) {
621 /* parent - wait for child to finish, then cleanup
622 session */
623 wait(NULL);
624 PAM_END;
625 exit(0);
626 } else {
627 if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
628 syslog(LOG_ERR, "pam_end: %s",
629 pam_strerror(pamh, e));
630 }
631 }
632#endif /* NO_PAM */
633
634 /*
635 * We don't need to be root anymore, so
636 * set the user and session context
637 */
638 if (setlogin(username) != 0) {
639 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
640 exit(1);
641 }
642 if (setusercontext(lc, pwd, pwd->pw_uid,
643 LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
644 syslog(LOG_ERR, "setusercontext() failed - exiting");
645 exit(1);
646 }
647
648 (void)setenv("SHELL", pwd->pw_shell, 1);
649 (void)setenv("HOME", pwd->pw_dir, 1);
650 if (term != NULL && *term != '\0')
651 (void)setenv("TERM", term, 1); /* Preset overrides */
652 else {
653 (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */
654 }
655 (void)setenv("LOGNAME", username, 1);
656 (void)setenv("USER", username, 1);
657 (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
658
659 if (!quietlog) {
660 const char *cw;
661
662 cw = login_getcapstr(lc, "copyright", NULL, NULL);
663 if (cw != NULL && access(cw, F_OK) == 0)
664 motd(cw);
665 else
666 (void)printf("%s\n\t%s %s\n",
667 "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
668 "The Regents of the University of California. ",
669 "All rights reserved.");
670
671 (void)printf("\n");
672
673 cw = login_getcapstr(lc, "welcome", NULL, NULL);
674 if (cw == NULL || access(cw, F_OK) != 0)
675 cw = _PATH_MOTDFILE;
676 motd(cw);
677
678 if (login_getcapbool(lc, "nocheckmail", 0) == 0) {
679 /* $MAIL may have been set by class. */
680 cw = getenv("MAIL");
681 if (cw != NULL)
682 strlcpy(tbuf, cw, sizeof(tbuf));
683 else
684 snprintf(tbuf, sizeof(tbuf), "%s/%s",
685 _PATH_MAILDIR, pwd->pw_name);
686 if (stat(tbuf, &st) == 0 && st.st_size != 0)
687 (void)printf("You have %smail.\n",
688 (st.st_mtime > st.st_atime) ? "new " : "");
689 }
690 }
691
692 login_close(lc);
693
694 (void)signal(SIGALRM, SIG_DFL);
695 (void)signal(SIGQUIT, SIG_DFL);
696 (void)signal(SIGINT, SIG_DFL);
697 (void)signal(SIGTSTP, SIG_IGN);
698
699 /*
700 * Login shells have a leading '-' in front of argv[0]
701 */
702 if ((u_int)snprintf(tbuf, sizeof(tbuf), "-%s",
703 (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell) >=
704 sizeof(tbuf)) {
705 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
706 username);
707 errx(1, "shell exceeds maximum pathname size");
708 }
709
710 execlp(shell, tbuf, (char *)0);
711 err(1, "%s", shell);
712}
713
714static int
715auth_traditional()
716{
717 int rval;
718 char *p;
719 const char *ep;
720 const char *salt;
721
722 rval = 1;
723 salt = pwd != NULL ? pwd->pw_passwd : "xx";
724
725 p = getpass(passwd_prompt);
726 ep = crypt(p, salt);
727
728 if (pwd) {
729 if (!p[0] && pwd->pw_passwd[0])
730 ep = ":";
731 if (strcmp(ep, pwd->pw_passwd) == 0)
732 rval = 0;
733 }
734
735 /* clear entered password */
736 memset(p, 0, strlen(p));
737 return rval;
738}
739
740#ifndef NO_PAM
741/*
742 * Attempt to authenticate the user using PAM. Returns 0 if the user is
743 * authenticated, or 1 if not authenticated. If some sort of PAM system
744 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
745 * function returns -1. This can be used as an indication that we should
746 * fall back to a different authentication mechanism.
747 */
748static int
749auth_pam()
750{
751 const char *tmpl_user;
752 const void *item;
753 int rval;
754 int e;
755 static struct pam_conv conv = { misc_conv, NULL };
756
757 if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
758 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
759 return -1;
760 }
761 if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
762 syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
763 pam_strerror(pamh, e));
764 return -1;
765 }
766 if (hostname != NULL &&
767 (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
768 syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
769 pam_strerror(pamh, e));
770 return -1;
771 }
772 e = pam_authenticate(pamh, 0);
773 switch (e) {
774
775 case PAM_SUCCESS:
776 /*
777 * With PAM we support the concept of a "template"
778 * user. The user enters a login name which is
779 * authenticated by PAM, usually via a remote service
780 * such as RADIUS or TACACS+. If authentication
781 * succeeds, a different but related "template" name
782 * is used for setting the credentials, shell, and
783 * home directory. The name the user enters need only
784 * exist on the remote authentication server, but the
785 * template name must be present in the local password
786 * database.
787 *
788 * This is supported by two various mechanisms in the
789 * individual modules. However, from the application's
790 * point of view, the template user is always passed
791 * back as a changed value of the PAM_USER item.
792 */
793 if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
794 PAM_SUCCESS) {
795 tmpl_user = (const char *) item;
796 if (strcmp(username, tmpl_user) != 0)
797 pwd = getpwnam(tmpl_user);
798 } else
799 syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
800 pam_strerror(pamh, e));
801 rval = 0;
802 break;
803
804 case PAM_AUTH_ERR:
805 case PAM_USER_UNKNOWN:
806 case PAM_MAXTRIES:
807 rval = 1;
808 break;
809
810 default:
811 syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
812 rval = -1;
813 break;
814 }
815
816 if (rval == 0) {
817 e = pam_acct_mgmt(pamh, 0);
818 if (e == PAM_NEW_AUTHTOK_REQD) {
819 e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
820 if (e != PAM_SUCCESS) {
821 syslog(LOG_ERR, "pam_chauthtok: %s",
822 pam_strerror(pamh, e));
823 rval = 1;
824 }
825 } else if (e != PAM_SUCCESS) {
826 rval = 1;
827 }
828 }
829
830 if (rval != 0) {
831 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
832 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
833 }
834 pamh = NULL;
835 }
836 return rval;
837}
838
839static int
840export_pam_environment()
841{
842 char **pp;
843
844 for (pp = environ_pam; *pp != NULL; pp++) {
845 if (ok_to_export(*pp))
846 (void) putenv(*pp);
847 free(*pp);
848 }
849 return PAM_SUCCESS;
850}
851
852/*
853 * Sanity checks on PAM environmental variables:
854 * - Make sure there is an '=' in the string.
855 * - Make sure the string doesn't run on too long.
856 * - Do not export certain variables. This list was taken from the
857 * Solaris pam_putenv(3) man page.
858 */
859static int
860ok_to_export(s)
861 const char *s;
862{
863 static const char *noexport[] = {
864 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
865 "IFS", "PATH", NULL
866 };
867 const char **pp;
868 size_t n;
869
870 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
871 return 0;
872 if (strncmp(s, "LD_", 3) == 0)
873 return 0;
874 for (pp = noexport; *pp != NULL; pp++) {
875 n = strlen(*pp);
876 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
877 return 0;
878 }
879 return 1;
880}
881#endif /* NO_PAM */
882
883static void
884usage()
885{
886
887 (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
888 exit(1);
889}
890
891/*
892 * Allow for authentication style and/or kerberos instance
893 */
894
895void
896getloginname()
897{
898 int ch;
899 char *p;
900 static char nbuf[NBUFSIZ];
901
902 for (;;) {
903 (void)printf("%s", prompt);
904 for (p = nbuf; (ch = getchar()) != '\n'; ) {
905 if (ch == EOF) {
906 badlogin(username);
907 exit(0);
908 }
909 if (p < nbuf + (NBUFSIZ - 1))
910 *p++ = ch;
911 }
912 if (p > nbuf) {
913 if (nbuf[0] == '-')
914 (void)fprintf(stderr,
915 "login names may not start with '-'.\n");
916 else {
917 *p = '\0';
918 username = nbuf;
919 break;
920 }
921 }
922 }
923}
924
925int
926rootterm(ttyn)
927 char *ttyn;
928{
929 struct ttyent *t;
930
931 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
932}
933
934volatile int motdinterrupt;
935
936void
937sigint(signo)
938 int signo __unused;
939{
940 motdinterrupt = 1;
941}
942
943void
944motd(motdfile)
945 const char *motdfile;
946{
947 int fd, nchars;
948 sig_t oldint;
949 char tbuf[256];
950
951 if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
952 return;
953 motdinterrupt = 0;
954 oldint = signal(SIGINT, sigint);
955 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
956 (void)write(fileno(stdout), tbuf, nchars);
957 (void)signal(SIGINT, oldint);
958 (void)close(fd);
959}
960
961/* ARGSUSED */
962void
963timedout(signo)
964 int signo;
965{
966
967 longjmp(timeout_buf, signo);
968}
969
970
971void
972dolastlog(quiet)
973 int quiet;
974{
975 struct lastlog ll;
976 int fd;
977
978 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
979 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
980 if (!quiet) {
981 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
982 ll.ll_time != 0) {
983 (void)printf("Last login: %.*s ",
984 24-5, (char *)ctime(&ll.ll_time));
985 if (*ll.ll_host != '\0')
986 (void)printf("from %.*s\n",
987 (int)sizeof(ll.ll_host),
988 ll.ll_host);
989 else
990 (void)printf("on %.*s\n",
991 (int)sizeof(ll.ll_line),
992 ll.ll_line);
993 }
994 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
995 }
996 memset((void *)&ll, 0, sizeof(ll));
997 (void)time(&ll.ll_time);
998 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
999 if (hostname)
1000 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1001 (void)write(fd, (char *)&ll, sizeof(ll));
1002 (void)close(fd);
1003 } else {
1004 syslog(LOG_ERR, "cannot open %s: %m", _PATH_LASTLOG);
1005 }
1006}
1007
1008void
1009badlogin(name)
1010 char *name;
1011{
1012
1013 if (failures == 0)
1014 return;
1015 if (hostname) {
1016 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1017 failures, failures > 1 ? "S" : "", full_hostname);
1018 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1019 "%d LOGIN FAILURE%s FROM %s, %s",
1020 failures, failures > 1 ? "S" : "", full_hostname, name);
1021 } else {
1022 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1023 failures, failures > 1 ? "S" : "", tty);
1024 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1025 "%d LOGIN FAILURE%s ON %s, %s",
1026 failures, failures > 1 ? "S" : "", tty, name);
1027 }
1028 failures = 0;
1029}
1030
1031const char *
1032stypeof(ttyid)
1033 char *ttyid;
1034{
1035 struct ttyent *t;
1036
1037 if (ttyid != NULL && *ttyid != '\0') {
1038 t = getttynam(ttyid);
1039 if (t != NULL && t->ty_type != NULL)
1040 return (t->ty_type);
1041 }
1042 return (UNKNOWN);
1043}
1044
1045void
1046refused(msg, rtype, lout)
1047 const char *msg;
1048 const char *rtype;
1049 int lout;
1050{
1051
1052 if (msg != NULL)
1053 printf("%s.\n", msg);
1054 if (hostname)
1055 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1056 pwd->pw_name, rtype, full_hostname, tty);
1057 else
1058 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1059 pwd->pw_name, rtype, tty);
1060 if (lout)
1061 sleepexit(1);
1062}
1063
1064void
1065sleepexit(eval)
1066 int eval;
1067{
1068
1069 (void)sleep(5);
1070 exit(eval);
1071}