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