2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
26 * The Regents of the University of California. All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 static char copyright
[] =
59 "@(#) Copyright (c) Apple Computer, Inc. 1997\n\n";
64 * login -h hostname (for telnetd, etc.)
65 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
68 #include <sys/param.h>
71 #include <sys/resource.h>
91 #include <pam/pam_appl.h>
92 #include <pam/pam_misc.h>
95 #include "pathnames.h"
97 void badlogin
__P((char *));
98 void checknologin
__P((void));
99 void dolastlog
__P((int));
100 void getloginname
__P((void));
101 void motd
__P((void));
102 int rootterm
__P((char *));
103 void sigint
__P((int));
104 void sleepexit
__P((int));
105 char *stypeof
__P((char *));
106 void timedout
__P((int));
108 int klogin
__P((struct passwd
*, char *, char *, char *));
111 extern void login
__P((struct utmp
*));
113 #define TTYGRPNAME "tty" /* name of group to own ttys */
116 * This bounds the time given to login. Not a define so it can
117 * be patched on machines where it's too small.
130 char term
[64], *hostname
, *username
= NULL
, *tty
;
137 extern char **environ
;
142 int ask
, ch
, cnt
, oflag
= 0, fflag
, hflag
, pflag
, quietlog
, rootlogin
= 0, rval
;
146 char *domain
, *p
, *salt
, *ttyn
;
147 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
148 char localhost
[MAXHOSTNAMELEN
];
150 pam_handle_t
*pamh
= NULL
;
151 struct pam_conv conv
= { misc_conv
, NULL
};
156 (void)signal(SIGALRM
, timedout
);
157 (void)alarm(timeout
);
158 (void)signal(SIGQUIT
, SIG_IGN
);
159 (void)signal(SIGINT
, SIG_IGN
);
160 (void)setpriority(PRIO_PROCESS
, 0, 0);
162 openlog("login", LOG_ODELAY
, LOG_AUTH
);
165 * -p is used by getty to tell login not to destroy the environment
166 * -f is used to skip a second login authentication
167 * -h is used by other servers to pass the name of the remote
168 * host to login so that it may be placed in utmp and wtmp
171 if (gethostname(localhost
, sizeof(localhost
)) < 0)
172 syslog(LOG_ERR
, "couldn't get local hostname: %m");
174 domain
= strchr(localhost
, '.');
179 fflag
= hflag
= pflag
= 0;
181 while ((ch
= getopt(argc
, argv
, "1fh:p")) != EOF
)
191 errx(1, "-h option: %s", strerror(EPERM
));
193 if (domain
&& (p
= strchr(optarg
, '.')) &&
194 strcasecmp(p
, domain
) == 0)
204 syslog(LOG_ERR
, "invalid flag %c", ch
);
205 (void)fprintf(stderr
,
206 "usage: login [-fp] [-h hostname] [username]\n");
218 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
221 ttyn
= ttyname(STDIN_FILENO
);
222 if (ttyn
== NULL
|| *ttyn
== '\0') {
223 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
226 if (tty
= strrchr(ttyn
, '/'))
232 rval
= pam_start("login", username
, &conv
, &pamh
);
233 if( rval
!= PAM_SUCCESS
) {
234 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
237 rval
= pam_set_item(pamh
, PAM_TTY
, tty
);
238 if( rval
!= PAM_SUCCESS
) {
239 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
243 rval
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
244 if( rval
!= PAM_SUCCESS
) {
245 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
249 rval
= pam_set_item(pamh
, PAM_USER_PROMPT
, "login: ");
250 if( rval
!= PAM_SUCCESS
) {
251 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
257 pam_set_item(pamh
, PAM_USER
, username
);
258 pwd
= getpwnam(username
);
259 if( (pwd
!= NULL
) && (pwd
->pw_uid
== 0) )
262 if( (pwd
!= NULL
) && fflag
&& ((uid
== 0) || (uid
== pwd
->pw_uid
)) ){
266 rval
= pam_authenticate(pamh
, 0);
267 while( (!oflag
) && (cnt
++ < 10) && ((rval
== PAM_AUTH_ERR
) ||
268 (rval
== PAM_USER_UNKNOWN
) ||
269 (rval
== PAM_CRED_INSUFFICIENT
) ||
270 (rval
== PAM_AUTHINFO_UNAVAIL
))) {
272 printf("Login incorrect\n");
275 pwd
= getpwnam(username
);
276 if( (pwd
!= NULL
) && (pwd
->pw_uid
== 0) )
278 pam_set_item(pamh
, PAM_USER
, username
);
279 rval
= pam_authenticate(pamh
, 0);
282 if( rval
!= PAM_SUCCESS
) {
283 pam_get_item(pamh
, PAM_USER
, (void *)&username
);
285 printf("Login incorrect\n");
289 rval
= pam_acct_mgmt(pamh
, 0);
290 if( rval
== PAM_NEW_AUTHTOK_REQD
) {
291 rval
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
293 if( rval
!= PAM_SUCCESS
) {
294 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
299 rval
= pam_get_item(pamh
, PAM_USER
, (void *)&username
);
300 if( (rval
== PAM_SUCCESS
) && username
&& *username
)
301 pwd
= getpwnam(username
);
303 rval
= pam_open_session(pamh
, 0);
304 if( rval
!= PAM_SUCCESS
) {
305 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
309 rval
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
310 if( rval
!= PAM_SUCCESS
) {
311 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
316 for (cnt
= 0;; ask
= 1) {
323 if ((instance
= strchr(username
, '.')) != NULL
) {
324 if (strncmp(instance
, ".root", 5) == 0)
330 if (strlen(username
) > UT_NAMESIZE
)
331 username
[UT_NAMESIZE
] = '\0';
334 * Note if trying multiple user names; log failures for
335 * previous user name, but don't bother logging one failure
336 * for nonexistent name (mistyped username).
338 if (failures
&& strcmp(tbuf
, username
)) {
339 if (failures
> (pwd
? 0 : 1))
343 (void)strcpy(tbuf
, username
);
345 if (pwd
= getpwnam(username
))
346 salt
= pwd
->pw_passwd
;
351 * if we have a valid account name, and it doesn't have a
352 * password, or the -f option was specified and the caller
353 * is root or the caller isn't changing their uid, don't
356 if (pwd
&& (*pwd
->pw_passwd
== '\0' ||
357 fflag
&& (uid
== 0 || uid
== pwd
->pw_uid
)))
360 if (pwd
&& pwd
->pw_uid
== 0)
363 (void)setpriority(PRIO_PROCESS
, 0, -4);
365 p
= getpass("Password:");
369 rval
= klogin(pwd
, instance
, localhost
, p
);
370 if (rval
!= 0 && rootlogin
&& pwd
->pw_uid
!= 0)
375 rval
= strcmp(crypt(p
, salt
), pwd
->pw_passwd
);
377 rval
= strcmp(crypt(p
, salt
), pwd
->pw_passwd
);
380 memset(p
, 0, strlen(p
));
382 (void)setpriority(PRIO_PROCESS
, 0, 0);
385 * If trying to log in as root without Kerberos,
386 * but with insecure terminal, refuse the login attempt.
391 if (pwd
&& rootlogin
&& !rootterm(tty
)) {
392 (void)fprintf(stderr
,
393 "%s login refused on this terminal.\n",
397 "LOGIN %s REFUSED FROM %s ON TTY %s",
398 pwd
->pw_name
, hostname
, tty
);
401 "LOGIN %s REFUSED ON TTY %s",
409 (void)printf("Login incorrect\n");
411 /* we allow 10 tries, but after 3 we start backing off */
417 sleep((u_int
)((cnt
- 3) * 5));
422 /* committed to login -- turn off timeout */
423 (void)alarm((u_int
)0);
427 /* if user not super-user, check for disabled logins */
431 setegid(pwd
->pw_gid
);
432 seteuid(rootlogin
? 0 : pwd
->pw_uid
);
434 /* First do a stat in case the homedir is automounted */
435
stat(pwd
->pw_dir
,&st
);
437 if (chdir(pwd
->pw_dir
) < 0) {
438 (void)printf("No home directory %s!\n", pwd
->pw_dir
);
442 (void)printf("Logging in with home = \"/\".\n");
447 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
449 /* Nothing else left to fail -- really log in. */
450 memset((void *)&utmp
, 0, sizeof(utmp
));
451 (void)time(&utmp
.ut_time
);
452 (void)strncpy(utmp
.ut_name
, username
, sizeof(utmp
.ut_name
));
454 (void)strncpy(utmp
.ut_host
, hostname
, sizeof(utmp
.ut_host
));
455 (void)strncpy(utmp
.ut_line
, tty
, sizeof(utmp
.ut_line
));
460 (void)chown(ttyn
, pwd
->pw_uid
,
461 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
462 (void)chmod(ttyn
, 0620);
463 (void)setgid(pwd
->pw_gid
);
465 initgroups(username
, pwd
->pw_gid
);
467 if (*pwd
->pw_shell
== '\0')
468 pwd
->pw_shell
= _PATH_BSHELL
;
470 /* Destroy environment unless user has requested its preservation. */
472 environ
= malloc(sizeof(char *));
475 (void)setenv("HOME", pwd
->pw_dir
, 1);
476 (void)setenv("SHELL", pwd
->pw_shell
, 1);
478 (void)strncpy(term
, stypeof(tty
), sizeof(term
));
479 (void)setenv("TERM", term
, 0);
480 (void)setenv("LOGNAME", pwd
->pw_name
, 1);
481 (void)setenv("USER", pwd
->pw_name
, 1);
482 (void)setenv("PATH", _PATH_DEFPATH
, 0);
485 (void)setenv("KRBTKFILE", krbtkfile_env
, 1);
489 pmenv
= pam_getenvlist(pamh
);
490 for( cnt
= 0; pmenv
&& pmenv
[cnt
]; cnt
++ )
496 } else if( pid
!= 0 ) {
497 waitpid(pid
, NULL
, 0);
498 pam_setcred(pamh
, PAM_DELETE_CRED
);
499 rval
= pam_close_session(pamh
, 0);
506 if (tty
[sizeof("tty")-1] == 'd')
507 syslog(LOG_INFO
, "DIALUP %s, %s", tty
, pwd
->pw_name
);
509 /* If fflag is on, assume caller/authenticator has logged root login. */
510 if (rootlogin
&& fflag
== 0)
512 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s FROM %s",
513 username
, tty
, hostname
);
515 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s", username
, tty
);
518 if (!quietlog
&& notickets
== 1)
519 (void)printf("Warning: no Kerberos tickets issued.\n");
525 sizeof(tbuf
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
526 if (stat(tbuf
, &st
) == 0 && st
.st_size
!= 0)
527 (void)printf("You have %smail.\n",
528 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
531 (void)signal(SIGALRM
, SIG_DFL
);
532 (void)signal(SIGQUIT
, SIG_DFL
);
533 (void)signal(SIGINT
, SIG_DFL
);
534 (void)signal(SIGTSTP
, SIG_IGN
);
537 (void)strcpy(tbuf
+ 1, (p
= strrchr(pwd
->pw_shell
, '/')) ?
538 p
+ 1 : pwd
->pw_shell
);
540 if (setlogin(pwd
->pw_name
) < 0)
541 syslog(LOG_ERR
, "setlogin() failure: %m");
543 /* Discard permissions last so can't get killed and drop core. */
547 (void) setuid(pwd
->pw_uid
);
549 execlp(pwd
->pw_shell
, tbuf
, 0);
550 err(1, "%s", pwd
->pw_shell
);
554 #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
556 #define NBUFSIZ (MAXLOGNAME + 1)
564 static char nbuf
[NBUFSIZ
];
567 (void)printf("login: ");
568 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
573 if (p
< nbuf
+ (NBUFSIZ
- 1))
578 (void)fprintf(stderr
,
579 "login names may not start with '-'.\n");
595 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);
598 jmp_buf motdinterrupt
;
607 if ((fd
= open(_PATH_MOTDFILE
, O_RDONLY
, 0)) < 0)
609 oldint
= signal(SIGINT
, sigint
);
610 if (setjmp(motdinterrupt
) == 0)
611 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
612 (void)write(fileno(stdout
), tbuf
, nchars
);
613 (void)signal(SIGINT
, oldint
);
623 longjmp(motdinterrupt
, 1);
632 (void)fprintf(stderr
, "Login timed out after %d seconds\n", timeout
);
642 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
643 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
644 (void)write(fileno(stdout
), tbuf
, nchars
);
656 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
657 (void)lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), L_SET
);
659 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
661 (void)printf("Last login: %.*s ",
662 24-5, (char *)ctime(&ll
.ll_time
));
663 if (*ll
.ll_host
!= '\0')
664 (void)printf("from %.*s\n",
665 (int)sizeof(ll
.ll_host
),
668 (void)printf("on %.*s\n",
669 (int)sizeof(ll
.ll_line
),
672 (void)lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), L_SET
);
674 memset((void *)&ll
, 0, sizeof(ll
));
675 (void)time(&ll
.ll_time
);
676 (void)strncpy(ll
.ll_line
, tty
, sizeof(ll
.ll_line
));
678 (void)strncpy(ll
.ll_host
, hostname
, sizeof(ll
.ll_host
));
679 (void)write(fd
, (char *)&ll
, sizeof(ll
));
692 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s FROM %s",
693 failures
, failures
> 1 ? "S" : "", hostname
);
694 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
695 "%d LOGIN FAILURE%s FROM %s, %s",
696 failures
, failures
> 1 ? "S" : "", hostname
, name
);
698 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s ON %s",
699 failures
, failures
> 1 ? "S" : "", tty
);
700 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
701 "%d LOGIN FAILURE%s ON %s, %s",
702 failures
, failures
> 1 ? "S" : "", tty
, name
);
715 return (ttyid
&& (t
= getttynam(ttyid
)) ? t
->ty_type
: UNKNOWN
);