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