1 /* $NetBSD: login_pam.c,v 1.20 2009/12/29 19:26:13 christos Exp $ */
4 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
42 __RCSID("$NetBSD: login_pam.c,v 1.20 2009/12/29 19:26:13 christos Exp $");
47 * login -h hostname (for telnetd, etc.)
48 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
51 #include <sys/param.h>
54 #include <sys/resource.h>
57 #include <sys/socket.h>
74 #include <login_cap.h>
77 #include <security/pam_appl.h>
78 #include <security/openpam.h>
80 #include "pathnames.h"
84 static int rootterm(char *);
86 static void usage(void) __attribute__((__noreturn__
));
88 static struct pam_conv pamc
= { openpam_ttyconv
, NULL
};
90 #define TTYGRPNAME "tty" /* name of group to own ttys */
92 #define DEFAULT_BACKOFF 3
93 #define DEFAULT_RETRIES 10
95 static struct passwd pwres
;
96 static char pwbuf
[1024];
97 static struct group grs
, *grp
;
98 static char grbuf
[1024];
99 extern char **environ
;
102 main(int argc
, char *argv
[])
105 int ask
, ch
, cnt
, fflag
, pflag
, quietlog
, rootlogin
;
107 uid_t uid
, saved_uid
;
108 gid_t saved_gid
, saved_gids
[NGROUPS_MAX
];
110 char *domain
, *p
, *ttyn
, *pwprompt
;
111 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
112 char localhost
[MAXHOSTNAMELEN
+ 1];
113 int need_chpass
, require_chpass
;
114 int login_retries
= DEFAULT_RETRIES
,
115 login_backoff
= DEFAULT_BACKOFF
;
117 login_cap_t
*lc
= NULL
;
118 pam_handle_t
*pamh
= NULL
;
120 sig_t oint
, oabrt
, oquit
, oalrm
;
122 int pam_silent
= PAM_SILENT
;
131 need_chpass
= require_chpass
= 0;
133 oabrt
= signal(SIGABRT
, SIG_IGN
);
134 oalrm
= signal(SIGALRM
, timedout
);
135 oint
= signal(SIGINT
, SIG_IGN
);
136 oquit
= signal(SIGQUIT
, SIG_IGN
);
138 (void)alarm(timeout
);
139 (void)setpriority(PRIO_PROCESS
, 0, 0);
141 openlog("login", 0, LOG_AUTH
);
144 * -p is used by getty to tell login not to destroy the environment
145 * -f is used to skip a second login authentication
146 * -h is used by other servers to pass the name of the remote host to
147 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx
148 * -a in addition to -h, a server my supply -a to pass the actual
152 if (gethostname(localhost
, sizeof(localhost
)) < 0)
153 syslog(LOG_ERR
, "couldn't get local hostname: %m");
155 domain
= strchr(localhost
, '.');
156 localhost
[sizeof(localhost
) - 1] = '\0';
161 while ((ch
= getopt(argc
, argv
, "a:fh:p")) != -1)
166 err(EXIT_FAILURE
, "-a option");
176 err(EXIT_FAILURE
, "-h option");
178 if (domain
&& (p
= strchr(optarg
, '.')) != NULL
&&
179 strcasecmp(p
, domain
) == 0)
203 (void)fcntl(3, F_CLOSEM
, 0);
205 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
209 ttyn
= ttyname(STDIN_FILENO
);
210 if (ttyn
== NULL
|| *ttyn
== '\0') {
211 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
214 if ((tty
= strstr(ttyn
, "/pts/")) != NULL
)
216 else if ((tty
= strrchr(ttyn
, '/')) != NULL
)
222 nested
= strdup(user_from_uid(getuid(), 0));
223 if (nested
== NULL
) {
224 syslog(LOG_ERR
, "strdup: %m");
225 sleepexit(EXIT_FAILURE
);
229 /* Get "login-retries" and "login-backoff" from default class */
230 if ((lc
= login_getclass(NULL
)) != NULL
) {
231 login_retries
= (int)login_getcapnum(lc
, "login-retries",
232 DEFAULT_RETRIES
, DEFAULT_RETRIES
);
233 login_backoff
= (int)login_getcapnum(lc
, "login-backoff",
234 DEFAULT_BACKOFF
, DEFAULT_BACKOFF
);
240 for (cnt
= 0;; ask
= 1) {
247 if (strlen(username
) > MAXLOGNAME
)
248 username
[MAXLOGNAME
] = '\0';
251 * Note if trying multiple user names; log failures for
252 * previous user name, but don't bother logging one failure
253 * for nonexistent name (mistyped username).
255 if (failures
&& strcmp(tbuf
, username
)) {
256 if (failures
> (pwd
? 0 : 1))
261 #define PAM_END(msg) do { \
262 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
263 warnx("%s: %s", msg, pam_strerror(pamh, pam_err)); \
264 pam_end(pamh, pam_err); \
265 sleepexit(EXIT_FAILURE); \
266 } while (/*CONSTCOND*/0)
268 pam_err
= pam_start("login", username
, &pamc
, &pamh
);
269 if (pam_err
!= PAM_SUCCESS
) {
271 PAM_END("pam_start");
272 /* Things went really bad... */
273 syslog(LOG_ERR
, "pam_start failed: %s",
274 pam_strerror(pamh
, pam_err
));
275 errx(EXIT_FAILURE
, "pam_start failed");
278 #define PAM_SET_ITEM(item, var) do { \
279 pam_err = pam_set_item(pamh, (item), (var)); \
280 if (pam_err != PAM_SUCCESS) \
281 PAM_END("pam_set_item(" # item ")"); \
282 } while (/*CONSTCOND*/0)
285 * Fill hostname tty, and nested user
287 PAM_SET_ITEM(PAM_RHOST
, hostname
);
288 PAM_SET_ITEM(PAM_TTY
, tty
);
290 PAM_SET_ITEM(PAM_NUSER
, nested
);
292 PAM_SET_ITEM(PAM_SOCKADDR
, &ss
);
295 * Don't check for errors, because we don't want to give
296 * out any information.
299 (void)getpwnam_r(username
, &pwres
, pwbuf
, sizeof(pwbuf
), &pwd
);
302 * Establish the class now, before we might goto
303 * within the next block. pwd can be NULL since it
304 * falls back to the "default" class if it is.
306 lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
309 * if we have a valid account name, and it doesn't have a
310 * password, or the -f option was specified and the caller
311 * is root or the caller isn't changing their uid, don't
315 if (pwd
->pw_uid
== 0)
318 if (fflag
&& (uid
== 0 || uid
== pwd
->pw_uid
)) {
319 /* already authenticated */
325 (void)setpriority(PRIO_PROCESS
, 0, -4);
327 switch(pam_err
= pam_authenticate(pamh
, pam_silent
)) {
330 * PAM can change the user, refresh
331 * username, pwd, and lc.
333 pam_err
= pam_get_item(pamh
, PAM_USER
, &newuser
);
334 if (pam_err
!= PAM_SUCCESS
)
335 PAM_END("pam_get_item(PAM_USER)");
337 username
= (char *)newuser
;
339 * Don't check for errors, because we don't want to give
340 * out any information.
343 (void)getpwnam_r(username
, &pwres
, pwbuf
, sizeof(pwbuf
),
345 lc
= login_getpwclass(pwd
);
348 switch (pam_err
= pam_acct_mgmt(pamh
, pam_silent
)) {
352 case PAM_NEW_AUTHTOK_REQD
:
353 pam_err
= pam_chauthtok(pamh
,
354 pam_silent
|PAM_CHANGE_EXPIRED_AUTHTOK
);
356 if (pam_err
!= PAM_SUCCESS
)
357 PAM_END("pam_chauthtok");
361 case PAM_USER_UNKNOWN
:
367 PAM_END("pam_acct_mgmt");
373 case PAM_USER_UNKNOWN
:
379 PAM_END("pam_authenticate");
383 (void)setpriority(PRIO_PROCESS
, 0, 0);
387 * If the user exists and authentication passed,
388 * get out of the loop and login the user.
390 if (pwd
&& auth_passed
)
393 (void)printf("Login incorrect or refused on this terminal.\n");
397 * We allow login_retries tries, but after login_backoff
398 * we start backing off. These default to 10 and 3
401 if (cnt
> login_backoff
) {
402 if (cnt
>= login_retries
) {
404 pam_end(pamh
, PAM_SUCCESS
);
405 sleepexit(EXIT_FAILURE
);
407 sleep((u_int
)((cnt
- login_backoff
) * 5));
411 /* committed to login -- turn off timeout */
412 (void)alarm((u_int
)0);
416 quietlog
= login_getcapbool(lc
, "hushlogin", 0);
419 * Temporarily give up special privileges so we can change
420 * into NFS-mounted homes that are exported for non-root
421 * access and have mode 7x0
423 saved_uid
= geteuid();
424 saved_gid
= getegid();
425 nsaved_gids
= getgroups(NGROUPS_MAX
, saved_gids
);
427 (void)setegid(pwd
->pw_gid
);
428 initgroups(username
, pwd
->pw_gid
);
429 (void)seteuid(pwd
->pw_uid
);
431 if (chdir(pwd
->pw_dir
) != 0) {
432 if (login_getcapbool(lc
, "requirehome", 0)) {
433 (void)printf("Home directory %s required\n",
435 pam_end(pamh
, PAM_SUCCESS
);
439 (void)printf("No home directory %s!\n", pwd
->pw_dir
);
440 if (chdir("/") == -1) {
441 pam_end(pamh
, PAM_SUCCESS
);
445 (void)printf("Logging in with home = \"/\".\n");
449 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
450 pam_silent
= quietlog
? PAM_SILENT
: 0;
453 /* regain special privileges */
455 setgroups(nsaved_gids
, saved_gids
);
458 (void)getgrnam_r(TTYGRPNAME
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
459 (void)chown(ttyn
, pwd
->pw_uid
,
460 (grp
!= NULL
) ? grp
->gr_gid
: pwd
->pw_gid
);
462 if (ttyaction(ttyn
, "login", pwd
->pw_name
))
463 (void)printf("Warning: ttyaction failed.\n");
465 /* Nothing else left to fail -- really log in. */
466 update_db(quietlog
, rootlogin
, fflag
);
468 if (nested
== NULL
&& setusercontext(lc
, pwd
, pwd
->pw_uid
,
469 LOGIN_SETLOGIN
) != 0) {
470 syslog(LOG_ERR
, "setusercontext failed");
471 pam_end(pamh
, PAM_SUCCESS
);
475 if (tty
[sizeof("tty")-1] == 'd')
476 syslog(LOG_INFO
, "DIALUP %s, %s", tty
, pwd
->pw_name
);
482 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
483 syslog(LOG_ERR
, "setusercontext failed");
484 pam_end(pamh
, PAM_SUCCESS
);
488 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_ESTABLISH_CRED
);
489 if (pam_err
!= PAM_SUCCESS
)
490 PAM_END("pam_setcred");
492 pam_err
= pam_open_session(pamh
, pam_silent
);
493 if (pam_err
!= PAM_SUCCESS
)
494 PAM_END("pam_open_session");
497 * Fork because we need to call pam_closesession as root.
498 * Make sure signals cannot kill the parent.
499 * This has been handled in the begining of main.
502 switch(pid
= fork()) {
504 pam_err
= pam_close_session(pamh
, 0);
505 if (pam_err
!= PAM_SUCCESS
) {
506 syslog(LOG_ERR
, "pam_close_session: %s",
507 pam_strerror(pamh
, pam_err
));
508 warnx("pam_close_session: %s",
509 pam_strerror(pamh
, pam_err
));
511 syslog(LOG_ERR
, "fork failed: %m");
513 pam_end(pamh
, pam_err
);
522 * Parent: wait for the child to terminate
523 * and call pam_close_session.
525 if ((xpid
= waitpid(pid
, &status
, 0)) != pid
) {
526 pam_err
= pam_close_session(pamh
, 0);
527 if (pam_err
!= PAM_SUCCESS
) {
529 "pam_close_session: %s",
530 pam_strerror(pamh
, pam_err
));
531 warnx("pam_close_session: %s",
532 pam_strerror(pamh
, pam_err
));
534 pam_end(pamh
, pam_err
);
536 warnx("wrong PID: %d != %d", pid
, xpid
);
538 warn("wait for pid %d failed", pid
);
542 (void)signal(SIGABRT
, oabrt
);
543 (void)signal(SIGALRM
, oalrm
);
544 (void)signal(SIGINT
, oint
);
545 (void)signal(SIGQUIT
, oquit
);
546 if ((pam_err
= pam_close_session(pamh
, 0)) != PAM_SUCCESS
) {
547 syslog(LOG_ERR
, "pam_close_session: %s",
548 pam_strerror(pamh
, pam_err
));
549 warnx("pam_close_session: %s",
550 pam_strerror(pamh
, pam_err
));
552 pam_end(pamh
, PAM_SUCCESS
);
558 * The child: starting here, we don't have to care about
559 * handling PAM issues if we exit, the parent will do the
562 * Destroy environment unless user has requested its preservation.
563 * Try to preserve TERM anyway.
565 saved_term
= getenv("TERM");
569 setenv("TERM", saved_term
, 0);
572 if (*pwd
->pw_shell
== '\0')
573 pwd
->pw_shell
= _PATH_BSHELL
;
575 shell
= login_getcapstr(lc
, "shell", pwd
->pw_shell
, pwd
->pw_shell
);
577 shell
= pwd
->pw_shell
;
579 if ((pwd
->pw_shell
= strdup(shell
)) == NULL
) {
580 syslog(LOG_ERR
, "Cannot alloc mem");
584 (void)setenv("HOME", pwd
->pw_dir
, 1);
585 (void)setenv("SHELL", pwd
->pw_shell
, 1);
586 if (term
[0] == '\0') {
587 char *tt
= (char *)stypeof(tty
);
590 tt
= login_getcapstr(lc
, "term", NULL
, NULL
);
592 /* unknown term -> "su" */
593 (void)strlcpy(term
, tt
!= NULL
? tt
: "su", sizeof(term
));
595 (void)setenv("TERM", term
, 0);
596 (void)setenv("LOGNAME", pwd
->pw_name
, 1);
597 (void)setenv("USER", pwd
->pw_name
, 1);
600 * Add PAM environement
602 if ((pamenv
= pam_getenvlist(pamh
)) != NULL
) {
605 for (envitem
= pamenv
; *envitem
; envitem
++) {
613 /* This drops root privs */
614 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
615 (LOGIN_SETALL
& ~LOGIN_SETLOGIN
)) != 0) {
616 syslog(LOG_ERR
, "setusercontext failed");
623 fname
= login_getcapstr(lc
, "copyright", NULL
, NULL
);
624 if (fname
!= NULL
&& access(fname
, F_OK
) == 0)
627 (void)printf("%s", copyrightstr
);
629 fname
= login_getcapstr(lc
, "welcome", NULL
, NULL
);
630 if (fname
== NULL
|| access(fname
, F_OK
) != 0)
631 fname
= _PATH_MOTDFILE
;
635 sizeof(tbuf
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
636 if (stat(tbuf
, &st
) == 0 && st
.st_size
!= 0)
637 (void)printf("You have %smail.\n",
638 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
645 (void)strlcpy(tbuf
+ 1, (p
= strrchr(pwd
->pw_shell
, '/')) ?
646 p
+ 1 : pwd
->pw_shell
, sizeof(tbuf
) - 1);
648 (void)signal(SIGABRT
, oabrt
);
649 (void)signal(SIGALRM
, oalrm
);
650 (void)signal(SIGINT
, oint
);
651 (void)signal(SIGQUIT
, oquit
);
652 (void)signal(SIGTSTP
, SIG_IGN
);
654 execlp(pwd
->pw_shell
, tbuf
, NULL
);
655 err(EXIT_FAILURE
, "%s", pwd
->pw_shell
);
661 (void)fprintf(stderr
,
662 "Usage: %s [-fp] [-a address] [-h hostname] [username]\n",
673 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);