Deleted Added
full compact
login.c (110966) login.c (114010)
1/*-
2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technologies, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#if 0
42#ifndef lint
43static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
44#endif
45#endif
46
47#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technologies, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#if 0
42#ifndef lint
43static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
44#endif
45#endif
46
47#include <sys/cdefs.h>
48__FBSDID("$FreeBSD: head/usr.bin/login/login.c 110966 2003-02-15 23:20:04Z des $");
48__FBSDID("$FreeBSD: head/usr.bin/login/login.c 114010 2003-04-25 11:57:20Z des $");
49
50/*
51 * login [ name ]
52 * login -h hostname (for telnetd, etc.)
53 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
54 */
55
56#include <sys/copyright.h>
57#include <sys/param.h>
58#include <sys/file.h>
59#include <sys/stat.h>
60#include <sys/time.h>
61#include <sys/resource.h>
62#include <sys/wait.h>
63
64#include <err.h>
65#include <errno.h>
66#include <grp.h>
67#include <libutil.h>
68#include <login_cap.h>
69#include <pwd.h>
70#include <setjmp.h>
71#include <signal.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <syslog.h>
76#include <ttyent.h>
77#include <unistd.h>
78
79#include <security/pam_appl.h>
80#include <security/openpam.h>
81
82#include "login.h"
83#include "pathnames.h"
84
85static int auth_pam(void);
86static void bail(int, int);
87static int export(const char *);
88static void export_pam_environment(void);
89static int motd(const char *);
90static void badlogin(char *);
91static char *getloginname(void);
92static void pam_syslog(const char *);
93static void pam_cleanup(void);
94static void refused(const char *, const char *, int);
95static const char *stypeof(char *);
96static void sigint(int);
97static void timedout(int);
98static void usage(void);
99
100#define TTYGRPNAME "tty" /* group to own ttys */
101#define DEFAULT_BACKOFF 3
102#define DEFAULT_RETRIES 10
103#define DEFAULT_PROMPT "login: "
104#define DEFAULT_PASSWD_PROMPT "Password:"
105#define TERM_UNKNOWN "su"
106#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
107#define NO_SLEEP_EXIT 0
108#define SLEEP_EXIT 5
109
110/*
111 * This bounds the time given to login. Not a define so it can
112 * be patched on machines where it's too small.
113 */
114static u_int timeout = 300;
115
116/* Buffer for signal handling of timeout */
117static jmp_buf timeout_buf;
118
119struct passwd *pwd;
120static int failures;
121
122static char *envinit[1]; /* empty environment list */
123
124/*
125 * Command line flags and arguments
126 */
127static int fflag; /* -f: do not perform authentication */
128static int hflag; /* -h: login from remote host */
129static char *hostname; /* hostname from command line */
130static int pflag; /* -p: preserve environment */
131
132/*
133 * User name
134 */
135static char *username; /* user name */
136static char *olduser; /* previous user name */
137
138/*
139 * Prompts
140 */
141static char default_prompt[] = DEFAULT_PROMPT;
142static const char *prompt;
143static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
144static const char *passwd_prompt;
145
146static char *tty;
147
148/*
149 * PAM data
150 */
151static pam_handle_t *pamh = NULL;
152static struct pam_conv pamc = { openpam_ttyconv, NULL };
153static int pam_err;
154static int pam_silent = PAM_SILENT;
155static int pam_cred_established;
156static int pam_session_established;
157
158int
159main(int argc, char *argv[])
160{
161 struct group *gr;
162 struct stat st;
163 int retries, backoff;
164 int ask, ch, cnt, quietlog, rootlogin, rval;
165 uid_t uid, euid;
166 gid_t egid;
167 char *term;
168 char *p, *ttyn;
169 char tname[sizeof(_PATH_TTY) + 10];
170 char *arg0;
171 const char *tp;
172 const char *shell = NULL;
173 login_cap_t *lc = NULL;
174 pid_t pid;
175
176 (void)signal(SIGQUIT, SIG_IGN);
177 (void)signal(SIGINT, SIG_IGN);
178 (void)signal(SIGHUP, SIG_IGN);
179 if (setjmp(timeout_buf)) {
180 if (failures)
181 badlogin(username);
182 (void)fprintf(stderr, "Login timed out after %d seconds\n",
183 timeout);
184 bail(NO_SLEEP_EXIT, 0);
185 }
186 (void)signal(SIGALRM, timedout);
187 (void)alarm(timeout);
188 (void)setpriority(PRIO_PROCESS, 0, 0);
189
190 openlog("login", LOG_ODELAY, LOG_AUTH);
191
192 uid = getuid();
193 euid = geteuid();
194 egid = getegid();
195
196 while ((ch = getopt(argc, argv, "fh:p")) != -1)
197 switch (ch) {
198 case 'f':
199 fflag = 1;
200 break;
201 case 'h':
202 if (uid != 0)
203 errx(1, "-h option: %s", strerror(EPERM));
204 if (strlen(optarg) >= MAXHOSTNAMELEN)
205 errx(1, "-h option: %s: exceeds maximum "
206 "hostname size", optarg);
207 hflag = 1;
208 hostname = optarg;
209 break;
210 case 'p':
211 pflag = 1;
212 break;
213 case '?':
214 default:
215 if (uid == 0)
216 syslog(LOG_ERR, "invalid flag %c", ch);
217 usage();
218 }
219 argc -= optind;
220 argv += optind;
221
222 if (argc > 0) {
223 username = strdup(*argv);
224 if (username == NULL)
225 err(1, "strdup()");
226 ask = 0;
227 } else {
228 ask = 1;
229 }
230
231 setproctitle("-%s", getprogname());
232
233 for (cnt = getdtablesize(); cnt > 2; cnt--)
234 (void)close(cnt);
235
236 /*
237 * Get current TTY
238 */
239 ttyn = ttyname(STDIN_FILENO);
240 if (ttyn == NULL || *ttyn == '\0') {
241 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
242 ttyn = tname;
243 }
244 if ((tty = strrchr(ttyn, '/')) != NULL)
245 ++tty;
246 else
247 tty = ttyn;
248
249 /*
250 * Get "login-retries" & "login-backoff" from default class
251 */
252 lc = login_getclass(NULL);
49
50/*
51 * login [ name ]
52 * login -h hostname (for telnetd, etc.)
53 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
54 */
55
56#include <sys/copyright.h>
57#include <sys/param.h>
58#include <sys/file.h>
59#include <sys/stat.h>
60#include <sys/time.h>
61#include <sys/resource.h>
62#include <sys/wait.h>
63
64#include <err.h>
65#include <errno.h>
66#include <grp.h>
67#include <libutil.h>
68#include <login_cap.h>
69#include <pwd.h>
70#include <setjmp.h>
71#include <signal.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <syslog.h>
76#include <ttyent.h>
77#include <unistd.h>
78
79#include <security/pam_appl.h>
80#include <security/openpam.h>
81
82#include "login.h"
83#include "pathnames.h"
84
85static int auth_pam(void);
86static void bail(int, int);
87static int export(const char *);
88static void export_pam_environment(void);
89static int motd(const char *);
90static void badlogin(char *);
91static char *getloginname(void);
92static void pam_syslog(const char *);
93static void pam_cleanup(void);
94static void refused(const char *, const char *, int);
95static const char *stypeof(char *);
96static void sigint(int);
97static void timedout(int);
98static void usage(void);
99
100#define TTYGRPNAME "tty" /* group to own ttys */
101#define DEFAULT_BACKOFF 3
102#define DEFAULT_RETRIES 10
103#define DEFAULT_PROMPT "login: "
104#define DEFAULT_PASSWD_PROMPT "Password:"
105#define TERM_UNKNOWN "su"
106#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
107#define NO_SLEEP_EXIT 0
108#define SLEEP_EXIT 5
109
110/*
111 * This bounds the time given to login. Not a define so it can
112 * be patched on machines where it's too small.
113 */
114static u_int timeout = 300;
115
116/* Buffer for signal handling of timeout */
117static jmp_buf timeout_buf;
118
119struct passwd *pwd;
120static int failures;
121
122static char *envinit[1]; /* empty environment list */
123
124/*
125 * Command line flags and arguments
126 */
127static int fflag; /* -f: do not perform authentication */
128static int hflag; /* -h: login from remote host */
129static char *hostname; /* hostname from command line */
130static int pflag; /* -p: preserve environment */
131
132/*
133 * User name
134 */
135static char *username; /* user name */
136static char *olduser; /* previous user name */
137
138/*
139 * Prompts
140 */
141static char default_prompt[] = DEFAULT_PROMPT;
142static const char *prompt;
143static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
144static const char *passwd_prompt;
145
146static char *tty;
147
148/*
149 * PAM data
150 */
151static pam_handle_t *pamh = NULL;
152static struct pam_conv pamc = { openpam_ttyconv, NULL };
153static int pam_err;
154static int pam_silent = PAM_SILENT;
155static int pam_cred_established;
156static int pam_session_established;
157
158int
159main(int argc, char *argv[])
160{
161 struct group *gr;
162 struct stat st;
163 int retries, backoff;
164 int ask, ch, cnt, quietlog, rootlogin, rval;
165 uid_t uid, euid;
166 gid_t egid;
167 char *term;
168 char *p, *ttyn;
169 char tname[sizeof(_PATH_TTY) + 10];
170 char *arg0;
171 const char *tp;
172 const char *shell = NULL;
173 login_cap_t *lc = NULL;
174 pid_t pid;
175
176 (void)signal(SIGQUIT, SIG_IGN);
177 (void)signal(SIGINT, SIG_IGN);
178 (void)signal(SIGHUP, SIG_IGN);
179 if (setjmp(timeout_buf)) {
180 if (failures)
181 badlogin(username);
182 (void)fprintf(stderr, "Login timed out after %d seconds\n",
183 timeout);
184 bail(NO_SLEEP_EXIT, 0);
185 }
186 (void)signal(SIGALRM, timedout);
187 (void)alarm(timeout);
188 (void)setpriority(PRIO_PROCESS, 0, 0);
189
190 openlog("login", LOG_ODELAY, LOG_AUTH);
191
192 uid = getuid();
193 euid = geteuid();
194 egid = getegid();
195
196 while ((ch = getopt(argc, argv, "fh:p")) != -1)
197 switch (ch) {
198 case 'f':
199 fflag = 1;
200 break;
201 case 'h':
202 if (uid != 0)
203 errx(1, "-h option: %s", strerror(EPERM));
204 if (strlen(optarg) >= MAXHOSTNAMELEN)
205 errx(1, "-h option: %s: exceeds maximum "
206 "hostname size", optarg);
207 hflag = 1;
208 hostname = optarg;
209 break;
210 case 'p':
211 pflag = 1;
212 break;
213 case '?':
214 default:
215 if (uid == 0)
216 syslog(LOG_ERR, "invalid flag %c", ch);
217 usage();
218 }
219 argc -= optind;
220 argv += optind;
221
222 if (argc > 0) {
223 username = strdup(*argv);
224 if (username == NULL)
225 err(1, "strdup()");
226 ask = 0;
227 } else {
228 ask = 1;
229 }
230
231 setproctitle("-%s", getprogname());
232
233 for (cnt = getdtablesize(); cnt > 2; cnt--)
234 (void)close(cnt);
235
236 /*
237 * Get current TTY
238 */
239 ttyn = ttyname(STDIN_FILENO);
240 if (ttyn == NULL || *ttyn == '\0') {
241 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
242 ttyn = tname;
243 }
244 if ((tty = strrchr(ttyn, '/')) != NULL)
245 ++tty;
246 else
247 tty = ttyn;
248
249 /*
250 * Get "login-retries" & "login-backoff" from default class
251 */
252 lc = login_getclass(NULL);
253 prompt = login_getcapstr(lc, "prompt",
253 prompt = login_getcapstr(lc, "login_prompt",
254 default_prompt, default_prompt);
255 passwd_prompt = login_getcapstr(lc, "passwd_prompt",
256 default_passwd_prompt, default_passwd_prompt);
257 retries = login_getcapnum(lc, "login-retries",
258 DEFAULT_RETRIES, DEFAULT_RETRIES);
259 backoff = login_getcapnum(lc, "login-backoff",
260 DEFAULT_BACKOFF, DEFAULT_BACKOFF);
261 login_close(lc);
262 lc = NULL;
263
264 /*
265 * Try to authenticate the user until we succeed or time out.
266 */
267 for (cnt = 0;; ask = 1) {
268 if (ask) {
269 fflag = 0;
270 if (olduser != NULL)
271 free(olduser);
272 olduser = username;
273 username = getloginname();
274 }
275 rootlogin = 0;
276
277 /*
278 * Note if trying multiple user names; log failures for
279 * previous user name, but don't bother logging one failure
280 * for nonexistent name (mistyped username).
281 */
282 if (failures && strcmp(olduser, username) != 0) {
283 if (failures > (pwd ? 0 : 1))
284 badlogin(olduser);
285 }
286
287 /*
288 * Load the PAM policy and set some variables
289 */
290 pam_err = pam_start("login", username, &pamc, &pamh);
291 if (pam_err != PAM_SUCCESS) {
292 pam_syslog("pam_start()");
293 bail(NO_SLEEP_EXIT, 1);
294 }
295 pam_err = pam_set_item(pamh, PAM_TTY, tty);
296 if (pam_err != PAM_SUCCESS) {
297 pam_syslog("pam_set_item(PAM_TTY)");
298 bail(NO_SLEEP_EXIT, 1);
299 }
300 pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
301 if (pam_err != PAM_SUCCESS) {
302 pam_syslog("pam_set_item(PAM_RHOST)");
303 bail(NO_SLEEP_EXIT, 1);
304 }
305
306 pwd = getpwnam(username);
307 if (pwd != NULL && pwd->pw_uid == 0)
308 rootlogin = 1;
309
310 /*
311 * If the -f option was specified and the caller is
312 * root or the caller isn't changing their uid, don't
313 * authenticate.
314 */
315 if (pwd != NULL && fflag &&
316 (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
317 /* already authenticated */
318 rval = 0;
319 } else {
320 fflag = 0;
321 (void)setpriority(PRIO_PROCESS, 0, -4);
322 rval = auth_pam();
323 (void)setpriority(PRIO_PROCESS, 0, 0);
324 }
325
326 if (pwd && rval == 0)
327 break;
328
329 pam_cleanup();
330
331 (void)printf("Login incorrect\n");
332 failures++;
333
334 /*
335 * Allow up to 'retry' (10) attempts, but start
336 * backing off after 'backoff' (3) attempts.
337 */
338 if (++cnt > backoff) {
339 if (cnt >= retries) {
340 badlogin(username);
341 bail(SLEEP_EXIT, 1);
342 }
343 sleep((u_int)((cnt - backoff) * 5));
344 }
345 }
346
347 /* committed to login -- turn off timeout */
348 (void)alarm((u_int)0);
349 (void)signal(SIGHUP, SIG_DFL);
350
351 endpwent();
352
353 /*
354 * Establish the login class.
355 */
356 lc = login_getpwclass(pwd);
357
358 quietlog = login_getcapbool(lc, "hushlogin", 0);
359 if (!quietlog)
360 pam_silent = 0;
361
362 /*
363 * Switching needed for NFS with root access disabled.
364 *
365 * XXX: This change fails to modify the additional groups for the
366 * process, and as such, may restrict rights normally granted
367 * through those groups.
368 */
369 (void)setegid(pwd->pw_gid);
370 (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
371 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
372 if (login_getcapbool(lc, "requirehome", 0))
373 refused("Home directory not available", "HOMEDIR", 1);
374 if (chdir("/") < 0)
375 refused("Cannot find root directory", "ROOTDIR", 1);
376 if (!quietlog || *pwd->pw_dir)
377 printf("No home directory.\nLogging in with home = \"/\".\n");
378 pwd->pw_dir = strdup("/");
379 if (pwd->pw_dir == NULL) {
380 syslog(LOG_NOTICE, "strdup(): %m");
381 bail(SLEEP_EXIT, 1);
382 }
383 }
384 (void)seteuid(euid);
385 (void)setegid(egid);
386 if (!quietlog)
387 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
388
389 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
390 if (*pwd->pw_shell == '\0')
391 pwd->pw_shell = strdup(_PATH_BSHELL);
392 if (pwd->pw_shell == NULL) {
393 syslog(LOG_NOTICE, "strdup(): %m");
394 bail(SLEEP_EXIT, 1);
395 }
396 if (*shell == '\0') /* Not overridden */
397 shell = pwd->pw_shell;
398 if ((shell = strdup(shell)) == NULL) {
399 syslog(LOG_NOTICE, "strdup(): %m");
400 bail(SLEEP_EXIT, 1);
401 }
402
403 /*
404 * Set device protections, depending on what terminal the
405 * user is logged in. This feature is used on Suns to give
406 * console users better privacy.
407 */
408 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
409
410 /*
411 * Clear flags of the tty. None should be set, and when the
412 * user sets them otherwise, this can cause the chown to fail.
413 * Since it isn't clear that flags are useful on character
414 * devices, we just clear them.
415 *
416 * We don't log in the case of EOPNOTSUPP because dev might be
417 * on NFS, which doesn't support chflags.
418 *
419 * We don't log in the EROFS because that means that /dev is on
420 * a read only file system and we assume that the permissions there
421 * are sane.
422 */
423 if (ttyn != tname && chflags(ttyn, 0))
424 if (errno != EOPNOTSUPP && errno != EROFS)
425 syslog(LOG_ERR, "chflags(%s): %m", ttyn);
426 if (ttyn != tname && chown(ttyn, pwd->pw_uid,
427 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
428 if (errno != EROFS)
429 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
430
431 /*
432 * Exclude cons/vt/ptys only, assume dialup otherwise
433 * TODO: Make dialup tty determination a library call
434 * for consistency (finger etc.)
435 */
436 if (hflag && isdialuptty(tty))
437 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
438
439#ifdef LOGALL
440 /*
441 * Syslog each successful login, so we don't have to watch
442 * hundreds of wtmp or lastlogin files.
443 */
444 if (hflag)
445 syslog(LOG_INFO, "login from %s on %s as %s",
446 hostname, tty, pwd->pw_name);
447 else
448 syslog(LOG_INFO, "login on %s as %s",
449 tty, pwd->pw_name);
450#endif
451
452 /*
453 * If fflag is on, assume caller/authenticator has logged root
454 * login.
455 */
456 if (rootlogin && fflag == 0) {
457 if (hflag)
458 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
459 username, tty, hostname);
460 else
461 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
462 username, tty);
463 }
464
465 /*
466 * Destroy environment unless user has requested its
467 * preservation - but preserve TERM in all cases
468 */
469 term = getenv("TERM");
470 if (!pflag)
471 environ = envinit;
472 if (term != NULL)
473 setenv("TERM", term, 0);
474
475 /*
476 * PAM modules might add supplementary groups during pam_setcred().
477 */
478 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
479 syslog(LOG_ERR, "setusercontext() failed - exiting");
480 bail(NO_SLEEP_EXIT, 1);
481 }
482
483 pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
484 if (pam_err != PAM_SUCCESS) {
485 pam_syslog("pam_setcred()");
486 bail(NO_SLEEP_EXIT, 1);
487 }
488 pam_cred_established = 1;
489
490 pam_err = pam_open_session(pamh, pam_silent);
491 if (pam_err != PAM_SUCCESS) {
492 pam_syslog("pam_open_session()");
493 bail(NO_SLEEP_EXIT, 1);
494 }
495 pam_session_established = 1;
496
497 /*
498 * We must fork() before setuid() because we need to call
499 * pam_close_session() as root.
500 */
501 pid = fork();
502 if (pid < 0) {
503 err(1, "fork");
504 } else if (pid != 0) {
505 /*
506 * Parent: wait for child to finish, then clean up
507 * session.
508 */
509 int status;
510 setproctitle("-%s [pam]", getprogname());
511 waitpid(pid, &status, 0);
512 bail(NO_SLEEP_EXIT, 0);
513 }
514
515 /*
516 * NOTICE: We are now in the child process!
517 */
518
519 /*
520 * Add any environment variables the PAM modules may have set.
521 */
522 export_pam_environment();
523
524 /*
525 * We're done with PAM now; our parent will deal with the rest.
526 */
527 pam_end(pamh, 0);
528 pamh = NULL;
529
530 /*
531 * We don't need to be root anymore, so set the login name and
532 * the UID.
533 */
534 if (setlogin(username) != 0) {
535 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
536 bail(NO_SLEEP_EXIT, 1);
537 }
538 if (setusercontext(lc, pwd, pwd->pw_uid,
539 LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
540 syslog(LOG_ERR, "setusercontext() failed - exiting");
541 exit(1);
542 }
543
544 (void)setenv("SHELL", pwd->pw_shell, 1);
545 (void)setenv("HOME", pwd->pw_dir, 1);
546 /* Overwrite "term" from login.conf(5) for any known TERM */
547 if (term == NULL && (tp = stypeof(tty)) != NULL)
548 (void)setenv("TERM", tp, 1);
549 else
550 (void)setenv("TERM", TERM_UNKNOWN, 0);
551 (void)setenv("LOGNAME", username, 1);
552 (void)setenv("USER", username, 1);
553 (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
554
555 if (!quietlog) {
556 const char *cw;
557
558 cw = login_getcapstr(lc, "copyright", NULL, NULL);
559 if (cw == NULL || motd(cw) == -1)
560 (void)printf("%s", copyright);
561
562 (void)printf("\n");
563
564 cw = login_getcapstr(lc, "welcome", NULL, NULL);
565 if (cw != NULL && access(cw, F_OK) == 0)
566 motd(cw);
567 else
568 motd(_PATH_MOTDFILE);
569
570 if (login_getcapbool(lc, "nocheckmail", 0) == 0) {
571 char *cx;
572
573 /* $MAIL may have been set by class. */
574 cx = getenv("MAIL");
575 if (cx == NULL) {
576 asprintf(&cx, "%s/%s",
577 _PATH_MAILDIR, pwd->pw_name);
578 }
579 if (cx && stat(cx, &st) == 0 && st.st_size != 0)
580 (void)printf("You have %smail.\n",
581 (st.st_mtime > st.st_atime) ? "new " : "");
582 if (getenv("MAIL") == NULL)
583 free(cx);
584 }
585 }
586
587 login_close(lc);
588
589 (void)signal(SIGALRM, SIG_DFL);
590 (void)signal(SIGQUIT, SIG_DFL);
591 (void)signal(SIGINT, SIG_DFL);
592 (void)signal(SIGTSTP, SIG_IGN);
593
594 /*
595 * Login shells have a leading '-' in front of argv[0]
596 */
597 p = strrchr(pwd->pw_shell, '/');
598 if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
599 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
600 username);
601 errx(1, "shell exceeds maximum pathname size");
602 } else if (arg0 == NULL) {
603 err(1, "asprintf()");
604 }
605
606 execlp(shell, arg0, (char *)0);
607 err(1, "%s", shell);
608
609 /*
610 * That's it, folks!
611 */
612}
613
614/*
615 * Attempt to authenticate the user using PAM. Returns 0 if the user is
616 * authenticated, or 1 if not authenticated. If some sort of PAM system
617 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
618 * function returns -1. This can be used as an indication that we should
619 * fall back to a different authentication mechanism.
620 */
621static int
622auth_pam(void)
623{
624 const char *tmpl_user;
625 const void *item;
626 int rval;
627
628 pam_err = pam_authenticate(pamh, pam_silent);
629 switch (pam_err) {
630
631 case PAM_SUCCESS:
632 /*
633 * With PAM we support the concept of a "template"
634 * user. The user enters a login name which is
635 * authenticated by PAM, usually via a remote service
636 * such as RADIUS or TACACS+. If authentication
637 * succeeds, a different but related "template" name
638 * is used for setting the credentials, shell, and
639 * home directory. The name the user enters need only
640 * exist on the remote authentication server, but the
641 * template name must be present in the local password
642 * database.
643 *
644 * This is supported by two various mechanisms in the
645 * individual modules. However, from the application's
646 * point of view, the template user is always passed
647 * back as a changed value of the PAM_USER item.
648 */
649 pam_err = pam_get_item(pamh, PAM_USER, &item);
650 if (pam_err == PAM_SUCCESS) {
651 tmpl_user = (const char *)item;
652 if (strcmp(username, tmpl_user) != 0)
653 pwd = getpwnam(tmpl_user);
654 } else {
655 pam_syslog("pam_get_item(PAM_USER)");
656 }
657 rval = 0;
658 break;
659
660 case PAM_AUTH_ERR:
661 case PAM_USER_UNKNOWN:
662 case PAM_MAXTRIES:
663 rval = 1;
664 break;
665
666 default:
667 pam_syslog("pam_authenticate()");
668 rval = -1;
669 break;
670 }
671
672 if (rval == 0) {
673 pam_err = pam_acct_mgmt(pamh, pam_silent);
674 switch (pam_err) {
675 case PAM_SUCCESS:
676 break;
677 case PAM_NEW_AUTHTOK_REQD:
678 pam_err = pam_chauthtok(pamh,
679 pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
680 if (pam_err != PAM_SUCCESS) {
681 pam_syslog("pam_chauthtok()");
682 rval = 1;
683 }
684 break;
685 default:
686 pam_syslog("pam_acct_mgmt()");
687 rval = 1;
688 break;
689 }
690 }
691
692 if (rval != 0) {
693 pam_end(pamh, pam_err);
694 pamh = NULL;
695 }
696 return (rval);
697}
698
699/*
700 * Export any environment variables PAM modules may have set
701 */
702static void
703export_pam_environment()
704{
705 char **pam_env;
706 char **pp;
707
708 pam_env = pam_getenvlist(pamh);
709 if (pam_env != NULL) {
710 for (pp = pam_env; *pp != NULL; pp++) {
711 (void)export(*pp);
712 free(*pp);
713 }
714 }
715}
716
717/*
718 * Perform sanity checks on an environment variable:
719 * - Make sure there is an '=' in the string.
720 * - Make sure the string doesn't run on too long.
721 * - Do not export certain variables. This list was taken from the
722 * Solaris pam_putenv(3) man page.
723 * Then export it.
724 */
725static int
726export(const char *s)
727{
728 static const char *noexport[] = {
729 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
730 "IFS", "PATH", NULL
731 };
732 const char **pp;
733 size_t n;
734
735 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
736 return (0);
737 if (strncmp(s, "LD_", 3) == 0)
738 return (0);
739 for (pp = noexport; *pp != NULL; pp++) {
740 n = strlen(*pp);
741 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
742 return (0);
743 }
744 (void)putenv(s);
745 return (1);
746}
747
748static void
749usage()
750{
751
752 (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
753 exit(1);
754}
755
756/*
757 * Prompt user and read login name from stdin.
758 */
759static char *
760getloginname()
761{
762 char *nbuf, *p;
763 int ch;
764
765 nbuf = malloc(MAXLOGNAME);
766 if (nbuf == NULL)
767 err(1, "malloc()");
768 do {
769 (void)printf("%s", prompt);
770 for (p = nbuf; (ch = getchar()) != '\n'; ) {
771 if (ch == EOF) {
772 badlogin(username);
773 bail(NO_SLEEP_EXIT, 0);
774 }
775 if (p < nbuf + MAXLOGNAME - 1)
776 *p++ = ch;
777 }
778 } while (p == nbuf);
779
780 *p = '\0';
781 if (nbuf[0] == '-') {
782 pam_silent = 0;
783 memmove(nbuf, nbuf + 1, strlen(nbuf));
784 } else {
785 pam_silent = PAM_SILENT;
786 }
787 return nbuf;
788}
789
790/*
791 * SIGINT handler for motd().
792 */
793static volatile int motdinterrupt;
794static void
795sigint(int signo __unused)
796{
797 motdinterrupt = 1;
798}
799
800/*
801 * Display the contents of a file (such as /etc/motd).
802 */
803static int
804motd(const char *motdfile)
805{
806 sig_t oldint;
807 FILE *f;
808 int ch;
809
810 if ((f = fopen(motdfile, "r")) == NULL)
811 return (-1);
812 motdinterrupt = 0;
813 oldint = signal(SIGINT, sigint);
814 while ((ch = fgetc(f)) != EOF && !motdinterrupt)
815 putchar(ch);
816 signal(SIGINT, oldint);
817 if (ch != EOF || ferror(f)) {
818 fclose(f);
819 return (-1);
820 }
821 fclose(f);
822 return (0);
823}
824
825/*
826 * SIGALRM handler, to enforce login prompt timeout.
827 *
828 * XXX This can potentially confuse the hell out of PAM. We should
829 * XXX instead implement a conversation function that returns
830 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
831 * XXX handler just set a flag.
832 */
833static void
834timedout(int signo __unused)
835{
836
837 longjmp(timeout_buf, signo);
838}
839
840static void
841badlogin(char *name)
842{
843
844 if (failures == 0)
845 return;
846 if (hflag) {
847 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
848 failures, failures > 1 ? "S" : "", hostname);
849 syslog(LOG_AUTHPRIV|LOG_NOTICE,
850 "%d LOGIN FAILURE%s FROM %s, %s",
851 failures, failures > 1 ? "S" : "", hostname, name);
852 } else {
853 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
854 failures, failures > 1 ? "S" : "", tty);
855 syslog(LOG_AUTHPRIV|LOG_NOTICE,
856 "%d LOGIN FAILURE%s ON %s, %s",
857 failures, failures > 1 ? "S" : "", tty, name);
858 }
859 failures = 0;
860}
861
862const char *
863stypeof(char *ttyid)
864{
865 struct ttyent *t;
866
867 if (ttyid != NULL && *ttyid != '\0') {
868 t = getttynam(ttyid);
869 if (t != NULL && t->ty_type != NULL)
870 return (t->ty_type);
871 }
872 return (NULL);
873}
874
875static void
876refused(const char *msg, const char *rtype, int lout)
877{
878
879 if (msg != NULL)
880 printf("%s.\n", msg);
881 if (hflag)
882 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
883 pwd->pw_name, rtype, hostname, tty);
884 else
885 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
886 pwd->pw_name, rtype, tty);
887 if (lout)
888 bail(SLEEP_EXIT, 1);
889}
890
891/*
892 * Log a PAM error
893 */
894static void
895pam_syslog(const char *msg)
896{
897 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
898}
899
900/*
901 * Shut down PAM
902 */
903static void
904pam_cleanup()
905{
906
907 if (pamh != NULL) {
908 if (pam_session_established) {
909 pam_err = pam_close_session(pamh, 0);
910 if (pam_err != PAM_SUCCESS)
911 pam_syslog("pam_close_session()");
912 }
913 pam_session_established = 0;
914 if (pam_cred_established) {
915 pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
916 if (pam_err != PAM_SUCCESS)
917 pam_syslog("pam_setcred()");
918 }
919 pam_cred_established = 0;
920 pam_end(pamh, pam_err);
921 pamh = NULL;
922 }
923}
924
925/*
926 * Exit, optionally after sleeping a few seconds
927 */
928void
929bail(int sec, int eval)
930{
931
932 pam_cleanup();
933 (void)sleep(sec);
934 exit(eval);
935}
254 default_prompt, default_prompt);
255 passwd_prompt = login_getcapstr(lc, "passwd_prompt",
256 default_passwd_prompt, default_passwd_prompt);
257 retries = login_getcapnum(lc, "login-retries",
258 DEFAULT_RETRIES, DEFAULT_RETRIES);
259 backoff = login_getcapnum(lc, "login-backoff",
260 DEFAULT_BACKOFF, DEFAULT_BACKOFF);
261 login_close(lc);
262 lc = NULL;
263
264 /*
265 * Try to authenticate the user until we succeed or time out.
266 */
267 for (cnt = 0;; ask = 1) {
268 if (ask) {
269 fflag = 0;
270 if (olduser != NULL)
271 free(olduser);
272 olduser = username;
273 username = getloginname();
274 }
275 rootlogin = 0;
276
277 /*
278 * Note if trying multiple user names; log failures for
279 * previous user name, but don't bother logging one failure
280 * for nonexistent name (mistyped username).
281 */
282 if (failures && strcmp(olduser, username) != 0) {
283 if (failures > (pwd ? 0 : 1))
284 badlogin(olduser);
285 }
286
287 /*
288 * Load the PAM policy and set some variables
289 */
290 pam_err = pam_start("login", username, &pamc, &pamh);
291 if (pam_err != PAM_SUCCESS) {
292 pam_syslog("pam_start()");
293 bail(NO_SLEEP_EXIT, 1);
294 }
295 pam_err = pam_set_item(pamh, PAM_TTY, tty);
296 if (pam_err != PAM_SUCCESS) {
297 pam_syslog("pam_set_item(PAM_TTY)");
298 bail(NO_SLEEP_EXIT, 1);
299 }
300 pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
301 if (pam_err != PAM_SUCCESS) {
302 pam_syslog("pam_set_item(PAM_RHOST)");
303 bail(NO_SLEEP_EXIT, 1);
304 }
305
306 pwd = getpwnam(username);
307 if (pwd != NULL && pwd->pw_uid == 0)
308 rootlogin = 1;
309
310 /*
311 * If the -f option was specified and the caller is
312 * root or the caller isn't changing their uid, don't
313 * authenticate.
314 */
315 if (pwd != NULL && fflag &&
316 (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
317 /* already authenticated */
318 rval = 0;
319 } else {
320 fflag = 0;
321 (void)setpriority(PRIO_PROCESS, 0, -4);
322 rval = auth_pam();
323 (void)setpriority(PRIO_PROCESS, 0, 0);
324 }
325
326 if (pwd && rval == 0)
327 break;
328
329 pam_cleanup();
330
331 (void)printf("Login incorrect\n");
332 failures++;
333
334 /*
335 * Allow up to 'retry' (10) attempts, but start
336 * backing off after 'backoff' (3) attempts.
337 */
338 if (++cnt > backoff) {
339 if (cnt >= retries) {
340 badlogin(username);
341 bail(SLEEP_EXIT, 1);
342 }
343 sleep((u_int)((cnt - backoff) * 5));
344 }
345 }
346
347 /* committed to login -- turn off timeout */
348 (void)alarm((u_int)0);
349 (void)signal(SIGHUP, SIG_DFL);
350
351 endpwent();
352
353 /*
354 * Establish the login class.
355 */
356 lc = login_getpwclass(pwd);
357
358 quietlog = login_getcapbool(lc, "hushlogin", 0);
359 if (!quietlog)
360 pam_silent = 0;
361
362 /*
363 * Switching needed for NFS with root access disabled.
364 *
365 * XXX: This change fails to modify the additional groups for the
366 * process, and as such, may restrict rights normally granted
367 * through those groups.
368 */
369 (void)setegid(pwd->pw_gid);
370 (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
371 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
372 if (login_getcapbool(lc, "requirehome", 0))
373 refused("Home directory not available", "HOMEDIR", 1);
374 if (chdir("/") < 0)
375 refused("Cannot find root directory", "ROOTDIR", 1);
376 if (!quietlog || *pwd->pw_dir)
377 printf("No home directory.\nLogging in with home = \"/\".\n");
378 pwd->pw_dir = strdup("/");
379 if (pwd->pw_dir == NULL) {
380 syslog(LOG_NOTICE, "strdup(): %m");
381 bail(SLEEP_EXIT, 1);
382 }
383 }
384 (void)seteuid(euid);
385 (void)setegid(egid);
386 if (!quietlog)
387 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
388
389 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
390 if (*pwd->pw_shell == '\0')
391 pwd->pw_shell = strdup(_PATH_BSHELL);
392 if (pwd->pw_shell == NULL) {
393 syslog(LOG_NOTICE, "strdup(): %m");
394 bail(SLEEP_EXIT, 1);
395 }
396 if (*shell == '\0') /* Not overridden */
397 shell = pwd->pw_shell;
398 if ((shell = strdup(shell)) == NULL) {
399 syslog(LOG_NOTICE, "strdup(): %m");
400 bail(SLEEP_EXIT, 1);
401 }
402
403 /*
404 * Set device protections, depending on what terminal the
405 * user is logged in. This feature is used on Suns to give
406 * console users better privacy.
407 */
408 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
409
410 /*
411 * Clear flags of the tty. None should be set, and when the
412 * user sets them otherwise, this can cause the chown to fail.
413 * Since it isn't clear that flags are useful on character
414 * devices, we just clear them.
415 *
416 * We don't log in the case of EOPNOTSUPP because dev might be
417 * on NFS, which doesn't support chflags.
418 *
419 * We don't log in the EROFS because that means that /dev is on
420 * a read only file system and we assume that the permissions there
421 * are sane.
422 */
423 if (ttyn != tname && chflags(ttyn, 0))
424 if (errno != EOPNOTSUPP && errno != EROFS)
425 syslog(LOG_ERR, "chflags(%s): %m", ttyn);
426 if (ttyn != tname && chown(ttyn, pwd->pw_uid,
427 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
428 if (errno != EROFS)
429 syslog(LOG_ERR, "chmod(%s): %m", ttyn);
430
431 /*
432 * Exclude cons/vt/ptys only, assume dialup otherwise
433 * TODO: Make dialup tty determination a library call
434 * for consistency (finger etc.)
435 */
436 if (hflag && isdialuptty(tty))
437 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
438
439#ifdef LOGALL
440 /*
441 * Syslog each successful login, so we don't have to watch
442 * hundreds of wtmp or lastlogin files.
443 */
444 if (hflag)
445 syslog(LOG_INFO, "login from %s on %s as %s",
446 hostname, tty, pwd->pw_name);
447 else
448 syslog(LOG_INFO, "login on %s as %s",
449 tty, pwd->pw_name);
450#endif
451
452 /*
453 * If fflag is on, assume caller/authenticator has logged root
454 * login.
455 */
456 if (rootlogin && fflag == 0) {
457 if (hflag)
458 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
459 username, tty, hostname);
460 else
461 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
462 username, tty);
463 }
464
465 /*
466 * Destroy environment unless user has requested its
467 * preservation - but preserve TERM in all cases
468 */
469 term = getenv("TERM");
470 if (!pflag)
471 environ = envinit;
472 if (term != NULL)
473 setenv("TERM", term, 0);
474
475 /*
476 * PAM modules might add supplementary groups during pam_setcred().
477 */
478 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
479 syslog(LOG_ERR, "setusercontext() failed - exiting");
480 bail(NO_SLEEP_EXIT, 1);
481 }
482
483 pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
484 if (pam_err != PAM_SUCCESS) {
485 pam_syslog("pam_setcred()");
486 bail(NO_SLEEP_EXIT, 1);
487 }
488 pam_cred_established = 1;
489
490 pam_err = pam_open_session(pamh, pam_silent);
491 if (pam_err != PAM_SUCCESS) {
492 pam_syslog("pam_open_session()");
493 bail(NO_SLEEP_EXIT, 1);
494 }
495 pam_session_established = 1;
496
497 /*
498 * We must fork() before setuid() because we need to call
499 * pam_close_session() as root.
500 */
501 pid = fork();
502 if (pid < 0) {
503 err(1, "fork");
504 } else if (pid != 0) {
505 /*
506 * Parent: wait for child to finish, then clean up
507 * session.
508 */
509 int status;
510 setproctitle("-%s [pam]", getprogname());
511 waitpid(pid, &status, 0);
512 bail(NO_SLEEP_EXIT, 0);
513 }
514
515 /*
516 * NOTICE: We are now in the child process!
517 */
518
519 /*
520 * Add any environment variables the PAM modules may have set.
521 */
522 export_pam_environment();
523
524 /*
525 * We're done with PAM now; our parent will deal with the rest.
526 */
527 pam_end(pamh, 0);
528 pamh = NULL;
529
530 /*
531 * We don't need to be root anymore, so set the login name and
532 * the UID.
533 */
534 if (setlogin(username) != 0) {
535 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
536 bail(NO_SLEEP_EXIT, 1);
537 }
538 if (setusercontext(lc, pwd, pwd->pw_uid,
539 LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
540 syslog(LOG_ERR, "setusercontext() failed - exiting");
541 exit(1);
542 }
543
544 (void)setenv("SHELL", pwd->pw_shell, 1);
545 (void)setenv("HOME", pwd->pw_dir, 1);
546 /* Overwrite "term" from login.conf(5) for any known TERM */
547 if (term == NULL && (tp = stypeof(tty)) != NULL)
548 (void)setenv("TERM", tp, 1);
549 else
550 (void)setenv("TERM", TERM_UNKNOWN, 0);
551 (void)setenv("LOGNAME", username, 1);
552 (void)setenv("USER", username, 1);
553 (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
554
555 if (!quietlog) {
556 const char *cw;
557
558 cw = login_getcapstr(lc, "copyright", NULL, NULL);
559 if (cw == NULL || motd(cw) == -1)
560 (void)printf("%s", copyright);
561
562 (void)printf("\n");
563
564 cw = login_getcapstr(lc, "welcome", NULL, NULL);
565 if (cw != NULL && access(cw, F_OK) == 0)
566 motd(cw);
567 else
568 motd(_PATH_MOTDFILE);
569
570 if (login_getcapbool(lc, "nocheckmail", 0) == 0) {
571 char *cx;
572
573 /* $MAIL may have been set by class. */
574 cx = getenv("MAIL");
575 if (cx == NULL) {
576 asprintf(&cx, "%s/%s",
577 _PATH_MAILDIR, pwd->pw_name);
578 }
579 if (cx && stat(cx, &st) == 0 && st.st_size != 0)
580 (void)printf("You have %smail.\n",
581 (st.st_mtime > st.st_atime) ? "new " : "");
582 if (getenv("MAIL") == NULL)
583 free(cx);
584 }
585 }
586
587 login_close(lc);
588
589 (void)signal(SIGALRM, SIG_DFL);
590 (void)signal(SIGQUIT, SIG_DFL);
591 (void)signal(SIGINT, SIG_DFL);
592 (void)signal(SIGTSTP, SIG_IGN);
593
594 /*
595 * Login shells have a leading '-' in front of argv[0]
596 */
597 p = strrchr(pwd->pw_shell, '/');
598 if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
599 syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
600 username);
601 errx(1, "shell exceeds maximum pathname size");
602 } else if (arg0 == NULL) {
603 err(1, "asprintf()");
604 }
605
606 execlp(shell, arg0, (char *)0);
607 err(1, "%s", shell);
608
609 /*
610 * That's it, folks!
611 */
612}
613
614/*
615 * Attempt to authenticate the user using PAM. Returns 0 if the user is
616 * authenticated, or 1 if not authenticated. If some sort of PAM system
617 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
618 * function returns -1. This can be used as an indication that we should
619 * fall back to a different authentication mechanism.
620 */
621static int
622auth_pam(void)
623{
624 const char *tmpl_user;
625 const void *item;
626 int rval;
627
628 pam_err = pam_authenticate(pamh, pam_silent);
629 switch (pam_err) {
630
631 case PAM_SUCCESS:
632 /*
633 * With PAM we support the concept of a "template"
634 * user. The user enters a login name which is
635 * authenticated by PAM, usually via a remote service
636 * such as RADIUS or TACACS+. If authentication
637 * succeeds, a different but related "template" name
638 * is used for setting the credentials, shell, and
639 * home directory. The name the user enters need only
640 * exist on the remote authentication server, but the
641 * template name must be present in the local password
642 * database.
643 *
644 * This is supported by two various mechanisms in the
645 * individual modules. However, from the application's
646 * point of view, the template user is always passed
647 * back as a changed value of the PAM_USER item.
648 */
649 pam_err = pam_get_item(pamh, PAM_USER, &item);
650 if (pam_err == PAM_SUCCESS) {
651 tmpl_user = (const char *)item;
652 if (strcmp(username, tmpl_user) != 0)
653 pwd = getpwnam(tmpl_user);
654 } else {
655 pam_syslog("pam_get_item(PAM_USER)");
656 }
657 rval = 0;
658 break;
659
660 case PAM_AUTH_ERR:
661 case PAM_USER_UNKNOWN:
662 case PAM_MAXTRIES:
663 rval = 1;
664 break;
665
666 default:
667 pam_syslog("pam_authenticate()");
668 rval = -1;
669 break;
670 }
671
672 if (rval == 0) {
673 pam_err = pam_acct_mgmt(pamh, pam_silent);
674 switch (pam_err) {
675 case PAM_SUCCESS:
676 break;
677 case PAM_NEW_AUTHTOK_REQD:
678 pam_err = pam_chauthtok(pamh,
679 pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
680 if (pam_err != PAM_SUCCESS) {
681 pam_syslog("pam_chauthtok()");
682 rval = 1;
683 }
684 break;
685 default:
686 pam_syslog("pam_acct_mgmt()");
687 rval = 1;
688 break;
689 }
690 }
691
692 if (rval != 0) {
693 pam_end(pamh, pam_err);
694 pamh = NULL;
695 }
696 return (rval);
697}
698
699/*
700 * Export any environment variables PAM modules may have set
701 */
702static void
703export_pam_environment()
704{
705 char **pam_env;
706 char **pp;
707
708 pam_env = pam_getenvlist(pamh);
709 if (pam_env != NULL) {
710 for (pp = pam_env; *pp != NULL; pp++) {
711 (void)export(*pp);
712 free(*pp);
713 }
714 }
715}
716
717/*
718 * Perform sanity checks on an environment variable:
719 * - Make sure there is an '=' in the string.
720 * - Make sure the string doesn't run on too long.
721 * - Do not export certain variables. This list was taken from the
722 * Solaris pam_putenv(3) man page.
723 * Then export it.
724 */
725static int
726export(const char *s)
727{
728 static const char *noexport[] = {
729 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
730 "IFS", "PATH", NULL
731 };
732 const char **pp;
733 size_t n;
734
735 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
736 return (0);
737 if (strncmp(s, "LD_", 3) == 0)
738 return (0);
739 for (pp = noexport; *pp != NULL; pp++) {
740 n = strlen(*pp);
741 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
742 return (0);
743 }
744 (void)putenv(s);
745 return (1);
746}
747
748static void
749usage()
750{
751
752 (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
753 exit(1);
754}
755
756/*
757 * Prompt user and read login name from stdin.
758 */
759static char *
760getloginname()
761{
762 char *nbuf, *p;
763 int ch;
764
765 nbuf = malloc(MAXLOGNAME);
766 if (nbuf == NULL)
767 err(1, "malloc()");
768 do {
769 (void)printf("%s", prompt);
770 for (p = nbuf; (ch = getchar()) != '\n'; ) {
771 if (ch == EOF) {
772 badlogin(username);
773 bail(NO_SLEEP_EXIT, 0);
774 }
775 if (p < nbuf + MAXLOGNAME - 1)
776 *p++ = ch;
777 }
778 } while (p == nbuf);
779
780 *p = '\0';
781 if (nbuf[0] == '-') {
782 pam_silent = 0;
783 memmove(nbuf, nbuf + 1, strlen(nbuf));
784 } else {
785 pam_silent = PAM_SILENT;
786 }
787 return nbuf;
788}
789
790/*
791 * SIGINT handler for motd().
792 */
793static volatile int motdinterrupt;
794static void
795sigint(int signo __unused)
796{
797 motdinterrupt = 1;
798}
799
800/*
801 * Display the contents of a file (such as /etc/motd).
802 */
803static int
804motd(const char *motdfile)
805{
806 sig_t oldint;
807 FILE *f;
808 int ch;
809
810 if ((f = fopen(motdfile, "r")) == NULL)
811 return (-1);
812 motdinterrupt = 0;
813 oldint = signal(SIGINT, sigint);
814 while ((ch = fgetc(f)) != EOF && !motdinterrupt)
815 putchar(ch);
816 signal(SIGINT, oldint);
817 if (ch != EOF || ferror(f)) {
818 fclose(f);
819 return (-1);
820 }
821 fclose(f);
822 return (0);
823}
824
825/*
826 * SIGALRM handler, to enforce login prompt timeout.
827 *
828 * XXX This can potentially confuse the hell out of PAM. We should
829 * XXX instead implement a conversation function that returns
830 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
831 * XXX handler just set a flag.
832 */
833static void
834timedout(int signo __unused)
835{
836
837 longjmp(timeout_buf, signo);
838}
839
840static void
841badlogin(char *name)
842{
843
844 if (failures == 0)
845 return;
846 if (hflag) {
847 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
848 failures, failures > 1 ? "S" : "", hostname);
849 syslog(LOG_AUTHPRIV|LOG_NOTICE,
850 "%d LOGIN FAILURE%s FROM %s, %s",
851 failures, failures > 1 ? "S" : "", hostname, name);
852 } else {
853 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
854 failures, failures > 1 ? "S" : "", tty);
855 syslog(LOG_AUTHPRIV|LOG_NOTICE,
856 "%d LOGIN FAILURE%s ON %s, %s",
857 failures, failures > 1 ? "S" : "", tty, name);
858 }
859 failures = 0;
860}
861
862const char *
863stypeof(char *ttyid)
864{
865 struct ttyent *t;
866
867 if (ttyid != NULL && *ttyid != '\0') {
868 t = getttynam(ttyid);
869 if (t != NULL && t->ty_type != NULL)
870 return (t->ty_type);
871 }
872 return (NULL);
873}
874
875static void
876refused(const char *msg, const char *rtype, int lout)
877{
878
879 if (msg != NULL)
880 printf("%s.\n", msg);
881 if (hflag)
882 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
883 pwd->pw_name, rtype, hostname, tty);
884 else
885 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
886 pwd->pw_name, rtype, tty);
887 if (lout)
888 bail(SLEEP_EXIT, 1);
889}
890
891/*
892 * Log a PAM error
893 */
894static void
895pam_syslog(const char *msg)
896{
897 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
898}
899
900/*
901 * Shut down PAM
902 */
903static void
904pam_cleanup()
905{
906
907 if (pamh != NULL) {
908 if (pam_session_established) {
909 pam_err = pam_close_session(pamh, 0);
910 if (pam_err != PAM_SUCCESS)
911 pam_syslog("pam_close_session()");
912 }
913 pam_session_established = 0;
914 if (pam_cred_established) {
915 pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
916 if (pam_err != PAM_SUCCESS)
917 pam_syslog("pam_setcred()");
918 }
919 pam_cred_established = 0;
920 pam_end(pamh, pam_err);
921 pamh = NULL;
922 }
923}
924
925/*
926 * Exit, optionally after sleeping a few seconds
927 */
928void
929bail(int sec, int eval)
930{
931
932 pam_cleanup();
933 (void)sleep(sec);
934 exit(eval);
935}