Print this page
4337 eliminate /etc/TIMEZONE
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/login/login.c
+++ new/usr/src/cmd/login/login.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 28 /* All Rights Reserved */
29 29
30 30 /*
31 31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 32 * The Regents of the University of California
33 33 * All Rights Reserved
34 34 *
35 35 * University Acknowledgment- Portions of this document are derived from
36 36 * software developed by the University of California, Berkeley, and its
37 37 * contributors.
38 38 */
39 39
40 40 /* Copyright (c) 1987, 1988 Microsoft Corporation */
41 41 /* All Rights Reserved */
42 42
43 43 /*
44 44 * For a complete reference to login(1), see the manual page. However,
45 45 * login has accreted some intentionally undocumented options, which are
46 46 * explained here:
47 47 *
48 48 * -a: This legacy flag appears to be unused.
49 49 *
50 50 * -f <username>: This flag was introduced by PSARC 1995/039 in support
51 51 * of Kerberos. But it's not used by Sun's Kerberos implementation.
52 52 * It is however employed by zlogin(1), since it allows one to tell
53 53 * login: "This user is authenticated." In the case of zlogin that's
54 54 * true because the zone always trusts the global zone.
55 55 *
56 56 * -z <zonename>: This flag is passed to login when zlogin(1) executes a
57 57 * zone login. This tells login(1) to skip it's normal CONSOLE check
58 58 * (i.e. that the root login must be on /dev/console) and tells us the
59 59 * name of the zone from which the login is occurring.
60 60 */
61 61
62 62 #include <sys/types.h>
63 63 #include <sys/param.h>
64 64 #include <unistd.h> /* For logfile locking */
65 65 #include <signal.h>
66 66 #include <stdio.h>
67 67 #include <sys/stat.h>
68 68 #include <string.h>
69 69 #include <deflt.h>
70 70 #include <grp.h>
71 71 #include <fcntl.h>
72 72 #include <lastlog.h>
73 73 #include <termio.h>
74 74 #include <utmpx.h>
75 75 #include <stdlib.h>
76 76 #include <wait.h>
77 77 #include <errno.h>
78 78 #include <ctype.h>
79 79 #include <syslog.h>
80 80 #include <ulimit.h>
81 81 #include <libgen.h>
82 82 #include <pwd.h>
83 83 #include <security/pam_appl.h>
84 84 #include <strings.h>
85 85 #include <libdevinfo.h>
86 86 #include <zone.h>
87 87 #include "login_audit.h"
88 88
89 89 #include <krb5_repository.h>
90 90 /*
91 91 *
92 92 * *** Defines, Macros, and String Constants ***
93 93 *
94 94 *
95 95 */
96 96
97 97 #define ISSUEFILE "/etc/issue" /* file to print before prompt */
98 98 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
99 99
100 100 /*
101 101 * These need to be defined for UTMPX management.
102 102 * If we add in the utility functions later, we
103 103 * can remove them.
104 104 */
105 105 #define __UPDATE_ENTRY 1
106 106 #define __LOGIN 2
107 107
108 108 /*
109 109 * Intervals to sleep after failed login
110 110 */
111 111 #ifndef SLEEPTIME
112 112 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
113 113 #endif
114 114 static int Sleeptime = SLEEPTIME;
115 115
116 116 /*
117 117 * seconds login disabled after allowable number of unsuccessful attempts
118 118 */
119 119 #ifndef DISABLETIME
120 120 #define DISABLETIME 20
121 121 #endif
122 122 static int Disabletime = DISABLETIME;
123 123
124 124 #define MAXTRYS 5
125 125
126 126 static int retry = MAXTRYS;
127 127
128 128 /*
129 129 * Login logging support
130 130 */
131 131 #define LOGINLOG "/var/adm/loginlog" /* login log file */
132 132 #define LNAME_SIZE 20 /* size of logged logname */
133 133 #define TTYN_SIZE 15 /* size of logged tty name */
134 134 #define TIME_SIZE 30 /* size of logged time string */
135 135 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
136 136 #define L_WAITTIME 5 /* waittime for log file to unlock */
137 137 #define LOGTRYS 10 /* depth of 'try' logging */
138 138
139 139 /*
140 140 * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
141 141 * SCPYL is the safer version of SCPYN
142 142 */
143 143 #define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a))
144 144 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
145 145 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
146 146 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
147 147 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
148 148
149 149 /*
150 150 * Other macros
151 151 */
152 152 #define NMAX sizeof (((struct utmpx *)0)->ut_name)
153 153 #define HMAX sizeof (((struct utmpx *)0)->ut_host)
154 154 #define min(a, b) (((a) < (b)) ? (a) : (b))
155 155
156 156 /*
157 157 * Various useful files and string constants
158 158 */
159 159 #define SHELL "/usr/bin/sh"
160 160 #define SHELL2 "/sbin/sh"
161 161 #define SUBLOGIN "<!sublogin>"
162 162 #define LASTLOG "/var/adm/lastlog"
163 163 #define PROG_NAME "login"
164 164 #define HUSHLOGIN ".hushlogin"
165 165
166 166 /*
167 167 * Array and Buffer sizes
168 168 */
169 169 #define PBUFSIZE 8 /* max significant characters in a password */
170 170 #define MAXARGS 63 /* change value below if changing this */
171 171 #define MAXARGSWIDTH 2 /* log10(MAXARGS) */
172 172 #define MAXENV 1024
173 173 #define MAXLINE 2048
174 174
175 175 /*
176 176 * Miscellaneous constants
177 177 */
178 178 #define ROOTUID 0
179 179 #define ERROR 1
180 180 #define OK 0
181 181 #define LOG_ERROR 1
182 182 #define DONT_LOG_ERROR 0
183 183 #define TRUE 1
184 184 #define FALSE 0
185 185
186 186 /*
187 187 * Counters for counting the number of failed login attempts
188 188 */
189 189 static int trys = 0;
190 190 static int count = 1;
191 191
192 192 /*
193 193 * error value for login_exit() audit output (0 == no audit record)
194 194 */
195 195 static int audit_error = 0;
196 196
197 197 /*
198 198 * Externs a plenty
199 199 */
200 200 extern int getsecretkey();
201 201
202 202 /*
203 203 * The current user name
204 204 */
205 205 static char user_name[NMAX];
206 206 static char minusnam[16] = "-";
207 207
208 208 /*
209 209 * login_pid, used to find utmpx entry to update.
210 210 */
211 211 static pid_t login_pid;
212 212
213 213 /*
214 214 * locale environments to be passed to shells.
215 215 */
216 216 static char *localeenv[] = {
217 217 "LANG",
218 218 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
219 219 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
220 220 static int locale_envmatch(char *, char *);
221 221
222 222 /*
223 223 * Environment variable support
224 224 */
225 225 static char shell[256] = { "SHELL=" };
226 226 static char home[MAXPATHLEN] = { "HOME=" };
227 227 static char term[64] = { "TERM=" };
228 228 static char logname[30] = { "LOGNAME=" };
229 229 static char timez[100] = { "TZ=" };
230 230 static char hertz[10] = { "HZ=" };
231 231 static char path[MAXPATHLEN] = { "PATH=" };
232 232 static char *newenv[10+MAXARGS] =
233 233 {home, path, logname, hertz, term, 0, 0};
234 234 static char **envinit = newenv;
235 235 static int basicenv;
236 236 static char *zero = (char *)0;
237 237 static char **envp;
238 238 #ifndef NO_MAIL
239 239 static char mail[30] = { "MAIL=/var/mail/" };
240 240 #endif
241 241 extern char **environ;
242 242 static char inputline[MAXLINE];
243 243
244 244 #define MAX_ID_LEN 256
245 245 #define MAX_REPOSITORY_LEN 256
246 246 #define MAX_PAMSERVICE_LEN 256
247 247
248 248 static char identity[MAX_ID_LEN];
249 249 static char repository[MAX_REPOSITORY_LEN];
250 250 static char progname[MAX_PAMSERVICE_LEN];
251 251
252 252
253 253 /*
254 254 * Strings used to prompt the user.
255 255 */
256 256 static char loginmsg[] = "login: ";
257 257 static char passwdmsg[] = "Password:";
258 258 static char incorrectmsg[] = "Login incorrect\n";
259 259
260 260 /*
261 261 * Password file support
262 262 */
263 263 static struct passwd *pwd = NULL;
264 264 static char remote_host[HMAX];
265 265 static char zone_name[ZONENAME_MAX];
266 266
267 267 /*
268 268 * Illegal passwd entries.
269 269 */
270 270 static struct passwd nouser = { "", "no:password", (uid_t)-1 };
271 271
272 272 /*
273 273 * Log file support
274 274 */
275 275 static char *log_entry[LOGTRYS];
276 276 static int writelog = 0;
277 277 static int lastlogok = 0;
278 278 static struct lastlog ll;
279 279 static int dosyslog = 0;
280 280 static int flogin = MAXTRYS; /* flag for SYSLOG_FAILED_LOGINS */
281 281
282 282 /*
283 283 * Default file toggles
284 284 */
285 285 static char *Pndefault = "/etc/default/login";
286 286 static char *Altshell = NULL;
287 287 static char *Console = NULL;
288 288 static int Passreqflag = 0;
289 289
290 290 #define DEFUMASK 022
291 291 static mode_t Umask = DEFUMASK;
292 292 static char *Def_tz = NULL;
293 293 static char *tmp_tz = NULL;
294 294 static char *Def_hertz = NULL;
295 295 #define SET_FSIZ 2 /* ulimit() command arg */
296 296 static long Def_ulimit = 0;
297 297 #define MAX_TIMEOUT (15 * 60)
298 298 #define DEF_TIMEOUT (5 * 60)
299 299 static unsigned Def_timeout = DEF_TIMEOUT;
300 300 static char *Def_path = NULL;
301 301 static char *Def_supath = NULL;
302 302 #define DEF_PATH "/usr/bin:" /* same as PATH */
303 303 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
304 304
305 305 /*
306 306 * Defaults for updating expired passwords
307 307 */
308 308 #define DEF_ATTEMPTS 3
309 309
310 310 /*
311 311 * ttyprompt will point to the environment variable TTYPROMPT.
312 312 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
313 313 */
314 314 static char *ttyprompt = NULL;
315 315 static char *ttyn = NULL;
316 316
317 317 /*
318 318 * Pass inherited environment. Used by telnetd in support of the telnet
319 319 * ENVIRON option.
320 320 */
321 321 static boolean_t pflag = B_FALSE;
322 322 static boolean_t uflag = B_FALSE;
323 323 static boolean_t Rflag = B_FALSE;
324 324 static boolean_t sflag = B_FALSE;
325 325 static boolean_t Uflag = B_FALSE;
326 326 static boolean_t tflag = B_FALSE;
327 327 static boolean_t hflag = B_FALSE;
328 328 static boolean_t rflag = B_FALSE;
329 329 static boolean_t zflag = B_FALSE;
330 330
331 331 /*
332 332 * Remote login support
333 333 */
334 334 static char rusername[NMAX+1], lusername[NMAX+1];
335 335 static char terminal[MAXPATHLEN];
336 336
337 337 /*
338 338 * Pre-authentication flag support
339 339 */
340 340 static int fflag;
341 341
342 342 static char ** getargs(char *);
343 343
344 344 static int login_conv(int, struct pam_message **,
345 345 struct pam_response **, void *);
346 346
347 347 static struct pam_conv pam_conv = {login_conv, NULL};
348 348 static pam_handle_t *pamh; /* Authentication handle */
349 349
350 350 /*
351 351 * Function declarations
352 352 */
353 353 static void turn_on_logging(void);
354 354 static void defaults(void);
355 355 static void usage(void);
356 356 static void process_rlogin(void);
357 357 static void login_authenticate();
358 358 static void setup_credentials(void);
359 359 static void adjust_nice(void);
360 360 static void update_utmpx_entry(int);
361 361 static void establish_user_environment(char **);
362 362 static void print_banner(void);
363 363 static void display_last_login_time(void);
364 364 static void exec_the_shell(void);
365 365 static int process_chroot_logins(void);
366 366 static void chdir_to_dir_user(void);
367 367 static void check_log(void);
368 368 static void validate_account(void);
369 369 static void doremoteterm(char *);
370 370 static int get_options(int, char **);
371 371 static void getstr(char *, int, char *);
372 372 static int legalenvvar(char *);
373 373 static void check_for_console(void);
374 374 static void check_for_dueling_unix(char *);
375 375 static void get_user_name(void);
376 376 static uint_t get_audit_id(void);
377 377 static void login_exit(int)__NORETURN;
378 378 static int logins_disabled(char *);
379 379 static void log_bad_attempts(void);
380 380 static int is_number(char *);
381 381
382 382 /*
383 383 * *** main ***
384 384 *
385 385 * The primary flow of control is directed in this routine.
386 386 * Control moves in line from top to bottom calling subfunctions
387 387 * which perform the bulk of the work. Many of these calls exit
388 388 * when a fatal error is encountered and do not return to main.
389 389 *
390 390 *
391 391 */
392 392
393 393 int
394 394 main(int argc, char *argv[], char **renvp)
395 395 {
396 396 int sublogin;
397 397 int pam_rc;
398 398
399 399 login_pid = getpid();
400 400
401 401 /*
402 402 * Set up Defaults and flags
403 403 */
404 404 defaults();
405 405 SCPYL(progname, PROG_NAME);
406 406
407 407 /*
408 408 * Set up default umask
409 409 */
410 410 if (Umask > ((mode_t)0777))
411 411 Umask = DEFUMASK;
412 412 (void) umask(Umask);
413 413
414 414 /*
415 415 * Set up default timeouts and delays
416 416 */
417 417 if (Def_timeout > MAX_TIMEOUT)
418 418 Def_timeout = MAX_TIMEOUT;
419 419 if (Sleeptime < 0 || Sleeptime > 5)
420 420 Sleeptime = SLEEPTIME;
421 421
422 422 (void) alarm(Def_timeout);
423 423
424 424 /*
425 425 * Ignore SIGQUIT and SIGINT and set nice to 0
426 426 */
427 427 (void) signal(SIGQUIT, SIG_IGN);
428 428 (void) signal(SIGINT, SIG_IGN);
429 429 (void) nice(0);
430 430
431 431 /*
432 432 * Set flag to disable the pid check if you find that you are
433 433 * a subsystem login.
434 434 */
435 435 sublogin = 0;
436 436 if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
437 437 sublogin = 1;
438 438
439 439 /*
440 440 * Parse Arguments
441 441 */
442 442 if (get_options(argc, argv) == -1) {
443 443 usage();
444 444 audit_error = ADT_FAIL_VALUE_BAD_CMD;
445 445 login_exit(1);
446 446 }
447 447
448 448 /*
449 449 * if devicename is not passed as argument, call ttyname(0)
450 450 */
451 451 if (ttyn == NULL) {
452 452 ttyn = ttyname(0);
453 453 if (ttyn == NULL)
454 454 ttyn = "/dev/???";
455 455 }
456 456
457 457 /*
458 458 * Call pam_start to initiate a PAM authentication operation
459 459 */
460 460
461 461 if ((pam_rc = pam_start(progname, user_name, &pam_conv, &pamh))
462 462 != PAM_SUCCESS) {
463 463 audit_error = ADT_FAIL_PAM + pam_rc;
464 464 login_exit(1);
465 465 }
466 466 if ((pam_rc = pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
467 467 audit_error = ADT_FAIL_PAM + pam_rc;
468 468 login_exit(1);
469 469 }
470 470 if ((pam_rc = pam_set_item(pamh, PAM_RHOST, remote_host)) !=
471 471 PAM_SUCCESS) {
472 472 audit_error = ADT_FAIL_PAM + pam_rc;
473 473 login_exit(1);
474 474 }
475 475
476 476 /*
477 477 * We currently only support special handling of the KRB5 PAM repository
478 478 */
479 479 if ((Rflag && strlen(repository)) &&
480 480 strcmp(repository, KRB5_REPOSITORY_NAME) == 0 &&
481 481 (uflag && strlen(identity))) {
482 482 krb5_repository_data_t krb5_data;
483 483 pam_repository_t pam_rep_data;
484 484
485 485 krb5_data.principal = identity;
486 486 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
487 487
488 488 pam_rep_data.type = repository;
489 489 pam_rep_data.scope = (void *)&krb5_data;
490 490 pam_rep_data.scope_len = sizeof (krb5_data);
491 491
492 492 (void) pam_set_item(pamh, PAM_REPOSITORY,
493 493 (void *)&pam_rep_data);
494 494 }
495 495
496 496 /*
497 497 * Open the log file which contains a record of successful and failed
498 498 * login attempts
499 499 */
500 500 turn_on_logging();
501 501
502 502 /*
503 503 * say "hi" to syslogd ..
504 504 */
505 505 openlog("login", 0, LOG_AUTH);
506 506
507 507 /*
508 508 * Do special processing for -r (rlogin) flag
509 509 */
510 510 if (rflag)
511 511 process_rlogin();
512 512
513 513 /*
514 514 * validate user
515 515 */
516 516 /* we are already authenticated. fill in what we must, then continue */
517 517 if (fflag) {
518 518 if ((pwd = getpwnam(user_name)) == NULL) {
519 519 audit_error = ADT_FAIL_VALUE_USERNAME;
520 520
521 521 log_bad_attempts();
522 522 (void) printf("Login failed: unknown user '%s'.\n",
523 523 user_name);
524 524 login_exit(1);
525 525 }
526 526 } else {
527 527 /*
528 528 * Perform the primary login authentication activity.
529 529 */
530 530 login_authenticate();
531 531 }
532 532
533 533 /* change root login, then we exec another login and try again */
534 534 if (process_chroot_logins() != OK)
535 535 login_exit(1);
536 536
537 537 /*
538 538 * If root login and not on system console then call exit(2)
539 539 */
540 540 check_for_console();
541 541
542 542 /*
543 543 * Check to see if a shutdown is in progress, if it is and
544 544 * we are not root then throw the user off the system
545 545 */
546 546 if (logins_disabled(user_name) == TRUE) {
547 547 audit_error = ADT_FAIL_VALUE_LOGIN_DISABLED;
548 548 login_exit(1);
549 549 }
550 550
551 551 if (pwd->pw_uid == 0) {
552 552 if (Def_supath != NULL)
553 553 Def_path = Def_supath;
554 554 else
555 555 Def_path = DEF_SUPATH;
556 556 }
557 557
558 558 /*
559 559 * Check account expiration and passwd aging
560 560 */
561 561 validate_account();
562 562
563 563 /*
564 564 * We only get here if we've been authenticated.
565 565 */
566 566
567 567 /*
568 568 * Now we set up the environment for the new user, which includes
569 569 * the users ulimit, nice value, ownership of this tty, uid, gid,
570 570 * and environment variables.
571 571 */
572 572 if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
573 573 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
574 574
575 575 /* di_devperm_login() sends detailed errors to syslog */
576 576 if (di_devperm_login((const char *)ttyn, pwd->pw_uid, pwd->pw_gid,
577 577 NULL) == -1) {
578 578 (void) fprintf(stderr, "error processing /etc/logindevperm,"
579 579 " see syslog for more details\n");
580 580 }
581 581
582 582 adjust_nice(); /* passwd file can specify nice value */
583 583
584 584 setup_credentials(); /* Set user credentials - exits on failure */
585 585
586 586 /*
587 587 * NOTE: telnetd and rlogind rely upon this updating of utmpx
588 588 * to indicate that the authentication completed successfully,
589 589 * pam_open_session was called and therefore they are required to
590 590 * call pam_close_session.
591 591 */
592 592 update_utmpx_entry(sublogin);
593 593
594 594 /* set the real (and effective) UID */
595 595 if (setuid(pwd->pw_uid) == -1) {
596 596 login_exit(1);
597 597 }
598 598
599 599 /*
600 600 * Set up the basic environment for the exec. This includes
601 601 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
602 602 */
603 603 chdir_to_dir_user();
604 604
605 605 establish_user_environment(renvp);
606 606
607 607 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
608 608 pamh = NULL;
609 609
610 610 if (pwd->pw_uid == 0) {
611 611 if (dosyslog) {
612 612 if (remote_host[0]) {
613 613 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
614 614 ttyn, HMAX, remote_host);
615 615 } else
616 616 syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
617 617 }
618 618 }
619 619 closelog();
620 620
621 621 (void) signal(SIGQUIT, SIG_DFL);
622 622 (void) signal(SIGINT, SIG_DFL);
623 623
624 624 /*
625 625 * Display some useful information to the new user like the banner
626 626 * and last login time if not a quiet login.
627 627 */
628 628
629 629 if (access(HUSHLOGIN, F_OK) != 0) {
630 630 print_banner();
631 631 display_last_login_time();
632 632 }
633 633
634 634 /*
635 635 * Set SIGXCPU and SIGXFSZ to default disposition.
636 636 * Shells inherit signal disposition from parent.
637 637 * And the shells should have default dispositions
638 638 * for the two below signals.
639 639 */
640 640 (void) signal(SIGXCPU, SIG_DFL);
641 641 (void) signal(SIGXFSZ, SIG_DFL);
642 642
643 643 /*
644 644 * Now fire off the shell of choice
645 645 */
646 646 exec_the_shell();
647 647
648 648 /*
649 649 * All done
650 650 */
651 651 login_exit(1);
652 652 return (0);
653 653 }
654 654
655 655
656 656 /*
657 657 * *** Utility functions ***
658 658 */
659 659
660 660
661 661
662 662 /*
663 663 * donothing & catch - Signal catching functions
664 664 */
665 665
666 666 /*ARGSUSED*/
667 667 static void
668 668 donothing(int sig)
669 669 {
670 670 if (pamh)
671 671 (void) pam_end(pamh, PAM_ABORT);
672 672 }
673 673
674 674 #ifdef notdef
675 675 static int intrupt;
676 676
677 677 /*ARGSUSED*/
678 678 static void
679 679 catch(int sig)
680 680 {
681 681 ++intrupt;
682 682 }
683 683 #endif
684 684
685 685 /*
686 686 * *** Bad login logging support ***
687 687 */
688 688
689 689 /*
690 690 * badlogin() - log to the log file 'trys'
691 691 * unsuccessful attempts
692 692 */
693 693
694 694 static void
695 695 badlogin(void)
696 696 {
697 697 int retval, count1, fildes;
698 698
699 699 /*
700 700 * Tries to open the log file. If succeed, lock it and write
701 701 * in the failed attempts
702 702 */
703 703 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
704 704
705 705 (void) sigset(SIGALRM, donothing);
706 706 (void) alarm(L_WAITTIME);
707 707 retval = lockf(fildes, F_LOCK, 0L);
708 708 (void) alarm(0);
709 709 (void) sigset(SIGALRM, SIG_DFL);
710 710 if (retval == 0) {
711 711 for (count1 = 0; count1 < trys; count1++)
712 712 (void) write(fildes, log_entry[count1],
713 713 (unsigned)strlen(log_entry[count1]));
714 714 (void) lockf(fildes, F_ULOCK, 0L);
715 715 }
716 716 (void) close(fildes);
717 717 }
718 718 }
719 719
720 720
721 721 /*
722 722 * log_bad_attempts - log each bad login attempt - called from
723 723 * login_authenticate. Exits when the maximum attempt
724 724 * count is exceeded.
725 725 */
726 726
727 727 static void
728 728 log_bad_attempts(void)
729 729 {
730 730 time_t timenow;
731 731
732 732 if (trys >= LOGTRYS)
733 733 return;
734 734 if (writelog) {
735 735 (void) time(&timenow);
736 736 (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
737 737 (void) strncat(log_entry[trys], ":", (size_t)1);
738 738 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
739 739 (void) strncat(log_entry[trys], ":", (size_t)1);
740 740 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE);
741 741 trys++;
742 742 }
743 743 if (count > flogin) {
744 744 if ((pwd = getpwnam(user_name)) != NULL) {
745 745 if (remote_host[0]) {
746 746 syslog(LOG_NOTICE,
747 747 "Login failure on %s from %.*s, "
748 748 "%.*s", ttyn, HMAX, remote_host,
749 749 NMAX, user_name);
750 750 } else {
751 751 syslog(LOG_NOTICE,
752 752 "Login failure on %s, %.*s",
753 753 ttyn, NMAX, user_name);
754 754 }
755 755 } else {
756 756 if (remote_host[0]) {
757 757 syslog(LOG_NOTICE,
758 758 "Login failure on %s from %.*s",
759 759 ttyn, HMAX, remote_host);
760 760 } else {
761 761 syslog(LOG_NOTICE,
762 762 "Login failure on %s", ttyn);
763 763 }
764 764 }
765 765 }
766 766 }
767 767
768 768
769 769 /*
770 770 * turn_on_logging - if the logfile exist, turn on attempt logging and
771 771 * initialize the string storage area
772 772 */
773 773
774 774 static void
775 775 turn_on_logging(void)
776 776 {
777 777 struct stat dbuf;
778 778 int i;
779 779
780 780 if (stat(LOGINLOG, &dbuf) == 0) {
781 781 writelog = 1;
782 782 for (i = 0; i < LOGTRYS; i++) {
783 783 if (!(log_entry[i] = malloc((size_t)ENT_SIZE))) {
784 784 writelog = 0;
785 785 break;
786 786 }
787 787 *log_entry[i] = '\0';
788 788 }
789 789 }
790 790 }
791 791
792 792
793 793 /*
794 794 * login_conv():
795 795 * This is the conv (conversation) function called from
796 796 * a PAM authentication module to print error messages
797 797 * or garner information from the user.
798 798 */
799 799 /*ARGSUSED*/
800 800 static int
801 801 login_conv(int num_msg, struct pam_message **msg,
802 802 struct pam_response **response, void *appdata_ptr)
803 803 {
804 804 struct pam_message *m;
805 805 struct pam_response *r;
806 806 char *temp;
807 807 int k, i;
808 808
809 809 if (num_msg <= 0)
810 810 return (PAM_CONV_ERR);
811 811
812 812 *response = calloc(num_msg, sizeof (struct pam_response));
813 813 if (*response == NULL)
814 814 return (PAM_BUF_ERR);
815 815
816 816 k = num_msg;
817 817 m = *msg;
818 818 r = *response;
819 819 while (k--) {
820 820
821 821 switch (m->msg_style) {
822 822
823 823 case PAM_PROMPT_ECHO_OFF:
824 824 errno = 0;
825 825 temp = getpassphrase(m->msg);
826 826 if (temp != NULL) {
827 827 if (errno == EINTR)
828 828 return (PAM_CONV_ERR);
829 829
830 830 r->resp = strdup(temp);
831 831 if (r->resp == NULL) {
832 832 /* free responses */
833 833 r = *response;
834 834 for (i = 0; i < num_msg; i++, r++) {
835 835 if (r->resp)
836 836 free(r->resp);
837 837 }
838 838 free(*response);
839 839 *response = NULL;
840 840 return (PAM_BUF_ERR);
841 841 }
842 842 }
843 843
844 844 m++;
845 845 r++;
846 846 break;
847 847
848 848 case PAM_PROMPT_ECHO_ON:
849 849 if (m->msg != NULL)
850 850 (void) fputs(m->msg, stdout);
851 851 r->resp = calloc(1, PAM_MAX_RESP_SIZE);
852 852 if (r->resp == NULL) {
853 853 /* free responses */
854 854 r = *response;
855 855 for (i = 0; i < num_msg; i++, r++) {
856 856 if (r->resp)
857 857 free(r->resp);
858 858 }
859 859 free(*response);
860 860 *response = NULL;
861 861 return (PAM_BUF_ERR);
862 862 }
863 863 /*
864 864 * The response might include environment variables
865 865 * information. We should store that information in
866 866 * envp if there is any; otherwise, envp is set to
867 867 * NULL.
868 868 */
869 869 bzero((void *)inputline, MAXLINE);
870 870
871 871 envp = getargs(inputline);
872 872
873 873 /* If we read in any input, process it. */
874 874 if (inputline[0] != '\0') {
875 875 int len;
876 876
877 877 if (envp != (char **)NULL)
878 878 /*
879 879 * If getargs() did not return NULL,
880 880 * *envp is the first string in
881 881 * inputline. envp++ makes envp point
882 882 * to environment variables information
883 883 * or be NULL.
884 884 */
885 885 envp++;
886 886
887 887 (void) strncpy(r->resp, inputline,
888 888 PAM_MAX_RESP_SIZE-1);
889 889 r->resp[PAM_MAX_RESP_SIZE-1] = NULL;
890 890 len = strlen(r->resp);
891 891 if (r->resp[len-1] == '\n')
892 892 r->resp[len-1] = '\0';
893 893 } else {
894 894 login_exit(1);
895 895 }
896 896 m++;
897 897 r++;
898 898 break;
899 899
900 900 case PAM_ERROR_MSG:
901 901 if (m->msg != NULL) {
902 902 (void) fputs(m->msg, stderr);
903 903 (void) fputs("\n", stderr);
904 904 }
905 905 m++;
906 906 r++;
907 907 break;
908 908 case PAM_TEXT_INFO:
909 909 if (m->msg != NULL) {
910 910 (void) fputs(m->msg, stdout);
911 911 (void) fputs("\n", stdout);
912 912 }
913 913 m++;
914 914 r++;
915 915 break;
916 916
917 917 default:
918 918 break;
919 919 }
920 920 }
921 921 return (PAM_SUCCESS);
922 922 }
923 923
924 924 /*
925 925 * verify_passwd - Authenticates the user.
926 926 * Returns: PAM_SUCCESS if authentication successful,
927 927 * PAM error code if authentication fails.
928 928 */
929 929
930 930 static int
931 931 verify_passwd(void)
932 932 {
933 933 int error;
934 934 char *user;
935 935 int flag = (Passreqflag ? PAM_DISALLOW_NULL_AUTHTOK : 0);
936 936
937 937 /*
938 938 * PAM authenticates the user for us.
939 939 */
940 940 error = pam_authenticate(pamh, flag);
941 941
942 942 /* get the user_name from the pam handle */
943 943 (void) pam_get_item(pamh, PAM_USER, (void**)&user);
944 944
945 945 if (user == NULL || *user == '\0')
946 946 return (PAM_SYSTEM_ERR);
947 947
948 948 SCPYL(user_name, user);
949 949 check_for_dueling_unix(user_name);
950 950
951 951 if (((pwd = getpwnam(user_name)) == NULL) &&
952 952 (error != PAM_USER_UNKNOWN)) {
953 953 return (PAM_SYSTEM_ERR);
954 954 }
955 955
956 956 return (error);
957 957 }
958 958
959 959 /*
960 960 * quotec - Called by getargs
961 961 */
962 962
963 963 static int
964 964 quotec(void)
965 965 {
966 966 int c, i, num;
967 967
968 968 switch (c = getc(stdin)) {
969 969
970 970 case 'n':
971 971 c = '\n';
972 972 break;
973 973
974 974 case 'r':
975 975 c = '\r';
976 976 break;
977 977
978 978 case 'v':
979 979 c = '\013';
980 980 break;
981 981
982 982 case 'b':
983 983 c = '\b';
984 984 break;
985 985
986 986 case 't':
987 987 c = '\t';
988 988 break;
989 989
990 990 case 'f':
991 991 c = '\f';
992 992 break;
993 993
994 994 case '0':
995 995 case '1':
996 996 case '2':
997 997 case '3':
998 998 case '4':
999 999 case '5':
1000 1000 case '6':
1001 1001 case '7':
1002 1002 for (num = 0, i = 0; i < 3; i++) {
1003 1003 num = num * 8 + (c - '0');
1004 1004 if ((c = getc(stdin)) < '0' || c > '7')
1005 1005 break;
1006 1006 }
1007 1007 (void) ungetc(c, stdin);
1008 1008 c = num & 0377;
1009 1009 break;
1010 1010
1011 1011 default:
1012 1012 break;
1013 1013 }
1014 1014 return (c);
1015 1015 }
1016 1016
1017 1017 /*
1018 1018 * getargs - returns an input line. Exits if EOF encountered.
1019 1019 */
1020 1020 #define WHITESPACE 0
1021 1021 #define ARGUMENT 1
1022 1022
1023 1023 static char **
1024 1024 getargs(char *input_line)
1025 1025 {
1026 1026 static char envbuf[MAXLINE];
1027 1027 static char *args[MAXARGS];
1028 1028 char *ptr, **answer;
1029 1029 int c;
1030 1030 int state;
1031 1031 char *p = input_line;
1032 1032
1033 1033 ptr = envbuf;
1034 1034 answer = &args[0];
1035 1035 state = WHITESPACE;
1036 1036
1037 1037 while ((c = getc(stdin)) != EOF && answer < &args[MAXARGS-1]) {
1038 1038
1039 1039 *(input_line++) = c;
1040 1040
1041 1041 switch (c) {
1042 1042
1043 1043 case '\n':
1044 1044 if (ptr == &envbuf[0])
1045 1045 return ((char **)NULL);
1046 1046 *input_line = *ptr = '\0';
1047 1047 *answer = NULL;
1048 1048 return (&args[0]);
1049 1049
1050 1050 case ' ':
1051 1051 case '\t':
1052 1052 if (state == ARGUMENT) {
1053 1053 *ptr++ = '\0';
1054 1054 state = WHITESPACE;
1055 1055 }
1056 1056 break;
1057 1057
1058 1058 case '\\':
1059 1059 c = quotec();
1060 1060
1061 1061 default:
1062 1062 if (state == WHITESPACE) {
1063 1063 *answer++ = ptr;
1064 1064 state = ARGUMENT;
1065 1065 }
1066 1066 *ptr++ = c;
1067 1067 }
1068 1068
1069 1069 /* Attempt at overflow, exit */
1070 1070 if (input_line - p >= MAXLINE - 1 ||
1071 1071 ptr >= &envbuf[sizeof (envbuf) - 1]) {
1072 1072 audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW;
1073 1073 login_exit(1);
1074 1074 }
1075 1075 }
1076 1076
1077 1077 /*
1078 1078 * If we left loop because an EOF was received or we've overflown
1079 1079 * args[], exit immediately.
1080 1080 */
1081 1081 login_exit(0);
1082 1082 /* NOTREACHED */
1083 1083 }
1084 1084
1085 1085 /*
1086 1086 * get_user_name - Gets the user name either passed in, or from the
1087 1087 * login: prompt.
1088 1088 */
1089 1089
1090 1090 static void
1091 1091 get_user_name(void)
1092 1092 {
1093 1093 FILE *fp;
1094 1094
1095 1095 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1096 1096 char *ptr, buffer[BUFSIZ];
1097 1097 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
1098 1098 (void) fputs(ptr, stdout);
1099 1099 }
1100 1100 (void) fclose(fp);
1101 1101 }
1102 1102
1103 1103 /*
1104 1104 * if TTYPROMPT is not set, use our own prompt
1105 1105 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1106 1106 * and let the module do the prompting.
1107 1107 */
1108 1108
1109 1109 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1110 1110 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1111 1111 else
1112 1112 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1113 1113
1114 1114 envp = &zero; /* XXX: is this right? */
1115 1115 }
1116 1116
1117 1117
1118 1118 /*
1119 1119 * Check_for_dueling_unix - Check to see if the another login is talking
1120 1120 * to the line we've got open as a login port
1121 1121 * Exits if we're talking to another unix system
1122 1122 */
1123 1123
1124 1124 static void
1125 1125 check_for_dueling_unix(char *inputline)
1126 1126 {
1127 1127 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1128 1128 EQN(incorrectmsg, inputline)) {
1129 1129 (void) printf("Looking at a login line.\n");
1130 1130 login_exit(8);
1131 1131 }
1132 1132 }
1133 1133
1134 1134 /*
1135 1135 * logins_disabled - if the file /etc/nologin exists and the user is not
1136 1136 * root then do not permit them to login
1137 1137 */
1138 1138 static int
1139 1139 logins_disabled(char *user_name)
1140 1140 {
1141 1141 FILE *nlfd;
1142 1142 int c;
1143 1143 if (!EQN("root", user_name) &&
1144 1144 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1145 1145 while ((c = getc(nlfd)) != EOF)
1146 1146 (void) putchar(c);
1147 1147 (void) fflush(stdout);
1148 1148 (void) sleep(5);
1149 1149 return (TRUE);
1150 1150 }
1151 1151 return (FALSE);
1152 1152 }
1153 1153
1154 1154 #define DEFAULT_CONSOLE "/dev/console"
1155 1155
1156 1156 /*
1157 1157 * check_for_console - Checks if we're getting a root login on the
1158 1158 * console, or a login from the global zone. Exits if not.
1159 1159 *
1160 1160 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins
1161 1161 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global
1162 1162 * zones, but checking them does no harm.
1163 1163 */
1164 1164 static void
1165 1165 check_for_console(void)
1166 1166 {
1167 1167 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL };
1168 1168 int i;
1169 1169
1170 1170 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE ||
1171 1171 Console == NULL)
1172 1172 return;
1173 1173
1174 1174 if (strcmp(Console, DEFAULT_CONSOLE) == 0) {
1175 1175 for (i = 0; consoles[i] != NULL; i ++) {
1176 1176 if (strncmp(ttyn, consoles[i],
1177 1177 strlen(consoles[i])) == 0)
1178 1178 return;
1179 1179 }
1180 1180 } else {
1181 1181 if (strcmp(ttyn, Console) == 0)
1182 1182 return;
1183 1183 }
1184 1184
1185 1185 (void) printf("Not on system console\n");
1186 1186
1187 1187 audit_error = ADT_FAIL_VALUE_CONSOLE;
1188 1188 login_exit(10);
1189 1189
1190 1190 }
1191 1191
1192 1192 /*
1193 1193 * List of environment variables or environment variable prefixes that should
1194 1194 * not be propagated across logins, such as when the login -p option is used.
1195 1195 */
1196 1196 static const char *const illegal[] = {
1197 1197 "SHELL=",
1198 1198 "HOME=",
1199 1199 "LOGNAME=",
1200 1200 #ifndef NO_MAIL
1201 1201 "MAIL=",
1202 1202 #endif
1203 1203 "CDPATH=",
1204 1204 "IFS=",
1205 1205 "PATH=",
1206 1206 "LD_",
1207 1207 "SMF_",
1208 1208 NULL
1209 1209 };
1210 1210
1211 1211 /*
1212 1212 * legalenvvar - Is it legal to insert this environmental variable?
1213 1213 */
1214 1214
1215 1215 static int
1216 1216 legalenvvar(char *s)
1217 1217 {
1218 1218 const char *const *p;
1219 1219
1220 1220 for (p = &illegal[0]; *p; p++) {
1221 1221 if (strncmp(s, *p, strlen(*p)) == 0)
1222 1222 return (0);
1223 1223 }
1224 1224
1225 1225 return (1);
1226 1226 }
1227 1227
1228 1228
1229 1229 /*
1230 1230 * getstr - Get a string from standard input
1231 1231 * Calls exit if read(2) fails.
1232 1232 */
1233 1233
1234 1234 static void
1235 1235 getstr(char *buf, int cnt, char *err)
1236 1236 {
1237 1237 char c;
1238 1238
1239 1239 do {
1240 1240 if (read(0, &c, 1) != 1)
1241 1241 login_exit(1);
1242 1242 *buf++ = c;
1243 1243 } while (--cnt > 1 && c != 0);
1244 1244
1245 1245 *buf = 0;
1246 1246 err = err; /* For lint */
1247 1247 }
1248 1248
1249 1249
1250 1250 /*
1251 1251 * defaults - read defaults
1252 1252 */
1253 1253
1254 1254 static void
1255 1255 defaults(void)
1256 1256 {
1257 1257 int flags;
1258 1258 char *ptr;
1259 1259
1260 1260 if (defopen(Pndefault) == 0) {
1261 1261 /*
1262 1262 * ignore case
1263 1263 */
1264 1264 flags = defcntl(DC_GETFLAGS, 0);
1265 1265 TURNOFF(flags, DC_CASE);
1266 1266 (void) defcntl(DC_SETFLAGS, flags);
1267 1267
1268 1268 if ((Console = defread("CONSOLE=")) != NULL)
1269 1269 Console = strdup(Console);
1270 1270
1271 1271 if ((Altshell = defread("ALTSHELL=")) != NULL)
1272 1272 Altshell = strdup(Altshell);
1273 1273
1274 1274 if ((ptr = defread("PASSREQ=")) != NULL &&
1275 1275 strcasecmp("YES", ptr) == 0)
1276 1276 Passreqflag = 1;
1277 1277
1278 1278 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1279 1279 Def_tz = strdup(Def_tz);
1280 1280
1281 1281 if ((Def_hertz = defread("HZ=")) != NULL)
1282 1282 Def_hertz = strdup(Def_hertz);
1283 1283
1284 1284 if ((Def_path = defread("PATH=")) != NULL)
1285 1285 Def_path = strdup(Def_path);
1286 1286
1287 1287 if ((Def_supath = defread("SUPATH=")) != NULL)
1288 1288 Def_supath = strdup(Def_supath);
1289 1289
1290 1290 if ((ptr = defread("ULIMIT=")) != NULL)
1291 1291 Def_ulimit = atol(ptr);
1292 1292
1293 1293 if ((ptr = defread("TIMEOUT=")) != NULL)
1294 1294 Def_timeout = (unsigned)atoi(ptr);
1295 1295
1296 1296 if ((ptr = defread("UMASK=")) != NULL)
1297 1297 if (sscanf(ptr, "%lo", &Umask) != 1)
1298 1298 Umask = DEFUMASK;
1299 1299
1300 1300 if ((ptr = defread("SLEEPTIME=")) != NULL) {
1301 1301 if (is_number(ptr))
1302 1302 Sleeptime = atoi(ptr);
1303 1303 }
1304 1304
1305 1305 if ((ptr = defread("DISABLETIME=")) != NULL) {
1306 1306 if (is_number(ptr))
1307 1307 Disabletime = atoi(ptr);
1308 1308 }
1309 1309
1310 1310 if ((ptr = defread("SYSLOG=")) != NULL)
1311 1311 dosyslog = strcmp(ptr, "YES") == 0;
1312 1312
1313 1313 if ((ptr = defread("RETRIES=")) != NULL) {
1314 1314 if (is_number(ptr))
1315 1315 retry = atoi(ptr);
1316 1316 }
1317 1317
1318 1318 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) {
1319 1319 if (is_number(ptr))
1320 1320 flogin = atoi(ptr);
1321 1321 else
1322 1322 flogin = retry;
1323 1323 } else
1324 1324 flogin = retry;
1325 1325 (void) defopen((char *)NULL);
1326 1326 }
1327 1327 }
1328 1328
1329 1329
1330 1330 /*
1331 1331 * get_options(argc, argv)
1332 1332 * - parse the cmd line.
1333 1333 * - return 0 if successful, -1 if failed.
1334 1334 * Calls login_exit() on misuse of -r, -h, and -z flags
1335 1335 */
1336 1336
1337 1337 static int
1338 1338 get_options(int argc, char *argv[])
1339 1339 {
1340 1340 int c;
1341 1341 int errflg = 0;
1342 1342 char sflagname[NMAX+1];
1343 1343 const char *flags_message = "Only one of -r, -h and -z allowed\n";
1344 1344
1345 1345 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
1346 1346 switch (c) {
1347 1347 case 'a':
1348 1348 break;
1349 1349
1350 1350 case 'd':
1351 1351 /*
1352 1352 * Must be root to pass in device name
1353 1353 * otherwise we exit() as punishment for trying.
1354 1354 */
1355 1355 if (getuid() != 0 || geteuid() != 0) {
1356 1356 audit_error = ADT_FAIL_VALUE_DEVICE_PERM;
1357 1357 login_exit(1); /* sigh */
1358 1358 /*NOTREACHED*/
1359 1359 }
1360 1360 ttyn = optarg;
1361 1361 break;
1362 1362
1363 1363 case 'h':
1364 1364 if (hflag || rflag || zflag) {
1365 1365 (void) fprintf(stderr, flags_message);
1366 1366 login_exit(1);
1367 1367 }
1368 1368 hflag = B_TRUE;
1369 1369 SCPYL(remote_host, optarg);
1370 1370 if (argv[optind]) {
1371 1371 if (argv[optind][0] != '-') {
1372 1372 SCPYL(terminal, argv[optind]);
1373 1373 optind++;
1374 1374 } else {
1375 1375 /*
1376 1376 * Allow "login -h hostname -" to
1377 1377 * skip setting up an username as "-".
1378 1378 */
1379 1379 if (argv[optind][1] == '\0')
1380 1380 optind++;
1381 1381 }
1382 1382
1383 1383 }
1384 1384 SCPYL(progname, "telnet");
1385 1385 break;
1386 1386
1387 1387 case 'r':
1388 1388 if (hflag || rflag || zflag) {
1389 1389 (void) fprintf(stderr, flags_message);
1390 1390 login_exit(1);
1391 1391 }
1392 1392 rflag = B_TRUE;
1393 1393 SCPYL(remote_host, optarg);
1394 1394 SCPYL(progname, "rlogin");
1395 1395 break;
1396 1396
1397 1397 case 'p':
1398 1398 pflag = B_TRUE;
1399 1399 break;
1400 1400
1401 1401 case 'f':
1402 1402 /*
1403 1403 * Must be root to bypass authentication
1404 1404 * otherwise we exit() as punishment for trying.
1405 1405 */
1406 1406 if (getuid() != 0 || geteuid() != 0) {
1407 1407 audit_error = ADT_FAIL_VALUE_AUTH_BYPASS;
1408 1408
1409 1409 login_exit(1); /* sigh */
1410 1410 /*NOTREACHED*/
1411 1411 }
1412 1412 /* save fflag user name for future use */
1413 1413 SCPYL(user_name, optarg);
1414 1414 fflag = B_TRUE;
1415 1415 break;
1416 1416 case 'u':
1417 1417 if (!strlen(optarg)) {
1418 1418 (void) fprintf(stderr,
1419 1419 "Empty string supplied with -u\n");
1420 1420 login_exit(1);
1421 1421 }
1422 1422 SCPYL(identity, optarg);
1423 1423 uflag = B_TRUE;
1424 1424 break;
1425 1425 case 's':
1426 1426 if (!strlen(optarg)) {
1427 1427 (void) fprintf(stderr,
1428 1428 "Empty string supplied with -s\n");
1429 1429 login_exit(1);
1430 1430 }
1431 1431 SCPYL(sflagname, optarg);
1432 1432 sflag = B_TRUE;
1433 1433 break;
1434 1434 case 'R':
1435 1435 if (!strlen(optarg)) {
1436 1436 (void) fprintf(stderr,
1437 1437 "Empty string supplied with -R\n");
1438 1438 login_exit(1);
1439 1439 }
1440 1440 SCPYL(repository, optarg);
1441 1441 Rflag = B_TRUE;
1442 1442 break;
1443 1443 case 't':
1444 1444 if (!strlen(optarg)) {
1445 1445 (void) fprintf(stderr,
1446 1446 "Empty string supplied with -t\n");
1447 1447 login_exit(1);
1448 1448 }
1449 1449 SCPYL(terminal, optarg);
1450 1450 tflag = B_TRUE;
1451 1451 break;
1452 1452 case 'U':
1453 1453 /*
1454 1454 * Kerberized rlogind may fork us with
1455 1455 * -U "" if the rlogin client used the "-a"
1456 1456 * option to send a NULL username. This is done
1457 1457 * to force login to prompt for a user/password.
1458 1458 * However, if Kerberos auth was used, we dont need
1459 1459 * to prompt, so we will accept the option and
1460 1460 * handle the situation later.
1461 1461 */
1462 1462 SCPYL(rusername, optarg);
1463 1463 Uflag = B_TRUE;
1464 1464 break;
1465 1465 case 'z':
1466 1466 if (hflag || rflag || zflag) {
1467 1467 (void) fprintf(stderr, flags_message);
1468 1468 login_exit(1);
1469 1469 }
1470 1470 (void) snprintf(zone_name, sizeof (zone_name),
1471 1471 "zone:%s", optarg);
1472 1472 SCPYL(progname, "zlogin");
1473 1473 zflag = B_TRUE;
1474 1474 break;
1475 1475 default:
1476 1476 errflg++;
1477 1477 break;
1478 1478 } /* end switch */
1479 1479 } /* end while */
1480 1480
1481 1481 /*
1482 1482 * If the 's svcname' flag was used, override the progname
1483 1483 * value that is to be used in the pam_start call.
1484 1484 */
1485 1485 if (sflag)
1486 1486 SCPYL(progname, sflagname);
1487 1487
1488 1488 /*
1489 1489 * get the prompt set by ttymon
1490 1490 */
1491 1491 ttyprompt = getenv("TTYPROMPT");
1492 1492
1493 1493 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1494 1494 /*
1495 1495 * if ttyprompt is set, there should be data on
1496 1496 * the stream already.
1497 1497 */
1498 1498 if ((envp = getargs(inputline)) != (char **)NULL) {
1499 1499 /*
1500 1500 * don't get name if name passed as argument.
1501 1501 */
1502 1502 SCPYL(user_name, *envp++);
1503 1503 }
1504 1504 } else if (optind < argc) {
1505 1505 SCPYL(user_name, argv[optind]);
1506 1506 (void) SCPYL(inputline, user_name);
1507 1507 (void) strlcat(inputline, " \n", sizeof (inputline));
1508 1508 envp = &argv[optind+1];
1509 1509
1510 1510 if (!fflag)
1511 1511 SCPYL(lusername, user_name);
1512 1512 }
1513 1513
1514 1514 if (errflg)
1515 1515 return (-1);
1516 1516 return (0);
1517 1517 }
1518 1518
1519 1519 /*
1520 1520 * usage - Print usage message
1521 1521 *
1522 1522 */
1523 1523 static void
1524 1524 usage(void)
1525 1525 {
1526 1526 (void) fprintf(stderr,
1527 1527 "usage:\n"
1528 1528 " login [-p] [-d device] [-R repository] [-s service]\n"
1529 1529 "\t[-t terminal] [-u identity] [-U ruser]\n"
1530 1530 "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n");
1531 1531
1532 1532 }
1533 1533
1534 1534 /*
1535 1535 * doremoteterm - Sets the appropriate ioctls for a remote terminal
1536 1536 */
1537 1537 static char *speeds[] = {
1538 1538 "0", "50", "75", "110", "134", "150", "200", "300",
1539 1539 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
1540 1540 "57600", "76800", "115200", "153600", "230400", "307200", "460800",
1541 1541 "921600"
1542 1542 };
1543 1543
1544 1544 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1545 1545
1546 1546
1547 1547 static void
1548 1548 doremoteterm(char *term)
1549 1549 {
1550 1550 struct termios tp;
1551 1551 char *cp = strchr(term, '/'), **cpp;
1552 1552 char *speed;
1553 1553
1554 1554 (void) ioctl(0, TCGETS, &tp);
1555 1555
1556 1556 if (cp) {
1557 1557 *cp++ = '\0';
1558 1558 speed = cp;
1559 1559 cp = strchr(speed, '/');
1560 1560
1561 1561 if (cp)
1562 1562 *cp++ = '\0';
1563 1563
1564 1564 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
1565 1565 if (strcmp(*cpp, speed) == 0) {
1566 1566 (void) cfsetospeed(&tp, cpp-speeds);
1567 1567 break;
1568 1568 }
1569 1569 }
1570 1570
1571 1571 tp.c_lflag |= ECHO|ICANON;
1572 1572 tp.c_iflag |= IGNPAR|ICRNL;
1573 1573
1574 1574 (void) ioctl(0, TCSETS, &tp);
1575 1575
1576 1576 }
1577 1577
1578 1578 /*
1579 1579 * Process_rlogin - Does the work that rlogin and telnet
1580 1580 * need done
1581 1581 */
1582 1582 static void
1583 1583 process_rlogin(void)
1584 1584 {
1585 1585 /*
1586 1586 * If a Kerberized rlogin was initiated, then these fields
1587 1587 * must be read by rlogin daemon itself and passed down via
1588 1588 * cmd line args.
1589 1589 */
1590 1590 if (!Uflag && !strlen(rusername))
1591 1591 getstr(rusername, sizeof (rusername), "remuser");
1592 1592 if (!strlen(lusername))
1593 1593 getstr(lusername, sizeof (lusername), "locuser");
1594 1594 if (!tflag && !strlen(terminal))
1595 1595 getstr(terminal, sizeof (terminal), "Terminal type");
1596 1596
1597 1597 if (strlen(terminal))
1598 1598 doremoteterm(terminal);
1599 1599
1600 1600 /* fflag has precedence over stuff passed by rlogind */
1601 1601 if (fflag || getuid()) {
1602 1602 pwd = &nouser;
1603 1603 return;
1604 1604 } else {
1605 1605 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1606 1606 login_exit(1);
1607 1607
1608 1608 pwd = getpwnam(lusername);
1609 1609 if (pwd == NULL) {
1610 1610 pwd = &nouser;
1611 1611 return;
1612 1612 }
1613 1613 }
1614 1614
1615 1615 /*
1616 1616 * Update PAM on the user name
1617 1617 */
1618 1618 if (strlen(lusername) &&
1619 1619 pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1620 1620 login_exit(1);
1621 1621
1622 1622 if (strlen(rusername) &&
1623 1623 pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1624 1624 login_exit(1);
1625 1625
1626 1626 SCPYL(user_name, lusername);
1627 1627 envp = &zero;
1628 1628 lusername[0] = '\0';
1629 1629 }
1630 1630
1631 1631 /*
1632 1632 * *** Account validation routines ***
1633 1633 *
1634 1634 */
1635 1635
1636 1636 /*
1637 1637 * validate_account - This is the PAM version of validate.
1638 1638 */
1639 1639
1640 1640 static void
1641 1641 validate_account(void)
1642 1642 {
1643 1643 int error;
1644 1644 int flag;
1645 1645 int tries; /* new password retries */
1646 1646
1647 1647 (void) alarm(0); /* give user time to come up with password */
1648 1648
1649 1649 check_log();
1650 1650
1651 1651 if (Passreqflag)
1652 1652 flag = PAM_DISALLOW_NULL_AUTHTOK;
1653 1653 else
1654 1654 flag = 0;
1655 1655
1656 1656 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1657 1657 if (error == PAM_NEW_AUTHTOK_REQD) {
1658 1658 tries = 1;
1659 1659 error = PAM_AUTHTOK_ERR;
1660 1660 while (error == PAM_AUTHTOK_ERR &&
1661 1661 tries <= DEF_ATTEMPTS) {
1662 1662 if (tries > 1)
1663 1663 (void) printf("Try again\n\n");
1664 1664
1665 1665 (void) printf("Choose a new password.\n");
1666 1666
1667 1667 error = pam_chauthtok(pamh,
1668 1668 PAM_CHANGE_EXPIRED_AUTHTOK);
1669 1669 if (error == PAM_TRY_AGAIN) {
1670 1670 (void) sleep(1);
1671 1671 error = pam_chauthtok(pamh,
1672 1672 PAM_CHANGE_EXPIRED_AUTHTOK);
1673 1673 }
1674 1674 tries++;
1675 1675 }
1676 1676
1677 1677 if (error != PAM_SUCCESS) {
1678 1678 if (dosyslog)
1679 1679 syslog(LOG_CRIT,
1680 1680 "change password failure: %s",
1681 1681 pam_strerror(pamh, error));
1682 1682 audit_error = ADT_FAIL_PAM + error;
1683 1683 login_exit(1);
1684 1684 } else {
1685 1685 audit_success(ADT_passwd, pwd, zone_name);
1686 1686 }
1687 1687 } else {
1688 1688 (void) printf(incorrectmsg);
1689 1689
1690 1690 if (dosyslog)
1691 1691 syslog(LOG_CRIT,
1692 1692 "login account failure: %s",
1693 1693 pam_strerror(pamh, error));
1694 1694 audit_error = ADT_FAIL_PAM + error;
1695 1695 login_exit(1);
1696 1696 }
1697 1697 }
1698 1698 }
1699 1699
1700 1700 /*
1701 1701 * Check_log - This is really a hack because PAM checks the log, but login
1702 1702 * wants to know if the log is okay and PAM doesn't have
1703 1703 * a module independent way of handing this info back.
1704 1704 */
1705 1705
1706 1706 static void
1707 1707 check_log(void)
1708 1708 {
1709 1709 int fdl;
1710 1710 long long offset;
1711 1711
1712 1712 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1713 1713
1714 1714 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) {
1715 1715 if (llseek(fdl, offset, SEEK_SET) == offset &&
1716 1716 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) &&
1717 1717 ll.ll_time != 0)
1718 1718 lastlogok = 1;
1719 1719 (void) close(fdl);
1720 1720 }
1721 1721 }
1722 1722
1723 1723 /*
1724 1724 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1725 1725 * place us in the user's home directory just in
1726 1726 * case it was protected and the first chdir failed.
1727 1727 * No chdir errors should happen at this point because
1728 1728 * all failures should have happened on the first
1729 1729 * time around.
1730 1730 */
1731 1731
1732 1732 static void
1733 1733 chdir_to_dir_user(void)
1734 1734 {
1735 1735 if (chdir(pwd->pw_dir) < 0) {
1736 1736 if (chdir("/") < 0) {
1737 1737 (void) printf("No directory!\n");
1738 1738 /*
1739 1739 * This probably won't work since we can't get to /.
1740 1740 */
1741 1741 if (dosyslog) {
1742 1742 if (remote_host[0]) {
1743 1743 syslog(LOG_CRIT,
1744 1744 "LOGIN FAILURES ON %s FROM %.*s ",
1745 1745 " %.*s", ttyn, HMAX,
1746 1746 remote_host, NMAX, pwd->pw_name);
1747 1747 } else {
1748 1748 syslog(LOG_CRIT,
1749 1749 "LOGIN FAILURES ON %s, %.*s",
1750 1750 ttyn, NMAX, pwd->pw_name);
1751 1751 }
1752 1752 }
1753 1753 closelog();
1754 1754 (void) sleep(Disabletime);
1755 1755 exit(1);
1756 1756 } else {
1757 1757 (void) printf("No directory! Logging in with home=/\n");
1758 1758 pwd->pw_dir = "/";
1759 1759 }
1760 1760 }
1761 1761 }
1762 1762
1763 1763
1764 1764 /*
1765 1765 * login_authenticate - Performs the main authentication work
1766 1766 * 1. Prints the login prompt
1767 1767 * 2. Requests and verifys the password
1768 1768 * 3. Checks the port password
1769 1769 */
1770 1770
1771 1771 static void
1772 1772 login_authenticate(void)
1773 1773 {
1774 1774 char *user;
1775 1775 int err;
1776 1776 int login_successful = 0;
1777 1777
1778 1778 do {
1779 1779 /* if scheme broken, then nothing to do but quit */
1780 1780 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS)
1781 1781 exit(1);
1782 1782
1783 1783 /*
1784 1784 * only get name from utility if it is not already
1785 1785 * supplied by pam_start or a pam_set_item.
1786 1786 */
1787 1787 if (!user || !user[0]) {
1788 1788 /* use call back to get user name */
1789 1789 get_user_name();
1790 1790 }
1791 1791
1792 1792 err = verify_passwd();
1793 1793
1794 1794 /*
1795 1795 * If root login and not on system console then call exit(2)
1796 1796 */
1797 1797 check_for_console();
1798 1798
1799 1799 switch (err) {
1800 1800 case PAM_SUCCESS:
1801 1801 case PAM_NEW_AUTHTOK_REQD:
1802 1802 /*
1803 1803 * Officially, pam_authenticate() shouldn't return this
1804 1804 * but it's probably the right thing to return if
1805 1805 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
1806 1806 * be forced to change password later in this code.
1807 1807 */
1808 1808 count = 0;
1809 1809 login_successful = 1;
1810 1810 break;
1811 1811 case PAM_MAXTRIES:
1812 1812 count = retry;
1813 1813 /*FALLTHROUGH*/
1814 1814 case PAM_AUTH_ERR:
1815 1815 case PAM_AUTHINFO_UNAVAIL:
1816 1816 case PAM_USER_UNKNOWN:
1817 1817 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd,
1818 1818 remote_host, ttyn, zone_name);
1819 1819 log_bad_attempts();
1820 1820 break;
1821 1821 case PAM_ABORT:
1822 1822 log_bad_attempts();
1823 1823 (void) sleep(Disabletime);
1824 1824 (void) printf(incorrectmsg);
1825 1825
1826 1826 audit_error = ADT_FAIL_PAM + err;
1827 1827 login_exit(1);
1828 1828 /*NOTREACHED*/
1829 1829 default: /* Some other PAM error */
1830 1830 audit_error = ADT_FAIL_PAM + err;
1831 1831 login_exit(1);
1832 1832 /*NOTREACHED*/
1833 1833 }
1834 1834
1835 1835 if (login_successful)
1836 1836 break;
1837 1837
1838 1838 /* sleep after bad passwd */
1839 1839 if (count)
1840 1840 (void) sleep(Sleeptime);
1841 1841 (void) printf(incorrectmsg);
1842 1842 /* force name to be null in this case */
1843 1843 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1844 1844 login_exit(1);
1845 1845 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1846 1846 login_exit(1);
1847 1847 } while (count++ < retry);
1848 1848
1849 1849 if (count >= retry) {
1850 1850 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd,
1851 1851 remote_host, ttyn, zone_name);
1852 1852 /*
1853 1853 * If logging is turned on, output the
1854 1854 * string storage area to the log file,
1855 1855 * and sleep for Disabletime
1856 1856 * seconds before exiting.
1857 1857 */
1858 1858 if (writelog)
1859 1859 badlogin();
1860 1860 if (dosyslog) {
1861 1861 if ((pwd = getpwnam(user_name)) != NULL) {
1862 1862 if (remote_host[0]) {
1863 1863 syslog(LOG_CRIT,
1864 1864 "REPEATED LOGIN FAILURES ON %s "
1865 1865 "FROM %.*s, %.*s",
1866 1866 ttyn, HMAX, remote_host, NMAX,
1867 1867 user_name);
1868 1868 } else {
1869 1869 syslog(LOG_CRIT,
1870 1870 "REPEATED LOGIN FAILURES ON "
1871 1871 "%s, %.*s",
1872 1872 ttyn, NMAX, user_name);
1873 1873 }
1874 1874 } else {
1875 1875 if (remote_host[0]) {
1876 1876 syslog(LOG_CRIT,
1877 1877 "REPEATED LOGIN FAILURES ON %s "
1878 1878 "FROM %.*s",
1879 1879 ttyn, HMAX, remote_host);
1880 1880 } else {
1881 1881 syslog(LOG_CRIT,
1882 1882 "REPEATED LOGIN FAILURES ON %s",
1883 1883 ttyn);
1884 1884 }
1885 1885 }
1886 1886 }
1887 1887 (void) sleep(Disabletime);
1888 1888 exit(1);
1889 1889 }
1890 1890
1891 1891 }
1892 1892
1893 1893 /*
1894 1894 * *** Credential Related routines ***
1895 1895 *
1896 1896 */
1897 1897
1898 1898 /*
1899 1899 * setup_credentials - sets the group ID, initializes the groups
1900 1900 * and sets up the secretkey.
1901 1901 * Exits if a failure occurrs.
1902 1902 */
1903 1903
1904 1904
1905 1905 /*
1906 1906 * setup_credentials - PAM does all the work for us on this one.
1907 1907 */
1908 1908
1909 1909 static void
1910 1910 setup_credentials(void)
1911 1911 {
1912 1912 int error = 0;
1913 1913
1914 1914 /* set the real (and effective) GID */
1915 1915 if (setgid(pwd->pw_gid) == -1) {
1916 1916 login_exit(1);
1917 1917 }
1918 1918
1919 1919 /*
1920 1920 * Initialize the supplementary group access list.
1921 1921 */
1922 1922 if ((user_name[0] == '\0') ||
1923 1923 (initgroups(user_name, pwd->pw_gid) == -1)) {
1924 1924 audit_error = ADT_FAIL_VALUE_PROGRAM;
1925 1925 login_exit(1);
1926 1926 }
1927 1927
1928 1928 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED :
1929 1929 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1930 1930 audit_error = ADT_FAIL_PAM + error;
1931 1931 login_exit(error);
1932 1932 }
1933 1933
1934 1934 /*
1935 1935 * Record successful login and fork process that records logout.
1936 1936 * We have to do this after setting credentials because pam_setcred()
1937 1937 * loads key audit info into the cred, but before setuid() so audit
1938 1938 * system calls will work.
1939 1939 */
1940 1940 audit_success(get_audit_id(), pwd, zone_name);
1941 1941 }
1942 1942
1943 1943 static uint_t
1944 1944 get_audit_id(void)
1945 1945 {
1946 1946 if (rflag)
1947 1947 return (ADT_rlogin);
1948 1948 else if (hflag)
1949 1949 return (ADT_telnet);
1950 1950 else if (zflag)
1951 1951 return (ADT_zlogin);
1952 1952
1953 1953 return (ADT_login);
1954 1954 }
1955 1955
1956 1956 /*
1957 1957 *
1958 1958 * *** Routines to get a new user set up and running ***
1959 1959 *
1960 1960 * Things to do when starting up a new user:
1961 1961 * adjust_nice
1962 1962 * update_utmpx_entry
1963 1963 * establish_user_environment
1964 1964 * print_banner
1965 1965 * display_last_login_time
1966 1966 * exec_the_shell
1967 1967 *
1968 1968 */
1969 1969
1970 1970
1971 1971 /*
1972 1972 * adjust_nice - Set the nice (process priority) value if the
1973 1973 * gecos value contains an appropriate value.
1974 1974 */
1975 1975
1976 1976 static void
1977 1977 adjust_nice(void)
1978 1978 {
1979 1979 int pri, mflg, i;
1980 1980
1981 1981 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1982 1982 pri = 0;
1983 1983 mflg = 0;
1984 1984 i = 4;
1985 1985
1986 1986 if (pwd->pw_gecos[i] == '-') {
1987 1987 mflg++;
1988 1988 i++;
1989 1989 }
1990 1990
1991 1991 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1992 1992 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1993 1993
1994 1994 if (mflg)
1995 1995 pri = -pri;
1996 1996
1997 1997 (void) nice(pri);
1998 1998 }
1999 1999 }
2000 2000
2001 2001 /*
2002 2002 * update_utmpx_entry - Searchs for the correct utmpx entry, making an
2003 2003 * entry there if it finds one, otherwise exits.
2004 2004 */
2005 2005
2006 2006 static void
2007 2007 update_utmpx_entry(int sublogin)
2008 2008 {
2009 2009 int err;
2010 2010 char *user;
2011 2011 static char *errmsg = "No utmpx entry. "
2012 2012 "You must exec \"login\" from the lowest level \"shell\".";
2013 2013 int tmplen;
2014 2014 struct utmpx *u = (struct utmpx *)0;
2015 2015 struct utmpx utmpx;
2016 2016 char *ttyntail;
2017 2017
2018 2018 /*
2019 2019 * If we're not a sublogin then
2020 2020 * we'll get an error back if our PID doesn't match the PID of the
2021 2021 * entry we are updating, otherwise if its a sublogin the flags
2022 2022 * field is set to 0, which means we just write a matching entry
2023 2023 * (without checking the pid), or a new entry if an entry doesn't
2024 2024 * exist.
2025 2025 */
2026 2026
2027 2027 if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
2028 2028 audit_error = ADT_FAIL_PAM + err;
2029 2029 login_exit(1);
2030 2030 }
2031 2031
2032 2032 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) !=
2033 2033 PAM_SUCCESS) {
2034 2034 audit_error = ADT_FAIL_PAM + err;
2035 2035 login_exit(1);
2036 2036 }
2037 2037
2038 2038 (void) memset((void *)&utmpx, 0, sizeof (utmpx));
2039 2039 (void) time(&utmpx.ut_tv.tv_sec);
2040 2040 utmpx.ut_pid = getpid();
2041 2041
2042 2042 if (rflag || hflag) {
2043 2043 SCPYN(utmpx.ut_host, remote_host);
2044 2044 tmplen = strlen(remote_host) + 1;
2045 2045 if (tmplen < sizeof (utmpx.ut_host))
2046 2046 utmpx.ut_syslen = tmplen;
2047 2047 else
2048 2048 utmpx.ut_syslen = sizeof (utmpx.ut_host);
2049 2049 } else if (zflag) {
2050 2050 /*
2051 2051 * If this is a login from another zone, put the
2052 2052 * zone:<zonename> string in the utmpx entry.
2053 2053 */
2054 2054 SCPYN(utmpx.ut_host, zone_name);
2055 2055 tmplen = strlen(zone_name) + 1;
2056 2056 if (tmplen < sizeof (utmpx.ut_host))
2057 2057 utmpx.ut_syslen = tmplen;
2058 2058 else
2059 2059 utmpx.ut_syslen = sizeof (utmpx.ut_host);
2060 2060 } else {
2061 2061 utmpx.ut_syslen = 0;
2062 2062 }
2063 2063
2064 2064 SCPYN(utmpx.ut_user, user);
2065 2065
2066 2066 /* skip over "/dev/" */
2067 2067 ttyntail = basename(ttyn);
2068 2068
2069 2069 while ((u = getutxent()) != NULL) {
2070 2070 if ((u->ut_type == INIT_PROCESS ||
2071 2071 u->ut_type == LOGIN_PROCESS ||
2072 2072 u->ut_type == USER_PROCESS) &&
2073 2073 ((sublogin && strncmp(u->ut_line, ttyntail,
2074 2074 sizeof (u->ut_line)) == 0) ||
2075 2075 u->ut_pid == login_pid)) {
2076 2076 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1));
2077 2077 (void) memcpy(utmpx.ut_id, u->ut_id,
2078 2078 sizeof (utmpx.ut_id));
2079 2079 utmpx.ut_exit.e_exit = u->ut_exit.e_exit;
2080 2080 utmpx.ut_type = USER_PROCESS;
2081 2081 (void) pututxline(&utmpx);
2082 2082 break;
2083 2083 }
2084 2084 }
2085 2085 endutxent();
2086 2086
2087 2087 if (u == (struct utmpx *)NULL) {
2088 2088 if (!sublogin) {
2089 2089 /*
2090 2090 * no utmpx entry already setup
2091 2091 * (init or rlogind/telnetd)
2092 2092 */
2093 2093 (void) puts(errmsg);
2094 2094
2095 2095 audit_error = ADT_FAIL_VALUE_PROGRAM;
2096 2096 login_exit(1);
2097 2097 }
2098 2098 } else {
2099 2099 /* Now attempt to write out this entry to the wtmp file if */
2100 2100 /* we were successful in getting it from the utmpx file and */
2101 2101 /* the wtmp file exists. */
2102 2102 updwtmpx(WTMPX_FILE, &utmpx);
2103 2103 }
2104 2104 }
2105 2105
2106 2106
2107 2107
2108 2108 /*
2109 2109 * process_chroot_logins - Chroots to the specified subdirectory and
2110 2110 * re executes login.
2111 2111 */
2112 2112
2113 2113 static int
2114 2114 process_chroot_logins(void)
2115 2115 {
2116 2116 /*
2117 2117 * If the shell field starts with a '*', do a chroot to the home
2118 2118 * directory and perform a new login.
2119 2119 */
2120 2120
2121 2121 if (*pwd->pw_shell == '*') {
2122 2122 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
2123 2123 pamh = NULL; /* really done */
2124 2124 if (chroot(pwd->pw_dir) < 0) {
2125 2125 (void) printf("No Root Directory\n");
2126 2126
2127 2127 audit_failure(get_audit_id(),
2128 2128 ADT_FAIL_VALUE_CHDIR_FAILED,
2129 2129 pwd, remote_host, ttyn, zone_name);
2130 2130
2131 2131 return (ERROR);
2132 2132 }
2133 2133 /*
2134 2134 * Set the environment flag <!sublogin> so that the next login
2135 2135 * knows that it is a sublogin.
2136 2136 */
2137 2137 envinit[0] = SUBLOGIN;
2138 2138 envinit[1] = (char *)NULL;
2139 2139 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
2140 2140 (void) execle("/usr/bin/login", "login", (char *)0,
2141 2141 &envinit[0]);
2142 2142 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
2143 2143 (void) printf("No /usr/bin/login or /etc/login on root\n");
2144 2144
2145 2145 audit_error = ADT_FAIL_VALUE_PROGRAM;
2146 2146
2147 2147 login_exit(1);
2148 2148 }
2149 2149 return (OK);
2150 2150 }
2151 2151
2152 2152 /*
2153 2153 * establish_user_environment - Set up the new users enviornment
2154 2154 */
2155 2155
2156 2156 static void
2157 2157 establish_user_environment(char **renvp)
2158 2158 {
2159 2159 int i, j, k, l_index, length, idx = 0;
2160 2160 char *endptr;
2161 2161 char **lenvp;
2162 2162 char **pam_env;
2163 2163
2164 2164 lenvp = environ;
2165 2165 while (*lenvp++)
2166 2166 ;
2167 2167
2168 2168 /* count the number of PAM environment variables set by modules */
2169 2169 if ((pam_env = pam_getenvlist(pamh)) != 0) {
2170 2170 for (idx = 0; pam_env[idx] != 0; idx++)
2171 2171 ;
2172 2172 }
2173 2173
2174 2174 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx,
2175 2175 sizeof (char *));
2176 2176 if (envinit == NULL) {
2177 2177 (void) printf("Calloc failed - out of swap space.\n");
2178 2178 login_exit(8);
2179 2179 }
2180 2180
2181 2181 /*
2182 2182 * add PAM environment variables first so they
2183 2183 * can be overwritten at login's discretion.
2184 2184 * check for illegal environment variables.
2185 2185 */
2186 2186 idx = 0; basicenv = 0;
2187 2187 if (pam_env != 0) {
2188 2188 while (pam_env[idx] != 0) {
2189 2189 if (legalenvvar(pam_env[idx])) {
2190 2190 envinit[basicenv] = pam_env[idx];
2191 2191 basicenv++;
2192 2192 }
2193 2193 idx++;
2194 2194 }
2195 2195 }
2196 2196 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv));
2197 2197
2198 2198 /* Set up environment */
2199 2199 if (rflag) {
2200 2200 ENVSTRNCAT(term, terminal);
2201 2201 } else if (hflag) {
2202 2202 if (strlen(terminal)) {
2203 2203 ENVSTRNCAT(term, terminal);
2204 2204 }
2205 2205 } else {
↓ open down ↓ |
2205 lines elided |
↑ open up ↑ |
2206 2206 char *tp = getenv("TERM");
2207 2207
2208 2208 if ((tp != NULL) && (*tp != '\0'))
2209 2209 ENVSTRNCAT(term, tp);
2210 2210 }
2211 2211
2212 2212 ENVSTRNCAT(logname, pwd->pw_name);
2213 2213
2214 2214 /*
2215 2215 * There are three places to get timezone info. init.c sets
2216 - * TZ if the file /etc/TIMEZONE contains a value for TZ.
2216 + * TZ if the file /etc/default/init contains a value for TZ.
2217 2217 * login.c looks in the file /etc/default/login for a
2218 2218 * variable called TIMEZONE being set. If TIMEZONE has a
2219 2219 * value, TZ is set to that value; no environment variable
2220 2220 * TIMEZONE is set, only TZ. If neither of these methods
2221 2221 * work to set TZ, then the library routines will default
2222 2222 * to using the file /usr/lib/locale/TZ/localtime.
2223 2223 *
2224 - * There is a priority set up here. If /etc/TIMEZONE has
2224 + * There is a priority set up here. If /etc/default/init has
2225 2225 * a value for TZ, that value remains top priority. If the
2226 2226 * file /etc/default/login has TIMEZONE set, that has second
2227 2227 * highest priority not overriding the value of TZ in
2228 - * /etc/TIMEZONE. The reason for this priority is that the
2229 - * file /etc/TIMEZONE is supposed to be sourced by
2228 + * /etc/default/init. The reason for this priority is that the
2229 + * file /etc/default/init is supposed to be sourced by
2230 2230 * /etc/profile. We are doing the "sourcing" prematurely in
2231 2231 * init.c. Additionally, a login C shell doesn't source the
2232 - * file /etc/profile thus not sourcing /etc/TIMEZONE thus not
2232 + * file /etc/profile thus not sourcing /etc/default/init thus not
2233 2233 * allowing an adminstrator to globally set TZ for all users
2234 2234 */
2235 2235 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
2236 2236 tmp_tz = Def_tz;
2237 2237
2238 2238 if ((Def_tz = getenv("TZ")) != NULL) {
2239 2239 ENVSTRNCAT(timez, Def_tz);
2240 2240 } else if (tmp_tz != NULL) {
2241 2241 Def_tz = tmp_tz;
2242 2242 ENVSTRNCAT(timez, Def_tz);
2243 2243 }
2244 2244
2245 2245 if (Def_hertz == NULL)
2246 2246 (void) sprintf(hertz + strlen(hertz), "%lu", HZ);
2247 2247 else
2248 2248 ENVSTRNCAT(hertz, Def_hertz);
2249 2249
2250 2250 if (Def_path == NULL)
2251 2251 (void) strlcat(path, DEF_PATH, sizeof (path));
2252 2252 else
2253 2253 ENVSTRNCAT(path, Def_path);
2254 2254
2255 2255 ENVSTRNCAT(home, pwd->pw_dir);
2256 2256
2257 2257 /*
2258 2258 * Find the end of the basic environment
2259 2259 */
2260 2260 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
2261 2261 ;
2262 2262
2263 2263 /*
2264 2264 * If TZ has a value, add it.
2265 2265 */
2266 2266 if (strcmp(timez, "TZ=") != 0)
2267 2267 envinit[basicenv++] = timez;
2268 2268
2269 2269 if (*pwd->pw_shell == '\0') {
2270 2270 /*
2271 2271 * If possible, use the primary default shell,
2272 2272 * otherwise, use the secondary one.
2273 2273 */
2274 2274 if (access(SHELL, X_OK) == 0)
2275 2275 pwd->pw_shell = SHELL;
2276 2276 else
2277 2277 pwd->pw_shell = SHELL2;
2278 2278 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
2279 2279 envinit[basicenv++] = shell;
2280 2280 ENVSTRNCAT(shell, pwd->pw_shell);
2281 2281 }
2282 2282
2283 2283 #ifndef NO_MAIL
2284 2284 envinit[basicenv++] = mail;
2285 2285 (void) strlcat(mail, pwd->pw_name, sizeof (mail));
2286 2286 #endif
2287 2287
2288 2288 /*
2289 2289 * Pick up locale environment variables, if any.
2290 2290 */
2291 2291 lenvp = renvp;
2292 2292 while (*lenvp != NULL) {
2293 2293 j = 0;
2294 2294 while (localeenv[j] != 0) {
2295 2295 /*
2296 2296 * locale_envmatch() returns 1 if
2297 2297 * *lenvp is localenev[j] and valid.
2298 2298 */
2299 2299 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2300 2300 envinit[basicenv++] = *lenvp;
2301 2301 break;
2302 2302 }
2303 2303 j++;
2304 2304 }
2305 2305 lenvp++;
2306 2306 }
2307 2307
2308 2308 /*
2309 2309 * If '-p' flag, then try to pass on allowable environment
2310 2310 * variables. Note that by processing this first, what is
2311 2311 * passed on the final "login:" line may over-ride the invocation
2312 2312 * values. XXX is this correct?
2313 2313 */
2314 2314 if (pflag) {
2315 2315 for (lenvp = renvp; *lenvp; lenvp++) {
2316 2316 if (!legalenvvar(*lenvp)) {
2317 2317 continue;
2318 2318 }
2319 2319 /*
2320 2320 * If this isn't 'xxx=yyy', skip it. XXX
2321 2321 */
2322 2322 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2323 2323 continue;
2324 2324 }
2325 2325 length = endptr + 1 - *lenvp;
2326 2326 for (j = 0; j < basicenv; j++) {
2327 2327 if (strncmp(envinit[j], *lenvp, length) == 0) {
2328 2328 /*
2329 2329 * Replace previously established value
2330 2330 */
2331 2331 envinit[j] = *lenvp;
2332 2332 break;
2333 2333 }
2334 2334 }
2335 2335 if (j == basicenv) {
2336 2336 /*
2337 2337 * It's a new definition, so add it at the end.
2338 2338 */
2339 2339 envinit[basicenv++] = *lenvp;
2340 2340 }
2341 2341 }
2342 2342 }
2343 2343
2344 2344 /*
2345 2345 * Add in all the environment variables picked up from the
2346 2346 * argument list to "login" or from the user response to the
2347 2347 * "login" request, if any.
2348 2348 */
2349 2349
2350 2350 if (envp == NULL)
2351 2351 goto switch_env; /* done */
2352 2352
2353 2353 for (j = 0, k = 0, l_index = 0;
2354 2354 *envp != NULL && j < (MAXARGS-1);
2355 2355 j++, envp++) {
2356 2356
2357 2357 /*
2358 2358 * Scan each string provided. If it doesn't have the
2359 2359 * format xxx=yyy, then add the string "Ln=" to the beginning.
2360 2360 */
2361 2361 if ((endptr = strchr(*envp, '=')) == NULL) {
2362 2362 /*
2363 2363 * This much to be malloc'd:
2364 2364 * strlen(*envp) + 1 char for 'L' +
2365 2365 * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
2366 2366 *
2367 2367 * total = strlen(*envp) + MAXARGSWIDTH + 3
2368 2368 */
2369 2369 int total = strlen(*envp) + MAXARGSWIDTH + 3;
2370 2370 envinit[basicenv+k] = malloc(total);
2371 2371 if (envinit[basicenv+k] == NULL) {
2372 2372 (void) printf("%s: malloc failed\n", PROG_NAME);
2373 2373 login_exit(1);
2374 2374 }
2375 2375 (void) snprintf(envinit[basicenv+k], total, "L%d=%s",
2376 2376 l_index, *envp);
2377 2377
2378 2378 k++;
2379 2379 l_index++;
2380 2380 } else {
2381 2381 if (!legalenvvar(*envp)) { /* this env var permited? */
2382 2382 continue;
2383 2383 } else {
2384 2384
2385 2385 /*
2386 2386 * Check to see whether this string replaces
2387 2387 * any previously defined string
2388 2388 */
2389 2389 for (i = 0, length = endptr + 1 - *envp;
2390 2390 i < basicenv + k; i++) {
2391 2391 if (strncmp(*envp, envinit[i], length)
2392 2392 == 0) {
2393 2393 envinit[i] = *envp;
2394 2394 break;
2395 2395 }
2396 2396 }
2397 2397
2398 2398 /*
2399 2399 * If it doesn't, place it at the end of
2400 2400 * environment array.
2401 2401 */
2402 2402 if (i == basicenv+k) {
2403 2403 envinit[basicenv+k] = *envp;
2404 2404 k++;
2405 2405 }
2406 2406 }
2407 2407 }
2408 2408 } /* for (j = 0 ... ) */
2409 2409
2410 2410 switch_env:
2411 2411 /*
2412 2412 * Switch to the new environment.
2413 2413 */
2414 2414 environ = envinit;
2415 2415 }
2416 2416
2417 2417 /*
2418 2418 * print_banner - Print the banner at start up
2419 2419 * Do not turn on DOBANNER ifdef. This is not
2420 2420 * relevant to SunOS.
2421 2421 */
2422 2422
2423 2423 static void
2424 2424 print_banner(void)
2425 2425 {
2426 2426 #ifdef DOBANNER
2427 2427 uname(&un);
2428 2428 #if i386
2429 2429 (void) printf("UNIX System V/386 Release %s\n%s\n"
2430 2430 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
2431 2431 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
2432 2432 un.release, un.nodename);
2433 2433 #elif sun
2434 2434 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
2435 2435 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
2436 2436 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
2437 2437 "All Rights Reserved\n",
2438 2438 un.release, un.machine, un.nodename);
2439 2439 #else
2440 2440 (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
2441 2441 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
2442 2442 un.release, un.machine, un.nodename);
2443 2443 #endif /* i386 */
2444 2444 #endif /* DOBANNER */
2445 2445 }
2446 2446
2447 2447 /*
2448 2448 * display_last_login_time - Advise the user the time and date
2449 2449 * that this login-id was last used.
2450 2450 */
2451 2451
2452 2452 static void
2453 2453 display_last_login_time(void)
2454 2454 {
2455 2455 if (lastlogok) {
2456 2456 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2457 2457
2458 2458 if (*ll.ll_host != '\0')
2459 2459 (void) printf("from %.*s\n", sizeof (ll.ll_host),
2460 2460 ll.ll_host);
2461 2461 else
2462 2462 (void) printf("on %.*s\n", sizeof (ll.ll_line),
2463 2463 ll.ll_line);
2464 2464 }
2465 2465 }
2466 2466
2467 2467 /*
2468 2468 * exec_the_shell - invoke the specified shell or start up program
2469 2469 */
2470 2470
2471 2471 static void
2472 2472 exec_the_shell(void)
2473 2473 {
2474 2474 char *endptr;
2475 2475 int i;
2476 2476
2477 2477 (void) strlcat(minusnam, basename(pwd->pw_shell),
2478 2478 sizeof (minusnam));
2479 2479
2480 2480 /*
2481 2481 * Exec the shell
2482 2482 */
2483 2483 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2484 2484
2485 2485 /*
2486 2486 * pwd->pw_shell was not an executable object file, maybe it
2487 2487 * is a shell proceedure or a command line with arguments.
2488 2488 * If so, turn off the SHELL= environment variable.
2489 2489 */
2490 2490 for (i = 0; envinit[i] != NULL; ++i) {
2491 2491 if ((envinit[i] == shell) &&
2492 2492 ((endptr = strchr(shell, '=')) != NULL))
2493 2493 (*++endptr) = '\0';
2494 2494 }
2495 2495
2496 2496 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2497 2497 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2498 2498 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
2499 2499 }
2500 2500
2501 2501 (void) printf("No shell\n");
2502 2502 }
2503 2503
2504 2504 /*
2505 2505 * login_exit - Call exit() and terminate.
2506 2506 * This function is here for PAM so cleanup can
2507 2507 * be done before the process exits.
2508 2508 */
2509 2509 static void
2510 2510 login_exit(int exit_code)
2511 2511 {
2512 2512 if (pamh)
2513 2513 (void) pam_end(pamh, PAM_ABORT);
2514 2514
2515 2515 if (audit_error)
2516 2516 audit_failure(get_audit_id(), audit_error,
2517 2517 pwd, remote_host, ttyn, zone_name);
2518 2518
2519 2519 exit(exit_code);
2520 2520 /*NOTREACHED*/
2521 2521 }
2522 2522
2523 2523 /*
2524 2524 * Check if lenv and penv matches or not.
2525 2525 */
2526 2526 static int
2527 2527 locale_envmatch(char *lenv, char *penv)
2528 2528 {
2529 2529 while ((*lenv == *penv) && *lenv && *penv != '=') {
2530 2530 lenv++;
2531 2531 penv++;
2532 2532 }
2533 2533
2534 2534 /*
2535 2535 * '/' is eliminated for security reason.
2536 2536 */
2537 2537 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2538 2538 return (1);
2539 2539 return (0);
2540 2540 }
2541 2541
2542 2542 static int
2543 2543 is_number(char *ptr)
2544 2544 {
2545 2545 while (*ptr != '\0') {
2546 2546 if (!isdigit(*ptr))
2547 2547 return (0);
2548 2548 ptr++;
2549 2549 }
2550 2550 return (1);
2551 2551 }
↓ open down ↓ |
309 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX