2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technologies, Inc.
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 * Portions copyright (c) 1999-2007 Apple Inc. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 static char sccsid
[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD: src/usr.bin/login/login.c,v 1.106 2007/07/04 00:00:40 scf Exp $");
53 * login -h hostname (for telnetd, etc.)
54 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
58 #include <sys/copyright.h>
61 #include <TargetConditionals.h>
63 #include <sys/param.h>
67 #include <sys/resource.h>
79 #include <login_cap.h>
95 #define _UTX_USERSIZE MAXLOGNAME
98 #endif /* __APPLE__ */
100 #include <sys/types.h>
101 #include <sys/socket.h>
102 #include <netinet/in.h>
103 #include <arpa/inet.h>
107 #include <bsm/libbsm.h>
108 #include <bsm/audit_uevents.h>
112 #include <mach/mach_types.h>
113 #include <mach/task.h>
114 #include <mach/mach_init.h>
115 #include <servers/bootstrap.h>
117 #include <sys/file.h>
119 #endif /* __APPLE__ */
122 #include <security/pam_appl.h>
123 #include <security/openpam.h>
127 #include "pathnames.h"
130 static int auth_pam(void);
132 static void bail(int, int);
134 static int export(const char *);
135 static void export_pam_environment(void);
137 static int motd(const char *);
138 static void badlogin(char *);
139 static char *getloginname(void);
141 static void pam_syslog(const char *);
142 static void pam_cleanup(void);
144 static void refused(const char *, const char *, int);
145 static const char *stypeof(char *);
146 static void sigint(int);
147 static void timedout(int);
148 static void usage(void);
151 static void dolastlog(int);
152 static void handle_sighup(int);
155 static void checknologin(void);
156 static int rootterm(const char *);
157 #endif /* !USE_PAM */
158 #endif /* __APPLE__ */
160 #define TTYGRPNAME "tty" /* group to own ttys */
161 #define DEFAULT_BACKOFF 3
162 #define DEFAULT_RETRIES 10
163 #define DEFAULT_PROMPT "login: "
164 #define DEFAULT_PASSWD_PROMPT "Password:"
165 #define TERM_UNKNOWN "su"
166 #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
167 #define NO_SLEEP_EXIT 0
171 * This bounds the time given to login. Not a define so it can
172 * be patched on machines where it's too small.
174 static u_int timeout
= 300;
176 /* Buffer for signal handling of timeout */
177 static jmp_buf timeout_buf
;
182 static char *envinit
[1]; /* empty environment list */
185 * Command line flags and arguments
187 static int fflag
; /* -f: do not perform authentication */
189 static int lflag
; /* -l: login session to the commmand that follows username */
191 static int hflag
; /* -h: login from remote host */
192 static char *hostname
; /* hostname from command line */
193 static int pflag
; /* -p: preserve environment */
198 static char *username
; /* user name */
199 static char *olduser
; /* previous user name */
204 static char default_prompt
[] = DEFAULT_PROMPT
;
205 static const char *prompt
;
206 static char default_passwd_prompt
[] = DEFAULT_PASSWD_PROMPT
;
207 static const char *passwd_prompt
;
215 static pam_handle_t
*pamh
= NULL
;
216 static struct pam_conv pamc
= { openpam_ttyconv
, NULL
};
218 static int pam_silent
= PAM_SILENT
;
219 static int pam_cred_established
;
220 static int pam_session_established
;
227 static struct lastlogx lastlog
;
232 #endif /* USE_BSM_AUDIT */
233 #endif /* __APPLE__ */
236 main(int argc
, char *argv
[])
240 int retries
, backoff
;
241 int ask
, ch
, cnt
, quietlog
= 0, rootlogin
, rval
;
246 char tname
[sizeof(_PATH_TTY
) + 10];
252 const char *name
= "login"; /* PAM config */
256 const char *shell
= NULL
;
257 #endif /* !__APPLE__ */
259 login_cap_t
*lc
= NULL
;
260 login_cap_t
*lc_user
= NULL
;
261 #endif /* LOGIN_CAP */
266 char auditsuccess
= 1;
269 (void)signal(SIGQUIT
, SIG_IGN
);
270 (void)signal(SIGINT
, SIG_IGN
);
271 (void)signal(SIGHUP
, SIG_IGN
);
272 if (setjmp(timeout_buf
)) {
275 (void)fprintf(stderr
, "Login timed out after %d seconds\n",
277 bail(NO_SLEEP_EXIT
, 0);
279 (void)signal(SIGALRM
, timedout
);
280 (void)alarm(timeout
);
282 prio
= getpriority(PRIO_PROCESS
, 0);
284 (void)setpriority(PRIO_PROCESS
, 0, 0);
286 openlog("login", LOG_ODELAY
, LOG_AUTH
);
293 while ((ch
= getopt(argc
, argv
, "1fh:lpq")) != -1)
295 while ((ch
= getopt(argc
, argv
, "fh:p")) != -1)
303 errx(1, "-h option: %s", strerror(EPERM
));
304 if (strlen(optarg
) >= MAXHOSTNAMELEN
)
305 errx(1, "-h option: %s: exceeds maximum "
306 "hostname size", optarg
);
326 syslog(LOG_ERR
, "invalid flag %c", ch
);
333 username
= strdup(*argv
);
334 if (username
== NULL
)
339 #endif /* __APPLE__ */
345 setproctitle("-%s", getprogname());
346 #endif /* !__APPLE__ */
348 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
354 ttyn
= ttyname(STDIN_FILENO
);
355 if (ttyn
== NULL
|| *ttyn
== '\0') {
356 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
359 if ((tty
= strrchr(ttyn
, '/')) != NULL
)
366 * Get "login-retries" & "login-backoff" from default class
368 lc
= login_getclass(NULL
);
369 prompt
= login_getcapstr(lc
, "login_prompt",
370 default_prompt
, default_prompt
);
371 passwd_prompt
= login_getcapstr(lc
, "passwd_prompt",
372 default_passwd_prompt
, default_passwd_prompt
);
373 retries
= login_getcapnum(lc
, "login-retries",
374 DEFAULT_RETRIES
, DEFAULT_RETRIES
);
375 backoff
= login_getcapnum(lc
, "login-backoff",
376 DEFAULT_BACKOFF
, DEFAULT_BACKOFF
);
379 #else /* !LOGIN_CAP */
380 prompt
= default_prompt
;
381 passwd_prompt
= default_passwd_prompt
;
382 retries
= DEFAULT_RETRIES
;
383 backoff
= DEFAULT_BACKOFF
;
384 #endif /* !LOGIN_CAP */
388 /* Set the terminal id */
389 audit_set_terminal_id(&tid
);
390 if (fstat(STDIN_FILENO
, &st
) < 0) {
391 fprintf(stderr
, "login: Unable to stat terminal\n");
392 au_login_fail("Unable to stat terminal", 1);
395 if (S_ISCHR(st
.st_mode
)) {
396 tid
.port
= st
.st_rdev
;
400 #endif /* USE_BSM_AUDIT */
401 #endif /* __APPLE__ */
404 * Try to authenticate the user until we succeed or time out.
406 for (cnt
= 0;; ask
= 1) {
412 username
= getloginname();
417 if (strlen(username
) > _UTX_USERSIZE
)
418 username
[_UTX_USERSIZE
] = '\0';
419 #endif /* __APPLE__ */
422 * Note if trying multiple user names; log failures for
423 * previous user name, but don't bother logging one failure
424 * for nonexistent name (mistyped username).
426 if (failures
&& strcmp(olduser
, username
) != 0) {
427 if (failures
> (pwd
? 0 : 1))
433 /* get lastlog info before PAM make a new entry */
435 getlastlogxbyname(username
, &lastlog
);
437 #endif /* __APPLE__ */
439 pwd
= getpwnam(username
);
443 * Load the PAM policy and set some variables
446 if (fflag
&& (pwd
!= NULL
) && (pwd
->pw_uid
== uid
)) {
450 pam_err
= pam_start(name
, username
, &pamc
, &pamh
);
451 if (pam_err
!= PAM_SUCCESS
) {
452 pam_syslog("pam_start()");
454 au_login_fail("PAM Error", 1);
456 bail(NO_SLEEP_EXIT
, 1);
458 pam_err
= pam_set_item(pamh
, PAM_TTY
, tty
);
459 if (pam_err
!= PAM_SUCCESS
) {
460 pam_syslog("pam_set_item(PAM_TTY)");
462 au_login_fail("PAM Error", 1);
464 bail(NO_SLEEP_EXIT
, 1);
466 pam_err
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
467 if (pam_err
!= PAM_SUCCESS
) {
468 pam_syslog("pam_set_item(PAM_RHOST)");
470 au_login_fail("PAM Error", 1);
472 bail(NO_SLEEP_EXIT
, 1);
476 if (pwd
!= NULL
&& pwd
->pw_uid
== 0)
480 * If the -f option was specified and the caller is
481 * root or the caller isn't changing their uid, don't
484 if (pwd
!= NULL
&& fflag
&&
485 (uid
== (uid_t
)0 || uid
== (uid_t
)pwd
->pw_uid
)) {
486 /* already authenticated */
489 auditsuccess
= 0; /* opened a terminal window only */
494 /* If the account doesn't have a password, authenticate. */
495 } else if (pwd
!= NULL
&& pwd
->pw_passwd
[0] == '\0') {
497 #endif /* !USE_PAM */
498 #endif /* __APPLE__ */
501 (void)setpriority(PRIO_PROCESS
, 0, -4);
506 char* salt
= pwd
->pw_passwd
;
507 char* p
= getpass(passwd_prompt
);
508 rval
= strcmp(crypt(p
, salt
), salt
);
509 memset(p
, 0, strlen(p
));
512 (void)setpriority(PRIO_PROCESS
, 0, 0);
520 * If trying to log in as root but with insecure terminal,
521 * refuse the login attempt.
523 if (pwd
&& rootlogin
&& !rootterm(tty
)) {
524 refused("root login refused on this terminal", "ROOTTERM", 0);
526 au_login_fail("Login refused on terminal", 0);
530 #endif /* !USE_PAM */
531 #endif /* __APPLE__ */
533 if (pwd
&& rval
== 0)
541 * We are not exiting here, but this corresponds to a failed
542 * login event, so set exitstatus to 1.
545 au_login_fail("Login incorrect", 1);
548 (void)printf("Login incorrect\n");
554 * Allow up to 'retry' (10) attempts, but start
555 * backing off after 'backoff' (3) attempts.
557 if (++cnt
> backoff
) {
558 if (cnt
>= retries
) {
562 sleep((u_int
)((cnt
- backoff
) * 5));
566 /* committed to login -- turn off timeout */
567 (void)alarm((u_int
)0);
568 (void)signal(SIGHUP
, SIG_DFL
);
574 fprintf(stderr
, "login: Unable to find user: %s\n", username
);
579 /* if user not super-user, check for disabled logins */
582 #endif /* !USE_PAM */
586 /* Audit successful login. */
593 * Establish the login class.
595 lc
= login_getpwclass(pwd
);
596 lc_user
= login_getuserclass(pwd
);
598 if (!(quietlog
= login_getcapbool(lc_user
, "hushlogin", 0)))
599 quietlog
= login_getcapbool(lc
, "hushlogin", 0);
600 #endif /* LOGIN_CAP */
604 * Switching needed for NFS with root access disabled.
606 * XXX: This change fails to modify the additional groups for the
607 * process, and as such, may restrict rights normally granted
608 * through those groups.
610 (void)setegid(pwd
->pw_gid
);
611 (void)seteuid(rootlogin
? 0 : pwd
->pw_uid
);
613 if (!*pwd
->pw_dir
|| chdir(pwd
->pw_dir
) < 0) {
615 if (login_getcapbool(lc
, "requirehome", 0))
616 refused("Home directory not available", "HOMEDIR", 1);
617 #endif /* LOGIN_CAP */
619 refused("Cannot find root directory", "ROOTDIR", 1);
620 if (!quietlog
|| *pwd
->pw_dir
)
621 printf("No home directory.\nLogging in with home = \"/\".\n");
622 pwd
->pw_dir
= strdup("/");
623 if (pwd
->pw_dir
== NULL
) {
624 syslog(LOG_NOTICE
, "strdup(): %m");
631 #endif /* !__APPLE__ */
633 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
641 /* Nothing else left to fail -- really log in. */
643 memset((void *)&utmp
, 0, sizeof(utmp
));
644 (void)gettimeofday(&utmp
.ut_tv
, NULL
);
645 (void)strncpy(utmp
.ut_user
, username
, sizeof(utmp
.ut_user
));
647 (void)strncpy(utmp
.ut_host
, hostname
, sizeof(utmp
.ut_host
));
648 (void)strncpy(utmp
.ut_line
, tty
, sizeof(utmp
.ut_line
));
649 utmp
.ut_type
= USER_PROCESS
| UTMPX_AUTOFILL_MASK
;
650 utmp
.ut_pid
= getpid();
655 #endif /* !__APPLE__ */
657 shell
= login_getcapstr(lc
, "shell", pwd
->pw_shell
, pwd
->pw_shell
);
658 #endif /* !LOGIN_CAP */
659 if (*pwd
->pw_shell
== '\0')
660 pwd
->pw_shell
= strdup(_PATH_BSHELL
);
661 if (pwd
->pw_shell
== NULL
) {
662 syslog(LOG_NOTICE
, "strdup(): %m");
666 #if defined(__APPLE__) && TARGET_OS_EMBEDDED
667 /* on embedded, allow a shell to live in /var/debug_mount/bin/sh */
668 #define _PATH_DEBUGSHELL "/var/debug_mount/bin/sh"
669 if (stat(pwd
->pw_shell
, &st
) != 0) {
670 if (stat(_PATH_DEBUGSHELL
, &st
) == 0) {
671 pwd
->pw_shell
= strdup(_PATH_DEBUGSHELL
);
676 if (*shell
== '\0') /* Not overridden */
677 shell
= pwd
->pw_shell
;
678 if ((shell
= strdup(shell
)) == NULL
) {
679 syslog(LOG_NOTICE
, "strdup(): %m");
689 * Set device protections, depending on what terminal the
690 * user is logged in. This feature is used on Suns to give
691 * console users better privacy.
693 login_fbtab(tty
, pwd
->pw_uid
, pwd
->pw_gid
);
694 #endif /* !__APPLE__ */
697 * Clear flags of the tty. None should be set, and when the
698 * user sets them otherwise, this can cause the chown to fail.
699 * Since it isn't clear that flags are useful on character
700 * devices, we just clear them.
702 * We don't log in the case of EOPNOTSUPP because dev might be
703 * on NFS, which doesn't support chflags.
705 * We don't log in the EROFS because that means that /dev is on
706 * a read only file system and we assume that the permissions there
709 if (ttyn
!= tname
&& chflags(ttyn
, 0))
711 if (errno
!= EOPNOTSUPP
&& errno
!= ENOTSUP
&& errno
!= EROFS
)
713 if (errno
!= EOPNOTSUPP
&& errno
!= EROFS
)
715 syslog(LOG_ERR
, "chflags(%s): %m", ttyn
);
716 if (ttyn
!= tname
&& chown(ttyn
, pwd
->pw_uid
,
717 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
))
719 syslog(LOG_ERR
, "chown(%s): %m", ttyn
);
722 (void)chmod(ttyn
, 0620);
723 #endif /* __APPLE__ */
727 * Exclude cons/vt/ptys only, assume dialup otherwise
728 * TODO: Make dialup tty determination a library call
729 * for consistency (finger etc.)
731 if (hflag
&& isdialuptty(tty
))
732 syslog(LOG_INFO
, "DIALUP %s, %s", tty
, pwd
->pw_name
);
733 #endif /* !__APPLE__ */
737 * Syslog each successful login, so we don't have to watch
738 * hundreds of wtmp or lastlogin files.
741 syslog(LOG_INFO
, "login from %s on %s as %s",
742 hostname
, tty
, pwd
->pw_name
);
744 syslog(LOG_INFO
, "login on %s as %s",
749 * If fflag is on, assume caller/authenticator has logged root
752 if (rootlogin
&& fflag
== 0) {
754 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s FROM %s",
755 username
, tty
, hostname
);
757 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s",
762 * Destroy environment unless user has requested its
763 * preservation - but preserve TERM in all cases
765 term
= getenv("TERM");
769 setenv("TERM", term
, 0);
773 * PAM modules might add supplementary groups during pam_setcred().
775 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
776 syslog(LOG_ERR
, "setusercontext() failed - exiting");
777 bail(NO_SLEEP_EXIT
, 1);
779 #endif /* !__APPLE__ */
781 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_ESTABLISH_CRED
);
782 if (pam_err
!= PAM_SUCCESS
) {
783 pam_syslog("pam_setcred()");
784 bail(NO_SLEEP_EXIT
, 1);
786 pam_cred_established
= 1;
788 pam_err
= pam_open_session(pamh
, pam_silent
);
789 if (pam_err
!= PAM_SUCCESS
) {
790 pam_syslog("pam_open_session()");
791 bail(NO_SLEEP_EXIT
, 1);
793 pam_session_established
= 1;
797 /* <rdar://problem/5377791>
798 Install a signal handler that will forward SIGHUP to the
799 child and process group. The parent should not exit on
800 SIGHUP so that the tty ownership can be reset. */
801 (void)signal(SIGHUP
, handle_sighup
);
802 #endif /* __APPLE__ */
805 * We must fork() before setuid() because we need to call
806 * pam_close_session() as root.
811 } else if (pid
!= 0) {
813 * Parent: wait for child to finish, then clean up
818 setproctitle("-%s [pam]", getprogname());
819 #endif /* !__APPLE__ */
821 /* Our SIGHUP handler may interrupt the wait */
824 res
= waitpid(pid
, &status
, 0);
825 } while (res
== -1 && errno
== EINTR
);
827 waitpid(pid
, &status
, 0);
832 #endif /* __APPLE__ */
833 bail(NO_SLEEP_EXIT
, 0);
837 * NOTICE: We are now in the child process!
841 /* Restore the default SIGHUP handler for the child. */
842 (void)signal(SIGHUP
, SIG_DFL
);
843 #endif /* __APPLE__ */
847 * Add any environment variables the PAM modules may have set.
849 export_pam_environment();
852 * We're done with PAM now; our parent will deal with the rest.
859 * We don't need to be root anymore, so set the login name and
862 if (setlogin(username
) != 0) {
863 syslog(LOG_ERR
, "setlogin(%s): %m - exiting", username
);
864 bail(NO_SLEEP_EXIT
, 1);
867 /* <rdar://problem/6041650> restore process priority if not changing uids */
868 if (uid
== (uid_t
)pwd
->pw_uid
) {
869 (void)setpriority(PRIO_PROCESS
, 0, prio
);
872 (void)setgid(pwd
->pw_gid
);
873 if (initgroups(username
, pwd
->pw_gid
) == -1)
874 syslog(LOG_ERR
, "login: initgroups() failed");
875 pwd
= getpwnam(username
); // 7258548
876 (void) setuid(rootlogin
? 0 : pwd
->pw_uid
);
877 #else /* !__APPLE__ */
878 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
879 LOGIN_SETALL
& ~(LOGIN_SETLOGIN
|LOGIN_SETGROUP
)) != 0) {
880 syslog(LOG_ERR
, "setusercontext() failed - exiting");
883 #endif /* !__APPLE__ */
886 /* We test for the home directory after pam_open_session(3)
887 * as the home directory may have been mounted by a session
888 * module, and after changing uid as the home directory may
889 * be NFS with root access disabled. */
891 /* First do a stat in case the homedir is automounted */
892 stat(pwd
->pw_dir
,&st
);
893 if (!*pwd
->pw_dir
|| chdir(pwd
->pw_dir
) < 0) {
894 printf("No home directory: %s\n", pwd
->pw_dir
);
895 if (chdir("/") < 0) {
896 refused("Cannot find root directory", "ROOTDIR", 0);
899 pwd
->pw_dir
= strdup("/");
900 if (pwd
->pw_dir
== NULL
) {
901 syslog(LOG_NOTICE
, "strdup(): %m");
906 #endif /* __APPLE__ */
908 (void)setenv("SHELL", pwd
->pw_shell
, 1);
910 syslog(LOG_ERR
, "pwd->pw_shell not set - exiting", username
);
911 bail(NO_SLEEP_EXIT
, 1);
914 (void)setenv("HOME", pwd
->pw_dir
, 1);
916 (void)setenv("HOME", "/", 1);
918 /* Overwrite "term" from login.conf(5) for any known TERM */
919 if (term
== NULL
&& (tp
= stypeof(tty
)) != NULL
)
920 (void)setenv("TERM", tp
, 1);
922 (void)setenv("TERM", TERM_UNKNOWN
, 0);
923 (void)setenv("LOGNAME", username
, 1);
924 (void)setenv("USER", username
, 1);
925 (void)setenv("PATH", rootlogin
? _PATH_STDPATH
: _PATH_DEFPATH
, 0);
928 /* Re-enable crash reporter */
931 mach_port_t bp
, ep
, mts
;
932 thread_state_flavor_t flavor
= 0;
935 flavor
= PPC_THREAD_STATE64
;
936 #elif defined(__i386__) || defined(__x86_64__)
937 flavor
= x86_THREAD_STATE
;
938 #elif defined(__arm__)
939 flavor
= ARM_THREAD_STATE
;
941 #error unsupported architecture
944 mts
= mach_task_self();
946 kr
= task_get_bootstrap_port(mts
, &bp
);
947 if (kr
!= KERN_SUCCESS
) {
948 syslog(LOG_ERR
, "task_get_bootstrap_port() failure: %s (%d)",
949 bootstrap_strerror(kr
), kr
);
953 const char* bs
= "com.apple.ReportCrash";
954 kr
= bootstrap_look_up(bp
, (char*)bs
, &ep
);
955 if (kr
!= KERN_SUCCESS
) {
956 syslog(LOG_ERR
, "bootstrap_look_up(%s) failure: %s (%d)",
957 bs
, bootstrap_strerror(kr
), kr
);
961 kr
= task_set_exception_ports(mts
, EXC_MASK_CRASH
, ep
, EXCEPTION_STATE_IDENTITY
| MACH_EXCEPTION_CODES
, flavor
);
962 if (kr
!= KERN_SUCCESS
) {
963 syslog(LOG_ERR
, "task_set_exception_ports() failure: %d", kr
);
973 cw
= login_getcapstr(lc
, "copyright", NULL
, NULL
);
974 if (cw
== NULL
|| motd(cw
) == -1)
975 (void)printf("%s", copyright
);
979 cw
= login_getcapstr(lc
, "welcome", NULL
, NULL
);
980 if (cw
!= NULL
&& access(cw
, F_OK
) == 0)
983 motd(_PATH_MOTDFILE
);
985 if (login_getcapbool(lc_user
, "nocheckmail", 0) == 0 &&
986 login_getcapbool(lc
, "nocheckmail", 0) == 0) {
987 #else /* !LOGIN_CAP */
988 motd(_PATH_MOTDFILE
);
990 #endif /* !LOGIN_CAP */
993 /* $MAIL may have been set by class. */
996 asprintf(&cx
, "%s/%s",
997 _PATH_MAILDIR
, pwd
->pw_name
);
999 if (cx
&& stat(cx
, &st
) == 0 && st
.st_size
!= 0)
1000 (void)printf("You have %smail.\n",
1001 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
1002 if (getenv("MAIL") == NULL
)
1008 login_close(lc_user
);
1010 #endif /* LOGIN_CAP */
1012 (void)signal(SIGALRM
, SIG_DFL
);
1013 (void)signal(SIGQUIT
, SIG_DFL
);
1014 (void)signal(SIGINT
, SIG_DFL
);
1015 (void)signal(SIGTSTP
, SIG_IGN
);
1018 if (fflag
&& *argv
) pwd
->pw_shell
= *argv
;
1019 #endif /* __APPLE__ */
1022 * Login shells have a leading '-' in front of argv[0]
1024 p
= strrchr(pwd
->pw_shell
, '/');
1026 if (asprintf(&arg0
, "%s%s", lflag
? "" : "-", p
? p
+ 1 : pwd
->pw_shell
) >= MAXPATHLEN
) {
1027 #else /* __APPLE__ */
1028 if (asprintf(&arg0
, "-%s", p
? p
+ 1 : pwd
->pw_shell
) >= MAXPATHLEN
) {
1029 #endif /* __APPLE__ */
1030 syslog(LOG_ERR
, "user: %s: shell exceeds maximum pathname size",
1032 errx(1, "shell exceeds maximum pathname size");
1033 } else if (arg0
== NULL
) {
1034 err(1, "asprintf()");
1038 if (fflag
&& *argv
) {
1040 execvp(pwd
->pw_shell
, argv
);
1043 #endif /* __APPLE__ */
1044 execlp(shell
, arg0
, (char *)0);
1045 err(1, "%s", shell
);
1054 * Attempt to authenticate the user using PAM. Returns 0 if the user is
1055 * authenticated, or 1 if not authenticated. If some sort of PAM system
1056 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
1057 * function returns -1. This can be used as an indication that we should
1058 * fall back to a different authentication mechanism.
1063 const char *tmpl_user
;
1067 pam_err
= pam_authenticate(pamh
, pam_silent
);
1072 * With PAM we support the concept of a "template"
1073 * user. The user enters a login name which is
1074 * authenticated by PAM, usually via a remote service
1075 * such as RADIUS or TACACS+. If authentication
1076 * succeeds, a different but related "template" name
1077 * is used for setting the credentials, shell, and
1078 * home directory. The name the user enters need only
1079 * exist on the remote authentication server, but the
1080 * template name must be present in the local password
1083 * This is supported by two various mechanisms in the
1084 * individual modules. However, from the application's
1085 * point of view, the template user is always passed
1086 * back as a changed value of the PAM_USER item.
1088 pam_err
= pam_get_item(pamh
, PAM_USER
, &item
);
1089 if (pam_err
== PAM_SUCCESS
) {
1090 tmpl_user
= (const char *)item
;
1091 if (strcmp(username
, tmpl_user
) != 0)
1092 pwd
= getpwnam(tmpl_user
);
1094 pam_syslog("pam_get_item(PAM_USER)");
1100 case PAM_USER_UNKNOWN
:
1106 pam_syslog("pam_authenticate()");
1112 pam_err
= pam_acct_mgmt(pamh
, pam_silent
);
1116 case PAM_NEW_AUTHTOK_REQD
:
1117 pam_err
= pam_chauthtok(pamh
,
1118 pam_silent
|PAM_CHANGE_EXPIRED_AUTHTOK
);
1119 if (pam_err
!= PAM_SUCCESS
) {
1120 pam_syslog("pam_chauthtok()");
1125 pam_syslog("pam_acct_mgmt()");
1132 pam_end(pamh
, pam_err
);
1139 * Export any environment variables PAM modules may have set
1142 export_pam_environment()
1147 pam_env
= pam_getenvlist(pamh
);
1148 if (pam_env
!= NULL
) {
1149 for (pp
= pam_env
; *pp
!= NULL
; pp
++) {
1157 * Perform sanity checks on an environment variable:
1158 * - Make sure there is an '=' in the string.
1159 * - Make sure the string doesn't run on too long.
1160 * - Do not export certain variables. This list was taken from the
1161 * Solaris pam_putenv(3) man page.
1165 export(const char *s
)
1167 static const char *noexport
[] = {
1168 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
1175 if (strlen(s
) > 1024 || (p
= strchr(s
, '=')) == NULL
)
1177 if (strncmp(s
, "LD_", 3) == 0)
1179 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
1181 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
1185 (void)setenv(s
, p
+ 1, 1);
1189 #endif /* USE_PAM */
1195 (void)fprintf(stderr
, "usage: login [-pq] [-h hostname] [username]\n");
1196 (void)fprintf(stderr
, " login -f [-lpq] [-h hostname] [username [prog [arg ...]]]\n");
1198 (void)fprintf(stderr
, "usage: login [-fp] [-h hostname] [username]\n");
1204 * Prompt user and read login name from stdin.
1212 nbuf
= malloc(MAXLOGNAME
);
1216 (void)printf("%s", prompt
);
1217 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
1220 bail(NO_SLEEP_EXIT
, 0);
1222 if (p
< nbuf
+ MAXLOGNAME
- 1)
1225 } while (p
== nbuf
);
1228 if (nbuf
[0] == '-') {
1231 #endif /* USE_PAM */
1232 memmove(nbuf
, nbuf
+ 1, strlen(nbuf
));
1235 pam_silent
= PAM_SILENT
;
1236 #endif /* USE_PAM */
1244 rootterm(const char* ttyn
)
1247 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);
1249 #endif /* !USE_PAM */
1250 #endif /* __APPLE__ */
1253 * SIGINT handler for motd().
1255 static volatile int motdinterrupt
;
1257 sigint(int signo __unused
)
1263 * Display the contents of a file (such as /etc/motd).
1266 motd(const char *motdfile
)
1272 if ((f
= fopen(motdfile
, "r")) == NULL
)
1275 oldint
= signal(SIGINT
, sigint
);
1276 while ((ch
= fgetc(f
)) != EOF
&& !motdinterrupt
)
1278 signal(SIGINT
, oldint
);
1279 if (ch
!= EOF
|| ferror(f
)) {
1289 * Forwards the SIGHUP to the child process and current process group.
1292 handle_sighup(int signo
)
1295 /* close the controlling terminal */
1296 close(STDIN_FILENO
);
1297 close(STDOUT_FILENO
);
1298 close(STDERR_FILENO
);
1299 /* Ignore SIGHUP to avoid tail-recursion on signaling
1300 the current process group (of which we are a member). */
1301 (void)signal(SIGHUP
, SIG_IGN
);
1302 /* Forward the signal to the current process group. */
1303 (void)kill(0, signo
);
1304 /* Forward the signal to the child if not a member of the current
1305 * process group <rdar://problem/6244808>. */
1306 if (getpgid(pid
) != getpgrp()) {
1307 (void)kill(pid
, signo
);
1313 * SIGALRM handler, to enforce login prompt timeout.
1315 * XXX This can potentially confuse the hell out of PAM. We should
1316 * XXX instead implement a conversation function that returns
1317 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
1318 * XXX handler just set a flag.
1321 timedout(int signo __unused
)
1324 longjmp(timeout_buf
, signo
);
1335 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
1336 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1337 (void)write(fileno(stdout
), tbuf
, nchars
);
1338 #ifdef USE_BSM_AUDIT
1339 au_login_fail("No login", 0);
1345 #endif /* !USE_PAM */
1354 if (*lastlog
.ll_line
) {
1355 (void)printf("Last login: %.*s ",
1356 24-5, (char *)ctime(&lastlog
.ll_tv
.tv_sec
));
1357 if (*lastlog
.ll_host
!= '\0')
1358 (void)printf("from %.*s\n",
1359 (int)sizeof(lastlog
.ll_host
),
1362 (void)printf("on %.*s\n",
1363 (int)sizeof(lastlog
.ll_line
),
1366 #else /* !USE_PAM */
1369 if(!quiet
&& getlastlogx(pwd
->pw_uid
, &ll
) != NULL
) {
1370 (void)printf("Last login: %.*s ",
1371 24-5, (char *)ctime(&ll
.ll_tv
.tv_sec
));
1372 if (*ll
.ll_host
!= '\0')
1373 (void)printf("from %.*s\n",
1374 (int)sizeof(ll
.ll_host
),
1377 (void)printf("on %.*s\n",
1378 (int)sizeof(ll
.ll_line
),
1381 #endif /* USE_PAM */
1383 #endif /* __APPLE__ */
1386 badlogin(char *name
)
1392 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s FROM %s",
1393 failures
, failures
> 1 ? "S" : "", hostname
);
1394 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
1395 "%d LOGIN FAILURE%s FROM %s, %s",
1396 failures
, failures
> 1 ? "S" : "", hostname
, name
);
1398 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s ON %s",
1399 failures
, failures
> 1 ? "S" : "", tty
);
1400 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
1401 "%d LOGIN FAILURE%s ON %s, %s",
1402 failures
, failures
> 1 ? "S" : "", tty
, name
);
1408 stypeof(char *ttyid
)
1412 if (ttyid
!= NULL
&& *ttyid
!= '\0') {
1413 t
= getttynam(ttyid
);
1414 if (t
!= NULL
&& t
->ty_type
!= NULL
)
1415 return (t
->ty_type
);
1421 refused(const char *msg
, const char *rtype
, int lout
)
1425 printf("%s.\n", msg
);
1427 syslog(LOG_NOTICE
, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1428 pwd
->pw_name
, rtype
, hostname
, tty
);
1430 syslog(LOG_NOTICE
, "LOGIN %s REFUSED (%s) ON TTY %s",
1431 pwd
->pw_name
, rtype
, tty
);
1433 bail(SLEEP_EXIT
, 1);
1441 pam_syslog(const char *msg
)
1443 syslog(LOG_ERR
, "%s: %s", msg
, pam_strerror(pamh
, pam_err
));
1454 if (pam_session_established
) {
1455 pam_err
= pam_close_session(pamh
, 0);
1456 if (pam_err
!= PAM_SUCCESS
)
1457 pam_syslog("pam_close_session()");
1459 pam_session_established
= 0;
1460 if (pam_cred_established
) {
1461 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_DELETE_CRED
);
1462 if (pam_err
!= PAM_SUCCESS
)
1463 pam_syslog("pam_setcred()");
1465 pam_cred_established
= 0;
1466 pam_end(pamh
, pam_err
);
1470 #endif /* USE_PAM */
1473 * Exit, optionally after sleeping a few seconds
1476 bail(int sec
, int eval
)
1481 #endif /* USE_PAM */
1482 #ifdef USE_BSM_AUDIT