1 /* $NetBSD: login.c,v 1.105 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.c,v 1.105 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>
85 #include <krb5/krb5.h>
86 #include <krb5/com_err.h>
89 #include <login_cap.h>
93 #include "pathnames.h"
97 int login_krb5_forwardable_tgt
= 0;
98 static int login_krb5_get_tickets
= 1;
99 static int login_krb5_retain_ccache
= 0;
102 static void checknologin(char *);
104 int k5login(struct passwd
*, char *, char *, char *);
105 void k5destroy(void);
106 int k5_read_creds(const char *);
107 int k5_write_creds(void);
109 #if defined(KERBEROS5)
110 static void dofork(void);
112 static void usage(void) __attribute__((__noreturn__
));
114 #define TTYGRPNAME "tty" /* name of group to own ttys */
116 #define DEFAULT_BACKOFF 3
117 #define DEFAULT_RETRIES 10
119 #if defined(KERBEROS5)
122 extern krb5_context kcontext
;
123 extern int have_forward
;
124 static char *instance
;
125 extern char *krb5tkfile_env
;
126 extern int krb5_configured
;
129 #if defined(KERBEROS5)
130 #define KERBEROS_CONFIGURED krb5_configured
133 extern char **environ
;
136 main(int argc
, char *argv
[])
140 int ask
, ch
, cnt
, fflag
, hflag
, pflag
, sflag
, quietlog
, rootlogin
, rval
;
141 uid_t uid
, saved_uid
;
142 gid_t saved_gid
, saved_gids
[NGROUPS_MAX
];
148 const char *pwprompt
;
149 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
150 char localhost
[MAXHOSTNAMELEN
+ 1];
151 int need_chpass
, require_chpass
;
152 int login_retries
= DEFAULT_RETRIES
,
153 login_backoff
= DEFAULT_BACKOFF
;
154 time_t pw_warntime
= _PASSWORD_WARNDAYS
* SECSPERDAY
;
155 char *loginname
= NULL
;
158 krb5_error_code kerror
;
160 #if defined(KERBEROS5)
165 login_cap_t
*lc
= NULL
;
172 need_chpass
= require_chpass
= 0;
174 (void)signal(SIGALRM
, timedout
);
175 (void)alarm(timeout
);
176 (void)signal(SIGQUIT
, SIG_IGN
);
177 (void)signal(SIGINT
, SIG_IGN
);
178 (void)setpriority(PRIO_PROCESS
, 0, 0);
180 openlog("login", 0, LOG_AUTH
);
183 * -p is used by getty to tell login not to destroy the environment
184 * -f is used to skip a second login authentication
185 * -h is used by other servers to pass the name of the remote host to
186 * login so that it may be placed in utmp/utmpx and wtmp/wtmpx
187 * -a in addition to -h, a server may supply -a to pass the actual
189 * -s is used to force use of S/Key or equivalent.
191 if (gethostname(localhost
, sizeof(localhost
)) < 0) {
192 syslog(LOG_ERR
, "couldn't get local hostname: %m");
193 strcpy(hostname
, "amnesiac");
196 domain
= strchr(localhost
, '.');
198 localhost
[sizeof(localhost
) - 1] = '\0';
200 fflag
= hflag
= pflag
= sflag
= 0;
207 while ((ch
= getopt(argc
, argv
, "a:Ffh:ps")) != -1)
211 errx(EXIT_FAILURE
, "-a option: %s", strerror(EPERM
));
214 (void)sockaddr_snprintf(optarg
,
215 sizeof(struct sockaddr_storage
), "%a", (void *)&ss
);
228 errx(EXIT_FAILURE
, "-h option: %s", strerror(EPERM
));
231 if (domain
&& (p
= strchr(optarg
, '.')) != NULL
&&
232 strcasecmp(p
, domain
) == 0)
254 username
= loginname
= *argv
;
260 (void)fcntl(3, F_CLOSEM
, 0);
262 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
266 ttyn
= ttyname(STDIN_FILENO
);
267 if (ttyn
== NULL
|| *ttyn
== '\0') {
268 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
271 if ((tty
= strstr(ttyn
, "/pts/")) != NULL
)
273 else if ((tty
= strrchr(ttyn
, '/')) != NULL
)
279 nested
= strdup(user_from_uid(getuid(), 0));
280 if (nested
== NULL
) {
281 syslog(LOG_ERR
, "strdup: %m");
282 sleepexit(EXIT_FAILURE
);
287 /* Get "login-retries" and "login-backoff" from default class */
288 if ((lc
= login_getclass(NULL
)) != NULL
) {
289 login_retries
= (int)login_getcapnum(lc
, "login-retries",
290 DEFAULT_RETRIES
, DEFAULT_RETRIES
);
291 login_backoff
= (int)login_getcapnum(lc
, "login-backoff",
292 DEFAULT_BACKOFF
, DEFAULT_BACKOFF
);
299 kerror
= krb5_init_context(&kcontext
);
302 * If Kerberos is not configured, that is, we are
303 * not using Kerberos, do not log the error message.
304 * However, if Kerberos is configured, and the
305 * context init fails for some other reason, we need
306 * to issue a no tickets warning to the user when the
309 if (kerror
!= ENXIO
) { /* XXX NetBSD-local Heimdal hack */
311 "%s when initializing Kerberos context",
312 error_message(kerror
));
315 login_krb5_get_tickets
= 0;
317 #endif /* KERBEROS5 */
319 for (cnt
= 0;; ask
= 1) {
320 #if defined(KERBEROS5)
321 if (login_krb5_get_tickets
)
326 loginname
= getloginname();
330 if ((instance
= strchr(loginname
, '/')) != NULL
)
333 instance
= __UNCONST("");
335 username
= trimloginname(loginname
);
337 * Note if trying multiple user names; log failures for
338 * previous user name, but don't bother logging one failure
339 * for nonexistent name (mistyped username).
341 if (failures
&& strcmp(tbuf
, username
)) {
342 if (failures
> (pwd
? 0 : 1))
346 (void)strlcpy(tbuf
, username
, sizeof(tbuf
));
348 pwd
= getpwnam(username
);
352 * Establish the class now, before we might goto
353 * within the next block. pwd can be NULL since it
354 * falls back to the "default" class if it is.
356 lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
359 * if we have a valid account name, and it doesn't have a
360 * password, or the -f option was specified and the caller
361 * is root or the caller isn't changing their uid, don't
365 if (pwd
->pw_uid
== 0)
368 if (fflag
&& (uid
== 0 || uid
== pwd
->pw_uid
)) {
369 /* already authenticated */
371 if (login_krb5_get_tickets
&& Fflag
)
372 k5_read_creds(username
);
375 } else if (pwd
->pw_passwd
[0] == '\0') {
376 /* pretend password okay */
384 (void)setpriority(PRIO_PROCESS
, 0, -4);
387 if (skey_haskey(username
) == 0) {
388 static char skprompt
[80];
389 const char *skinfo
= skey_keyinfo(username
);
391 (void)snprintf(skprompt
, sizeof(skprompt
),
393 skinfo
? skinfo
: "error getting challenge");
397 pwprompt
= "Password:";
399 p
= getpass(pwprompt
);
406 if (login_krb5_get_tickets
&&
407 k5login(pwd
, instance
, localhost
, p
) == 0) {
412 #if defined(KERBEROS5)
417 if (skey_haskey(username
) == 0 &&
418 skey_passcheck(username
, p
) != -1) {
423 if (!sflag
&& *pwd
->pw_passwd
!= '\0' &&
424 !strcmp(crypt(p
, pwd
->pw_passwd
), pwd
->pw_passwd
)) {
432 memset(p
, 0, strlen(p
));
434 (void)setpriority(PRIO_PROCESS
, 0, 0);
438 * If trying to log in as root without Kerberos,
439 * but with insecure terminal, refuse the login attempt.
441 if (pwd
&& !rval
&& rootlogin
&& !rootterm(tty
)) {
442 (void)printf("Login incorrect or refused on this "
446 "LOGIN %s REFUSED FROM %s ON TTY %s",
447 pwd
->pw_name
, hostname
, tty
);
450 "LOGIN %s REFUSED ON TTY %s",
458 (void)printf("Login incorrect or refused on this "
463 * We allow login_retries tries, but after login_backoff
464 * we start backing off. These default to 10 and 3
467 if (cnt
> login_backoff
) {
468 if (cnt
>= login_retries
) {
470 sleepexit(EXIT_FAILURE
);
472 sleep((u_int
)((cnt
- login_backoff
) * 5));
476 /* committed to login -- turn off timeout */
477 (void)alarm((u_int
)0);
481 /* if user not super-user, check for disabled logins */
483 if (!login_getcapbool(lc
, "ignorenologin", rootlogin
))
484 checknologin(login_getcapstr(lc
, "nologin", NULL
, NULL
));
491 quietlog
= login_getcapbool(lc
, "hushlogin", 0);
495 /* Temporarily give up special privileges so we can change */
496 /* into NFS-mounted homes that are exported for non-root */
497 /* access and have mode 7x0 */
498 saved_uid
= geteuid();
499 saved_gid
= getegid();
500 nsaved_gids
= getgroups(NGROUPS_MAX
, saved_gids
);
502 (void)setegid(pwd
->pw_gid
);
503 initgroups(username
, pwd
->pw_gid
);
504 (void)seteuid(pwd
->pw_uid
);
506 if (chdir(pwd
->pw_dir
) < 0) {
508 if (login_getcapbool(lc
, "requirehome", 0)) {
509 (void)printf("Home directory %s required\n",
511 sleepexit(EXIT_FAILURE
);
514 (void)printf("No home directory %s!\n", pwd
->pw_dir
);
515 if (chdir("/") == -1)
517 pwd
->pw_dir
= __UNCONST("/");
518 (void)printf("Logging in with home = \"/\".\n");
522 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
524 /* regain special privileges */
525 (void)seteuid(saved_uid
);
526 setgroups(nsaved_gids
, saved_gids
);
527 (void)setegid(saved_gid
);
530 pw_warntime
= login_getcaptime(lc
, "password-warn",
531 _PASSWORD_WARNDAYS
* SECSPERDAY
,
532 _PASSWORD_WARNDAYS
* SECSPERDAY
);
535 (void)gettimeofday(&now
, NULL
);
536 if (pwd
->pw_expire
) {
537 if (now
.tv_sec
>= pwd
->pw_expire
) {
538 (void)printf("Sorry -- your account has expired.\n");
539 sleepexit(EXIT_FAILURE
);
540 } else if (pwd
->pw_expire
- now
.tv_sec
< pw_warntime
&&
542 (void)printf("Warning: your account expires on %s",
543 ctime(&pwd
->pw_expire
));
545 if (pwd
->pw_change
) {
546 if (pwd
->pw_change
== _PASSWORD_CHGNOW
)
548 else if (now
.tv_sec
>= pwd
->pw_change
) {
549 (void)printf("Sorry -- your password has expired.\n");
550 sleepexit(EXIT_FAILURE
);
551 } else if (pwd
->pw_change
- now
.tv_sec
< pw_warntime
&&
553 (void)printf("Warning: your password expires on %s",
554 ctime(&pwd
->pw_change
));
557 /* Nothing else left to fail -- really log in. */
558 update_db(quietlog
, rootlogin
, fflag
);
560 (void)chown(ttyn
, pwd
->pw_uid
,
561 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
563 if (ttyaction(ttyn
, "login", pwd
->pw_name
))
564 (void)printf("Warning: ttyaction failed.\n");
566 #if defined(KERBEROS5)
567 /* Fork so that we can call kdestroy */
568 if (! login_krb5_retain_ccache
&& has_ccache
)
572 /* Destroy environment unless user has requested its preservation. */
577 if (nested
== NULL
&& setusercontext(lc
, pwd
, pwd
->pw_uid
,
578 LOGIN_SETLOGIN
) != 0) {
579 syslog(LOG_ERR
, "setusercontext failed");
582 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
583 (LOGIN_SETALL
& ~(LOGIN_SETPATH
|LOGIN_SETLOGIN
))) != 0) {
584 syslog(LOG_ERR
, "setusercontext failed");
588 (void)setgid(pwd
->pw_gid
);
590 initgroups(username
, pwd
->pw_gid
);
592 #if !defined(__minix)
593 if (nested
== NULL
&& setlogin(pwd
->pw_name
) < 0)
594 syslog(LOG_ERR
, "setlogin() failure: %m");
595 #endif /* !defined(__minix) */
597 /* Discard permissions last so can't get killed and drop core. */
601 (void)setuid(pwd
->pw_uid
);
604 if (*pwd
->pw_shell
== '\0')
605 pwd
->pw_shell
= __UNCONST(_PATH_BSHELL
);
607 if ((shell
= login_getcapstr(lc
, "shell", NULL
, NULL
)) != NULL
) {
608 if ((shell
= strdup(shell
)) == NULL
) {
609 syslog(LOG_ERR
, "Cannot alloc mem");
610 sleepexit(EXIT_FAILURE
);
612 pwd
->pw_shell
= shell
;
616 (void)setenv("HOME", pwd
->pw_dir
, 1);
617 (void)setenv("SHELL", pwd
->pw_shell
, 1);
618 if (term
[0] == '\0') {
619 const char *tt
= stypeof(tty
);
622 tt
= login_getcapstr(lc
, "term", NULL
, NULL
);
624 /* unknown term -> "su" */
625 (void)strlcpy(term
, tt
!= NULL
? tt
: "su", sizeof(term
));
627 (void)setenv("TERM", term
, 0);
628 (void)setenv("LOGNAME", pwd
->pw_name
, 1);
629 (void)setenv("USER", pwd
->pw_name
, 1);
632 setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETPATH
);
634 (void)setenv("PATH", _PATH_DEFPATH
, 0);
639 (void)setenv("KRB5CCNAME", krb5tkfile_env
, 1);
642 /* If fflag is on, assume caller/authenticator has logged root login. */
643 if (rootlogin
&& fflag
== 0) {
645 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s FROM %s",
646 username
, tty
, hostname
);
648 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s",
652 #if defined(KERBEROS5)
653 if (KERBEROS_CONFIGURED
&& !quietlog
&& notickets
== 1)
654 (void)printf("Warning: no Kerberos tickets issued.\n");
660 fname
= login_getcapstr(lc
, "copyright", NULL
, NULL
);
661 if (fname
!= NULL
&& access(fname
, F_OK
) == 0)
665 (void)printf("%s", copyrightstr
);
668 fname
= login_getcapstr(lc
, "welcome", NULL
, NULL
);
669 if (fname
== NULL
|| access(fname
, F_OK
) != 0)
671 fname
= _PATH_MOTDFILE
;
675 sizeof(tbuf
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
676 if (stat(tbuf
, &st
) == 0 && st
.st_size
!= 0)
677 (void)printf("You have %smail.\n",
678 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
685 (void)signal(SIGALRM
, SIG_DFL
);
686 (void)signal(SIGQUIT
, SIG_DFL
);
687 (void)signal(SIGINT
, SIG_DFL
);
688 (void)signal(SIGTSTP
, SIG_IGN
);
691 (void)strlcpy(tbuf
+ 1, (p
= strrchr(pwd
->pw_shell
, '/')) ?
692 p
+ 1 : pwd
->pw_shell
, sizeof(tbuf
) - 1);
694 /* Wait to change password until we're unprivileged */
698 "Warning: your password has expired. Please change it as soon as possible.\n");
703 "Your password has expired. Please choose a new one.\n");
707 sleepexit(EXIT_FAILURE
);
709 execl(_PATH_BINPASSWD
, "passwd", NULL
);
712 if (wait(&status
) == -1 ||
714 sleepexit(EXIT_FAILURE
);
720 if (login_krb5_get_tickets
)
723 execlp(pwd
->pw_shell
, tbuf
, NULL
);
724 err(EXIT_FAILURE
, "%s", pwd
->pw_shell
);
727 #if defined(KERBEROS5)
729 * This routine handles cleanup stuff, and the like.
730 * It exists only in the child process.
737 switch (child
= fork()) {
739 return; /* Child process */
741 err(EXIT_FAILURE
, "Can't fork");
748 * Setup stuff? This would be things we could do in parallel
751 if (chdir("/") == -1) /* Let's not keep the fs busy... */
752 err(EXIT_FAILURE
, "Can't chdir to `/'");
754 /* If we're the parent, watch the child until it dies */
755 while ((wchild
= wait(NULL
)) != child
)
757 err(EXIT_FAILURE
, "Can't wait");
760 /* Run kdestroy to destroy tickets */
761 if (login_krb5_get_tickets
)
770 checknologin(char *fname
)
775 if ((fd
= open(fname
? fname
: _PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
776 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
777 (void)write(fileno(stdout
), tbuf
, nchars
);
778 sleepexit(EXIT_SUCCESS
);
785 (void)fprintf(stderr
,
786 "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n",