]> git.saurik.com Git - apple/system_cmds.git/blob - login.tproj/login.c
system_cmds-431.tar.gz
[apple/system_cmds.git] / login.tproj / login.c
1 /*
2 * Copyright (c) 1999, 2004, 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*-
24 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 */
55
56 #if 0
57 static char copyright[] =
58 "@(#) Copyright (c) Apple Computer, Inc. 1997\n\n";
59 #endif
60
61 /*
62 * login [ name ]
63 * login -h hostname (for telnetd, etc.)
64 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
65 */
66
67 #include <sys/param.h>
68 #include <sys/stat.h>
69 #include <sys/time.h>
70 #include <sys/resource.h>
71 #include <sys/file.h>
72 #include <sys/wait.h>
73
74 #include <err.h>
75 #include <errno.h>
76 #include <grp.h>
77 #include <pwd.h>
78 #include <setjmp.h>
79 #include <signal.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <syslog.h>
84 #include <ttyent.h>
85 #include <tzfile.h>
86 #include <unistd.h>
87 #ifdef USE_PAM
88 #include <utmpx.h>
89 #else /* !USE_PAM */
90 #include <utmp.h>
91 #endif /* USE_PAM */
92
93 #include <sys/types.h>
94 #include <sys/socket.h>
95 #include <netinet/in.h>
96 #include <arpa/inet.h>
97 #include <netdb.h>
98
99 #include <TargetConditionals.h>
100
101 #ifdef USE_BSM
102 #include <bsm/libbsm.h>
103 #include <bsm/audit_uevents.h>
104 #endif
105
106 #ifdef USE_PAM
107 #include <pam/pam_appl.h>
108 #include <pam/pam_misc.h>
109 #endif
110
111 #include <mach/mach_types.h>
112
113 #include <servers/bootstrap.h>
114
115 #include "pathnames.h"
116
117 void badlogin __P((char *));
118 void checknologin __P((void));
119 void dolastlog __P((int));
120 void getloginname __P((void));
121 void motd __P((void));
122 int rootterm __P((char *));
123 void sigint __P((int));
124 void sleepexit __P((int));
125 char *stypeof __P((char *));
126 void timedout __P((int));
127 #ifdef KERBEROS
128 int klogin __P((struct passwd *, char *, char *, char *));
129 #endif
130 void au_success();
131 void au_fail(char *, int);
132
133
134 #ifndef USE_PAM
135 extern void login __P((struct utmp *));
136 #endif /* !USE_PAM */
137 static void bail(int, int);
138 static void refused(const char *, const char *, int);
139
140 #define TTYGRPNAME "tty" /* name of group to own ttys */
141 #define NO_SLEEP_EXIT 0
142 #define SLEEP_EXIT 5
143
144 /*
145 * This bounds the time given to login. Not a define so it can
146 * be patched on machines where it's too small.
147 */
148 u_int timeout = 300;
149
150 #ifdef KERBEROS
151 int notickets = 1;
152 char *instance;
153 char *krbtkfile_env;
154 int authok;
155 #endif
156
157 struct passwd *pwd;
158 int failures;
159 char term[64], *hostname, *username = NULL, *tty;
160 #ifdef USE_PAM
161 static pam_handle_t *pamh = NULL;
162 static struct pam_conv conv = { misc_conv, NULL };
163 static int pam_err;
164 static int pam_silent = PAM_SILENT;
165 static int pam_cred_established;
166 static int pam_session_established;
167 static struct lastlogx lastlog;
168 #endif /* USE_PAM */
169 int hflag;
170
171 #ifdef USE_BSM
172 #define NA_EVENT_STR_SIZE 25
173 au_tid_t tid;
174 #endif
175
176 int
177 main(argc, argv)
178 int argc;
179 char *argv[];
180 {
181 extern char **environ;
182 struct group *gr;
183 struct stat st;
184 #ifndef USE_PAM
185 struct utmp utmp;
186 #endif /* USE_PAM */
187 int ask, ch, cnt, oflag = 0, fflag, lflag, pflag;
188 int quietlog = 0, rootlogin = 0;
189 uid_t uid;
190 uid_t euid;
191 gid_t egid;
192 char *domain, *p, *ttyn;
193 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
194 char localhost[MAXHOSTNAMELEN];
195 #ifdef USE_PAM
196 char **pmenv;
197 pid_t pid;
198 #else
199 int rval;
200 char *salt;
201 #endif
202
203 char auditsuccess = 1;
204
205 (void)signal(SIGALRM, timedout);
206 (void)alarm(timeout);
207 (void)signal(SIGQUIT, SIG_IGN);
208 (void)signal(SIGINT, SIG_IGN);
209 (void)setpriority(PRIO_PROCESS, 0, 0);
210
211 openlog("login", LOG_ODELAY, LOG_AUTH);
212
213 /*
214 * -p is used by getty to tell login not to destroy the environment
215 * -f is used to skip a second login authentication
216 * -l is used to indicate a login session to the command that
217 * follows username
218 * -h is used by other servers to pass the name of the remote
219 * host to login so that it may be placed in utmp and wtmp
220 * -q is used to force hushlogin
221 */
222 domain = NULL;
223 if (gethostname(localhost, sizeof(localhost)) < 0)
224 syslog(LOG_ERR, "couldn't get local hostname: %m");
225 else
226 domain = strchr(localhost, '.');
227
228 euid = geteuid();
229 egid = getegid();
230
231 fflag = hflag = lflag = pflag = 0;
232 uid = getuid();
233 while ((ch = getopt(argc, argv, "1fh:lpq")) != EOF)
234 switch (ch) {
235 case '1':
236 oflag = 1;
237 break;
238 case 'f':
239 fflag = 1;
240 break;
241 case 'h':
242 if (uid)
243 errx(1, "-h option: %s", strerror(EPERM));
244 hflag = 1;
245 if (domain && (p = strchr(optarg, '.')) &&
246 strcasecmp(p, domain) == 0)
247 *p = 0;
248 hostname = optarg;
249 break;
250 case 'l':
251 lflag = 1;
252 break;
253 case 'p':
254 pflag = 1;
255 break;
256 case 'q':
257 quietlog = 1;
258 break;
259 case '?':
260 default:
261 if (!uid)
262 syslog(LOG_ERR, "invalid flag %c", ch);
263 (void)fprintf(stderr,
264 "usage: login [-pq] [-h hostname] [username]\n");
265 (void)fprintf(stderr,
266 " login -f [-lpq] [-h hostname] [username [prog [arg ...]]]\n");
267 exit(1);
268 }
269 argc -= optind;
270 argv += optind;
271
272 if (*argv) {
273 username = *argv++;
274 ask = 0;
275 } else
276 ask = 1;
277
278 for (cnt = getdtablesize(); cnt > 2; cnt--)
279 (void)close(cnt);
280
281 ttyn = ttyname(STDIN_FILENO);
282 if (ttyn == NULL || *ttyn == '\0') {
283 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
284 ttyn = tname;
285 }
286 if (tty = strrchr(ttyn, '/'))
287 ++tty;
288 else
289 tty = ttyn;
290
291 #ifdef USE_BSM
292 /* Set the terminal id */
293 audit_set_terminal_id(&tid);
294 if (fstat(STDIN_FILENO, &st) < 0) {
295 fprintf(stderr, "login: Unable to stat terminal\n");
296 au_fail("Unable to stat terminal", 1);
297 exit(-1);
298 }
299 if (S_ISCHR(st.st_mode)) {
300 tid.port = st.st_rdev;
301 } else {
302 tid.port = 0;
303 }
304 #endif
305
306 #ifdef USE_PAM
307 pam_err = pam_start("login", username, &conv, &pamh);
308 if( pam_err != PAM_SUCCESS ) {
309 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
310 au_fail("PAM Error", 1);
311 exit(1);
312 }
313 pam_err = pam_set_item(pamh, PAM_TTY, tty);
314 if( pam_err != PAM_SUCCESS ) {
315 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
316 au_fail("PAM Error", 1);
317 exit(1);
318 }
319
320 pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
321 if( pam_err != PAM_SUCCESS ) {
322 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
323 au_fail("PAM Error", 1);
324 exit(1);
325 }
326
327 pam_err = pam_set_item(pamh, PAM_USER_PROMPT, "login: ");
328 if( pam_err != PAM_SUCCESS ) {
329 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
330 au_fail("PAM Error", 1);
331 exit(1);
332 }
333
334 if( !username )
335 getloginname();
336 pam_set_item(pamh, PAM_USER, username);
337 pwd = getpwnam(username);
338 if( (pwd != NULL) && (pwd->pw_uid == 0) )
339 rootlogin = 1;
340
341 if( (pwd != NULL) && fflag && ((uid == 0) || (uid == pwd->pw_uid)) ){
342 pam_err = 0;
343 auditsuccess = 0; /* we've simply opened a terminal window */
344 } else {
345
346 pam_err = pam_authenticate(pamh, 0);
347 while( (!oflag) && (cnt++ < 10) && ((pam_err == PAM_AUTH_ERR) ||
348 (pam_err == PAM_USER_UNKNOWN) ||
349 (pam_err == PAM_CRED_INSUFFICIENT) ||
350 (pam_err == PAM_AUTHINFO_UNAVAIL))) {
351 /*
352 * we are not exiting here, but this corresponds to
353 * a failed login event, so set exitstatus to 1
354 */
355 au_fail("Login incorrect", 1);
356 badlogin(username);
357 printf("Login incorrect\n");
358 rootlogin = 0;
359 getloginname();
360 pwd = getpwnam(username);
361 if( (pwd != NULL) && (pwd->pw_uid == 0) )
362 rootlogin = 1;
363 pam_set_item(pamh, PAM_USER, username);
364 pam_err = pam_authenticate(pamh, 0);
365 }
366
367 if( pam_err != PAM_SUCCESS ) {
368 pam_get_item(pamh, PAM_USER, (void *)&username);
369 badlogin(username);
370 printf("Login incorrect\n");
371 au_fail("Login incorrect", 1);
372 exit(1);
373 }
374
375 pam_err = pam_acct_mgmt(pamh, 0);
376 if( pam_err == PAM_NEW_AUTHTOK_REQD ) {
377 pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
378 }
379 if( pam_err != PAM_SUCCESS ) {
380 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
381 au_fail("PAM error", 1);
382 exit(1);
383 }
384 }
385
386 pam_err = pam_get_item(pamh, PAM_USER, (void *)&username);
387 if( (pam_err == PAM_SUCCESS) && username && *username)
388 pwd = getpwnam(username);
389
390 /* get lastlog info before PAM make a new entry */
391 if (!quietlog)
392 getlastlogxbyname(username, &lastlog);
393
394 pam_err = pam_open_session(pamh, 0);
395 if( pam_err != PAM_SUCCESS ) {
396 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
397 au_fail("PAM error", 1);
398 exit(1);
399 }
400
401 pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED);
402 if( pam_err != PAM_SUCCESS ) {
403 fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
404 au_fail("PAM error", 1);
405 exit(1);
406 }
407
408 #else /* USE_PAM */
409 for (cnt = 0;; ask = 1) {
410 if (ask) {
411 fflag = 0;
412 getloginname();
413 }
414 rootlogin = 0;
415 #ifdef KERBEROS
416 if ((instance = strchr(username, '.')) != NULL) {
417 if (strncmp(instance, ".root", 5) == 0)
418 rootlogin = 1;
419 *instance++ = '\0';
420 } else
421 instance = "";
422 #endif
423 if (strlen(username) > UT_NAMESIZE)
424 username[UT_NAMESIZE] = '\0';
425
426 /*
427 * Note if trying multiple user names; log failures for
428 * previous user name, but don't bother logging one failure
429 * for nonexistent name (mistyped username).
430 */
431 if (failures && strcmp(tbuf, username)) {
432 if (failures > (pwd ? 0 : 1)) {
433 badlogin(tbuf);
434 }
435 failures = 0;
436 }
437 (void)strcpy(tbuf, username);
438
439 if (pwd = getpwnam(username))
440 salt = pwd->pw_passwd;
441 else
442 salt = "xx";
443
444 /*
445 * if we have a valid account name, and it doesn't have a
446 * password, or the -f option was specified and the caller
447 * is root or the caller isn't changing their uid, don't
448 * authenticate.
449 */
450 if (pwd && (*pwd->pw_passwd == '\0' ||
451 fflag && (uid == 0 || uid == pwd->pw_uid)))
452 break;
453 fflag = 0;
454 if (pwd && pwd->pw_uid == 0)
455 rootlogin = 1;
456
457 (void)setpriority(PRIO_PROCESS, 0, -4);
458
459 p = getpass("Password:");
460
461 if (pwd) {
462 #ifdef KERBEROS
463 rval = klogin(pwd, instance, localhost, p);
464 if (rval != 0 && rootlogin && pwd->pw_uid != 0)
465 rootlogin = 0;
466 if (rval == 0)
467 authok = 1;
468 else if (rval == 1)
469 rval = strcmp(crypt(p, salt), pwd->pw_passwd);
470 #else
471 rval = strcmp(crypt(p, salt), pwd->pw_passwd);
472 #endif
473 }
474 memset(p, 0, strlen(p));
475
476 (void)setpriority(PRIO_PROCESS, 0, 0);
477
478 /*
479 * If trying to log in as root without Kerberos,
480 * but with insecure terminal, refuse the login attempt.
481 */
482 #ifdef KERBEROS
483 if (authok == 0)
484 #endif
485 if (pwd && rootlogin && !rootterm(tty)) {
486 (void)fprintf(stderr,
487 "%s login refused on this terminal.\n",
488 pwd->pw_name);
489 if (hostname)
490 syslog(LOG_NOTICE,
491 "LOGIN %s REFUSED FROM %s ON TTY %s",
492 pwd->pw_name, hostname, tty);
493 else
494 syslog(LOG_NOTICE,
495 "LOGIN %s REFUSED ON TTY %s",
496 pwd->pw_name, tty);
497 au_fail("Login refused on terminal", 0);
498 continue;
499 }
500
501 if (pwd && !rval)
502 break;
503
504 (void)printf("Login incorrect\n");
505 failures++;
506 /* we allow 10 tries, but after 3 we start backing off */
507 if (++cnt > 3) {
508 if (cnt >= 10) {
509 badlogin(username);
510 au_fail("Login incorrect", 1);
511 sleepexit(1);
512 }
513 au_fail("Login incorrect", 1);
514 sleep((u_int)((cnt - 3) * 5));
515 }
516 }
517 #endif
518
519 /* committed to login -- turn off timeout */
520 (void)alarm((u_int)0);
521
522 endpwent();
523
524 if (!pwd) {
525 fprintf(stderr, "login: Unable to find user: %s\n", username);
526 exit(1);
527 }
528
529 /* if user not super-user, check for disabled logins */
530 if (!rootlogin)
531 checknologin();
532
533 /* Audit successful login */
534 if (auditsuccess)
535 au_success();
536
537 setegid(pwd->pw_gid);
538 seteuid(rootlogin ? 0 : pwd->pw_uid);
539
540 if (!lflag) {
541 /* First do a stat in case the homedir is automounted */
542 stat(pwd->pw_dir,&st);
543
544 if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
545 printf("No home directory %s!\n", pwd->pw_dir);
546 if (chdir("/"))
547 refused("Cannot find root directory", "HOMEDIR", 1);
548 pwd->pw_dir = strdup("/");
549 if (pwd->pw_dir == NULL) {
550 syslog(LOG_NOTICE, "strdup(): %m");
551 bail(SLEEP_EXIT, 1);
552 }
553 printf("Logging in with home = \"/\".\n");
554 }
555 }
556 seteuid(euid);
557 setegid(egid);
558
559 if (!quietlog)
560 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
561
562 /* Nothing else left to fail -- really log in. */
563 #ifndef USE_PAM
564 memset((void *)&utmp, 0, sizeof(utmp));
565 (void)time(&utmp.ut_time);
566 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
567 if (hostname)
568 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
569 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
570 login(&utmp);
571 #endif /* USE_PAM */
572
573 dolastlog(quietlog);
574
575 (void)chown(ttyn, pwd->pw_uid,
576 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
577 (void)chmod(ttyn, 0620);
578 (void)setgid(pwd->pw_gid);
579
580 if (initgroups(username, pwd->pw_gid) == -1)
581 syslog(LOG_ERR, "login: initgroups() failed");
582
583 if (*pwd->pw_shell == '\0')
584 pwd->pw_shell = strdup(_PATH_BSHELL);
585 if (pwd->pw_shell == NULL) {
586 syslog(LOG_NOTICE, "strdup(): %m");
587 bail(SLEEP_EXIT, 1);
588 }
589
590 #if TARGET_OS_EMBEDDED
591 /* on embedded, allow a shell to live in /var/debug_mount/bin/sh */
592 #define _PATH_DEBUGSHELL "/var/debug_mount/bin/sh"
593 if (stat(pwd->pw_shell, &st) != 0) {
594 if (stat(_PATH_DEBUGSHELL, &st) == 0) {
595 pwd->pw_shell = strdup(_PATH_DEBUGSHELL);
596 }
597 }
598 #endif
599
600 /* Destroy environment unless user has requested its preservation. */
601 if (!pflag) {
602 environ = malloc(sizeof(char *));
603 *environ = NULL;
604 }
605 (void)setenv("HOME", pwd->pw_dir, 1);
606 (void)setenv("SHELL", pwd->pw_shell, 1);
607 if (term[0] == '\0')
608 (void)strncpy(term, stypeof(tty), sizeof(term));
609 (void)setenv("TERM", term, 0);
610 (void)setenv("LOGNAME", pwd->pw_name, 1);
611 (void)setenv("USER", pwd->pw_name, 1);
612 (void)setenv("PATH", _PATH_DEFPATH, 0);
613 #ifdef KERBEROS
614 if (krbtkfile_env)
615 (void)setenv("KRBTKFILE", krbtkfile_env, 1);
616 #endif
617
618 #ifdef USE_PAM
619 pmenv = pam_getenvlist(pamh);
620 for( cnt = 0; pmenv && pmenv[cnt]; cnt++ )
621 putenv(pmenv[cnt]);
622
623 /* Ignore SIGHUP so that the parent's call to waitpid will
624 succeed and the tty ownership can be reset. */
625 (void)signal(SIGHUP, SIG_IGN);
626
627 pid = fork();
628 if ( pid < 0 ) {
629 err(1, "fork");
630 } else if( pid != 0 ) {
631 waitpid(pid, NULL, 0);
632 pam_setcred(pamh, PAM_DELETE_CRED);
633 pam_err = pam_close_session(pamh, 0);
634 pam_end(pamh,pam_err);
635 chown(ttyn, 0, 0);
636 chmod(ttyn, 0666);
637 exit(0);
638 }
639
640 /* Restore the default SIGHUP handler for the child. */
641 (void)signal(SIGHUP, SIG_DFL);
642
643 #endif
644
645 if (tty[sizeof("tty")-1] == 'd')
646 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
647
648 /* If fflag is on, assume caller/authenticator has logged root login. */
649 if (rootlogin && fflag == 0)
650 if (hostname)
651 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
652 username, tty, hostname);
653 else
654 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
655
656 #ifdef KERBEROS
657 if (!quietlog && notickets == 1)
658 (void)printf("Warning: no Kerberos tickets issued.\n");
659 #endif
660
661 if (!quietlog) {
662 motd();
663 (void)snprintf(tbuf,
664 sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
665 if (stat(tbuf, &st) == 0 && st.st_size != 0)
666 (void)printf("You have %smail.\n",
667 (st.st_mtime > st.st_atime) ? "new " : "");
668 }
669
670 (void)signal(SIGALRM, SIG_DFL);
671 (void)signal(SIGQUIT, SIG_DFL);
672 (void)signal(SIGINT, SIG_DFL);
673 (void)signal(SIGTSTP, SIG_IGN);
674
675 if (setlogin(pwd->pw_name) < 0)
676 syslog(LOG_ERR, "setlogin() failure: %m");
677
678 /* Discard permissions last so can't get killed and drop core. */
679 if (rootlogin)
680 (void) setuid(0);
681 else
682 (void) setuid(pwd->pw_uid);
683
684 /* Re-enable crash reporter */
685 do {
686 kern_return_t kr;
687 mach_port_t bp, ep, mts;
688 thread_state_flavor_t flavor = 0;
689
690 #if defined(__ppc__)
691 flavor = PPC_THREAD_STATE64;
692 #elif defined(__i386__)
693 flavor = x86_THREAD_STATE;
694 #endif
695
696 mts = mach_task_self();
697
698 kr = task_get_bootstrap_port(mts, &bp);
699 if (kr != KERN_SUCCESS) {
700 syslog(LOG_ERR, "task_get_bootstrap_port() failure: %s (%d)",
701 bootstrap_strerror(kr), kr);
702 break;
703 }
704
705 const char* bs = "com.apple.ReportCrash";
706 kr = bootstrap_look_up(bp, (char*)bs, &ep);
707 if (kr != KERN_SUCCESS) {
708 syslog(LOG_ERR, "bootstrap_look_up(%s) failure: %s (%d)",
709 bs, bootstrap_strerror(kr), kr);
710 break;
711 }
712
713 kr = task_set_exception_ports(mts, EXC_MASK_CRASH, ep, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, flavor);
714 if (kr != KERN_SUCCESS) {
715 syslog(LOG_ERR, "task_set_exception_ports() failure: %d", kr);
716 break;
717 }
718 } while (0);
719
720 if (fflag && *argv) {
721 char *arg0 = *argv;
722 if (lflag)
723 (void)strlcpy(tbuf, (p = strrchr(*argv, '/')) ?
724 p + 1 : *argv, sizeof(tbuf));
725 else {
726 tbuf[0] = '-';
727 (void)strlcpy(tbuf + 1, (p = strrchr(*argv, '/')) ?
728 p + 1 : *argv, sizeof(tbuf) - 1);
729 }
730 *argv = tbuf;
731 execvp(arg0, argv);
732 err(1, "%s", arg0);
733 } else {
734 if (lflag)
735 (void)strlcpy(tbuf, (p = strrchr(pwd->pw_shell, '/')) ?
736 p + 1 : pwd->pw_shell, sizeof(tbuf));
737 else {
738 tbuf[0] = '-';
739 (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
740 p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
741 }
742 execlp(pwd->pw_shell, tbuf, (char *)NULL);
743 err(1, "%s", pwd->pw_shell);
744 }
745 }
746
747 #ifdef KERBEROS
748 #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
749 #else
750 #define NBUFSIZ (MAXLOGNAME + 1)
751 #endif
752
753 /*
754 * The following tokens are included in the audit record for successful login attempts
755 * header
756 * subject
757 * return
758 */
759 void au_success()
760 {
761 #ifdef USE_BSM
762 token_t *tok;
763 int aufd;
764 au_mask_t aumask;
765 auditinfo_t auinfo;
766 uid_t uid = pwd->pw_uid;
767 gid_t gid = pwd->pw_gid;
768 pid_t pid = getpid();
769 long au_cond;
770
771 /* If we are not auditing, don't cut an audit record; just return */
772 if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
773 fprintf(stderr, "login: Could not determine audit condition\n");
774 exit(1);
775 }
776 if (au_cond == AUC_NOAUDIT)
777 return;
778
779 /* Compute and Set the user's preselection mask */
780 if(au_user_mask(pwd->pw_name, &aumask) == -1) {
781 fprintf(stderr, "login: Could not set audit mask\n");
782 exit(1);
783 }
784
785 /* Set the audit info for the user */
786 auinfo.ai_auid = uid;
787 auinfo.ai_asid = pid;
788 bcopy(&tid, &auinfo.ai_termid, sizeof(auinfo.ai_termid));
789 bcopy(&aumask, &auinfo.ai_mask, sizeof(auinfo.ai_mask));
790 if(setaudit(&auinfo) != 0) {
791 fprintf(stderr, "login: setaudit failed: %s\n", strerror(errno));
792 exit(1);
793 }
794
795 if((aufd = au_open()) == -1) {
796 fprintf(stderr, "login: Audit Error: au_open() failed\n");
797 exit(1);
798 }
799
800 /* The subject that is created (euid, egid of the current process) */
801 if((tok = au_to_subject32(uid, geteuid(), getegid(),
802 uid, gid, pid, pid, &tid)) == NULL) {
803 fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
804 exit(1);
805 }
806 au_write(aufd, tok);
807
808 if((tok = au_to_return32(0, 0)) == NULL) {
809 fprintf(stderr, "login: Audit Error: au_to_return32() failed\n");
810 exit(1);
811 }
812 au_write(aufd, tok);
813
814 if(au_close(aufd, 1, AUE_login) == -1) {
815 fprintf(stderr, "login: Audit Record was not committed.\n");
816 exit(1);
817 }
818 #endif
819 }
820
821 /*
822 * The following tokens are included in the audit record for successful login attempts
823 * header
824 * subject
825 * text
826 * return
827 */
828 void au_fail(char *errmsg, int na)
829 {
830 #ifdef USE_BSM
831 token_t *tok;
832 int aufd;
833 long au_cond;
834 uid_t uid;
835 gid_t gid;
836 pid_t pid = getpid();
837
838 /* If we are not auditing, don't cut an audit record; just return */
839 if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
840 fprintf(stderr, "login: Could not determine audit condition\n");
841 exit(1);
842 }
843 if (au_cond == AUC_NOAUDIT)
844 return;
845
846 if((aufd = au_open()) == -1) {
847 fprintf(stderr, "login: Audit Error: au_open() failed\n");
848 exit(1);
849 }
850
851 if(na) {
852 /* Non attributable event */
853 /* Assuming that login is not called within a users' session => auid,asid == -1 */
854 if((tok = au_to_subject32(-1, geteuid(), getegid(), -1, -1,
855 pid, -1, &tid)) == NULL) {
856
857 fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
858 exit(1);
859 }
860 }
861 else {
862 /* we know the subject -- so use its value instead */
863 uid = pwd->pw_uid;
864 gid = pwd->pw_gid;
865 if((tok = au_to_subject32(uid, geteuid(), getegid(),
866 uid, gid, pid, pid, &tid)) == NULL) {
867 fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
868 exit(1);
869 }
870 }
871 au_write(aufd, tok);
872
873 /* Include the error message */
874 if((tok = au_to_text(errmsg)) == NULL) {
875 fprintf(stderr, "login: Audit Error: au_to_text() failed\n");
876 exit(1);
877 }
878 au_write(aufd, tok);
879
880 if((tok = au_to_return32(1, errno)) == NULL) {
881 fprintf(stderr, "login: Audit Error: au_to_return32() failed\n");
882 exit(1);
883 }
884 au_write(aufd, tok);
885
886 if(au_close(aufd, 1, AUE_login) == -1) {
887 fprintf(stderr, "login: Audit Error: au_close() was not committed\n");
888 exit(1);
889 }
890 #endif
891 }
892
893 void
894 getloginname()
895 {
896 int ch;
897 char *p;
898 static char nbuf[NBUFSIZ];
899
900 for (;;) {
901 (void)printf("login: ");
902 for (p = nbuf; (ch = getchar()) != '\n'; ) {
903 if (ch == EOF) {
904 badlogin(username);
905 exit(0);
906 }
907 if (p < nbuf + (NBUFSIZ - 1))
908 *p++ = ch;
909 }
910 if (p > nbuf) {
911 if (nbuf[0] == '-')
912 (void)fprintf(stderr,
913 "login names may not start with '-'.\n");
914 else {
915 *p = '\0';
916 username = nbuf;
917 break;
918 }
919 }
920 }
921 }
922
923 int
924 rootterm(ttyn)
925 char *ttyn;
926 {
927 struct ttyent *t;
928
929 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
930 }
931
932 jmp_buf motdinterrupt;
933
934 void
935 motd()
936 {
937 int fd, nchars;
938 sig_t oldint;
939 char tbuf[8192];
940
941 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
942 return;
943 oldint = signal(SIGINT, sigint);
944 if (setjmp(motdinterrupt) == 0)
945 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
946 (void)write(fileno(stdout), tbuf, nchars);
947 (void)signal(SIGINT, oldint);
948 (void)close(fd);
949 }
950
951 /* ARGSUSED */
952 void
953 sigint(signo)
954 int signo;
955 {
956
957 longjmp(motdinterrupt, 1);
958 }
959
960 /* ARGSUSED */
961 void
962 timedout(signo)
963 int signo;
964 {
965
966 (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
967 exit(0);
968 }
969
970 void
971 checknologin()
972 {
973 int fd, nchars;
974 char tbuf[8192];
975
976 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
977 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
978 (void)write(fileno(stdout), tbuf, nchars);
979 au_fail("No login", 0);
980 sleepexit(0);
981 }
982 }
983
984 void
985 dolastlog(quiet)
986 int quiet;
987 {
988 #ifdef USE_PAM
989 if (quiet)
990 return;
991 if (*lastlog.ll_line) {
992 (void)printf("Last login: %.*s ",
993 24-5, (char *)ctime(&lastlog.ll_tv.tv_sec));
994 if (*lastlog.ll_host != '\0')
995 (void)printf("from %.*s\n",
996 (int)sizeof(lastlog.ll_host),
997 lastlog.ll_host);
998 else
999 (void)printf("on %.*s\n",
1000 (int)sizeof(lastlog.ll_line),
1001 lastlog.ll_line);
1002 }
1003 #else /* !USE_PAM */
1004 struct lastlog ll;
1005 int fd;
1006
1007 /* HACK HACK HACK: This is because HFS doesn't support sparse files
1008 * and seeking into the file too far is too slow. The "solution"
1009 * is to just bail if the seek time for a large uid would be too
1010 * slow.
1011 */
1012 if(pwd->pw_uid > 100000) {
1013 syslog(LOG_NOTICE, "User login %s (%d) not logged in lastlog. UID too large.", pwd->pw_name, pwd->pw_uid);
1014 return;
1015 }
1016
1017 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1018 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
1019 if (!quiet) {
1020 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1021 ll.ll_time != 0) {
1022 (void)printf("Last login: %.*s ",
1023 24-5, (char *)ctime(&ll.ll_time));
1024 if (*ll.ll_host != '\0')
1025 (void)printf("from %.*s\n",
1026 (int)sizeof(ll.ll_host),
1027 ll.ll_host);
1028 else
1029 (void)printf("on %.*s\n",
1030 (int)sizeof(ll.ll_line),
1031 ll.ll_line);
1032 }
1033 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
1034 }
1035 memset((void *)&ll, 0, sizeof(ll));
1036 (void)time(&ll.ll_time);
1037 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1038 if (hostname)
1039 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1040 (void)write(fd, (char *)&ll, sizeof(ll));
1041 (void)close(fd);
1042 }
1043 #endif /* USE_PAM */
1044 }
1045
1046 void
1047 badlogin(name)
1048 char *name;
1049 {
1050
1051 if (failures == 0)
1052 return;
1053 if (hostname) {
1054 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1055 failures, failures > 1 ? "S" : "", hostname);
1056 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1057 "%d LOGIN FAILURE%s FROM %s, %s",
1058 failures, failures > 1 ? "S" : "", hostname, name);
1059 } else {
1060 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1061 failures, failures > 1 ? "S" : "", tty);
1062 syslog(LOG_AUTHPRIV|LOG_NOTICE,
1063 "%d LOGIN FAILURE%s ON %s, %s",
1064 failures, failures > 1 ? "S" : "", tty, name);
1065 }
1066 }
1067
1068 #undef UNKNOWN
1069 #define UNKNOWN "su"
1070
1071 char *
1072 stypeof(ttyid)
1073 char *ttyid;
1074 {
1075 struct ttyent *t;
1076
1077 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
1078 }
1079
1080 void
1081 sleepexit(eval)
1082 int eval;
1083 {
1084
1085 (void)sleep(5);
1086 exit(eval);
1087 }
1088
1089 static void
1090 refused(const char *msg, const char *rtype, int lout)
1091 {
1092
1093 if (msg != NULL)
1094 printf("%s.\n", msg);
1095 if (hflag)
1096 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1097 pwd->pw_name, rtype, hostname, tty);
1098 else
1099 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1100 pwd->pw_name, rtype, tty);
1101 if (lout)
1102 bail(SLEEP_EXIT, 1);
1103 }
1104
1105 #ifdef USE_PAM
1106 /*
1107 * Log a PAM error
1108 */
1109 static void
1110 pam_syslog(const char *msg)
1111 {
1112 syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
1113 }
1114
1115 /*
1116 * Shut down PAM
1117 */
1118 static void
1119 pam_cleanup()
1120 {
1121
1122 if (pamh != NULL) {
1123 if (pam_session_established) {
1124 pam_err = pam_close_session(pamh, 0);
1125 if (pam_err != PAM_SUCCESS)
1126 pam_syslog("pam_close_session()");
1127 }
1128 pam_session_established = 0;
1129 if (pam_cred_established) {
1130 pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
1131 if (pam_err != PAM_SUCCESS)
1132 pam_syslog("pam_setcred()");
1133 }
1134 pam_cred_established = 0;
1135 pam_end(pamh, pam_err);
1136 pamh = NULL;
1137 }
1138 }
1139 #endif /* USE_PAM */
1140 /*
1141 * Exit, optionally after sleeping a few seconds
1142 */
1143 void
1144 bail(int sec, int eval)
1145 {
1146 #ifdef USE_PAM
1147 pam_cleanup();
1148 #endif
1149 (void)sleep(sec);
1150 exit(eval);
1151 }