2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
27 * The Regents of the University of California. All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 static char copyright
[] =
60 "@(#) Copyright (c) Apple Computer, Inc. 1997\n\n";
65 * login -h hostname (for telnetd, etc.)
66 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
69 #include <sys/param.h>
72 #include <sys/resource.h>
92 #include <pam/pam_appl.h>
93 #include <pam/pam_misc.h>
96 #include "pathnames.h"
98 void badlogin
__P((char *));
99 void checknologin
__P((void));
100 void dolastlog
__P((int));
101 void getloginname
__P((void));
102 void motd
__P((void));
103 int rootterm
__P((char *));
104 void sigint
__P((int));
105 void sleepexit
__P((int));
106 char *stypeof
__P((char *));
107 void timedout
__P((int));
109 int klogin
__P((struct passwd
*, char *, char *, char *));
112 extern void login
__P((struct utmp
*));
114 #define TTYGRPNAME "tty" /* name of group to own ttys */
117 * This bounds the time given to login. Not a define so it can
118 * be patched on machines where it's too small.
131 char term
[64], *hostname
, *username
= NULL
, *tty
;
138 extern char **environ
;
143 int ask
, ch
, cnt
, oflag
= 0, fflag
, hflag
, pflag
, quietlog
, rootlogin
= 0, rval
;
147 char *domain
, *p
, *salt
, *ttyn
;
148 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
149 char localhost
[MAXHOSTNAMELEN
];
151 pam_handle_t
*pamh
= NULL
;
152 struct pam_conv conv
= { misc_conv
, NULL
};
157 (void)signal(SIGALRM
, timedout
);
158 (void)alarm(timeout
);
159 (void)signal(SIGQUIT
, SIG_IGN
);
160 (void)signal(SIGINT
, SIG_IGN
);
161 (void)setpriority(PRIO_PROCESS
, 0, 0);
163 openlog("login", LOG_ODELAY
, LOG_AUTH
);
166 * -p is used by getty to tell login not to destroy the environment
167 * -f is used to skip a second login authentication
168 * -h is used by other servers to pass the name of the remote
169 * host to login so that it may be placed in utmp and wtmp
172 if (gethostname(localhost
, sizeof(localhost
)) < 0)
173 syslog(LOG_ERR
, "couldn't get local hostname: %m");
175 domain
= strchr(localhost
, '.');
180 fflag
= hflag
= pflag
= 0;
182 while ((ch
= getopt(argc
, argv
, "1fh:p")) != EOF
)
192 errx(1, "-h option: %s", strerror(EPERM
));
194 if (domain
&& (p
= strchr(optarg
, '.')) &&
195 strcasecmp(p
, domain
) == 0)
205 syslog(LOG_ERR
, "invalid flag %c", ch
);
206 (void)fprintf(stderr
,
207 "usage: login [-fp] [-h hostname] [username]\n");
219 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
222 ttyn
= ttyname(STDIN_FILENO
);
223 if (ttyn
== NULL
|| *ttyn
== '\0') {
224 (void)snprintf(tname
, sizeof(tname
), "%s??", _PATH_TTY
);
227 if (tty
= strrchr(ttyn
, '/'))
233 rval
= pam_start("login", username
, &conv
, &pamh
);
234 if( rval
!= PAM_SUCCESS
) {
235 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
238 rval
= pam_set_item(pamh
, PAM_TTY
, tty
);
239 if( rval
!= PAM_SUCCESS
) {
240 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
244 rval
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
245 if( rval
!= PAM_SUCCESS
) {
246 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
250 rval
= pam_set_item(pamh
, PAM_USER_PROMPT
, "login: ");
251 if( rval
!= PAM_SUCCESS
) {
252 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
258 pam_set_item(pamh
, PAM_USER
, username
);
259 pwd
= getpwnam(username
);
260 if( (pwd
!= NULL
) && (pwd
->pw_uid
== 0) )
263 if( (pwd
!= NULL
) && fflag
&& ((uid
== 0) || (uid
== pwd
->pw_uid
)) ){
267 rval
= pam_authenticate(pamh
, 0);
268 while( (!oflag
) && (cnt
++ < 10) && ((rval
== PAM_AUTH_ERR
) ||
269 (rval
== PAM_USER_UNKNOWN
) ||
270 (rval
== PAM_CRED_INSUFFICIENT
) ||
271 (rval
== PAM_AUTHINFO_UNAVAIL
))) {
273 printf("Login incorrect\n");
276 pwd
= getpwnam(username
);
277 if( (pwd
!= NULL
) && (pwd
->pw_uid
== 0) )
279 pam_set_item(pamh
, PAM_USER
, username
);
280 rval
= pam_authenticate(pamh
, 0);
283 if( rval
!= PAM_SUCCESS
) {
284 pam_get_item(pamh
, PAM_USER
, (void *)&username
);
286 printf("Login incorrect\n");
290 rval
= pam_acct_mgmt(pamh
, 0);
291 if( rval
== PAM_NEW_AUTHTOK_REQD
) {
292 rval
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
294 if( rval
!= PAM_SUCCESS
) {
295 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
300 rval
= pam_get_item(pamh
, PAM_USER
, (void *)&username
);
301 if( (rval
== PAM_SUCCESS
) && username
&& *username
)
302 pwd
= getpwnam(username
);
304 rval
= pam_open_session(pamh
, 0);
305 if( rval
!= PAM_SUCCESS
) {
306 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
310 rval
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
311 if( rval
!= PAM_SUCCESS
) {
312 fprintf(stderr
, "login: PAM Error: %s\n", pam_strerror(pamh
, rval
));
317 for (cnt
= 0;; ask
= 1) {
324 if ((instance
= strchr(username
, '.')) != NULL
) {
325 if (strncmp(instance
, ".root", 5) == 0)
331 if (strlen(username
) > UT_NAMESIZE
)
332 username
[UT_NAMESIZE
] = '\0';
335 * Note if trying multiple user names; log failures for
336 * previous user name, but don't bother logging one failure
337 * for nonexistent name (mistyped username).
339 if (failures
&& strcmp(tbuf
, username
)) {
340 if (failures
> (pwd
? 0 : 1))
344 (void)strcpy(tbuf
, username
);
346 if (pwd
= getpwnam(username
))
347 salt
= pwd
->pw_passwd
;
352 * if we have a valid account name, and it doesn't have a
353 * password, or the -f option was specified and the caller
354 * is root or the caller isn't changing their uid, don't
357 if (pwd
&& (*pwd
->pw_passwd
== '\0' ||
358 fflag
&& (uid
== 0 || uid
== pwd
->pw_uid
)))
361 if (pwd
&& pwd
->pw_uid
== 0)
364 (void)setpriority(PRIO_PROCESS
, 0, -4);
366 p
= getpass("Password:");
370 rval
= klogin(pwd
, instance
, localhost
, p
);
371 if (rval
!= 0 && rootlogin
&& pwd
->pw_uid
!= 0)
376 rval
= strcmp(crypt(p
, salt
), pwd
->pw_passwd
);
378 rval
= strcmp(crypt(p
, salt
), pwd
->pw_passwd
);
381 memset(p
, 0, strlen(p
));
383 (void)setpriority(PRIO_PROCESS
, 0, 0);
386 * If trying to log in as root without Kerberos,
387 * but with insecure terminal, refuse the login attempt.
392 if (pwd
&& rootlogin
&& !rootterm(tty
)) {
393 (void)fprintf(stderr
,
394 "%s login refused on this terminal.\n",
398 "LOGIN %s REFUSED FROM %s ON TTY %s",
399 pwd
->pw_name
, hostname
, tty
);
402 "LOGIN %s REFUSED ON TTY %s",
410 (void)printf("Login incorrect\n");
412 /* we allow 10 tries, but after 3 we start backing off */
418 sleep((u_int
)((cnt
- 3) * 5));
423 /* committed to login -- turn off timeout */
424 (void)alarm((u_int
)0);
428 /* if user not super-user, check for disabled logins */
432 setegid(pwd
->pw_gid
);
433 seteuid(rootlogin
? 0 : pwd
->pw_uid
);
435 /* First do a stat in case the homedir is automounted */
436
stat(pwd
->pw_dir
,&st
);
438 if (chdir(pwd
->pw_dir
) < 0) {
439 (void)printf("No home directory %s!\n", pwd
->pw_dir
);
443 (void)printf("Logging in with home = \"/\".\n");
448 quietlog
= access(_PATH_HUSHLOGIN
, F_OK
) == 0;
450 /* Nothing else left to fail -- really log in. */
451 memset((void *)&utmp
, 0, sizeof(utmp
));
452 (void)time(&utmp
.ut_time
);
453 (void)strncpy(utmp
.ut_name
, username
, sizeof(utmp
.ut_name
));
455 (void)strncpy(utmp
.ut_host
, hostname
, sizeof(utmp
.ut_host
));
456 (void)strncpy(utmp
.ut_line
, tty
, sizeof(utmp
.ut_line
));
461 (void)chown(ttyn
, pwd
->pw_uid
,
462 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
463 (void)chmod(ttyn
, 0620);
464 (void)setgid(pwd
->pw_gid
);
466 initgroups(username
, pwd
->pw_gid
);
468 if (*pwd
->pw_shell
== '\0')
469 pwd
->pw_shell
= _PATH_BSHELL
;
471 /* Destroy environment unless user has requested its preservation. */
473 environ
= malloc(sizeof(char *));
476 (void)setenv("HOME", pwd
->pw_dir
, 1);
477 (void)setenv("SHELL", pwd
->pw_shell
, 1);
479 (void)strncpy(term
, stypeof(tty
), sizeof(term
));
480 (void)setenv("TERM", term
, 0);
481 (void)setenv("LOGNAME", pwd
->pw_name
, 1);
482 (void)setenv("USER", pwd
->pw_name
, 1);
483 (void)setenv("PATH", _PATH_DEFPATH
, 0);
486 (void)setenv("KRBTKFILE", krbtkfile_env
, 1);
490 pmenv
= pam_getenvlist(pamh
);
491 for( cnt
= 0; pmenv
&& pmenv
[cnt
]; cnt
++ )
497 } else if( pid
!= 0 ) {
498 waitpid(pid
, NULL
, 0);
499 pam_setcred(pamh
, PAM_DELETE_CRED
);
500 rval
= pam_close_session(pamh
, 0);
507 if (tty
[sizeof("tty")-1] == 'd')
508 syslog(LOG_INFO
, "DIALUP %s, %s", tty
, pwd
->pw_name
);
510 /* If fflag is on, assume caller/authenticator has logged root login. */
511 if (rootlogin
&& fflag
== 0)
513 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s FROM %s",
514 username
, tty
, hostname
);
516 syslog(LOG_NOTICE
, "ROOT LOGIN (%s) ON %s", username
, tty
);
519 if (!quietlog
&& notickets
== 1)
520 (void)printf("Warning: no Kerberos tickets issued.\n");
526 sizeof(tbuf
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
527 if (stat(tbuf
, &st
) == 0 && st
.st_size
!= 0)
528 (void)printf("You have %smail.\n",
529 (st
.st_mtime
> st
.st_atime
) ? "new " : "");
532 (void)signal(SIGALRM
, SIG_DFL
);
533 (void)signal(SIGQUIT
, SIG_DFL
);
534 (void)signal(SIGINT
, SIG_DFL
);
535 (void)signal(SIGTSTP
, SIG_IGN
);
538 (void)strcpy(tbuf
+ 1, (p
= strrchr(pwd
->pw_shell
, '/')) ?
539 p
+ 1 : pwd
->pw_shell
);
541 if (setlogin(pwd
->pw_name
) < 0)
542 syslog(LOG_ERR
, "setlogin() failure: %m");
544 /* Discard permissions last so can't get killed and drop core. */
548 (void) setuid(pwd
->pw_uid
);
550 execlp(pwd
->pw_shell
, tbuf
, 0);
551 err(1, "%s", pwd
->pw_shell
);
555 #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
557 #define NBUFSIZ (MAXLOGNAME + 1)
565 static char nbuf
[NBUFSIZ
];
568 (void)printf("login: ");
569 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
574 if (p
< nbuf
+ (NBUFSIZ
- 1))
579 (void)fprintf(stderr
,
580 "login names may not start with '-'.\n");
596 return ((t
= getttynam(ttyn
)) && t
->ty_status
& TTY_SECURE
);
599 jmp_buf motdinterrupt
;
608 if ((fd
= open(_PATH_MOTDFILE
, O_RDONLY
, 0)) < 0)
610 oldint
= signal(SIGINT
, sigint
);
611 if (setjmp(motdinterrupt
) == 0)
612 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
613 (void)write(fileno(stdout
), tbuf
, nchars
);
614 (void)signal(SIGINT
, oldint
);
624 longjmp(motdinterrupt
, 1);
633 (void)fprintf(stderr
, "Login timed out after %d seconds\n", timeout
);
643 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
644 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
645 (void)write(fileno(stdout
), tbuf
, nchars
);
657 /* HACK HACK HACK: This is because HFS doesn't support sparse files
658 * and seeking into the file too far is too slow. The "solution"
659 * is to just bail if the seek time for a large uid would be too
662 if(pwd
->pw_uid
> 100000) {
663 syslog(LOG_NOTICE
, "User login %s (%d) not logged in lastlog. UID too large.", pwd
->pw_name
, pwd
->pw_uid
);
667 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
668 (void)lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), L_SET
);
670 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
672 (void)printf("Last login: %.*s ",
673 24-5, (char *)ctime(&ll
.ll_time
));
674 if (*ll
.ll_host
!= '\0')
675 (void)printf("from %.*s\n",
676 (int)sizeof(ll
.ll_host
),
679 (void)printf("on %.*s\n",
680 (int)sizeof(ll
.ll_line
),
683 (void)lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), L_SET
);
685 memset((void *)&ll
, 0, sizeof(ll
));
686 (void)time(&ll
.ll_time
);
687 (void)strncpy(ll
.ll_line
, tty
, sizeof(ll
.ll_line
));
689 (void)strncpy(ll
.ll_host
, hostname
, sizeof(ll
.ll_host
));
690 (void)write(fd
, (char *)&ll
, sizeof(ll
));
703 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s FROM %s",
704 failures
, failures
> 1 ? "S" : "", hostname
);
705 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
706 "%d LOGIN FAILURE%s FROM %s, %s",
707 failures
, failures
> 1 ? "S" : "", hostname
, name
);
709 syslog(LOG_NOTICE
, "%d LOGIN FAILURE%s ON %s",
710 failures
, failures
> 1 ? "S" : "", tty
);
711 syslog(LOG_AUTHPRIV
|LOG_NOTICE
,
712 "%d LOGIN FAILURE%s ON %s, %s",
713 failures
, failures
> 1 ? "S" : "", tty
, name
);
726 return (ttyid
&& (t
= getttynam(ttyid
)) ? t
->ty_type
: UNKNOWN
);