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