1 /* $NetBSD: login_pam.c,v 1.24 2014/11/12 22:23:38 aymeric 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.24 2014/11/12 22:23:38 aymeric 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
;
111 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
112 char localhost
[MAXHOSTNAMELEN
+ 1];
113 int login_retries
= DEFAULT_RETRIES
,
114 login_backoff
= DEFAULT_BACKOFF
;
116 login_cap_t
*lc
= NULL
;
117 pam_handle_t
*pamh
= NULL
;
119 sig_t oint
, oabrt
, oquit
, oalrm
;
121 int pam_silent
= PAM_SILENT
;
130 oabrt
= signal(SIGABRT
, SIG_IGN
);
131 oalrm
= signal(SIGALRM
, timedout
);
132 oint
= signal(SIGINT
, SIG_IGN
);
133 oquit
= signal(SIGQUIT
, SIG_IGN
);
135 (void)alarm(timeout
);
136 (void)setpriority(PRIO_PROCESS
, 0, 0);
138 openlog("login", 0, LOG_AUTH
);
141 * -p is used by getty to tell login not to destroy the environment
142 * -f is used to skip a second login authentication
143 * -h is used by other servers to pass the name of the remote host to
144 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx
145 * -a in addition to -h, a server my supply -a to pass the actual
149 if (gethostname(localhost
, sizeof(localhost
)) < 0)
150 syslog(LOG_ERR
, "couldn't get local hostname: %m");
152 domain
= strchr(localhost
, '.');
153 localhost
[sizeof(localhost
) - 1] = '\0';
158 while ((ch
= getopt(argc
, argv
, "a:fh:p")) != -1)
163 err(EXIT_FAILURE
, "-a option");
173 err(EXIT_FAILURE
, "-h option");
175 if (domain
&& (p
= strchr(optarg
, '.')) != NULL
&&
176 strcasecmp(p
, domain
) == 0)
194 username
= trimloginname(*argv
);
200 (void)fcntl(3, F_CLOSEM
, 0);
202 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
206 ttyn
= ttyname(STDIN_FILENO
);
207 if (ttyn
== NULL
|| *ttyn
== '\0') {
208 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
211 if ((tty
= strstr(ttyn
, "/pts/")) != NULL
)
213 else if ((tty
= strrchr(ttyn
, '/')) != NULL
)
219 nested
= strdup(user_from_uid(getuid(), 0));
220 if (nested
== NULL
) {
221 syslog(LOG_ERR
, "strdup: %m");
222 sleepexit(EXIT_FAILURE
);
226 /* Get "login-retries" and "login-backoff" from default class */
227 if ((lc
= login_getclass(NULL
)) != NULL
) {
228 login_retries
= (int)login_getcapnum(lc
, "login-retries",
229 DEFAULT_RETRIES
, DEFAULT_RETRIES
);
230 login_backoff
= (int)login_getcapnum(lc
, "login-backoff",
231 DEFAULT_BACKOFF
, DEFAULT_BACKOFF
);
237 for (cnt
= 0;; ask
= 1) {
240 username
= trimloginname(getloginname());
246 * Note if trying multiple user names; log failures for
247 * previous user name, but don't bother logging one failure
248 * for nonexistent name (mistyped username).
250 if (failures
&& strcmp(tbuf
, username
)) {
251 if (failures
> (pwd
? 0 : 1))
256 #define PAM_END(msg) do { \
257 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
258 warnx("%s: %s", msg, pam_strerror(pamh, pam_err)); \
259 pam_end(pamh, pam_err); \
260 sleepexit(EXIT_FAILURE); \
261 } while (/*CONSTCOND*/0)
263 pam_err
= pam_start("login", username
, &pamc
, &pamh
);
264 if (pam_err
!= PAM_SUCCESS
) {
266 PAM_END("pam_start");
267 /* Things went really bad... */
268 syslog(LOG_ERR
, "pam_start failed: %s",
269 pam_strerror(pamh
, pam_err
));
270 errx(EXIT_FAILURE
, "pam_start failed");
273 #define PAM_SET_ITEM(item, var) do { \
274 pam_err = pam_set_item(pamh, (item), (var)); \
275 if (pam_err != PAM_SUCCESS) \
276 PAM_END("pam_set_item(" # item ")"); \
277 } while (/*CONSTCOND*/0)
280 * Fill hostname tty, and nested user
282 PAM_SET_ITEM(PAM_RHOST
, hostname
);
283 PAM_SET_ITEM(PAM_TTY
, tty
);
285 PAM_SET_ITEM(PAM_NUSER
, nested
);
287 PAM_SET_ITEM(PAM_SOCKADDR
, &ss
);
290 * Don't check for errors, because we don't want to give
291 * out any information.
294 (void)getpwnam_r(username
, &pwres
, pwbuf
, sizeof(pwbuf
), &pwd
);
297 * Establish the class now, before we might goto
298 * within the next block. pwd can be NULL since it
299 * falls back to the "default" class if it is.
301 lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
304 * if we have a valid account name, and it doesn't have a
305 * password, or the -f option was specified and the caller
306 * is root or the caller isn't changing their uid, don't
310 if (pwd
->pw_uid
== 0)
313 if (fflag
&& (uid
== 0 || uid
== pwd
->pw_uid
)) {
314 /* already authenticated */
320 (void)setpriority(PRIO_PROCESS
, 0, -4);
322 switch(pam_err
= pam_authenticate(pamh
, pam_silent
)) {
325 * PAM can change the user, refresh
326 * username, pwd, and lc.
328 pam_err
= pam_get_item(pamh
, PAM_USER
, &newuser
);
329 if (pam_err
!= PAM_SUCCESS
)
330 PAM_END("pam_get_item(PAM_USER)");
334 * Don't check for errors, because we don't want to give
335 * out any information.
338 (void)getpwnam_r(username
, &pwres
, pwbuf
, sizeof(pwbuf
),
340 lc
= login_getpwclass(pwd
);
343 switch (pam_err
= pam_acct_mgmt(pamh
, pam_silent
)) {
347 case PAM_NEW_AUTHTOK_REQD
:
348 pam_err
= pam_chauthtok(pamh
,
349 pam_silent
|PAM_CHANGE_EXPIRED_AUTHTOK
);
351 if (pam_err
!= PAM_SUCCESS
)
352 PAM_END("pam_chauthtok");
356 case PAM_USER_UNKNOWN
:
362 PAM_END("pam_acct_mgmt");
368 case PAM_USER_UNKNOWN
:
374 PAM_END("pam_authenticate");
378 (void)setpriority(PRIO_PROCESS
, 0, 0);
382 * If the user exists and authentication passed,
383 * get out of the loop and login the user.
385 if (pwd
&& auth_passed
)
388 (void)printf("Login incorrect or refused on this terminal.\n");
392 * We allow login_retries tries, but after login_backoff
393 * we start backing off. These default to 10 and 3
396 if (cnt
> login_backoff
) {
397 if (cnt
>= login_retries
) {
399 pam_end(pamh
, PAM_SUCCESS
);
400 sleepexit(EXIT_FAILURE
);
402 sleep((u_int
)((cnt
- login_backoff
) * 5));
406 /* committed to login -- turn off timeout */
407 (void)alarm((u_int
)0);
411 quietlog
= login_getcapbool(lc
, "hushlogin", 0);
414 * Temporarily give up special privileges so we can change
415 * into NFS-mounted homes that are exported for non-root
416 * access and have mode 7x0
418 saved_uid
= geteuid();
419 saved_gid
= getegid();
420 nsaved_gids
= getgroups(NGROUPS_MAX
, saved_gids
);
422 (void)setegid(pwd
->pw_gid
);
423 initgroups(username
, pwd
->pw_gid
);
424 (void)seteuid(pwd
->pw_uid
);
426 if (chdir(pwd
->pw_dir
) != 0) {
427 if (login_getcapbool(lc
, "requirehome", 0)) {
428 (void)printf("Home directory %s required\n",
430 pam_end(pamh
, PAM_SUCCESS
);
434 (void)printf("No home directory %s!\n", pwd
->pw_dir
);
435 if (chdir("/") == -1) {
436 pam_end(pamh
, PAM_SUCCESS
);
439 pwd
->pw_dir
= __UNCONST("/");
440 (void)printf("Logging in with home = \"/\".\n");
444 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
445 pam_silent
= quietlog
? PAM_SILENT
: 0;
448 /* regain special privileges */
450 setgroups(nsaved_gids
, saved_gids
);
453 (void)getgrnam_r(TTYGRPNAME
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
454 (void)chown(ttyn
, pwd
->pw_uid
,
455 (grp
!= NULL
) ? grp
->gr_gid
: pwd
->pw_gid
);
457 if (ttyaction(ttyn
, "login", pwd
->pw_name
))
458 (void)printf("Warning: ttyaction failed.\n");
460 /* Nothing else left to fail -- really log in. */
461 update_db(quietlog
, rootlogin
, fflag
);
463 if (nested
== NULL
&& setusercontext(lc
, pwd
, pwd
->pw_uid
,
464 LOGIN_SETLOGIN
) != 0) {
465 syslog(LOG_ERR
, "setusercontext failed");
466 pam_end(pamh
, PAM_SUCCESS
);
473 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
474 syslog(LOG_ERR
, "setusercontext failed");
475 pam_end(pamh
, PAM_SUCCESS
);
479 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_ESTABLISH_CRED
);
480 if (pam_err
!= PAM_SUCCESS
)
481 PAM_END("pam_setcred");
483 pam_err
= pam_open_session(pamh
, pam_silent
);
484 if (pam_err
!= PAM_SUCCESS
)
485 PAM_END("pam_open_session");
488 * Fork because we need to call pam_closesession as root.
489 * Make sure signals cannot kill the parent.
490 * This has been handled in the begining of main.
493 switch(pid
= fork()) {
495 pam_err
= pam_close_session(pamh
, 0);
496 if (pam_err
!= PAM_SUCCESS
) {
497 syslog(LOG_ERR
, "pam_close_session: %s",
498 pam_strerror(pamh
, pam_err
));
499 warnx("pam_close_session: %s",
500 pam_strerror(pamh
, pam_err
));
502 syslog(LOG_ERR
, "fork failed: %m");
504 pam_end(pamh
, pam_err
);
513 * Parent: wait for the child to terminate
514 * and call pam_close_session.
516 if ((xpid
= waitpid(pid
, &status
, 0)) != pid
) {
517 pam_err
= pam_close_session(pamh
, 0);
518 if (pam_err
!= PAM_SUCCESS
) {
520 "pam_close_session: %s",
521 pam_strerror(pamh
, pam_err
));
522 warnx("pam_close_session: %s",
523 pam_strerror(pamh
, pam_err
));
525 pam_end(pamh
, pam_err
);
527 warnx("wrong PID: %d != %d", pid
, xpid
);
529 warn("wait for pid %d failed", pid
);
533 (void)signal(SIGABRT
, oabrt
);
534 (void)signal(SIGALRM
, oalrm
);
535 (void)signal(SIGINT
, oint
);
536 (void)signal(SIGQUIT
, oquit
);
537 if ((pam_err
= pam_close_session(pamh
, 0)) != PAM_SUCCESS
) {
538 syslog(LOG_ERR
, "pam_close_session: %s",
539 pam_strerror(pamh
, pam_err
));
540 warnx("pam_close_session: %s",
541 pam_strerror(pamh
, pam_err
));
543 pam_end(pamh
, PAM_SUCCESS
);
549 * The child: starting here, we don't have to care about
550 * handling PAM issues if we exit, the parent will do the
553 * Destroy environment unless user has requested its preservation.
554 * Try to preserve TERM anyway.
556 saved_term
= getenv("TERM");
560 setenv("TERM", saved_term
, 0);
563 if (*pwd
->pw_shell
== '\0')
564 pwd
->pw_shell
= __UNCONST(_PATH_BSHELL
);
566 shell
= login_getcapstr(lc
, "shell", pwd
->pw_shell
, pwd
->pw_shell
);
568 shell
= pwd
->pw_shell
;
570 if ((pwd
->pw_shell
= strdup(shell
)) == NULL
) {
571 syslog(LOG_ERR
, "Cannot alloc mem");
575 (void)setenv("HOME", pwd
->pw_dir
, 1);
576 (void)setenv("SHELL", pwd
->pw_shell
, 1);
577 if (term
[0] == '\0') {
578 const char *tt
= stypeof(tty
);
581 tt
= login_getcapstr(lc
, "term", NULL
, NULL
);
583 /* unknown term -> "su" */
584 (void)strlcpy(term
, tt
!= NULL
? tt
: "su", sizeof(term
));
586 (void)setenv("TERM", term
, 0);
587 (void)setenv("LOGNAME", pwd
->pw_name
, 1);
588 (void)setenv("USER", pwd
->pw_name
, 1);
591 * Add PAM environement
593 if ((pamenv
= pam_getenvlist(pamh
)) != NULL
) {
596 for (envitem
= pamenv
; *envitem
; envitem
++) {
604 /* This drops root privs */
605 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
606 (LOGIN_SETALL
& ~LOGIN_SETLOGIN
)) != 0) {
607 syslog(LOG_ERR
, "setusercontext failed");
614 fname
= login_getcapstr(lc
, "copyright", NULL
, NULL
);
615 if (fname
!= NULL
&& access(fname
, F_OK
) == 0)
618 (void)printf("%s", copyrightstr
);
620 fname
= login_getcapstr(lc
, "welcome", NULL
, NULL
);
621 if (fname
== NULL
|| access(fname
, F_OK
) != 0)
622 fname
= _PATH_MOTDFILE
;
626 sizeof(tbuf
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
627 if (stat(tbuf
, &st
) == 0 && st
.st_size
!= 0)
628 (void)printf("You have %smail.\n",
629 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
636 (void)strlcpy(tbuf
+ 1, (p
= strrchr(pwd
->pw_shell
, '/')) ?
637 p
+ 1 : pwd
->pw_shell
, sizeof(tbuf
) - 1);
639 (void)signal(SIGABRT
, oabrt
);
640 (void)signal(SIGALRM
, oalrm
);
641 (void)signal(SIGINT
, oint
);
642 (void)signal(SIGQUIT
, oquit
);
643 (void)signal(SIGTSTP
, SIG_IGN
);
645 execlp(pwd
->pw_shell
, tbuf
, NULL
);
646 err(EXIT_FAILURE
, "%s", pwd
->pw_shell
);
652 (void)fprintf(stderr
,
653 "Usage: %s [-fp] [-a address] [-h hostname] [username]\n",
664 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);