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.h>
109 #include <bsm/audit_session.h>
110 #include <bsm/audit_uevents.h>
114 #include <mach/mach_types.h>
115 #include <mach/task.h>
116 #include <mach/mach_init.h>
117 #include <servers/bootstrap.h>
119 #include <sys/file.h>
121 #endif /* __APPLE__ */
124 #include <security/pam_appl.h>
125 #include <security/openpam.h>
128 #include <libiosexec.h>
131 #include "pathnames.h"
134 static int auth_pam(int skip_auth
);
136 static void bail(int, int);
138 static int export(const char *);
139 static void export_pam_environment(void);
141 static int motd(const char *);
142 static void badlogin(char *);
143 static char *getloginname(void);
145 static void pam_syslog(const char *);
146 static void pam_cleanup(void);
148 static void refused(const char *, const char *, int);
149 static const char *stypeof(char *);
150 static void sigint(int);
151 static void timedout(int);
152 static void usage(void);
155 static void dolastlog(int);
156 static void handle_sighup(int);
159 static void checknologin(void);
160 static int rootterm(const char *);
161 #endif /* !USE_PAM */
162 #endif /* __APPLE__ */
164 #define TTYGRPNAME "tty" /* group to own ttys */
165 #define DEFAULT_BACKOFF 3
166 #define DEFAULT_RETRIES 10
167 #define DEFAULT_PROMPT "login: "
168 #define DEFAULT_PASSWD_PROMPT "Password:"
169 #define TERM_UNKNOWN "su"
170 #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */
171 #define NO_SLEEP_EXIT 0
175 * This bounds the time given to login. Not a define so it can
176 * be patched on machines where it's too small.
178 static u_int timeout
= 300;
180 /* Buffer for signal handling of timeout */
181 static jmp_buf timeout_buf
;
186 static char *envinit
[1]; /* empty environment list */
189 * Command line flags and arguments
191 static int fflag
; /* -f: do not perform authentication */
193 static int lflag
; /* -l: login session to the commmand that follows username */
195 static int hflag
; /* -h: login from remote host */
196 static char *hostname
; /* hostname from command line */
197 static int pflag
; /* -p: preserve environment */
202 static char *username
; /* user name */
203 static char *olduser
; /* previous user name */
208 static char default_prompt
[] = DEFAULT_PROMPT
;
209 static const char *prompt
;
210 static char default_passwd_prompt
[] = DEFAULT_PASSWD_PROMPT
;
211 static const char *passwd_prompt
;
219 static pam_handle_t
*pamh
= NULL
;
220 static struct pam_conv pamc
= { openpam_ttyconv
, NULL
};
222 static int pam_silent
= PAM_SILENT
;
223 static int pam_cred_established
;
224 static int pam_session_established
;
231 static struct lastlogx lastlog
;
235 extern au_tid_addr_t tid
;
236 #endif /* USE_BSM_AUDIT */
237 #endif /* __APPLE__ */
240 main(int argc
, char *argv
[])
244 int retries
, backoff
;
245 int ask
, ch
, cnt
, quietlog
= 0, rootlogin
, rval
;
250 char tname
[sizeof(_PATH_TTY
) + 10];
256 const char *name
= "login"; /* PAM config */
260 const char *shell
= NULL
;
261 #endif /* !__APPLE__ */
263 login_cap_t
*lc
= NULL
;
264 login_cap_t
*lc_user
= NULL
;
265 #endif /* LOGIN_CAP */
270 char auditsuccess
= 1;
273 (void)signal(SIGQUIT
, SIG_IGN
);
274 (void)signal(SIGINT
, SIG_IGN
);
275 (void)signal(SIGHUP
, SIG_IGN
);
276 if (setjmp(timeout_buf
)) {
279 (void)fprintf(stderr
, "Login timed out after %d seconds\n",
281 bail(NO_SLEEP_EXIT
, 0);
283 (void)signal(SIGALRM
, timedout
);
284 (void)alarm(timeout
);
286 prio
= getpriority(PRIO_PROCESS
, 0);
288 (void)setpriority(PRIO_PROCESS
, 0, 0);
290 openlog("login", LOG_ODELAY
, LOG_AUTH
);
297 while ((ch
= getopt(argc
, argv
, "1fh:lpq")) != -1)
299 while ((ch
= getopt(argc
, argv
, "fh:p")) != -1)
307 errx(1, "-h option: %s", strerror(EPERM
));
308 if (strlen(optarg
) >= MAXHOSTNAMELEN
)
309 errx(1, "-h option: %s: exceeds maximum "
310 "hostname size", optarg
);
330 syslog(LOG_ERR
, "invalid flag %c", ch
);
337 username
= strdup(*argv
);
338 if (username
== NULL
)
343 #endif /* __APPLE__ */
349 setproctitle("-%s", getprogname());
350 #endif /* !__APPLE__ */
352 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
358 ttyn
= ttyname(STDIN_FILENO
);
359 if (ttyn
== NULL
|| *ttyn
== '\0') {
360 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
363 if ((tty
= strrchr(ttyn
, '/')) != NULL
)
370 * Get "login-retries" & "login-backoff" from default class
372 lc
= login_getclass(NULL
);
373 prompt
= login_getcapstr(lc
, "login_prompt",
374 default_prompt
, default_prompt
);
375 passwd_prompt
= login_getcapstr(lc
, "passwd_prompt",
376 default_passwd_prompt
, default_passwd_prompt
);
377 retries
= login_getcapnum(lc
, "login-retries",
378 DEFAULT_RETRIES
, DEFAULT_RETRIES
);
379 backoff
= login_getcapnum(lc
, "login-backoff",
380 DEFAULT_BACKOFF
, DEFAULT_BACKOFF
);
383 #else /* !LOGIN_CAP */
384 prompt
= default_prompt
;
385 passwd_prompt
= default_passwd_prompt
;
386 retries
= DEFAULT_RETRIES
;
387 backoff
= DEFAULT_BACKOFF
;
388 #endif /* !LOGIN_CAP */
392 /* Set the terminal id */
394 audit_set_terminal_id(&old_tid
);
395 tid
.at_type
= AU_IPv4
;
396 tid
.at_addr
[0] = old_tid
.machine
;
397 if (fstat(STDIN_FILENO
, &st
) < 0) {
398 fprintf(stderr
, "login: Unable to stat terminal\n");
399 au_login_fail("Unable to stat terminal", 1);
402 if (S_ISCHR(st
.st_mode
)) {
403 tid
.at_port
= st
.st_rdev
;
407 #endif /* USE_BSM_AUDIT */
408 #endif /* __APPLE__ */
411 * Try to authenticate the user until we succeed or time out.
413 for (cnt
= 0;; ask
= 1) {
419 username
= getloginname();
424 if (strlen(username
) > _UTX_USERSIZE
)
425 username
[_UTX_USERSIZE
] = '\0';
426 #endif /* __APPLE__ */
429 * Note if trying multiple user names; log failures for
430 * previous user name, but don't bother logging one failure
431 * for nonexistent name (mistyped username).
433 if (failures
&& strcmp(olduser
, username
) != 0) {
434 if (failures
> (pwd
? 0 : 1))
440 /* get lastlog info before PAM make a new entry */
442 getlastlogxbyname(username
, &lastlog
);
444 #endif /* __APPLE__ */
446 pwd
= getpwnam(username
);
450 * Load the PAM policy and set some variables
453 if (fflag
&& (pwd
!= NULL
) && (pwd
->pw_uid
== uid
)) {
457 pam_err
= pam_start(name
, username
, &pamc
, &pamh
);
458 if (pam_err
!= PAM_SUCCESS
) {
459 pam_syslog("pam_start()");
461 au_login_fail("PAM Error", 1);
463 bail(NO_SLEEP_EXIT
, 1);
465 pam_err
= pam_set_item(pamh
, PAM_TTY
, tty
);
466 if (pam_err
!= PAM_SUCCESS
) {
467 pam_syslog("pam_set_item(PAM_TTY)");
469 au_login_fail("PAM Error", 1);
471 bail(NO_SLEEP_EXIT
, 1);
473 pam_err
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
474 if (pam_err
!= PAM_SUCCESS
) {
475 pam_syslog("pam_set_item(PAM_RHOST)");
477 au_login_fail("PAM Error", 1);
479 bail(NO_SLEEP_EXIT
, 1);
483 if (pwd
!= NULL
&& pwd
->pw_uid
== 0)
487 * If the -f option was specified and the caller is
488 * root or the caller isn't changing their uid, don't
491 if (pwd
!= NULL
&& fflag
&&
492 (uid
== (uid_t
)0 || uid
== (uid_t
)pwd
->pw_uid
)) {
494 rval
= auth_pam(fflag
);
499 auditsuccess
= 0; /* opened a terminal window only */
504 /* If the account doesn't have a password, authenticate. */
505 } else if (pwd
!= NULL
&& pwd
->pw_passwd
[0] == '\0') {
507 #endif /* !USE_PAM */
508 #endif /* __APPLE__ */
511 (void)setpriority(PRIO_PROCESS
, 0, -4);
513 rval
= auth_pam(fflag
);
516 char* salt
= pwd
->pw_passwd
;
517 char* p
= getpass(passwd_prompt
);
518 rval
= strcmp(crypt(p
, salt
), salt
);
519 memset(p
, 0, strlen(p
));
522 (void)setpriority(PRIO_PROCESS
, 0, 0);
530 * If trying to log in as root but with insecure terminal,
531 * refuse the login attempt.
533 if (pwd
&& rootlogin
&& !rootterm(tty
)) {
534 refused("root login refused on this terminal", "ROOTTERM", 0);
536 au_login_fail("Login refused on terminal", 0);
540 #endif /* !USE_PAM */
541 #endif /* __APPLE__ */
543 if (pwd
&& rval
== 0)
551 * We are not exiting here, but this corresponds to a failed
552 * login event, so set exitstatus to 1.
555 au_login_fail("Login incorrect", 1);
558 (void)printf("Login incorrect\n");
564 * Allow up to 'retry' (10) attempts, but start
565 * backing off after 'backoff' (3) attempts.
567 if (++cnt
> backoff
) {
568 if (cnt
>= retries
) {
572 sleep((u_int
)((cnt
- backoff
) * 5));
576 /* committed to login -- turn off timeout */
577 (void)alarm((u_int
)0);
578 (void)signal(SIGHUP
, SIG_DFL
);
584 fprintf(stderr
, "login: Unable to find user: %s\n", username
);
589 /* if user not super-user, check for disabled logins */
592 #endif /* !USE_PAM */
596 /* Audit successful login. */
598 au_login_success(fflag
);
603 * Establish the login class.
605 lc
= login_getpwclass(pwd
);
606 lc_user
= login_getuserclass(pwd
);
608 if (!(quietlog
= login_getcapbool(lc_user
, "hushlogin", 0)))
609 quietlog
= login_getcapbool(lc
, "hushlogin", 0);
610 #endif /* LOGIN_CAP */
614 * Switching needed for NFS with root access disabled.
616 * XXX: This change fails to modify the additional groups for the
617 * process, and as such, may restrict rights normally granted
618 * through those groups.
620 (void)setegid(pwd
->pw_gid
);
621 (void)seteuid(rootlogin
? 0 : pwd
->pw_uid
);
623 if (!*pwd
->pw_dir
|| chdir(pwd
->pw_dir
) < 0) {
625 if (login_getcapbool(lc
, "requirehome", 0))
626 refused("Home directory not available", "HOMEDIR", 1);
627 #endif /* LOGIN_CAP */
629 refused("Cannot find root directory", "ROOTDIR", 1);
630 if (!quietlog
|| *pwd
->pw_dir
)
631 printf("No home directory.\nLogging in with home = \"/\".\n");
632 pwd
->pw_dir
= strdup("/");
633 if (pwd
->pw_dir
== NULL
) {
634 syslog(LOG_NOTICE
, "strdup(): %m");
641 #endif /* !__APPLE__ */
643 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
651 /* Nothing else left to fail -- really log in. */
653 memset((void *)&utmp
, 0, sizeof(utmp
));
654 (void)gettimeofday(&utmp
.ut_tv
, NULL
);
655 (void)strncpy(utmp
.ut_user
, username
, sizeof(utmp
.ut_user
));
657 (void)strncpy(utmp
.ut_host
, hostname
, sizeof(utmp
.ut_host
));
658 (void)strncpy(utmp
.ut_line
, tty
, sizeof(utmp
.ut_line
));
659 utmp
.ut_type
= USER_PROCESS
| UTMPX_AUTOFILL_MASK
;
660 utmp
.ut_pid
= getpid();
665 #endif /* !__APPLE__ */
667 shell
= login_getcapstr(lc
, "shell", pwd
->pw_shell
, pwd
->pw_shell
);
668 #endif /* !LOGIN_CAP */
669 if (*pwd
->pw_shell
== '\0')
670 pwd
->pw_shell
= strdup(_PATH_BSHELL
);
671 if (pwd
->pw_shell
== NULL
) {
672 syslog(LOG_NOTICE
, "strdup(): %m");
676 #if defined(__APPLE__) && (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
677 /* on embedded, allow a shell to live in /private/var/personalized_debug/bin/sh */
678 #define _PATH_DEBUGSHELL "/private/var/personalized_debug/bin/sh"
679 if (stat(pwd
->pw_shell
, &st
) != 0) {
680 if (stat(_PATH_DEBUGSHELL
, &st
) == 0) {
681 pwd
->pw_shell
= strdup(_PATH_DEBUGSHELL
);
686 if (*shell
== '\0') /* Not overridden */
687 shell
= pwd
->pw_shell
;
688 if ((shell
= strdup(shell
)) == NULL
) {
689 syslog(LOG_NOTICE
, "strdup(): %m");
699 * Set device protections, depending on what terminal the
700 * user is logged in. This feature is used on Suns to give
701 * console users better privacy.
703 login_fbtab(tty
, pwd
->pw_uid
, pwd
->pw_gid
);
704 #endif /* !__APPLE__ */
707 * Clear flags of the tty. None should be set, and when the
708 * user sets them otherwise, this can cause the chown to fail.
709 * Since it isn't clear that flags are useful on character
710 * devices, we just clear them.
712 * We don't log in the case of EOPNOTSUPP because dev might be
713 * on NFS, which doesn't support chflags.
715 * We don't log in the EROFS because that means that /dev is on
716 * a read only file system and we assume that the permissions there
719 if (ttyn
!= tname
&& chflags(ttyn
, 0))
721 if (errno
!= EOPNOTSUPP
&& errno
!= ENOTSUP
&& errno
!= EROFS
)
723 if (errno
!= EOPNOTSUPP
&& errno
!= EROFS
)
725 syslog(LOG_ERR
, "chflags(%s): %m", ttyn
);
726 if (ttyn
!= tname
&& chown(ttyn
, pwd
->pw_uid
,
727 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
))
729 syslog(LOG_ERR
, "chown(%s): %m", ttyn
);
732 (void)chmod(ttyn
, 0620);
733 #endif /* __APPLE__ */
737 * Exclude cons/vt/ptys only, assume dialup otherwise
738 * TODO: Make dialup tty determination a library call
739 * for consistency (finger etc.)
741 if (hflag
&& isdialuptty(tty
))
742 syslog(LOG_INFO
, "DIALUP %s, %s", tty
, pwd
->pw_name
);
743 #endif /* !__APPLE__ */
747 * Syslog each successful login, so we don't have to watch
748 * hundreds of wtmp or lastlogin files.
751 syslog(LOG_INFO
, "login from %s on %s as %s",
752 hostname
, tty
, pwd
->pw_name
);
754 syslog(LOG_INFO
, "login on %s as %s",
759 * If fflag is on, assume caller/authenticator has logged root
762 if (rootlogin
&& fflag
== 0) {
764 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s FROM %s",
765 username
, tty
, hostname
);
767 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s",
772 * Destroy environment unless user has requested its
773 * preservation - but preserve TERM in all cases
775 term
= getenv("TERM");
779 setenv("TERM", term
, 0);
783 * PAM modules might add supplementary groups during pam_setcred().
785 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
786 syslog(LOG_ERR
, "setusercontext() failed - exiting");
787 bail(NO_SLEEP_EXIT
, 1);
789 #endif /* !__APPLE__ */
792 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_ESTABLISH_CRED
);
793 if (pam_err
!= PAM_SUCCESS
) {
794 pam_syslog("pam_setcred()");
795 bail(NO_SLEEP_EXIT
, 1);
797 pam_cred_established
= 1;
800 pam_err
= pam_open_session(pamh
, pam_silent
);
801 if (pam_err
!= PAM_SUCCESS
) {
802 pam_syslog("pam_open_session()");
803 bail(NO_SLEEP_EXIT
, 1);
805 pam_session_established
= 1;
809 /* <rdar://problem/5377791>
810 Install a signal handler that will forward SIGHUP to the
811 child and process group. The parent should not exit on
812 SIGHUP so that the tty ownership can be reset. */
813 (void)signal(SIGHUP
, handle_sighup
);
814 #endif /* __APPLE__ */
817 * We must fork() before setuid() because we need to call
818 * pam_close_session() as root.
823 } else if (pid
!= 0) {
825 * Parent: wait for child to finish, then clean up
830 setproctitle("-%s [pam]", getprogname());
831 #endif /* !__APPLE__ */
833 /* Our SIGHUP handler may interrupt the wait */
836 res
= waitpid(pid
, &status
, 0);
837 } while (res
== -1 && errno
== EINTR
);
839 waitpid(pid
, &status
, 0);
844 #endif /* __APPLE__ */
845 bail(NO_SLEEP_EXIT
, 0);
849 * NOTICE: We are now in the child process!
853 /* Restore the default SIGHUP handler for the child. */
854 (void)signal(SIGHUP
, SIG_DFL
);
855 #endif /* __APPLE__ */
859 * Add any environment variables the PAM modules may have set.
861 export_pam_environment();
864 * We're done with PAM now; our parent will deal with the rest.
871 * We don't need to be root anymore, so set the login name and
874 if (setlogin(username
) != 0) {
875 syslog(LOG_ERR
, "setlogin(%s): %m - exiting", username
);
876 bail(NO_SLEEP_EXIT
, 1);
879 /* <rdar://problem/6041650> restore process priority if not changing uids */
880 if (uid
== (uid_t
)pwd
->pw_uid
) {
881 (void)setpriority(PRIO_PROCESS
, 0, prio
);
884 (void)setgid(pwd
->pw_gid
);
885 if (initgroups(username
, pwd
->pw_gid
) == -1)
886 syslog(LOG_ERR
, "login: initgroups() failed");
887 (void) setuid(rootlogin
? 0 : pwd
->pw_uid
);
888 #else /* !__APPLE__ */
889 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
890 LOGIN_SETALL
& ~(LOGIN_SETLOGIN
|LOGIN_SETGROUP
)) != 0) {
891 syslog(LOG_ERR
, "setusercontext() failed - exiting");
894 #endif /* !__APPLE__ */
897 /* We test for the home directory after pam_open_session(3)
898 * as the home directory may have been mounted by a session
899 * module, and after changing uid as the home directory may
900 * be NFS with root access disabled. */
902 /* First do a stat in case the homedir is automounted */
903 stat(pwd
->pw_dir
,&st
);
904 if (!*pwd
->pw_dir
|| chdir(pwd
->pw_dir
) < 0) {
905 printf("No home directory: %s\n", pwd
->pw_dir
);
906 if (chdir("/") < 0) {
907 refused("Cannot find root directory", "ROOTDIR", 0);
910 pwd
->pw_dir
= strdup("/");
911 if (pwd
->pw_dir
== NULL
) {
912 syslog(LOG_NOTICE
, "strdup(): %m");
917 #endif /* __APPLE__ */
919 (void)setenv("SHELL", pwd
->pw_shell
, 1);
921 syslog(LOG_ERR
, "pwd->pw_shell not set - exiting");
922 bail(NO_SLEEP_EXIT
, 1);
925 (void)setenv("HOME", pwd
->pw_dir
, 1);
927 (void)setenv("HOME", "/", 1);
929 /* Overwrite "term" from login.conf(5) for any known TERM */
930 if (term
== NULL
&& (tp
= stypeof(tty
)) != NULL
)
931 (void)setenv("TERM", tp
, 1);
933 (void)setenv("TERM", TERM_UNKNOWN
, 0);
934 (void)setenv("LOGNAME", username
, 1);
935 (void)setenv("USER", username
, 1);
936 (void)setenv("PATH", rootlogin
? _PATH_STDPATH
: _PATH_DEFPATH
, 0);
939 /* Re-enable crash reporter */
942 mach_port_t bp
, ep
, mts
;
943 thread_state_flavor_t flavor
= 0;
946 flavor
= PPC_THREAD_STATE64
;
947 #elif defined(__i386__) || defined(__x86_64__)
948 flavor
= x86_THREAD_STATE
;
949 #elif defined(__arm__) || defined(__arm64__)
950 flavor
= ARM_THREAD_STATE
;
952 #error unsupported architecture
955 mts
= mach_task_self();
957 kr
= task_get_bootstrap_port(mts
, &bp
);
958 if (kr
!= KERN_SUCCESS
) {
959 syslog(LOG_ERR
, "task_get_bootstrap_port() failure: %s (%d)",
960 bootstrap_strerror(kr
), kr
);
964 const char* bs
= "com.apple.ReportCrash";
965 kr
= bootstrap_look_up(bp
, (char*)bs
, &ep
);
966 if (kr
!= KERN_SUCCESS
) {
967 syslog(LOG_ERR
, "bootstrap_look_up(%s) failure: %s (%d)",
968 bs
, bootstrap_strerror(kr
), kr
);
972 kr
= task_set_exception_ports(mts
, EXC_MASK_RESOURCE
| EXC_MASK_GUARD
| EXC_MASK_CORPSE_NOTIFY
, ep
, EXCEPTION_STATE_IDENTITY
| MACH_EXCEPTION_CODES
, flavor
);
973 if (kr
!= KERN_SUCCESS
) {
974 syslog(LOG_ERR
, "task_set_exception_ports() failure: %d", kr
);
978 #endif /* __APPLE__ */
984 cw
= login_getcapstr(lc
, "copyright", NULL
, NULL
);
985 if (cw
== NULL
|| motd(cw
) == -1)
986 (void)printf("%s", copyright
);
990 cw
= login_getcapstr(lc
, "welcome", NULL
, NULL
);
991 if (cw
!= NULL
&& access(cw
, F_OK
) == 0)
994 motd(_PATH_MOTDFILE
);
996 if (login_getcapbool(lc_user
, "nocheckmail", 0) == 0 &&
997 login_getcapbool(lc
, "nocheckmail", 0) == 0) {
998 #else /* !LOGIN_CAP */
999 motd(_PATH_MOTDFILE
);
1001 #endif /* !LOGIN_CAP */
1004 /* $MAIL may have been set by class. */
1005 cx
= getenv("MAIL");
1007 asprintf(&cx
, "%s/%s",
1008 _PATH_MAILDIR
, pwd
->pw_name
);
1010 if (cx
&& stat(cx
, &st
) == 0 && st
.st_size
!= 0)
1011 (void)printf("You have %smail.\n",
1012 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
1013 if (getenv("MAIL") == NULL
)
1019 login_close(lc_user
);
1021 #endif /* LOGIN_CAP */
1023 (void)signal(SIGALRM
, SIG_DFL
);
1024 (void)signal(SIGQUIT
, SIG_DFL
);
1025 (void)signal(SIGINT
, SIG_DFL
);
1026 (void)signal(SIGTSTP
, SIG_IGN
);
1029 if (fflag
&& *argv
) pwd
->pw_shell
= *argv
;
1030 #endif /* __APPLE__ */
1033 * Login shells have a leading '-' in front of argv[0]
1035 p
= strrchr(pwd
->pw_shell
, '/');
1037 if (asprintf(&arg0
, "%s%s", lflag
? "" : "-", p
? p
+ 1 : pwd
->pw_shell
) >= MAXPATHLEN
) {
1038 #else /* __APPLE__ */
1039 if (asprintf(&arg0
, "-%s", p
? p
+ 1 : pwd
->pw_shell
) >= MAXPATHLEN
) {
1040 #endif /* __APPLE__ */
1041 syslog(LOG_ERR
, "user: %s: shell exceeds maximum pathname size",
1043 errx(1, "shell exceeds maximum pathname size");
1044 } else if (arg0
== NULL
) {
1045 err(1, "asprintf()");
1049 if (fflag
&& *argv
) {
1051 execvp(pwd
->pw_shell
, argv
);
1054 #endif /* __APPLE__ */
1055 execlp(shell
, arg0
, (char *)0);
1056 err(1, "%s", shell
);
1065 * Attempt to authenticate the user using PAM. Returns 0 if the user is
1066 * authenticated, or 1 if not authenticated. If some sort of PAM system
1067 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
1068 * function returns -1. This can be used as an indication that we should
1069 * fall back to a different authentication mechanism.
1072 auth_pam(int skip_auth
)
1074 const char *tmpl_user
;
1082 pam_err
= pam_authenticate(pamh
, pam_silent
);
1087 * With PAM we support the concept of a "template"
1088 * user. The user enters a login name which is
1089 * authenticated by PAM, usually via a remote service
1090 * such as RADIUS or TACACS+. If authentication
1091 * succeeds, a different but related "template" name
1092 * is used for setting the credentials, shell, and
1093 * home directory. The name the user enters need only
1094 * exist on the remote authentication server, but the
1095 * template name must be present in the local password
1098 * This is supported by two various mechanisms in the
1099 * individual modules. However, from the application's
1100 * point of view, the template user is always passed
1101 * back as a changed value of the PAM_USER item.
1103 pam_err
= pam_get_item(pamh
, PAM_USER
, &item
);
1104 if (pam_err
== PAM_SUCCESS
) {
1105 tmpl_user
= (const char *)item
;
1106 if (strcmp(username
, tmpl_user
) != 0)
1107 pwd
= getpwnam(tmpl_user
);
1109 pam_syslog("pam_get_item(PAM_USER)");
1115 case PAM_USER_UNKNOWN
:
1121 pam_syslog("pam_authenticate()");
1128 pam_err
= pam_acct_mgmt(pamh
, pam_silent
);
1132 case PAM_NEW_AUTHTOK_REQD
:
1135 pam_err
= pam_chauthtok(pamh
,
1136 pam_silent
|PAM_CHANGE_EXPIRED_AUTHTOK
);
1137 if (pam_err
!= PAM_SUCCESS
) {
1138 pam_syslog("pam_chauthtok()");
1144 pam_syslog("pam_acct_mgmt()");
1148 pam_syslog("pam_acct_mgmt()");
1155 pam_end(pamh
, pam_err
);
1162 * Export any environment variables PAM modules may have set
1165 export_pam_environment(void)
1170 pam_env
= pam_getenvlist(pamh
);
1171 if (pam_env
!= NULL
) {
1172 for (pp
= pam_env
; *pp
!= NULL
; pp
++) {
1180 * Perform sanity checks on an environment variable:
1181 * - Make sure there is an '=' in the string.
1182 * - Make sure the string doesn't run on too long.
1183 * - Do not export certain variables. This list was taken from the
1184 * Solaris pam_putenv(3) man page.
1188 export(const char *s
)
1190 static const char *noexport
[] = {
1191 "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
1198 if (strlen(s
) > 1024 || (p
= strchr(s
, '=')) == NULL
)
1200 if (strncmp(s
, "LD_", 3) == 0)
1202 for (pp
= noexport
; *pp
!= NULL
; pp
++) {
1204 if (s
[n
] == '=' && strncmp(s
, *pp
, n
) == 0)
1208 (void)setenv(s
, p
+ 1, 1);
1212 #endif /* USE_PAM */
1218 (void)fprintf(stderr
, "usage: login [-pq] [-h hostname] [username]\n");
1219 (void)fprintf(stderr
, " login -f [-lpq] [-h hostname] [username [prog [arg ...]]]\n");
1221 (void)fprintf(stderr
, "usage: login [-fp] [-h hostname] [username]\n");
1227 * Prompt user and read login name from stdin.
1235 nbuf
= malloc(MAXLOGNAME
);
1239 (void)printf("%s", prompt
);
1240 /* rdar://43101375 login process on 2018 hardware is blocked forever waiting on new line char
1241 * The carriage return char is added to the termination condition of the
1242 * for loop because for some reason, '\r' is returned by getchar() on M9 hardware.
1244 for (p
= nbuf
; (((ch
= getchar()) != '\n') && (ch
!= '\r')); ) {
1247 bail(NO_SLEEP_EXIT
, 0);
1249 if (p
< nbuf
+ MAXLOGNAME
- 1)
1252 } while (p
== nbuf
);
1255 if (nbuf
[0] == '-') {
1258 #endif /* USE_PAM */
1259 memmove(nbuf
, nbuf
+ 1, strlen(nbuf
));
1262 pam_silent
= PAM_SILENT
;
1263 #endif /* USE_PAM */
1271 rootterm(const char* ttyn
)
1274 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);
1276 #endif /* !USE_PAM */
1277 #endif /* __APPLE__ */
1280 * SIGINT handler for motd().
1282 static volatile int motdinterrupt
;
1284 sigint(int signo __unused
)
1290 * Display the contents of a file (such as /etc/motd).
1293 motd(const char *motdfile
)
1299 if ((f
= fopen(motdfile
, "r")) == NULL
)
1302 oldint
= signal(SIGINT
, sigint
);
1303 while ((ch
= fgetc(f
)) != EOF
&& !motdinterrupt
)
1305 signal(SIGINT
, oldint
);
1306 if (ch
!= EOF
|| ferror(f
)) {
1316 * Forwards the SIGHUP to the child process and current process group.
1319 handle_sighup(int signo
)
1322 /* close the controlling terminal */
1323 close(STDIN_FILENO
);
1324 close(STDOUT_FILENO
);
1325 close(STDERR_FILENO
);
1326 /* Ignore SIGHUP to avoid tail-recursion on signaling
1327 the current process group (of which we are a member). */
1328 (void)signal(SIGHUP
, SIG_IGN
);
1329 /* Forward the signal to the current process group. */
1330 (void)kill(0, signo
);
1331 /* Forward the signal to the child if not a member of the current
1332 * process group <rdar://problem/6244808>. */
1333 if (getpgid(pid
) != getpgrp()) {
1334 (void)kill(pid
, signo
);
1340 * SIGALRM handler, to enforce login prompt timeout.
1342 * XXX This can potentially confuse the hell out of PAM. We should
1343 * XXX instead implement a conversation function that returns
1344 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
1345 * XXX handler just set a flag.
1348 timedout(int signo __unused
)
1351 longjmp(timeout_buf
, signo
);
1362 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
1363 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1364 (void)write(fileno(stdout
), tbuf
, nchars
);
1365 #ifdef USE_BSM_AUDIT
1366 au_login_fail("No login", 0);
1372 #endif /* !USE_PAM */
1375 dolastlog(int quiet
)
1380 if (*lastlog
.ll_line
) {
1381 (void)printf("Last login: %.*s ",
1382 24-5, (char *)ctime(&lastlog
.ll_tv
.tv_sec
));
1383 if (*lastlog
.ll_host
!= '\0')
1384 (void)printf("from %.*s\n",
1385 (int)sizeof(lastlog
.ll_host
),
1388 (void)printf("on %.*s\n",
1389 (int)sizeof(lastlog
.ll_line
),
1392 #else /* !USE_PAM */
1395 if(!quiet
&& getlastlogx(pwd
->pw_uid
, &ll
) != NULL
) {
1396 (void)printf("Last login: %.*s ",
1397 24-5, (char *)ctime(&ll
.ll_tv
.tv_sec
));
1398 if (*ll
.ll_host
!= '\0')
1399 (void)printf("from %.*s\n",
1400 (int)sizeof(ll
.ll_host
),
1403 (void)printf("on %.*s\n",
1404 (int)sizeof(ll
.ll_line
),
1407 #endif /* USE_PAM */
1409 #endif /* __APPLE__ */
1412 badlogin(char *name
)
1417 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s FROM %s",
1418 failures
, failures
> 1 ? "S" : "", hostname
);
1419 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
1420 "%d LOGIN FAILURE%s FROM %s, %s",
1421 failures
, failures
> 1 ? "S" : "", hostname
, name
);
1423 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s ON %s",
1424 failures
, failures
> 1 ? "S" : "", tty
);
1425 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
1426 "%d LOGIN FAILURE%s ON %s, %s",
1427 failures
, failures
> 1 ? "S" : "", tty
, name
);
1433 stypeof(char *ttyid
)
1437 if (ttyid
!= NULL
&& *ttyid
!= '\0') {
1438 t
= getttynam(ttyid
);
1439 if (t
!= NULL
&& t
->ty_type
!= NULL
)
1440 return (t
->ty_type
);
1446 refused(const char *msg
, const char *rtype
, int lout
)
1450 printf("%s.\n", msg
);
1452 syslog(LOG_NOTICE
, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1453 pwd
->pw_name
, rtype
, hostname
, tty
);
1455 syslog(LOG_NOTICE
, "LOGIN %s REFUSED (%s) ON TTY %s",
1456 pwd
->pw_name
, rtype
, tty
);
1458 bail(SLEEP_EXIT
, 1);
1466 pam_syslog(const char *msg
)
1468 syslog(LOG_ERR
, "%s: %s", msg
, pam_strerror(pamh
, pam_err
));
1478 if (pam_session_established
) {
1479 pam_err
= pam_close_session(pamh
, 0);
1480 if (pam_err
!= PAM_SUCCESS
)
1481 pam_syslog("pam_close_session()");
1483 pam_session_established
= 0;
1484 if (pam_cred_established
) {
1485 pam_err
= pam_setcred(pamh
, pam_silent
|PAM_DELETE_CRED
);
1486 if (pam_err
!= PAM_SUCCESS
)
1487 pam_syslog("pam_setcred()");
1489 pam_cred_established
= 0;
1490 pam_end(pamh
, pam_err
);
1494 #endif /* USE_PAM */
1497 * Exit, optionally after sleeping a few seconds
1500 bail(int sec
, int eval
)
1504 #endif /* USE_PAM */
1505 #ifdef USE_BSM_AUDIT