19b50d902SRodney W. Grimes /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
49b50d902SRodney W. Grimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved.
6f2c44cceSDag-Erling Smørgrav * Copyright (c) 2002 Networks Associates Technologies, Inc.
7f2c44cceSDag-Erling Smørgrav * All rights reserved.
8f2c44cceSDag-Erling Smørgrav *
9f2c44cceSDag-Erling Smørgrav * Portions of this software were developed for the FreeBSD Project by
10f2c44cceSDag-Erling Smørgrav * ThinkSec AS and NAI Labs, the Security Research Division of Network
11f2c44cceSDag-Erling Smørgrav * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
12f2c44cceSDag-Erling Smørgrav * ("CBOSS"), as part of the DARPA CHATS research program.
139b50d902SRodney W. Grimes *
149b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
159b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions
169b50d902SRodney W. Grimes * are met:
179b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
189b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
199b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
209b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
219b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution.
229b50d902SRodney W. Grimes * 3. All advertising materials mentioning features or use of this software
239b50d902SRodney W. Grimes * must display the following acknowledgement:
249b50d902SRodney W. Grimes * This product includes software developed by the University of
259b50d902SRodney W. Grimes * California, Berkeley and its contributors.
269b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors
279b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software
289b50d902SRodney W. Grimes * without specific prior written permission.
299b50d902SRodney W. Grimes *
309b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
319b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
329b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
339b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
349b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
359b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
369b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
379b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
389b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
399b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
409b50d902SRodney W. Grimes * SUCH DAMAGE.
419b50d902SRodney W. Grimes */
429b50d902SRodney W. Grimes
439f5b04e9SDavid Malone #include <sys/cdefs.h>
449b50d902SRodney W. Grimes /*
459b50d902SRodney W. Grimes * login [ name ]
469b50d902SRodney W. Grimes * login -h hostname (for telnetd, etc.)
479b50d902SRodney W. Grimes * login -f name (for pre-authenticated login: datakit, xterm, etc.)
489b50d902SRodney W. Grimes */
499b50d902SRodney W. Grimes
501a8b24c2SMark Murray #include <sys/param.h>
51a9648779SMark Murray #include <sys/file.h>
52a9648779SMark Murray #include <sys/stat.h>
539b50d902SRodney W. Grimes #include <sys/time.h>
549b50d902SRodney W. Grimes #include <sys/resource.h>
55a9648779SMark Murray #include <sys/wait.h>
569b50d902SRodney W. Grimes
579b50d902SRodney W. Grimes #include <err.h>
589b50d902SRodney W. Grimes #include <errno.h>
599b50d902SRodney W. Grimes #include <grp.h>
606717b4a8SJohn Polstra #include <login_cap.h>
619b50d902SRodney W. Grimes #include <pwd.h>
629b50d902SRodney W. Grimes #include <setjmp.h>
639b50d902SRodney W. Grimes #include <signal.h>
649b50d902SRodney W. Grimes #include <stdio.h>
659b50d902SRodney W. Grimes #include <stdlib.h>
669b50d902SRodney W. Grimes #include <string.h>
679b50d902SRodney W. Grimes #include <syslog.h>
689b50d902SRodney W. Grimes #include <ttyent.h>
699b50d902SRodney W. Grimes #include <unistd.h>
709b50d902SRodney W. Grimes
71e8334816SJohn Polstra #include <security/pam_appl.h>
72519b6a4cSDag-Erling Smørgrav #include <security/openpam.h>
732ddadf84SPaul Traina
74e317b970SMark Murray #include "login.h"
759b50d902SRodney W. Grimes #include "pathnames.h"
769b50d902SRodney W. Grimes
77c60ed00aSDag-Erling Smørgrav static int auth_pam(void);
78c60ed00aSDag-Erling Smørgrav static void bail(int, int);
792482c270SJilles Tjoelker static void bail_internal(int, int, int);
80c60ed00aSDag-Erling Smørgrav static int export(const char *);
81c60ed00aSDag-Erling Smørgrav static void export_pam_environment(void);
82c60ed00aSDag-Erling Smørgrav static int motd(const char *);
83c60ed00aSDag-Erling Smørgrav static void badlogin(char *);
84c60ed00aSDag-Erling Smørgrav static char *getloginname(void);
85c60ed00aSDag-Erling Smørgrav static void pam_syslog(const char *);
86c60ed00aSDag-Erling Smørgrav static void pam_cleanup(void);
87c60ed00aSDag-Erling Smørgrav static void refused(const char *, const char *, int);
88c60ed00aSDag-Erling Smørgrav static const char *stypeof(char *);
89c60ed00aSDag-Erling Smørgrav static void sigint(int);
90c60ed00aSDag-Erling Smørgrav static void timedout(int);
912482c270SJilles Tjoelker static void bail_sig(int);
92c60ed00aSDag-Erling Smørgrav static void usage(void);
93142277ceSMark Murray
94e317b970SMark Murray #define TTYGRPNAME "tty" /* group to own ttys */
95a52c1be6SDavid Nugent #define DEFAULT_BACKOFF 3
96a52c1be6SDavid Nugent #define DEFAULT_RETRIES 10
97078ae588SDavid E. O'Brien #define DEFAULT_PROMPT "login: "
98078ae588SDavid E. O'Brien #define DEFAULT_PASSWD_PROMPT "Password:"
99c60ed00aSDag-Erling Smørgrav #define TERM_UNKNOWN "su"
100e317b970SMark Murray #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
101c60ed00aSDag-Erling Smørgrav #define NO_SLEEP_EXIT 0
102c60ed00aSDag-Erling Smørgrav #define SLEEP_EXIT 5
1039b50d902SRodney W. Grimes
1049b50d902SRodney W. Grimes /*
1059b50d902SRodney W. Grimes * This bounds the time given to login. Not a define so it can
1069b50d902SRodney W. Grimes * be patched on machines where it's too small.
1079b50d902SRodney W. Grimes */
108c60ed00aSDag-Erling Smørgrav static u_int timeout = 300;
1099b50d902SRodney W. Grimes
110b606e33cSEivind Eklund /* Buffer for signal handling of timeout */
111c60ed00aSDag-Erling Smørgrav static jmp_buf timeout_buf;
112b606e33cSEivind Eklund
113a3d80dd8SDag-Erling Smørgrav char pwbuf[1024];
114a3d80dd8SDag-Erling Smørgrav struct passwd pwres;
1159b50d902SRodney W. Grimes struct passwd *pwd;
116c60ed00aSDag-Erling Smørgrav static int failures;
117c60ed00aSDag-Erling Smørgrav
118c60ed00aSDag-Erling Smørgrav static char *envinit[1]; /* empty environment list */
119c60ed00aSDag-Erling Smørgrav
120c60ed00aSDag-Erling Smørgrav /*
121c60ed00aSDag-Erling Smørgrav * Command line flags and arguments
122c60ed00aSDag-Erling Smørgrav */
123c60ed00aSDag-Erling Smørgrav static int fflag; /* -f: do not perform authentication */
124c60ed00aSDag-Erling Smørgrav static int hflag; /* -h: login from remote host */
125c60ed00aSDag-Erling Smørgrav static char *hostname; /* hostname from command line */
126c60ed00aSDag-Erling Smørgrav static int pflag; /* -p: preserve environment */
127c60ed00aSDag-Erling Smørgrav
128c60ed00aSDag-Erling Smørgrav /*
129c60ed00aSDag-Erling Smørgrav * User name
130c60ed00aSDag-Erling Smørgrav */
131c60ed00aSDag-Erling Smørgrav static char *username; /* user name */
132c60ed00aSDag-Erling Smørgrav static char *olduser; /* previous user name */
133c60ed00aSDag-Erling Smørgrav
134c60ed00aSDag-Erling Smørgrav /*
135c60ed00aSDag-Erling Smørgrav * Prompts
136c60ed00aSDag-Erling Smørgrav */
137c60ed00aSDag-Erling Smørgrav static char default_prompt[] = DEFAULT_PROMPT;
138f2f306b6SRuslan Ermilov static const char *prompt;
139c60ed00aSDag-Erling Smørgrav static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
140f2f306b6SRuslan Ermilov static const char *passwd_prompt;
141c60ed00aSDag-Erling Smørgrav
142c60ed00aSDag-Erling Smørgrav static char *tty;
143c60ed00aSDag-Erling Smørgrav
144c60ed00aSDag-Erling Smørgrav /*
145c60ed00aSDag-Erling Smørgrav * PAM data
146c60ed00aSDag-Erling Smørgrav */
147c60ed00aSDag-Erling Smørgrav static pam_handle_t *pamh = NULL;
148519b6a4cSDag-Erling Smørgrav static struct pam_conv pamc = { openpam_ttyconv, NULL };
149c60ed00aSDag-Erling Smørgrav static int pam_err;
150c60ed00aSDag-Erling Smørgrav static int pam_silent = PAM_SILENT;
151c60ed00aSDag-Erling Smørgrav static int pam_cred_established;
152c60ed00aSDag-Erling Smørgrav static int pam_session_established;
1539b50d902SRodney W. Grimes
1549b50d902SRodney W. Grimes int
main(int argc,char * argv[])155c60ed00aSDag-Erling Smørgrav main(int argc, char *argv[])
1569b50d902SRodney W. Grimes {
1579b50d902SRodney W. Grimes struct group *gr;
1589b50d902SRodney W. Grimes struct stat st;
159c60ed00aSDag-Erling Smørgrav int retries, backoff;
160c60ed00aSDag-Erling Smørgrav int ask, ch, cnt, quietlog, rootlogin, rval;
161c8ff1808SPeter Wemm uid_t uid, euid;
1620514336dSAndrey A. Chernov gid_t egid;
163c60ed00aSDag-Erling Smørgrav char *term;
1643a6afd0dSBrian Somers char *p, *ttyn;
165b606e33cSEivind Eklund char tname[sizeof(_PATH_TTY) + 10];
166f2f306b6SRuslan Ermilov char *arg0;
16779a20d3bSAndrey A. Chernov const char *tp;
168f2f306b6SRuslan Ermilov const char *shell = NULL;
1696acc486bSDavid Nugent login_cap_t *lc = NULL;
17042dc3715SMaxim Konovalov login_cap_t *lc_user = NULL;
1715bc9d93dSMark Murray pid_t pid;
1722482c270SJilles Tjoelker sigset_t mask, omask;
1732482c270SJilles Tjoelker struct sigaction sa;
1740c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
175a1c73d21SWayne Salamon char auditsuccess = 1;
1760c59c145SChristian S.J. Peron #endif
1779b50d902SRodney W. Grimes
1782482c270SJilles Tjoelker sa.sa_flags = SA_RESTART;
1792482c270SJilles Tjoelker (void)sigfillset(&sa.sa_mask);
1802482c270SJilles Tjoelker sa.sa_handler = SIG_IGN;
1812482c270SJilles Tjoelker (void)sigaction(SIGQUIT, &sa, NULL);
1822482c270SJilles Tjoelker (void)sigaction(SIGINT, &sa, NULL);
1832482c270SJilles Tjoelker (void)sigaction(SIGHUP, &sa, NULL);
184b606e33cSEivind Eklund if (setjmp(timeout_buf)) {
185b606e33cSEivind Eklund if (failures)
186c60ed00aSDag-Erling Smørgrav badlogin(username);
18791a72a92SDavid E. O'Brien (void)fprintf(stderr, "Login timed out after %d seconds\n",
18891a72a92SDavid E. O'Brien timeout);
189c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 0);
190b606e33cSEivind Eklund }
1912482c270SJilles Tjoelker sa.sa_handler = timedout;
1922482c270SJilles Tjoelker (void)sigaction(SIGALRM, &sa, NULL);
193b606e33cSEivind Eklund (void)alarm(timeout);
1949b50d902SRodney W. Grimes (void)setpriority(PRIO_PROCESS, 0, 0);
1959b50d902SRodney W. Grimes
1964d552825SAlex Richardson openlog("login", LOG_CONS, LOG_AUTH);
1979b50d902SRodney W. Grimes
1989b50d902SRodney W. Grimes uid = getuid();
199159da441SPeter Wemm euid = geteuid();
2000514336dSAndrey A. Chernov egid = getegid();
201c60ed00aSDag-Erling Smørgrav
2021c8af878SWarner Losh while ((ch = getopt(argc, argv, "fh:p")) != -1)
2039b50d902SRodney W. Grimes switch (ch) {
2049b50d902SRodney W. Grimes case 'f':
2059b50d902SRodney W. Grimes fflag = 1;
2069b50d902SRodney W. Grimes break;
2079b50d902SRodney W. Grimes case 'h':
208c60ed00aSDag-Erling Smørgrav if (uid != 0)
2099b50d902SRodney W. Grimes errx(1, "-h option: %s", strerror(EPERM));
210c60ed00aSDag-Erling Smørgrav if (strlen(optarg) >= MAXHOSTNAMELEN)
2119ab4f412SMike Barcroft errx(1, "-h option: %s: exceeds maximum "
2129ab4f412SMike Barcroft "hostname size", optarg);
213c60ed00aSDag-Erling Smørgrav hflag = 1;
2149b50d902SRodney W. Grimes hostname = optarg;
2159b50d902SRodney W. Grimes break;
2169b50d902SRodney W. Grimes case 'p':
2179b50d902SRodney W. Grimes pflag = 1;
2189b50d902SRodney W. Grimes break;
2199b50d902SRodney W. Grimes case '?':
2209b50d902SRodney W. Grimes default:
221c60ed00aSDag-Erling Smørgrav if (uid == 0)
2229b50d902SRodney W. Grimes syslog(LOG_ERR, "invalid flag %c", ch);
2239c9cb2bfSPhilippe Charnier usage();
2249b50d902SRodney W. Grimes }
2259b50d902SRodney W. Grimes argc -= optind;
2269b50d902SRodney W. Grimes argv += optind;
2279b50d902SRodney W. Grimes
228c60ed00aSDag-Erling Smørgrav if (argc > 0) {
229c60ed00aSDag-Erling Smørgrav username = strdup(*argv);
230c60ed00aSDag-Erling Smørgrav if (username == NULL)
231c60ed00aSDag-Erling Smørgrav err(1, "strdup()");
2329b50d902SRodney W. Grimes ask = 0;
233c60ed00aSDag-Erling Smørgrav } else {
2349b50d902SRodney W. Grimes ask = 1;
235c60ed00aSDag-Erling Smørgrav }
2369b50d902SRodney W. Grimes
2372517862eSDag-Erling Smørgrav setproctitle("-%s", getprogname());
2382517862eSDag-Erling Smørgrav
239587250b2SEd Schouten closefrom(3);
2409b50d902SRodney W. Grimes
241c60ed00aSDag-Erling Smørgrav /*
242c60ed00aSDag-Erling Smørgrav * Get current TTY
243c60ed00aSDag-Erling Smørgrav */
2449b50d902SRodney W. Grimes ttyn = ttyname(STDIN_FILENO);
2459b50d902SRodney W. Grimes if (ttyn == NULL || *ttyn == '\0') {
2469b50d902SRodney W. Grimes (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
2479b50d902SRodney W. Grimes ttyn = tname;
2489b50d902SRodney W. Grimes }
24995e7b94aSEd Schouten if (strncmp(ttyn, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
25095e7b94aSEd Schouten tty = ttyn + sizeof _PATH_DEV - 1;
2519b50d902SRodney W. Grimes else
2529b50d902SRodney W. Grimes tty = ttyn;
2539b50d902SRodney W. Grimes
254a52c1be6SDavid Nugent /*
255a52c1be6SDavid Nugent * Get "login-retries" & "login-backoff" from default class
256a52c1be6SDavid Nugent */
257a52c1be6SDavid Nugent lc = login_getclass(NULL);
2585c4b7a56SDag-Erling Smørgrav prompt = login_getcapstr(lc, "login_prompt",
259c60ed00aSDag-Erling Smørgrav default_prompt, default_prompt);
260078ae588SDavid E. O'Brien passwd_prompt = login_getcapstr(lc, "passwd_prompt",
261e317b970SMark Murray default_passwd_prompt, default_passwd_prompt);
262c60ed00aSDag-Erling Smørgrav retries = login_getcapnum(lc, "login-retries",
263c60ed00aSDag-Erling Smørgrav DEFAULT_RETRIES, DEFAULT_RETRIES);
264c60ed00aSDag-Erling Smørgrav backoff = login_getcapnum(lc, "login-backoff",
265c60ed00aSDag-Erling Smørgrav DEFAULT_BACKOFF, DEFAULT_BACKOFF);
266a52c1be6SDavid Nugent login_close(lc);
267a52c1be6SDavid Nugent lc = NULL;
2686acc486bSDavid Nugent
269c60ed00aSDag-Erling Smørgrav /*
270c60ed00aSDag-Erling Smørgrav * Try to authenticate the user until we succeed or time out.
271c60ed00aSDag-Erling Smørgrav */
2729b50d902SRodney W. Grimes for (cnt = 0;; ask = 1) {
2739b50d902SRodney W. Grimes if (ask) {
2749b50d902SRodney W. Grimes fflag = 0;
275c60ed00aSDag-Erling Smørgrav if (olduser != NULL)
276c60ed00aSDag-Erling Smørgrav free(olduser);
277c60ed00aSDag-Erling Smørgrav olduser = username;
278c60ed00aSDag-Erling Smørgrav username = getloginname();
2799b50d902SRodney W. Grimes }
2809b50d902SRodney W. Grimes rootlogin = 0;
2819b50d902SRodney W. Grimes
2829b50d902SRodney W. Grimes /*
2839b50d902SRodney W. Grimes * Note if trying multiple user names; log failures for
2849b50d902SRodney W. Grimes * previous user name, but don't bother logging one failure
2859b50d902SRodney W. Grimes * for nonexistent name (mistyped username).
2869b50d902SRodney W. Grimes */
287c60ed00aSDag-Erling Smørgrav if (failures && strcmp(olduser, username) != 0) {
2889b50d902SRodney W. Grimes if (failures > (pwd ? 0 : 1))
289c60ed00aSDag-Erling Smørgrav badlogin(olduser);
2909b50d902SRodney W. Grimes }
291c60ed00aSDag-Erling Smørgrav
292c60ed00aSDag-Erling Smørgrav /*
293c60ed00aSDag-Erling Smørgrav * Load the PAM policy and set some variables
294c60ed00aSDag-Erling Smørgrav */
295c60ed00aSDag-Erling Smørgrav pam_err = pam_start("login", username, &pamc, &pamh);
296c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
297c60ed00aSDag-Erling Smørgrav pam_syslog("pam_start()");
2980c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
299a1c73d21SWayne Salamon au_login_fail("PAM Error", 1);
3000c59c145SChristian S.J. Peron #endif
301c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
302c60ed00aSDag-Erling Smørgrav }
303c60ed00aSDag-Erling Smørgrav pam_err = pam_set_item(pamh, PAM_TTY, tty);
304c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
305c60ed00aSDag-Erling Smørgrav pam_syslog("pam_set_item(PAM_TTY)");
3060c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
307a1c73d21SWayne Salamon au_login_fail("PAM Error", 1);
3080c59c145SChristian S.J. Peron #endif
309c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
310c60ed00aSDag-Erling Smørgrav }
311d477c0caSDag-Erling Smørgrav pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
312c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
313c60ed00aSDag-Erling Smørgrav pam_syslog("pam_set_item(PAM_RHOST)");
3140c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
315a1c73d21SWayne Salamon au_login_fail("PAM Error", 1);
3160c59c145SChristian S.J. Peron #endif
317c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
318c60ed00aSDag-Erling Smørgrav }
3199b50d902SRodney W. Grimes
320a3d80dd8SDag-Erling Smørgrav (void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd);
321e8334816SJohn Polstra if (pwd != NULL && pwd->pw_uid == 0)
322e8334816SJohn Polstra rootlogin = 1;
323e8334816SJohn Polstra
3249b50d902SRodney W. Grimes /*
325c60ed00aSDag-Erling Smørgrav * If the -f option was specified and the caller is
326c60ed00aSDag-Erling Smørgrav * root or the caller isn't changing their uid, don't
327c60ed00aSDag-Erling Smørgrav * authenticate.
3289b50d902SRodney W. Grimes */
329c60ed00aSDag-Erling Smørgrav if (pwd != NULL && fflag &&
330c60ed00aSDag-Erling Smørgrav (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
331c60ed00aSDag-Erling Smørgrav /* already authenticated */
332c60ed00aSDag-Erling Smørgrav rval = 0;
3330c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
334a1c73d21SWayne Salamon auditsuccess = 0; /* opened a terminal window only */
3350c59c145SChristian S.J. Peron #endif
336c60ed00aSDag-Erling Smørgrav } else {
337c60ed00aSDag-Erling Smørgrav fflag = 0;
338c60ed00aSDag-Erling Smørgrav (void)setpriority(PRIO_PROCESS, 0, -4);
339c60ed00aSDag-Erling Smørgrav rval = auth_pam();
340c60ed00aSDag-Erling Smørgrav (void)setpriority(PRIO_PROCESS, 0, 0);
341d8a7b347SDavid Nugent }
3429b50d902SRodney W. Grimes
343a3d80dd8SDag-Erling Smørgrav if (pwd != NULL && rval == 0)
344c60ed00aSDag-Erling Smørgrav break;
345c60ed00aSDag-Erling Smørgrav
346c60ed00aSDag-Erling Smørgrav pam_cleanup();
347c60ed00aSDag-Erling Smørgrav
348a1c73d21SWayne Salamon /*
349a1c73d21SWayne Salamon * We are not exiting here, but this corresponds to a failed
350a1c73d21SWayne Salamon * login event, so set exitstatus to 1.
351a1c73d21SWayne Salamon */
3520c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
353a1c73d21SWayne Salamon au_login_fail("Login incorrect", 1);
3540c59c145SChristian S.J. Peron #endif
355a1c73d21SWayne Salamon
3569b50d902SRodney W. Grimes (void)printf("Login incorrect\n");
3579b50d902SRodney W. Grimes failures++;
358a52c1be6SDavid Nugent
3598f9370b0SOlivier Houchard pwd = NULL;
3608f9370b0SOlivier Houchard
361a52c1be6SDavid Nugent /*
362c60ed00aSDag-Erling Smørgrav * Allow up to 'retry' (10) attempts, but start
363c60ed00aSDag-Erling Smørgrav * backing off after 'backoff' (3) attempts.
364a52c1be6SDavid Nugent */
365a52c1be6SDavid Nugent if (++cnt > backoff) {
366a52c1be6SDavid Nugent if (cnt >= retries) {
3679b50d902SRodney W. Grimes badlogin(username);
368c60ed00aSDag-Erling Smørgrav bail(SLEEP_EXIT, 1);
3699b50d902SRodney W. Grimes }
370580367f2SJoseph Koshy sleep((u_int)((cnt - backoff) * 5));
3719b50d902SRodney W. Grimes }
3729b50d902SRodney W. Grimes }
3739b50d902SRodney W. Grimes
3749b50d902SRodney W. Grimes /* committed to login -- turn off timeout */
3759b50d902SRodney W. Grimes (void)alarm((u_int)0);
3762482c270SJilles Tjoelker
3772482c270SJilles Tjoelker (void)sigemptyset(&mask);
3782482c270SJilles Tjoelker (void)sigaddset(&mask, SIGHUP);
3792482c270SJilles Tjoelker (void)sigaddset(&mask, SIGTERM);
3802482c270SJilles Tjoelker (void)sigprocmask(SIG_BLOCK, &mask, &omask);
3812482c270SJilles Tjoelker sa.sa_handler = bail_sig;
3822482c270SJilles Tjoelker (void)sigaction(SIGHUP, &sa, NULL);
3832482c270SJilles Tjoelker (void)sigaction(SIGTERM, &sa, NULL);
3849b50d902SRodney W. Grimes
3859b50d902SRodney W. Grimes endpwent();
3869b50d902SRodney W. Grimes
3870c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
388a1c73d21SWayne Salamon /* Audit successful login. */
389a1c73d21SWayne Salamon if (auditsuccess)
390a1c73d21SWayne Salamon au_login_success();
3910c59c145SChristian S.J. Peron #endif
392a1c73d21SWayne Salamon
393e8334816SJohn Polstra /*
394ba675b41SDoug Rabson * This needs to happen before login_getpwclass to support
395ba675b41SDoug Rabson * home directories on GSS-API authenticated NFS where the
396ba675b41SDoug Rabson * kerberos credentials need to be saved so that the kernel
397ba675b41SDoug Rabson * can authenticate to the NFS server.
398ba675b41SDoug Rabson */
399ba675b41SDoug Rabson pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
400ba675b41SDoug Rabson if (pam_err != PAM_SUCCESS) {
401ba675b41SDoug Rabson pam_syslog("pam_setcred()");
402ba675b41SDoug Rabson bail(NO_SLEEP_EXIT, 1);
403ba675b41SDoug Rabson }
404ba675b41SDoug Rabson pam_cred_established = 1;
405ba675b41SDoug Rabson
406ba675b41SDoug Rabson /*
407e8334816SJohn Polstra * Establish the login class.
408e8334816SJohn Polstra */
409e8334816SJohn Polstra lc = login_getpwclass(pwd);
41042dc3715SMaxim Konovalov lc_user = login_getuserclass(pwd);
411e8334816SJohn Polstra
41242dc3715SMaxim Konovalov if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0)))
4136acc486bSDavid Nugent quietlog = login_getcapbool(lc, "hushlogin", 0);
414c60ed00aSDag-Erling Smørgrav
4152c19b38fSRobert Watson /*
4162c19b38fSRobert Watson * Switching needed for NFS with root access disabled.
4172c19b38fSRobert Watson *
4182c19b38fSRobert Watson * XXX: This change fails to modify the additional groups for the
4192c19b38fSRobert Watson * process, and as such, may restrict rights normally granted
4202c19b38fSRobert Watson * through those groups.
4212c19b38fSRobert Watson */
4220514336dSAndrey A. Chernov (void)setegid(pwd->pw_gid);
423159da441SPeter Wemm (void)seteuid(rootlogin ? 0 : pwd->pw_uid);
4246acc486bSDavid Nugent if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
4254b2fab75SPaul Traina if (login_getcapbool(lc, "requirehome", 0))
426a52c1be6SDavid Nugent refused("Home directory not available", "HOMEDIR", 1);
427f72b1ff3SDavid Nugent if (chdir("/") < 0)
428a52c1be6SDavid Nugent refused("Cannot find root directory", "ROOTDIR", 1);
429a52c1be6SDavid Nugent if (!quietlog || *pwd->pw_dir)
430a52c1be6SDavid Nugent printf("No home directory.\nLogging in with home = \"/\".\n");
431e317b970SMark Murray pwd->pw_dir = strdup("/");
432e317b970SMark Murray if (pwd->pw_dir == NULL) {
433e317b970SMark Murray syslog(LOG_NOTICE, "strdup(): %m");
434c60ed00aSDag-Erling Smørgrav bail(SLEEP_EXIT, 1);
435e317b970SMark Murray }
4366acc486bSDavid Nugent }
437159da441SPeter Wemm (void)seteuid(euid);
4380514336dSAndrey A. Chernov (void)setegid(egid);
43971f4a30dSMax Khon if (!quietlog) {
4409b50d902SRodney W. Grimes quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
44171f4a30dSMax Khon if (!quietlog)
44271f4a30dSMax Khon pam_silent = 0;
44371f4a30dSMax Khon }
4449b50d902SRodney W. Grimes
445a52c1be6SDavid Nugent shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
446a52c1be6SDavid Nugent if (*pwd->pw_shell == '\0')
447e317b970SMark Murray pwd->pw_shell = strdup(_PATH_BSHELL);
448e317b970SMark Murray if (pwd->pw_shell == NULL) {
449e317b970SMark Murray syslog(LOG_NOTICE, "strdup(): %m");
450c60ed00aSDag-Erling Smørgrav bail(SLEEP_EXIT, 1);
451e317b970SMark Murray }
452a52c1be6SDavid Nugent if (*shell == '\0') /* Not overridden */
453a52c1be6SDavid Nugent shell = pwd->pw_shell;
454a52c1be6SDavid Nugent if ((shell = strdup(shell)) == NULL) {
4559ab4f412SMike Barcroft syslog(LOG_NOTICE, "strdup(): %m");
456c60ed00aSDag-Erling Smørgrav bail(SLEEP_EXIT, 1);
4576acc486bSDavid Nugent }
4586acc486bSDavid Nugent
459f88fe867SGuido van Rooij /*
460f88fe867SGuido van Rooij * Set device protections, depending on what terminal the
461f88fe867SGuido van Rooij * user is logged in. This feature is used on Suns to give
462f88fe867SGuido van Rooij * console users better privacy.
463f88fe867SGuido van Rooij */
464f88fe867SGuido van Rooij login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
465f88fe867SGuido van Rooij
466a761a465SWarner Losh /*
467a761a465SWarner Losh * Clear flags of the tty. None should be set, and when the
468a761a465SWarner Losh * user sets them otherwise, this can cause the chown to fail.
469a761a465SWarner Losh * Since it isn't clear that flags are useful on character
470a761a465SWarner Losh * devices, we just clear them.
47184bbb6caSWarner Losh *
47284bbb6caSWarner Losh * We don't log in the case of EOPNOTSUPP because dev might be
47384bbb6caSWarner Losh * on NFS, which doesn't support chflags.
47484bbb6caSWarner Losh *
47584bbb6caSWarner Losh * We don't log in the EROFS because that means that /dev is on
47684bbb6caSWarner Losh * a read only file system and we assume that the permissions there
47784bbb6caSWarner Losh * are sane.
478a761a465SWarner Losh */
47984bbb6caSWarner Losh if (ttyn != tname && chflags(ttyn, 0))
48084bbb6caSWarner Losh if (errno != EOPNOTSUPP && errno != EROFS)
481c60ed00aSDag-Erling Smørgrav syslog(LOG_ERR, "chflags(%s): %m", ttyn);
482c60ed00aSDag-Erling Smørgrav if (ttyn != tname && chown(ttyn, pwd->pw_uid,
483a761a465SWarner Losh (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
48484bbb6caSWarner Losh if (errno != EROFS)
485a2ba8df6SRobert Watson syslog(LOG_ERR, "chown(%s): %m", ttyn);
486a761a465SWarner Losh
487c9bdc152SPaul Traina #ifdef LOGALL
488c9bdc152SPaul Traina /*
489c60ed00aSDag-Erling Smørgrav * Syslog each successful login, so we don't have to watch
490c60ed00aSDag-Erling Smørgrav * hundreds of wtmp or lastlogin files.
491c9bdc152SPaul Traina */
492c60ed00aSDag-Erling Smørgrav if (hflag)
493a52c1be6SDavid Nugent syslog(LOG_INFO, "login from %s on %s as %s",
494c60ed00aSDag-Erling Smørgrav hostname, tty, pwd->pw_name);
495a52c1be6SDavid Nugent else
496a52c1be6SDavid Nugent syslog(LOG_INFO, "login on %s as %s",
497a52c1be6SDavid Nugent tty, pwd->pw_name);
4986acc486bSDavid Nugent #endif
4996acc486bSDavid Nugent
500a52c1be6SDavid Nugent /*
501c60ed00aSDag-Erling Smørgrav * If fflag is on, assume caller/authenticator has logged root
502c60ed00aSDag-Erling Smørgrav * login.
503a52c1be6SDavid Nugent */
504c60ed00aSDag-Erling Smørgrav if (rootlogin && fflag == 0) {
505c60ed00aSDag-Erling Smørgrav if (hflag)
506a52c1be6SDavid Nugent syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
507c60ed00aSDag-Erling Smørgrav username, tty, hostname);
508a52c1be6SDavid Nugent else
509a52c1be6SDavid Nugent syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
510a52c1be6SDavid Nugent username, tty);
511a52c1be6SDavid Nugent }
512a52c1be6SDavid Nugent
513a52c1be6SDavid Nugent /*
514c60ed00aSDag-Erling Smørgrav * Destroy environment unless user has requested its
515c60ed00aSDag-Erling Smørgrav * preservation - but preserve TERM in all cases
516a52c1be6SDavid Nugent */
517c60ed00aSDag-Erling Smørgrav term = getenv("TERM");
5186acc486bSDavid Nugent if (!pflag)
5196acc486bSDavid Nugent environ = envinit;
520c60ed00aSDag-Erling Smørgrav if (term != NULL)
521c60ed00aSDag-Erling Smørgrav setenv("TERM", term, 0);
5226acc486bSDavid Nugent
5235bc9d93dSMark Murray /*
5245bc9d93dSMark Murray * PAM modules might add supplementary groups during pam_setcred().
5255bc9d93dSMark Murray */
5265bc9d93dSMark Murray if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
5275bc9d93dSMark Murray syslog(LOG_ERR, "setusercontext() failed - exiting");
528c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
5295bc9d93dSMark Murray }
5305bc9d93dSMark Murray
531ba675b41SDoug Rabson pam_err = pam_setcred(pamh, pam_silent|PAM_REINITIALIZE_CRED);
532c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
533c60ed00aSDag-Erling Smørgrav pam_syslog("pam_setcred()");
534c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
5355bc9d93dSMark Murray }
5365bc9d93dSMark Murray
537c60ed00aSDag-Erling Smørgrav pam_err = pam_open_session(pamh, pam_silent);
538c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
539c60ed00aSDag-Erling Smørgrav pam_syslog("pam_open_session()");
540c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
5419567ba9dSMark Murray }
542c60ed00aSDag-Erling Smørgrav pam_session_established = 1;
5439567ba9dSMark Murray
5449567ba9dSMark Murray /*
5455bc9d93dSMark Murray * We must fork() before setuid() because we need to call
5465bc9d93dSMark Murray * pam_close_session() as root.
5475bc9d93dSMark Murray */
5485bc9d93dSMark Murray pid = fork();
5495bc9d93dSMark Murray if (pid < 0) {
5505bc9d93dSMark Murray err(1, "fork");
551c60ed00aSDag-Erling Smørgrav } else if (pid != 0) {
552c60ed00aSDag-Erling Smørgrav /*
553c60ed00aSDag-Erling Smørgrav * Parent: wait for child to finish, then clean up
554c60ed00aSDag-Erling Smørgrav * session.
5552482c270SJilles Tjoelker *
5562482c270SJilles Tjoelker * If we get SIGHUP or SIGTERM, clean up the session
5572482c270SJilles Tjoelker * and exit right away. This will make the terminal
5582482c270SJilles Tjoelker * inaccessible and send SIGHUP to the foreground
5592482c270SJilles Tjoelker * process group.
560c60ed00aSDag-Erling Smørgrav */
561fea1e414SDag-Erling Smørgrav int status;
5622517862eSDag-Erling Smørgrav setproctitle("-%s [pam]", getprogname());
5632482c270SJilles Tjoelker (void)sigprocmask(SIG_SETMASK, &omask, NULL);
564fea1e414SDag-Erling Smørgrav waitpid(pid, &status, 0);
5652482c270SJilles Tjoelker (void)sigprocmask(SIG_BLOCK, &mask, NULL);
566c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 0);
5675bc9d93dSMark Murray }
56817ada684SJacques Vidrine
569a52c1be6SDavid Nugent /*
570c60ed00aSDag-Erling Smørgrav * NOTICE: We are now in the child process!
571c60ed00aSDag-Erling Smørgrav */
572c60ed00aSDag-Erling Smørgrav
573c60ed00aSDag-Erling Smørgrav /*
574c60ed00aSDag-Erling Smørgrav * Add any environment variables the PAM modules may have set.
575c60ed00aSDag-Erling Smørgrav */
576c60ed00aSDag-Erling Smørgrav export_pam_environment();
577c60ed00aSDag-Erling Smørgrav
578c60ed00aSDag-Erling Smørgrav /*
579c60ed00aSDag-Erling Smørgrav * We're done with PAM now; our parent will deal with the rest.
580c60ed00aSDag-Erling Smørgrav */
581519b6a4cSDag-Erling Smørgrav pam_end(pamh, 0);
582c60ed00aSDag-Erling Smørgrav pamh = NULL;
583c60ed00aSDag-Erling Smørgrav
584c60ed00aSDag-Erling Smørgrav /*
585c60ed00aSDag-Erling Smørgrav * We don't need to be root anymore, so set the login name and
586c60ed00aSDag-Erling Smørgrav * the UID.
5876acc486bSDavid Nugent */
588e8334816SJohn Polstra if (setlogin(username) != 0) {
589e8334816SJohn Polstra syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
590c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 1);
591e8334816SJohn Polstra }
592e8334816SJohn Polstra if (setusercontext(lc, pwd, pwd->pw_uid,
5935bc9d93dSMark Murray LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
5946acc486bSDavid Nugent syslog(LOG_ERR, "setusercontext() failed - exiting");
5956acc486bSDavid Nugent exit(1);
596c9bdc152SPaul Traina }
5976acc486bSDavid Nugent
5987adec208SAndrey A. Chernov (void)setenv("SHELL", pwd->pw_shell, 1);
5996acc486bSDavid Nugent (void)setenv("HOME", pwd->pw_dir, 1);
60079a20d3bSAndrey A. Chernov /* Overwrite "term" from login.conf(5) for any known TERM */
60155f0377cSAndrey A. Chernov if (term == NULL && (tp = stypeof(tty)) != NULL)
60279a20d3bSAndrey A. Chernov (void)setenv("TERM", tp, 1);
60379a20d3bSAndrey A. Chernov else
60479a20d3bSAndrey A. Chernov (void)setenv("TERM", TERM_UNKNOWN, 0);
605e8334816SJohn Polstra (void)setenv("LOGNAME", username, 1);
606e8334816SJohn Polstra (void)setenv("USER", username, 1);
6076acc486bSDavid Nugent (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
608c9bdc152SPaul Traina
609a52c1be6SDavid Nugent if (!quietlog) {
610f2f306b6SRuslan Ermilov const char *cw;
611a52c1be6SDavid Nugent
6126acc486bSDavid Nugent cw = login_getcapstr(lc, "welcome", NULL, NULL);
613c60ed00aSDag-Erling Smørgrav if (cw != NULL && access(cw, F_OK) == 0)
6146acc486bSDavid Nugent motd(cw);
615c60ed00aSDag-Erling Smørgrav else
616c60ed00aSDag-Erling Smørgrav motd(_PATH_MOTDFILE);
617a52c1be6SDavid Nugent
61842dc3715SMaxim Konovalov if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
61942dc3715SMaxim Konovalov login_getcapbool(lc, "nocheckmail", 0) == 0) {
62081b4504bSDavid Malone char *cx;
62181b4504bSDavid Malone
6220e80e8b2SRobert Watson /* $MAIL may have been set by class. */
62381b4504bSDavid Malone cx = getenv("MAIL");
62481b4504bSDavid Malone if (cx == NULL) {
62581b4504bSDavid Malone asprintf(&cx, "%s/%s",
6260e80e8b2SRobert Watson _PATH_MAILDIR, pwd->pw_name);
627c60ed00aSDag-Erling Smørgrav }
62881b4504bSDavid Malone if (cx && stat(cx, &st) == 0 && st.st_size != 0)
629a52c1be6SDavid Nugent (void)printf("You have %smail.\n",
630a52c1be6SDavid Nugent (st.st_mtime > st.st_atime) ? "new " : "");
631c60ed00aSDag-Erling Smørgrav if (getenv("MAIL") == NULL)
63281b4504bSDavid Malone free(cx);
6339b50d902SRodney W. Grimes }
6340e80e8b2SRobert Watson }
6359b50d902SRodney W. Grimes
63642dc3715SMaxim Konovalov login_close(lc_user);
6376acc486bSDavid Nugent login_close(lc);
638c9bdc152SPaul Traina
6392482c270SJilles Tjoelker sa.sa_handler = SIG_DFL;
6402482c270SJilles Tjoelker (void)sigaction(SIGALRM, &sa, NULL);
6412482c270SJilles Tjoelker (void)sigaction(SIGQUIT, &sa, NULL);
6422482c270SJilles Tjoelker (void)sigaction(SIGINT, &sa, NULL);
6432482c270SJilles Tjoelker (void)sigaction(SIGTERM, &sa, NULL);
6442482c270SJilles Tjoelker (void)sigaction(SIGHUP, &sa, NULL);
6452482c270SJilles Tjoelker sa.sa_handler = SIG_IGN;
6462482c270SJilles Tjoelker (void)sigaction(SIGTSTP, &sa, NULL);
6472482c270SJilles Tjoelker (void)sigprocmask(SIG_SETMASK, &omask, NULL);
6489b50d902SRodney W. Grimes
649a52c1be6SDavid Nugent /*
650a52c1be6SDavid Nugent * Login shells have a leading '-' in front of argv[0]
651a52c1be6SDavid Nugent */
652c60ed00aSDag-Erling Smørgrav p = strrchr(pwd->pw_shell, '/');
653c60ed00aSDag-Erling Smørgrav if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
6549ab4f412SMike Barcroft syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
6559ab4f412SMike Barcroft username);
6569ab4f412SMike Barcroft errx(1, "shell exceeds maximum pathname size");
657c60ed00aSDag-Erling Smørgrav } else if (arg0 == NULL) {
658c60ed00aSDag-Erling Smørgrav err(1, "asprintf()");
6599ab4f412SMike Barcroft }
660a52c1be6SDavid Nugent
661c60ed00aSDag-Erling Smørgrav execlp(shell, arg0, (char *)0);
6626acc486bSDavid Nugent err(1, "%s", shell);
663c60ed00aSDag-Erling Smørgrav
664c60ed00aSDag-Erling Smørgrav /*
665c60ed00aSDag-Erling Smørgrav * That's it, folks!
666c60ed00aSDag-Erling Smørgrav */
6679b50d902SRodney W. Grimes }
6689b50d902SRodney W. Grimes
669e8334816SJohn Polstra /*
670e8334816SJohn Polstra * Attempt to authenticate the user using PAM. Returns 0 if the user is
671e8334816SJohn Polstra * authenticated, or 1 if not authenticated. If some sort of PAM system
672e8334816SJohn Polstra * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
673e8334816SJohn Polstra * function returns -1. This can be used as an indication that we should
674e8334816SJohn Polstra * fall back to a different authentication mechanism.
675e8334816SJohn Polstra */
676e8334816SJohn Polstra static int
auth_pam(void)677c60ed00aSDag-Erling Smørgrav auth_pam(void)
678e8334816SJohn Polstra {
679e8334816SJohn Polstra const char *tmpl_user;
680e8334816SJohn Polstra const void *item;
681e8334816SJohn Polstra int rval;
682e8334816SJohn Polstra
683c60ed00aSDag-Erling Smørgrav pam_err = pam_authenticate(pamh, pam_silent);
684c60ed00aSDag-Erling Smørgrav switch (pam_err) {
685e8334816SJohn Polstra
686e8334816SJohn Polstra case PAM_SUCCESS:
687e8334816SJohn Polstra /*
688e8334816SJohn Polstra * With PAM we support the concept of a "template"
689e8334816SJohn Polstra * user. The user enters a login name which is
690e8334816SJohn Polstra * authenticated by PAM, usually via a remote service
691e8334816SJohn Polstra * such as RADIUS or TACACS+. If authentication
692e8334816SJohn Polstra * succeeds, a different but related "template" name
693e8334816SJohn Polstra * is used for setting the credentials, shell, and
694e8334816SJohn Polstra * home directory. The name the user enters need only
695e8334816SJohn Polstra * exist on the remote authentication server, but the
696e8334816SJohn Polstra * template name must be present in the local password
697e8334816SJohn Polstra * database.
698e8334816SJohn Polstra *
699e8334816SJohn Polstra * This is supported by two various mechanisms in the
700e8334816SJohn Polstra * individual modules. However, from the application's
701e8334816SJohn Polstra * point of view, the template user is always passed
702e8334816SJohn Polstra * back as a changed value of the PAM_USER item.
703e8334816SJohn Polstra */
704c60ed00aSDag-Erling Smørgrav pam_err = pam_get_item(pamh, PAM_USER, &item);
705c60ed00aSDag-Erling Smørgrav if (pam_err == PAM_SUCCESS) {
706e8334816SJohn Polstra tmpl_user = (const char *)item;
707*1e25eb28SDag-Erling Smørgrav if (strcmp(username, tmpl_user) != 0) {
708*1e25eb28SDag-Erling Smørgrav (void)getpwnam_r(tmpl_user, &pwres, pwbuf,
709*1e25eb28SDag-Erling Smørgrav sizeof(pwbuf), &pwd);
710*1e25eb28SDag-Erling Smørgrav }
711c60ed00aSDag-Erling Smørgrav } else {
712c60ed00aSDag-Erling Smørgrav pam_syslog("pam_get_item(PAM_USER)");
713c60ed00aSDag-Erling Smørgrav }
714e8334816SJohn Polstra rval = 0;
715e8334816SJohn Polstra break;
716e8334816SJohn Polstra
717e8334816SJohn Polstra case PAM_AUTH_ERR:
718e8334816SJohn Polstra case PAM_USER_UNKNOWN:
719e8334816SJohn Polstra case PAM_MAXTRIES:
720e8334816SJohn Polstra rval = 1;
721e8334816SJohn Polstra break;
722e8334816SJohn Polstra
723e8334816SJohn Polstra default:
724c60ed00aSDag-Erling Smørgrav pam_syslog("pam_authenticate()");
725e8334816SJohn Polstra rval = -1;
726e8334816SJohn Polstra break;
727e8334816SJohn Polstra }
7285bc9d93dSMark Murray
7295bc9d93dSMark Murray if (rval == 0) {
730c60ed00aSDag-Erling Smørgrav pam_err = pam_acct_mgmt(pamh, pam_silent);
731c60ed00aSDag-Erling Smørgrav switch (pam_err) {
732c60ed00aSDag-Erling Smørgrav case PAM_SUCCESS:
733c60ed00aSDag-Erling Smørgrav break;
734c60ed00aSDag-Erling Smørgrav case PAM_NEW_AUTHTOK_REQD:
735c60ed00aSDag-Erling Smørgrav pam_err = pam_chauthtok(pamh,
736c60ed00aSDag-Erling Smørgrav pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
737c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS) {
738c60ed00aSDag-Erling Smørgrav pam_syslog("pam_chauthtok()");
7395bc9d93dSMark Murray rval = 1;
7405bc9d93dSMark Murray }
741c60ed00aSDag-Erling Smørgrav break;
742c60ed00aSDag-Erling Smørgrav default:
743c60ed00aSDag-Erling Smørgrav pam_syslog("pam_acct_mgmt()");
7445bc9d93dSMark Murray rval = 1;
745c60ed00aSDag-Erling Smørgrav break;
7465bc9d93dSMark Murray }
7475bc9d93dSMark Murray }
7485bc9d93dSMark Murray
7495bc9d93dSMark Murray if (rval != 0) {
750c60ed00aSDag-Erling Smørgrav pam_end(pamh, pam_err);
7515bc9d93dSMark Murray pamh = NULL;
752e8334816SJohn Polstra }
753c60ed00aSDag-Erling Smørgrav return (rval);
75417ada684SJacques Vidrine }
75517ada684SJacques Vidrine
75617ada684SJacques Vidrine /*
757c60ed00aSDag-Erling Smørgrav * Export any environment variables PAM modules may have set
758c60ed00aSDag-Erling Smørgrav */
759c60ed00aSDag-Erling Smørgrav static void
export_pam_environment(void)760ef636796SEd Schouten export_pam_environment(void)
761c60ed00aSDag-Erling Smørgrav {
762c60ed00aSDag-Erling Smørgrav char **pam_env;
763c60ed00aSDag-Erling Smørgrav char **pp;
764c60ed00aSDag-Erling Smørgrav
765c60ed00aSDag-Erling Smørgrav pam_env = pam_getenvlist(pamh);
766c60ed00aSDag-Erling Smørgrav if (pam_env != NULL) {
767c60ed00aSDag-Erling Smørgrav for (pp = pam_env; *pp != NULL; pp++) {
768ba174a5eSAndrey A. Chernov (void)export(*pp);
7698673ed15SAndrey A. Chernov free(*pp);
770c60ed00aSDag-Erling Smørgrav }
771c60ed00aSDag-Erling Smørgrav }
772c60ed00aSDag-Erling Smørgrav }
773c60ed00aSDag-Erling Smørgrav
774c60ed00aSDag-Erling Smørgrav /*
775c60ed00aSDag-Erling Smørgrav * Perform sanity checks on an environment variable:
77617ada684SJacques Vidrine * - Make sure there is an '=' in the string.
77717ada684SJacques Vidrine * - Make sure the string doesn't run on too long.
77817ada684SJacques Vidrine * - Do not export certain variables. This list was taken from the
77917ada684SJacques Vidrine * Solaris pam_putenv(3) man page.
780c60ed00aSDag-Erling Smørgrav * Then export it.
78117ada684SJacques Vidrine */
78217ada684SJacques Vidrine static int
export(const char * s)783c60ed00aSDag-Erling Smørgrav export(const char *s)
78417ada684SJacques Vidrine {
78517ada684SJacques Vidrine static const char *noexport[] = {
78617ada684SJacques Vidrine "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
78717ada684SJacques Vidrine "IFS", "PATH", NULL
78817ada684SJacques Vidrine };
7892966d28cSSean Farley char *p;
79017ada684SJacques Vidrine const char **pp;
79117ada684SJacques Vidrine size_t n;
792dcc6f625SPedro F. Giffuni int rv;
79317ada684SJacques Vidrine
7942966d28cSSean Farley if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL)
795c60ed00aSDag-Erling Smørgrav return (0);
79617ada684SJacques Vidrine if (strncmp(s, "LD_", 3) == 0)
797c60ed00aSDag-Erling Smørgrav return (0);
79817ada684SJacques Vidrine for (pp = noexport; *pp != NULL; pp++) {
79917ada684SJacques Vidrine n = strlen(*pp);
80017ada684SJacques Vidrine if (s[n] == '=' && strncmp(s, *pp, n) == 0)
801c60ed00aSDag-Erling Smørgrav return (0);
80217ada684SJacques Vidrine }
8032966d28cSSean Farley *p = '\0';
804dcc6f625SPedro F. Giffuni rv = setenv(s, p + 1, 1);
8052966d28cSSean Farley *p = '=';
806dcc6f625SPedro F. Giffuni if (rv == -1)
807dcc6f625SPedro F. Giffuni return (0);
808c60ed00aSDag-Erling Smørgrav return (1);
80917ada684SJacques Vidrine }
810e8334816SJohn Polstra
8119c9cb2bfSPhilippe Charnier static void
usage(void)812ef636796SEd Schouten usage(void)
8139c9cb2bfSPhilippe Charnier {
8145de20e57SDavid E. O'Brien
8159c9cb2bfSPhilippe Charnier (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
8169c9cb2bfSPhilippe Charnier exit(1);
8179c9cb2bfSPhilippe Charnier }
8186acc486bSDavid Nugent
819a52c1be6SDavid Nugent /*
820c60ed00aSDag-Erling Smørgrav * Prompt user and read login name from stdin.
8215bc9d93dSMark Murray */
822c60ed00aSDag-Erling Smørgrav static char *
getloginname(void)823ef636796SEd Schouten getloginname(void)
8249b50d902SRodney W. Grimes {
825c60ed00aSDag-Erling Smørgrav char *nbuf, *p;
8269b50d902SRodney W. Grimes int ch;
8279b50d902SRodney W. Grimes
828c60ed00aSDag-Erling Smørgrav nbuf = malloc(MAXLOGNAME);
829c60ed00aSDag-Erling Smørgrav if (nbuf == NULL)
830c60ed00aSDag-Erling Smørgrav err(1, "malloc()");
831c60ed00aSDag-Erling Smørgrav do {
8329ab4f412SMike Barcroft (void)printf("%s", prompt);
8339b50d902SRodney W. Grimes for (p = nbuf; (ch = getchar()) != '\n'; ) {
8349b50d902SRodney W. Grimes if (ch == EOF) {
8359b50d902SRodney W. Grimes badlogin(username);
836c60ed00aSDag-Erling Smørgrav bail(NO_SLEEP_EXIT, 0);
8379b50d902SRodney W. Grimes }
838c60ed00aSDag-Erling Smørgrav if (p < nbuf + MAXLOGNAME - 1)
8399b50d902SRodney W. Grimes *p++ = ch;
8409b50d902SRodney W. Grimes }
841c60ed00aSDag-Erling Smørgrav } while (p == nbuf);
842c60ed00aSDag-Erling Smørgrav
8439b50d902SRodney W. Grimes *p = '\0';
844c60ed00aSDag-Erling Smørgrav if (nbuf[0] == '-') {
845c60ed00aSDag-Erling Smørgrav pam_silent = 0;
846c60ed00aSDag-Erling Smørgrav memmove(nbuf, nbuf + 1, strlen(nbuf));
847c60ed00aSDag-Erling Smørgrav } else {
848c60ed00aSDag-Erling Smørgrav pam_silent = PAM_SILENT;
8499b50d902SRodney W. Grimes }
850c60ed00aSDag-Erling Smørgrav return nbuf;
85169f2cf17SSheldon Hearn }
8529b50d902SRodney W. Grimes
853c60ed00aSDag-Erling Smørgrav /*
854c60ed00aSDag-Erling Smørgrav * SIGINT handler for motd().
855c60ed00aSDag-Erling Smørgrav */
856c60ed00aSDag-Erling Smørgrav static volatile int motdinterrupt;
857c60ed00aSDag-Erling Smørgrav static void
sigint(int signo __unused)858c60ed00aSDag-Erling Smørgrav sigint(int signo __unused)
8596acc486bSDavid Nugent {
8606acc486bSDavid Nugent motdinterrupt = 1;
8616acc486bSDavid Nugent }
8629b50d902SRodney W. Grimes
863c60ed00aSDag-Erling Smørgrav /*
864c60ed00aSDag-Erling Smørgrav * Display the contents of a file (such as /etc/motd).
865c60ed00aSDag-Erling Smørgrav */
866c60ed00aSDag-Erling Smørgrav static int
motd(const char * motdfile)867c60ed00aSDag-Erling Smørgrav motd(const char *motdfile)
8689b50d902SRodney W. Grimes {
8692482c270SJilles Tjoelker struct sigaction newint, oldint;
870c60ed00aSDag-Erling Smørgrav FILE *f;
871c60ed00aSDag-Erling Smørgrav int ch;
8729b50d902SRodney W. Grimes
873c60ed00aSDag-Erling Smørgrav if ((f = fopen(motdfile, "r")) == NULL)
874c60ed00aSDag-Erling Smørgrav return (-1);
8756acc486bSDavid Nugent motdinterrupt = 0;
8762482c270SJilles Tjoelker newint.sa_handler = sigint;
8772482c270SJilles Tjoelker newint.sa_flags = 0;
8782482c270SJilles Tjoelker sigfillset(&newint.sa_mask);
8792482c270SJilles Tjoelker sigaction(SIGINT, &newint, &oldint);
880c60ed00aSDag-Erling Smørgrav while ((ch = fgetc(f)) != EOF && !motdinterrupt)
881c60ed00aSDag-Erling Smørgrav putchar(ch);
8822482c270SJilles Tjoelker sigaction(SIGINT, &oldint, NULL);
883c60ed00aSDag-Erling Smørgrav if (ch != EOF || ferror(f)) {
884c60ed00aSDag-Erling Smørgrav fclose(f);
885c60ed00aSDag-Erling Smørgrav return (-1);
886c60ed00aSDag-Erling Smørgrav }
887c60ed00aSDag-Erling Smørgrav fclose(f);
888c60ed00aSDag-Erling Smørgrav return (0);
8899b50d902SRodney W. Grimes }
8909b50d902SRodney W. Grimes
891c60ed00aSDag-Erling Smørgrav /*
892c60ed00aSDag-Erling Smørgrav * SIGALRM handler, to enforce login prompt timeout.
893c60ed00aSDag-Erling Smørgrav *
894c60ed00aSDag-Erling Smørgrav * XXX This can potentially confuse the hell out of PAM. We should
895c60ed00aSDag-Erling Smørgrav * XXX instead implement a conversation function that returns
896c60ed00aSDag-Erling Smørgrav * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
897c60ed00aSDag-Erling Smørgrav * XXX handler just set a flag.
898c60ed00aSDag-Erling Smørgrav */
899c60ed00aSDag-Erling Smørgrav static void
timedout(int signo __unused)900c60ed00aSDag-Erling Smørgrav timedout(int signo __unused)
9019b50d902SRodney W. Grimes {
90291a72a92SDavid E. O'Brien
903b606e33cSEivind Eklund longjmp(timeout_buf, signo);
9049b50d902SRodney W. Grimes }
9059b50d902SRodney W. Grimes
9060845b8faSPoul-Henning Kamp static void
badlogin(char * name)907c60ed00aSDag-Erling Smørgrav badlogin(char *name)
9089b50d902SRodney W. Grimes {
9099b50d902SRodney W. Grimes
9109b50d902SRodney W. Grimes if (failures == 0)
9119b50d902SRodney W. Grimes return;
912c60ed00aSDag-Erling Smørgrav if (hflag) {
9139b50d902SRodney W. Grimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
914c60ed00aSDag-Erling Smørgrav failures, failures > 1 ? "S" : "", hostname);
9159b50d902SRodney W. Grimes syslog(LOG_AUTHPRIV|LOG_NOTICE,
9169b50d902SRodney W. Grimes "%d LOGIN FAILURE%s FROM %s, %s",
917c60ed00aSDag-Erling Smørgrav failures, failures > 1 ? "S" : "", hostname, name);
9189b50d902SRodney W. Grimes } else {
9199b50d902SRodney W. Grimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
9209b50d902SRodney W. Grimes failures, failures > 1 ? "S" : "", tty);
9219b50d902SRodney W. Grimes syslog(LOG_AUTHPRIV|LOG_NOTICE,
9229b50d902SRodney W. Grimes "%d LOGIN FAILURE%s ON %s, %s",
9239b50d902SRodney W. Grimes failures, failures > 1 ? "S" : "", tty, name);
9249b50d902SRodney W. Grimes }
925b606e33cSEivind Eklund failures = 0;
9269b50d902SRodney W. Grimes }
9279b50d902SRodney W. Grimes
928e317b970SMark Murray const char *
stypeof(char * ttyid)929c60ed00aSDag-Erling Smørgrav stypeof(char *ttyid)
9309b50d902SRodney W. Grimes {
9319b50d902SRodney W. Grimes struct ttyent *t;
932a52c1be6SDavid Nugent
9339ab4f412SMike Barcroft if (ttyid != NULL && *ttyid != '\0') {
9349ab4f412SMike Barcroft t = getttynam(ttyid);
9359ab4f412SMike Barcroft if (t != NULL && t->ty_type != NULL)
9369ab4f412SMike Barcroft return (t->ty_type);
9379ab4f412SMike Barcroft }
93879a20d3bSAndrey A. Chernov return (NULL);
9399b50d902SRodney W. Grimes }
9409b50d902SRodney W. Grimes
9410845b8faSPoul-Henning Kamp static void
refused(const char * msg,const char * rtype,int lout)942c60ed00aSDag-Erling Smørgrav refused(const char *msg, const char *rtype, int lout)
943a52c1be6SDavid Nugent {
944a52c1be6SDavid Nugent
945a52c1be6SDavid Nugent if (msg != NULL)
946a52c1be6SDavid Nugent printf("%s.\n", msg);
947c60ed00aSDag-Erling Smørgrav if (hflag)
948a52c1be6SDavid Nugent syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
949c60ed00aSDag-Erling Smørgrav pwd->pw_name, rtype, hostname, tty);
950a52c1be6SDavid Nugent else
951a52c1be6SDavid Nugent syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
952a52c1be6SDavid Nugent pwd->pw_name, rtype, tty);
953a52c1be6SDavid Nugent if (lout)
954c60ed00aSDag-Erling Smørgrav bail(SLEEP_EXIT, 1);
955a52c1be6SDavid Nugent }
956a52c1be6SDavid Nugent
957c60ed00aSDag-Erling Smørgrav /*
958c60ed00aSDag-Erling Smørgrav * Log a PAM error
959c60ed00aSDag-Erling Smørgrav */
9600845b8faSPoul-Henning Kamp static void
pam_syslog(const char * msg)961c60ed00aSDag-Erling Smørgrav pam_syslog(const char *msg)
962c60ed00aSDag-Erling Smørgrav {
963c60ed00aSDag-Erling Smørgrav syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
964c60ed00aSDag-Erling Smørgrav }
965c60ed00aSDag-Erling Smørgrav
966c60ed00aSDag-Erling Smørgrav /*
967c60ed00aSDag-Erling Smørgrav * Shut down PAM
968c60ed00aSDag-Erling Smørgrav */
9690845b8faSPoul-Henning Kamp static void
pam_cleanup(void)970ef636796SEd Schouten pam_cleanup(void)
9719b50d902SRodney W. Grimes {
972a52c1be6SDavid Nugent
973c60ed00aSDag-Erling Smørgrav if (pamh != NULL) {
974c60ed00aSDag-Erling Smørgrav if (pam_session_established) {
975c60ed00aSDag-Erling Smørgrav pam_err = pam_close_session(pamh, 0);
976c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS)
977c60ed00aSDag-Erling Smørgrav pam_syslog("pam_close_session()");
978c60ed00aSDag-Erling Smørgrav }
979c60ed00aSDag-Erling Smørgrav pam_session_established = 0;
980c60ed00aSDag-Erling Smørgrav if (pam_cred_established) {
981c60ed00aSDag-Erling Smørgrav pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
982c60ed00aSDag-Erling Smørgrav if (pam_err != PAM_SUCCESS)
983c60ed00aSDag-Erling Smørgrav pam_syslog("pam_setcred()");
984c60ed00aSDag-Erling Smørgrav }
985c60ed00aSDag-Erling Smørgrav pam_cred_established = 0;
986c60ed00aSDag-Erling Smørgrav pam_end(pamh, pam_err);
987c60ed00aSDag-Erling Smørgrav pamh = NULL;
988c60ed00aSDag-Erling Smørgrav }
989c60ed00aSDag-Erling Smørgrav }
990c60ed00aSDag-Erling Smørgrav
9912482c270SJilles Tjoelker static void
bail_internal(int sec,int eval,int signo)9922482c270SJilles Tjoelker bail_internal(int sec, int eval, int signo)
993c60ed00aSDag-Erling Smørgrav {
9942482c270SJilles Tjoelker struct sigaction sa;
995c60ed00aSDag-Erling Smørgrav
996c60ed00aSDag-Erling Smørgrav pam_cleanup();
9970c59c145SChristian S.J. Peron #ifdef USE_BSM_AUDIT
9988f9370b0SOlivier Houchard if (pwd != NULL)
999a1c73d21SWayne Salamon audit_logout();
10000c59c145SChristian S.J. Peron #endif
1001c60ed00aSDag-Erling Smørgrav (void)sleep(sec);
10022482c270SJilles Tjoelker if (signo == 0)
10039b50d902SRodney W. Grimes exit(eval);
10042482c270SJilles Tjoelker else {
10052482c270SJilles Tjoelker sa.sa_handler = SIG_DFL;
10062482c270SJilles Tjoelker sa.sa_flags = 0;
10072482c270SJilles Tjoelker (void)sigemptyset(&sa.sa_mask);
10082482c270SJilles Tjoelker (void)sigaction(signo, &sa, NULL);
10092482c270SJilles Tjoelker (void)sigaddset(&sa.sa_mask, signo);
10102482c270SJilles Tjoelker (void)sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
10112482c270SJilles Tjoelker raise(signo);
10122482c270SJilles Tjoelker exit(128 + signo);
10132482c270SJilles Tjoelker }
10142482c270SJilles Tjoelker }
10152482c270SJilles Tjoelker
10162482c270SJilles Tjoelker /*
10172482c270SJilles Tjoelker * Exit, optionally after sleeping a few seconds
10182482c270SJilles Tjoelker */
10192482c270SJilles Tjoelker static void
bail(int sec,int eval)10202482c270SJilles Tjoelker bail(int sec, int eval)
10212482c270SJilles Tjoelker {
10222482c270SJilles Tjoelker bail_internal(sec, eval, 0);
10232482c270SJilles Tjoelker }
10242482c270SJilles Tjoelker
10252482c270SJilles Tjoelker /*
10262482c270SJilles Tjoelker * Exit because of a signal.
10272482c270SJilles Tjoelker * This is not async-signal safe, so only call async-signal safe functions
10282482c270SJilles Tjoelker * while the signal is unmasked.
10292482c270SJilles Tjoelker */
10302482c270SJilles Tjoelker static void
bail_sig(int signo)10312482c270SJilles Tjoelker bail_sig(int signo)
10322482c270SJilles Tjoelker {
10332482c270SJilles Tjoelker bail_internal(NO_SLEEP_EXIT, 0, signo);
10349b50d902SRodney W. Grimes }
1035