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] = '\0';
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 /* FALLTHROUGH */
1061
1062 default:
1063 if (state == WHITESPACE) {
1064 *answer++ = ptr;
1065 state = ARGUMENT;
1066 }
1067 *ptr++ = c;
1068 }
1069
1070 /* Attempt at overflow, exit */
1071 if (input_line - p >= MAXLINE - 1 ||
1072 ptr >= &envbuf[sizeof (envbuf) - 1]) {
1073 audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW;
1074 login_exit(1);
1075 }
1076 }
1077
1078 /*
1079 * If we left loop because an EOF was received or we've overflown
1080 * args[], exit immediately.
1081 */
1082 login_exit(0);
1083 /* NOTREACHED */
1084 }
1085
1086 /*
1087 * get_user_name - Gets the user name either passed in, or from the
1088 * login: prompt.
1089 */
1090
1091 static void
get_user_name(void)1092 get_user_name(void)
1093 {
1094 FILE *fp;
1095
1096 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1097 char *ptr, buffer[BUFSIZ];
1098 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
1099 (void) fputs(ptr, stdout);
1100 }
1101 (void) fclose(fp);
1102 }
1103
1104 /*
1105 * if TTYPROMPT is not set, use our own prompt
1106 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1107 * and let the module do the prompting.
1108 */
1109
1110 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1111 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1112 else
1113 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1114
1115 envp = &zero; /* XXX: is this right? */
1116 }
1117
1118
1119 /*
1120 * Check_for_dueling_unix - Check to see if the another login is talking
1121 * to the line we've got open as a login port
1122 * Exits if we're talking to another unix system
1123 */
1124
1125 static void
check_for_dueling_unix(char * inputline)1126 check_for_dueling_unix(char *inputline)
1127 {
1128 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1129 EQN(incorrectmsg, inputline)) {
1130 (void) printf("Looking at a login line.\n");
1131 login_exit(8);
1132 }
1133 }
1134
1135 /*
1136 * logins_disabled - if the file /etc/nologin exists and the user is not
1137 * root then do not permit them to login
1138 */
1139 static int
logins_disabled(char * user_name)1140 logins_disabled(char *user_name)
1141 {
1142 FILE *nlfd;
1143 int c;
1144 if (!EQN("root", user_name) &&
1145 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1146 while ((c = getc(nlfd)) != EOF)
1147 (void) putchar(c);
1148 (void) fflush(stdout);
1149 (void) sleep(5);
1150 return (TRUE);
1151 }
1152 return (FALSE);
1153 }
1154
1155 #define DEFAULT_CONSOLE "/dev/console"
1156
1157 /*
1158 * check_for_console - Checks if we're getting a root login on the
1159 * console, or a login from the global zone. Exits if not.
1160 *
1161 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins
1162 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global
1163 * zones, but checking them does no harm.
1164 */
1165 static void
check_for_console(void)1166 check_for_console(void)
1167 {
1168 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL };
1169 int i;
1170
1171 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE ||
1172 Console == NULL)
1173 return;
1174
1175 if (strcmp(Console, DEFAULT_CONSOLE) == 0) {
1176 for (i = 0; consoles[i] != NULL; i ++) {
1177 if (strncmp(ttyn, consoles[i],
1178 strlen(consoles[i])) == 0)
1179 return;
1180 }
1181 } else {
1182 if (strcmp(ttyn, Console) == 0)
1183 return;
1184 }
1185
1186 (void) printf("Not on system console\n");
1187
1188 audit_error = ADT_FAIL_VALUE_CONSOLE;
1189 login_exit(10);
1190
1191 }
1192
1193 /*
1194 * List of environment variables or environment variable prefixes that should
1195 * not be propagated across logins, such as when the login -p option is used.
1196 */
1197 static const char *const illegal[] = {
1198 "SHELL=",
1199 "HOME=",
1200 "LOGNAME=",
1201 #ifndef NO_MAIL
1202 "MAIL=",
1203 #endif
1204 "CDPATH=",
1205 "IFS=",
1206 "PATH=",
1207 "LD_",
1208 "SMF_",
1209 NULL
1210 };
1211
1212 /*
1213 * legalenvvar - Is it legal to insert this environmental variable?
1214 */
1215
1216 static int
legalenvvar(char * s)1217 legalenvvar(char *s)
1218 {
1219 const char *const *p;
1220
1221 for (p = &illegal[0]; *p; p++) {
1222 if (strncmp(s, *p, strlen(*p)) == 0)
1223 return (0);
1224 }
1225
1226 return (1);
1227 }
1228
1229
1230 /*
1231 * getstr - Get a string from standard input
1232 * Calls exit if read(2) fails.
1233 */
1234
1235 static void
getstr(char * buf,int cnt,char * err)1236 getstr(char *buf, int cnt, char *err)
1237 {
1238 char c;
1239
1240 do {
1241 if (read(0, &c, 1) != 1)
1242 login_exit(1);
1243 *buf++ = c;
1244 } while (--cnt > 1 && c != 0);
1245
1246 *buf = 0;
1247 err = err; /* For lint */
1248 }
1249
1250
1251 /*
1252 * defaults - read defaults
1253 */
1254
1255 static void
defaults(void)1256 defaults(void)
1257 {
1258 int flags;
1259 char *ptr;
1260
1261 if (defopen(Pndefault) == 0) {
1262 /*
1263 * ignore case
1264 */
1265 flags = defcntl(DC_GETFLAGS, 0);
1266 TURNOFF(flags, DC_CASE);
1267 (void) defcntl(DC_SETFLAGS, flags);
1268
1269 if ((Console = defread("CONSOLE=")) != NULL)
1270 Console = strdup(Console);
1271
1272 if ((Altshell = defread("ALTSHELL=")) != NULL)
1273 Altshell = strdup(Altshell);
1274
1275 if ((ptr = defread("PASSREQ=")) != NULL &&
1276 strcasecmp("YES", ptr) == 0)
1277 Passreqflag = 1;
1278
1279 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1280 Def_tz = strdup(Def_tz);
1281
1282 if ((Def_hertz = defread("HZ=")) != NULL)
1283 Def_hertz = strdup(Def_hertz);
1284
1285 if ((Def_path = defread("PATH=")) != NULL)
1286 Def_path = strdup(Def_path);
1287
1288 if ((Def_supath = defread("SUPATH=")) != NULL)
1289 Def_supath = strdup(Def_supath);
1290
1291 if ((ptr = defread("ULIMIT=")) != NULL)
1292 Def_ulimit = atol(ptr);
1293
1294 if ((ptr = defread("TIMEOUT=")) != NULL)
1295 Def_timeout = (unsigned)atoi(ptr);
1296
1297 if ((ptr = defread("UMASK=")) != NULL)
1298 if (sscanf(ptr, "%lo", &Umask) != 1)
1299 Umask = DEFUMASK;
1300
1301 if ((ptr = defread("SLEEPTIME=")) != NULL) {
1302 if (is_number(ptr))
1303 Sleeptime = atoi(ptr);
1304 }
1305
1306 if ((ptr = defread("DISABLETIME=")) != NULL) {
1307 if (is_number(ptr))
1308 Disabletime = atoi(ptr);
1309 }
1310
1311 if ((ptr = defread("SYSLOG=")) != NULL)
1312 dosyslog = strcmp(ptr, "YES") == 0;
1313
1314 if ((ptr = defread("RETRIES=")) != NULL) {
1315 if (is_number(ptr))
1316 retry = atoi(ptr);
1317 }
1318
1319 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) {
1320 if (is_number(ptr))
1321 flogin = atoi(ptr);
1322 else
1323 flogin = retry;
1324 } else
1325 flogin = retry;
1326 (void) defopen((char *)NULL);
1327 }
1328 }
1329
1330
1331 /*
1332 * get_options(argc, argv)
1333 * - parse the cmd line.
1334 * - return 0 if successful, -1 if failed.
1335 * Calls login_exit() on misuse of -r, -h, and -z flags
1336 */
1337
1338 static int
get_options(int argc,char * argv[])1339 get_options(int argc, char *argv[])
1340 {
1341 int c;
1342 int errflg = 0;
1343 char sflagname[NMAX+1];
1344 const char *flags_message = "Only one of -r, -h and -z allowed\n";
1345
1346 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
1347 switch (c) {
1348 case 'a':
1349 break;
1350
1351 case 'd':
1352 /*
1353 * Must be root to pass in device name
1354 * otherwise we exit() as punishment for trying.
1355 */
1356 if (getuid() != 0 || geteuid() != 0) {
1357 audit_error = ADT_FAIL_VALUE_DEVICE_PERM;
1358 login_exit(1); /* sigh */
1359 /*NOTREACHED*/
1360 }
1361 ttyn = optarg;
1362 break;
1363
1364 case 'h':
1365 if (hflag || rflag || zflag) {
1366 (void) fprintf(stderr, flags_message);
1367 login_exit(1);
1368 }
1369 hflag = B_TRUE;
1370 SCPYL(remote_host, optarg);
1371 if (argv[optind]) {
1372 if (argv[optind][0] != '-') {
1373 SCPYL(terminal, argv[optind]);
1374 optind++;
1375 } else {
1376 /*
1377 * Allow "login -h hostname -" to
1378 * skip setting up an username as "-".
1379 */
1380 if (argv[optind][1] == '\0')
1381 optind++;
1382 }
1383
1384 }
1385 SCPYL(progname, "telnet");
1386 break;
1387
1388 case 'r':
1389 if (hflag || rflag || zflag) {
1390 (void) fprintf(stderr, flags_message);
1391 login_exit(1);
1392 }
1393 rflag = B_TRUE;
1394 SCPYL(remote_host, optarg);
1395 SCPYL(progname, "rlogin");
1396 break;
1397
1398 case 'p':
1399 pflag = B_TRUE;
1400 break;
1401
1402 case 'f':
1403 /*
1404 * Must be root to bypass authentication
1405 * otherwise we exit() as punishment for trying.
1406 */
1407 if (getuid() != 0 || geteuid() != 0) {
1408 audit_error = ADT_FAIL_VALUE_AUTH_BYPASS;
1409
1410 login_exit(1); /* sigh */
1411 /*NOTREACHED*/
1412 }
1413 /* save fflag user name for future use */
1414 SCPYL(user_name, optarg);
1415 fflag = B_TRUE;
1416 break;
1417 case 'u':
1418 if (!strlen(optarg)) {
1419 (void) fprintf(stderr,
1420 "Empty string supplied with -u\n");
1421 login_exit(1);
1422 }
1423 SCPYL(identity, optarg);
1424 uflag = B_TRUE;
1425 break;
1426 case 's':
1427 if (!strlen(optarg)) {
1428 (void) fprintf(stderr,
1429 "Empty string supplied with -s\n");
1430 login_exit(1);
1431 }
1432 SCPYL(sflagname, optarg);
1433 sflag = B_TRUE;
1434 break;
1435 case 'R':
1436 if (!strlen(optarg)) {
1437 (void) fprintf(stderr,
1438 "Empty string supplied with -R\n");
1439 login_exit(1);
1440 }
1441 SCPYL(repository, optarg);
1442 Rflag = B_TRUE;
1443 break;
1444 case 't':
1445 if (!strlen(optarg)) {
1446 (void) fprintf(stderr,
1447 "Empty string supplied with -t\n");
1448 login_exit(1);
1449 }
1450 SCPYL(terminal, optarg);
1451 tflag = B_TRUE;
1452 break;
1453 case 'U':
1454 /*
1455 * Kerberized rlogind may fork us with
1456 * -U "" if the rlogin client used the "-a"
1457 * option to send a NULL username. This is done
1458 * to force login to prompt for a user/password.
1459 * However, if Kerberos auth was used, we dont need
1460 * to prompt, so we will accept the option and
1461 * handle the situation later.
1462 */
1463 SCPYL(rusername, optarg);
1464 Uflag = B_TRUE;
1465 break;
1466 case 'z':
1467 if (hflag || rflag || zflag) {
1468 (void) fprintf(stderr, flags_message);
1469 login_exit(1);
1470 }
1471 (void) snprintf(zone_name, sizeof (zone_name),
1472 "zone:%s", optarg);
1473 SCPYL(progname, "zlogin");
1474 zflag = B_TRUE;
1475 break;
1476 default:
1477 errflg++;
1478 break;
1479 } /* end switch */
1480 } /* end while */
1481
1482 /*
1483 * If the 's svcname' flag was used, override the progname
1484 * value that is to be used in the pam_start call.
1485 */
1486 if (sflag)
1487 SCPYL(progname, sflagname);
1488
1489 /*
1490 * get the prompt set by ttymon
1491 */
1492 ttyprompt = getenv("TTYPROMPT");
1493
1494 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1495 /*
1496 * if ttyprompt is set, there should be data on
1497 * the stream already.
1498 */
1499 if ((envp = getargs(inputline)) != (char **)NULL) {
1500 /*
1501 * don't get name if name passed as argument.
1502 */
1503 SCPYL(user_name, *envp++);
1504 }
1505 } else if (optind < argc) {
1506 SCPYL(user_name, argv[optind]);
1507 (void) SCPYL(inputline, user_name);
1508 (void) strlcat(inputline, " \n", sizeof (inputline));
1509 envp = &argv[optind+1];
1510
1511 if (!fflag)
1512 SCPYL(lusername, user_name);
1513 }
1514
1515 if (errflg)
1516 return (-1);
1517 return (0);
1518 }
1519
1520 /*
1521 * usage - Print usage message
1522 *
1523 */
1524 static void
usage(void)1525 usage(void)
1526 {
1527 (void) fprintf(stderr,
1528 "usage:\n"
1529 " login [-p] [-d device] [-R repository] [-s service]\n"
1530 "\t[-t terminal] [-u identity] [-U ruser]\n"
1531 "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n");
1532
1533 }
1534
1535 /*
1536 * doremoteterm - Sets the appropriate ioctls for a remote terminal
1537 */
1538 static char *speeds[] = {
1539 "0", "50", "75", "110", "134", "150", "200", "300",
1540 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
1541 "57600", "76800", "115200", "153600", "230400", "307200", "460800",
1542 "921600", "1000000", "1152000", "1500000", "2000000", "2500000",
1543 "3000000", "3500000", "4000000"
1544 };
1545
1546 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1547
1548
1549 static void
doremoteterm(char * term)1550 doremoteterm(char *term)
1551 {
1552 struct termios tp;
1553 char *cp = strchr(term, '/'), **cpp;
1554 char *speed;
1555
1556 (void) ioctl(0, TCGETS, &tp);
1557
1558 if (cp) {
1559 *cp++ = '\0';
1560 speed = cp;
1561 cp = strchr(speed, '/');
1562
1563 if (cp)
1564 *cp++ = '\0';
1565
1566 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) {
1567 if (strcmp(*cpp, speed) == 0) {
1568 (void) cfsetospeed(&tp, cpp-speeds);
1569 break;
1570 }
1571 }
1572 }
1573
1574 tp.c_lflag |= ECHO|ICANON;
1575 tp.c_iflag |= IGNPAR|ICRNL;
1576
1577 (void) ioctl(0, TCSETS, &tp);
1578
1579 }
1580
1581 /*
1582 * Process_rlogin - Does the work that rlogin and telnet
1583 * need done
1584 */
1585 static void
process_rlogin(void)1586 process_rlogin(void)
1587 {
1588 /*
1589 * If a Kerberized rlogin was initiated, then these fields
1590 * must be read by rlogin daemon itself and passed down via
1591 * cmd line args.
1592 */
1593 if (!Uflag && !strlen(rusername))
1594 getstr(rusername, sizeof (rusername), "remuser");
1595 if (!strlen(lusername))
1596 getstr(lusername, sizeof (lusername), "locuser");
1597 if (!tflag && !strlen(terminal))
1598 getstr(terminal, sizeof (terminal), "Terminal type");
1599
1600 if (strlen(terminal))
1601 doremoteterm(terminal);
1602
1603 /* fflag has precedence over stuff passed by rlogind */
1604 if (fflag || getuid()) {
1605 pwd = &nouser;
1606 return;
1607 } else {
1608 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1609 login_exit(1);
1610
1611 pwd = getpwnam(lusername);
1612 if (pwd == NULL) {
1613 pwd = &nouser;
1614 return;
1615 }
1616 }
1617
1618 /*
1619 * Update PAM on the user name
1620 */
1621 if (strlen(lusername) &&
1622 pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1623 login_exit(1);
1624
1625 if (strlen(rusername) &&
1626 pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1627 login_exit(1);
1628
1629 SCPYL(user_name, lusername);
1630 envp = &zero;
1631 lusername[0] = '\0';
1632 }
1633
1634 /*
1635 * *** Account validation routines ***
1636 *
1637 */
1638
1639 /*
1640 * validate_account - This is the PAM version of validate.
1641 */
1642
1643 static void
validate_account(void)1644 validate_account(void)
1645 {
1646 int error;
1647 int flag;
1648 int tries; /* new password retries */
1649
1650 (void) alarm(0); /* give user time to come up with password */
1651
1652 check_log();
1653
1654 if (Passreqflag)
1655 flag = PAM_DISALLOW_NULL_AUTHTOK;
1656 else
1657 flag = 0;
1658
1659 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1660 if (error == PAM_NEW_AUTHTOK_REQD) {
1661 tries = 1;
1662 error = PAM_AUTHTOK_ERR;
1663 while (error == PAM_AUTHTOK_ERR &&
1664 tries <= DEF_ATTEMPTS) {
1665 if (tries > 1)
1666 (void) printf("Try again\n\n");
1667
1668 (void) printf("Choose a new password.\n");
1669
1670 error = pam_chauthtok(pamh,
1671 PAM_CHANGE_EXPIRED_AUTHTOK);
1672 if (error == PAM_TRY_AGAIN) {
1673 (void) sleep(1);
1674 error = pam_chauthtok(pamh,
1675 PAM_CHANGE_EXPIRED_AUTHTOK);
1676 }
1677 tries++;
1678 }
1679
1680 if (error != PAM_SUCCESS) {
1681 if (dosyslog)
1682 syslog(LOG_CRIT,
1683 "change password failure: %s",
1684 pam_strerror(pamh, error));
1685 audit_error = ADT_FAIL_PAM + error;
1686 login_exit(1);
1687 } else {
1688 audit_success(ADT_passwd, pwd, zone_name);
1689 }
1690 } else {
1691 (void) printf(incorrectmsg);
1692
1693 if (dosyslog)
1694 syslog(LOG_CRIT,
1695 "login account failure: %s",
1696 pam_strerror(pamh, error));
1697 audit_error = ADT_FAIL_PAM + error;
1698 login_exit(1);
1699 }
1700 }
1701 }
1702
1703 /*
1704 * Check_log - This is really a hack because PAM checks the log, but login
1705 * wants to know if the log is okay and PAM doesn't have
1706 * a module independent way of handing this info back.
1707 */
1708
1709 static void
check_log(void)1710 check_log(void)
1711 {
1712 int fdl;
1713 long long offset;
1714
1715 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1716
1717 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) {
1718 if (llseek(fdl, offset, SEEK_SET) == offset &&
1719 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) &&
1720 ll.ll_time != 0)
1721 lastlogok = 1;
1722 (void) close(fdl);
1723 }
1724 }
1725
1726 /*
1727 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1728 * place us in the user's home directory just in
1729 * case it was protected and the first chdir failed.
1730 * No chdir errors should happen at this point because
1731 * all failures should have happened on the first
1732 * time around.
1733 */
1734
1735 static void
chdir_to_dir_user(void)1736 chdir_to_dir_user(void)
1737 {
1738 if (chdir(pwd->pw_dir) < 0) {
1739 if (chdir("/") < 0) {
1740 (void) printf("No directory!\n");
1741 /*
1742 * This probably won't work since we can't get to /.
1743 */
1744 if (dosyslog) {
1745 if (remote_host[0]) {
1746 syslog(LOG_CRIT,
1747 "LOGIN FAILURES ON %s FROM %.*s ",
1748 " %.*s", ttyn, HMAX,
1749 remote_host, NMAX, pwd->pw_name);
1750 } else {
1751 syslog(LOG_CRIT,
1752 "LOGIN FAILURES ON %s, %.*s",
1753 ttyn, NMAX, pwd->pw_name);
1754 }
1755 }
1756 closelog();
1757 (void) sleep(Disabletime);
1758 exit(1);
1759 } else {
1760 (void) printf("No directory! Logging in with home=/\n");
1761 pwd->pw_dir = "/";
1762 }
1763 }
1764 }
1765
1766
1767 /*
1768 * login_authenticate - Performs the main authentication work
1769 * 1. Prints the login prompt
1770 * 2. Requests and verifys the password
1771 * 3. Checks the port password
1772 */
1773
1774 static void
login_authenticate(void)1775 login_authenticate(void)
1776 {
1777 char *user;
1778 int err;
1779 int login_successful = 0;
1780
1781 do {
1782 /* if scheme broken, then nothing to do but quit */
1783 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS)
1784 exit(1);
1785
1786 /*
1787 * only get name from utility if it is not already
1788 * supplied by pam_start or a pam_set_item.
1789 */
1790 if (!user || !user[0]) {
1791 /* use call back to get user name */
1792 get_user_name();
1793 }
1794
1795 err = verify_passwd();
1796
1797 /*
1798 * If root login and not on system console then call exit(2)
1799 */
1800 check_for_console();
1801
1802 switch (err) {
1803 case PAM_SUCCESS:
1804 case PAM_NEW_AUTHTOK_REQD:
1805 /*
1806 * Officially, pam_authenticate() shouldn't return this
1807 * but it's probably the right thing to return if
1808 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
1809 * be forced to change password later in this code.
1810 */
1811 count = 0;
1812 login_successful = 1;
1813 break;
1814 case PAM_MAXTRIES:
1815 count = retry;
1816 /*FALLTHROUGH*/
1817 case PAM_AUTH_ERR:
1818 case PAM_AUTHINFO_UNAVAIL:
1819 case PAM_USER_UNKNOWN:
1820 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd,
1821 remote_host, ttyn, zone_name);
1822 log_bad_attempts();
1823 break;
1824 case PAM_ABORT:
1825 log_bad_attempts();
1826 (void) sleep(Disabletime);
1827 (void) printf(incorrectmsg);
1828
1829 audit_error = ADT_FAIL_PAM + err;
1830 login_exit(1);
1831 /*NOTREACHED*/
1832 default: /* Some other PAM error */
1833 audit_error = ADT_FAIL_PAM + err;
1834 login_exit(1);
1835 /*NOTREACHED*/
1836 }
1837
1838 if (login_successful)
1839 break;
1840
1841 /* sleep after bad passwd */
1842 if (count)
1843 (void) sleep(Sleeptime);
1844 (void) printf(incorrectmsg);
1845 /* force name to be null in this case */
1846 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1847 login_exit(1);
1848 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1849 login_exit(1);
1850 } while (count++ < retry);
1851
1852 if (count >= retry) {
1853 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd,
1854 remote_host, ttyn, zone_name);
1855 /*
1856 * If logging is turned on, output the
1857 * string storage area to the log file,
1858 * and sleep for Disabletime
1859 * seconds before exiting.
1860 */
1861 if (writelog)
1862 badlogin();
1863 if (dosyslog) {
1864 if ((pwd = getpwnam(user_name)) != NULL) {
1865 if (remote_host[0]) {
1866 syslog(LOG_CRIT,
1867 "REPEATED LOGIN FAILURES ON %s "
1868 "FROM %.*s, %.*s",
1869 ttyn, HMAX, remote_host, NMAX,
1870 user_name);
1871 } else {
1872 syslog(LOG_CRIT,
1873 "REPEATED LOGIN FAILURES ON "
1874 "%s, %.*s",
1875 ttyn, NMAX, user_name);
1876 }
1877 } else {
1878 if (remote_host[0]) {
1879 syslog(LOG_CRIT,
1880 "REPEATED LOGIN FAILURES ON %s "
1881 "FROM %.*s",
1882 ttyn, HMAX, remote_host);
1883 } else {
1884 syslog(LOG_CRIT,
1885 "REPEATED LOGIN FAILURES ON %s",
1886 ttyn);
1887 }
1888 }
1889 }
1890 (void) sleep(Disabletime);
1891 exit(1);
1892 }
1893
1894 }
1895
1896 /*
1897 * *** Credential Related routines ***
1898 *
1899 */
1900
1901 /*
1902 * setup_credentials - sets the group ID, initializes the groups
1903 * and sets up the secretkey.
1904 * Exits if a failure occurrs.
1905 */
1906
1907
1908 /*
1909 * setup_credentials - PAM does all the work for us on this one.
1910 */
1911
1912 static void
setup_credentials(void)1913 setup_credentials(void)
1914 {
1915 int error = 0;
1916
1917 /* set the real (and effective) GID */
1918 if (setgid(pwd->pw_gid) == -1) {
1919 login_exit(1);
1920 }
1921
1922 /*
1923 * Initialize the supplementary group access list.
1924 */
1925 if ((user_name[0] == '\0') ||
1926 (initgroups(user_name, pwd->pw_gid) == -1)) {
1927 audit_error = ADT_FAIL_VALUE_PROGRAM;
1928 login_exit(1);
1929 }
1930
1931 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED :
1932 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1933 audit_error = ADT_FAIL_PAM + error;
1934 login_exit(error);
1935 }
1936
1937 /*
1938 * Record successful login and fork process that records logout.
1939 * We have to do this after setting credentials because pam_setcred()
1940 * loads key audit info into the cred, but before setuid() so audit
1941 * system calls will work.
1942 */
1943 audit_success(get_audit_id(), pwd, zone_name);
1944 }
1945
1946 static uint_t
get_audit_id(void)1947 get_audit_id(void)
1948 {
1949 if (rflag)
1950 return (ADT_rlogin);
1951 else if (hflag)
1952 return (ADT_telnet);
1953 else if (zflag)
1954 return (ADT_zlogin);
1955
1956 return (ADT_login);
1957 }
1958
1959 /*
1960 *
1961 * *** Routines to get a new user set up and running ***
1962 *
1963 * Things to do when starting up a new user:
1964 * adjust_nice
1965 * update_utmpx_entry
1966 * establish_user_environment
1967 * print_banner
1968 * display_last_login_time
1969 * exec_the_shell
1970 *
1971 */
1972
1973
1974 /*
1975 * adjust_nice - Set the nice (process priority) value if the
1976 * gecos value contains an appropriate value.
1977 */
1978
1979 static void
adjust_nice(void)1980 adjust_nice(void)
1981 {
1982 int pri, mflg, i;
1983
1984 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1985 pri = 0;
1986 mflg = 0;
1987 i = 4;
1988
1989 if (pwd->pw_gecos[i] == '-') {
1990 mflg++;
1991 i++;
1992 }
1993
1994 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1995 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1996
1997 if (mflg)
1998 pri = -pri;
1999
2000 (void) nice(pri);
2001 }
2002 }
2003
2004 /*
2005 * update_utmpx_entry - Searchs for the correct utmpx entry, making an
2006 * entry there if it finds one, otherwise exits.
2007 */
2008
2009 static void
update_utmpx_entry(int sublogin)2010 update_utmpx_entry(int sublogin)
2011 {
2012 int err;
2013 char *user;
2014 static char *errmsg = "No utmpx entry. "
2015 "You must exec \"login\" from the lowest level \"shell\".";
2016 int tmplen;
2017 struct utmpx *u = (struct utmpx *)0;
2018 struct utmpx utmpx;
2019 char *ttyntail;
2020
2021 /*
2022 * If we're not a sublogin then
2023 * we'll get an error back if our PID doesn't match the PID of the
2024 * entry we are updating, otherwise if its a sublogin the flags
2025 * field is set to 0, which means we just write a matching entry
2026 * (without checking the pid), or a new entry if an entry doesn't
2027 * exist.
2028 */
2029
2030 if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
2031 audit_error = ADT_FAIL_PAM + err;
2032 login_exit(1);
2033 }
2034
2035 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) !=
2036 PAM_SUCCESS) {
2037 audit_error = ADT_FAIL_PAM + err;
2038 login_exit(1);
2039 }
2040
2041 (void) memset((void *)&utmpx, 0, sizeof (utmpx));
2042 (void) time(&utmpx.ut_tv.tv_sec);
2043 utmpx.ut_pid = getpid();
2044
2045 if (rflag || hflag) {
2046 SCPYN(utmpx.ut_host, remote_host);
2047 tmplen = strlen(remote_host) + 1;
2048 if (tmplen < sizeof (utmpx.ut_host))
2049 utmpx.ut_syslen = tmplen;
2050 else
2051 utmpx.ut_syslen = sizeof (utmpx.ut_host);
2052 } else if (zflag) {
2053 /*
2054 * If this is a login from another zone, put the
2055 * zone:<zonename> string in the utmpx entry.
2056 */
2057 SCPYN(utmpx.ut_host, zone_name);
2058 tmplen = strlen(zone_name) + 1;
2059 if (tmplen < sizeof (utmpx.ut_host))
2060 utmpx.ut_syslen = tmplen;
2061 else
2062 utmpx.ut_syslen = sizeof (utmpx.ut_host);
2063 } else {
2064 utmpx.ut_syslen = 0;
2065 }
2066
2067 SCPYN(utmpx.ut_user, user);
2068
2069 /* skip over "/dev/" */
2070 ttyntail = basename(ttyn);
2071
2072 while ((u = getutxent()) != NULL) {
2073 if ((u->ut_type == INIT_PROCESS ||
2074 u->ut_type == LOGIN_PROCESS ||
2075 u->ut_type == USER_PROCESS) &&
2076 ((sublogin && strncmp(u->ut_line, ttyntail,
2077 sizeof (u->ut_line)) == 0) ||
2078 u->ut_pid == login_pid)) {
2079 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1));
2080 (void) memcpy(utmpx.ut_id, u->ut_id,
2081 sizeof (utmpx.ut_id));
2082 utmpx.ut_exit.e_exit = u->ut_exit.e_exit;
2083 utmpx.ut_type = USER_PROCESS;
2084 (void) pututxline(&utmpx);
2085 break;
2086 }
2087 }
2088 endutxent();
2089
2090 if (u == (struct utmpx *)NULL) {
2091 if (!sublogin) {
2092 /*
2093 * no utmpx entry already setup
2094 * (init or rlogind/telnetd)
2095 */
2096 (void) puts(errmsg);
2097
2098 audit_error = ADT_FAIL_VALUE_PROGRAM;
2099 login_exit(1);
2100 }
2101 } else {
2102 /* Now attempt to write out this entry to the wtmp file if */
2103 /* we were successful in getting it from the utmpx file and */
2104 /* the wtmp file exists. */
2105 updwtmpx(WTMPX_FILE, &utmpx);
2106 }
2107 }
2108
2109
2110
2111 /*
2112 * process_chroot_logins - Chroots to the specified subdirectory and
2113 * re executes login.
2114 */
2115
2116 static int
process_chroot_logins(void)2117 process_chroot_logins(void)
2118 {
2119 /*
2120 * If the shell field starts with a '*', do a chroot to the home
2121 * directory and perform a new login.
2122 */
2123
2124 if (*pwd->pw_shell == '*') {
2125 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
2126 pamh = NULL; /* really done */
2127 if (chroot(pwd->pw_dir) < 0) {
2128 (void) printf("No Root Directory\n");
2129
2130 audit_failure(get_audit_id(),
2131 ADT_FAIL_VALUE_CHDIR_FAILED,
2132 pwd, remote_host, ttyn, zone_name);
2133
2134 return (ERROR);
2135 }
2136 /*
2137 * Set the environment flag <!sublogin> so that the next login
2138 * knows that it is a sublogin.
2139 */
2140 envinit[0] = SUBLOGIN;
2141 envinit[1] = (char *)NULL;
2142 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
2143 (void) execle("/usr/bin/login", "login", (char *)0,
2144 &envinit[0]);
2145 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
2146 (void) printf("No /usr/bin/login or /etc/login on root\n");
2147
2148 audit_error = ADT_FAIL_VALUE_PROGRAM;
2149
2150 login_exit(1);
2151 }
2152 return (OK);
2153 }
2154
2155 /*
2156 * establish_user_environment - Set up the new users enviornment
2157 */
2158
2159 static void
establish_user_environment(char ** renvp)2160 establish_user_environment(char **renvp)
2161 {
2162 int i, j, k, l_index, length, idx = 0;
2163 char *endptr;
2164 char **lenvp;
2165 char **pam_env;
2166
2167 lenvp = environ;
2168 while (*lenvp++)
2169 ;
2170
2171 /* count the number of PAM environment variables set by modules */
2172 if ((pam_env = pam_getenvlist(pamh)) != 0) {
2173 for (idx = 0; pam_env[idx] != 0; idx++)
2174 ;
2175 }
2176
2177 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx,
2178 sizeof (char *));
2179 if (envinit == NULL) {
2180 (void) printf("Calloc failed - out of swap space.\n");
2181 login_exit(8);
2182 }
2183
2184 /*
2185 * add PAM environment variables first so they
2186 * can be overwritten at login's discretion.
2187 * check for illegal environment variables.
2188 */
2189 idx = 0; basicenv = 0;
2190 if (pam_env != 0) {
2191 while (pam_env[idx] != 0) {
2192 if (legalenvvar(pam_env[idx])) {
2193 envinit[basicenv] = pam_env[idx];
2194 basicenv++;
2195 }
2196 idx++;
2197 }
2198 }
2199 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv));
2200
2201 /* Set up environment */
2202 if (rflag) {
2203 ENVSTRNCAT(term, terminal);
2204 } else if (hflag) {
2205 if (strlen(terminal)) {
2206 ENVSTRNCAT(term, terminal);
2207 }
2208 } else {
2209 char *tp = getenv("TERM");
2210
2211 if ((tp != NULL) && (*tp != '\0'))
2212 ENVSTRNCAT(term, tp);
2213 }
2214
2215 ENVSTRNCAT(logname, pwd->pw_name);
2216
2217 /*
2218 * There are three places to get timezone info. init.c sets
2219 * TZ if the file /etc/default/init contains a value for TZ.
2220 * login.c looks in the file /etc/default/login for a
2221 * variable called TIMEZONE being set. If TIMEZONE has a
2222 * value, TZ is set to that value; no environment variable
2223 * TIMEZONE is set, only TZ. If neither of these methods
2224 * work to set TZ, then the library routines will default
2225 * to using the file /usr/lib/locale/TZ/localtime.
2226 *
2227 * There is a priority set up here. If /etc/default/init has
2228 * a value for TZ, that value remains top priority. If the
2229 * file /etc/default/login has TIMEZONE set, that has second
2230 * highest priority not overriding the value of TZ in
2231 * /etc/default/init. The reason for this priority is that the
2232 * file /etc/default/init is supposed to be sourced by
2233 * /etc/profile. We are doing the "sourcing" prematurely in
2234 * init.c. Additionally, a login C shell doesn't source the
2235 * file /etc/profile thus not sourcing /etc/default/init thus not
2236 * allowing an adminstrator to globally set TZ for all users
2237 */
2238 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
2239 tmp_tz = Def_tz;
2240
2241 if ((Def_tz = getenv("TZ")) != NULL) {
2242 ENVSTRNCAT(timez, Def_tz);
2243 } else if (tmp_tz != NULL) {
2244 Def_tz = tmp_tz;
2245 ENVSTRNCAT(timez, Def_tz);
2246 }
2247
2248 if (Def_hertz == NULL)
2249 (void) sprintf(hertz + strlen(hertz), "%lu", HZ);
2250 else
2251 ENVSTRNCAT(hertz, Def_hertz);
2252
2253 if (Def_path == NULL)
2254 (void) strlcat(path, DEF_PATH, sizeof (path));
2255 else
2256 ENVSTRNCAT(path, Def_path);
2257
2258 ENVSTRNCAT(home, pwd->pw_dir);
2259
2260 /*
2261 * Find the end of the basic environment
2262 */
2263 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
2264 ;
2265
2266 /*
2267 * If TZ has a value, add it.
2268 */
2269 if (strcmp(timez, "TZ=") != 0)
2270 envinit[basicenv++] = timez;
2271
2272 if (*pwd->pw_shell == '\0') {
2273 /*
2274 * If possible, use the primary default shell,
2275 * otherwise, use the secondary one.
2276 */
2277 if (access(SHELL, X_OK) == 0)
2278 pwd->pw_shell = SHELL;
2279 else
2280 pwd->pw_shell = SHELL2;
2281 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
2282 envinit[basicenv++] = shell;
2283 ENVSTRNCAT(shell, pwd->pw_shell);
2284 }
2285
2286 #ifndef NO_MAIL
2287 envinit[basicenv++] = mail;
2288 (void) strlcat(mail, pwd->pw_name, sizeof (mail));
2289 #endif
2290
2291 /*
2292 * Pick up locale environment variables, if any.
2293 */
2294 lenvp = renvp;
2295 while (*lenvp != NULL) {
2296 j = 0;
2297 while (localeenv[j] != 0) {
2298 /*
2299 * locale_envmatch() returns 1 if
2300 * *lenvp is localenev[j] and valid.
2301 */
2302 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2303 envinit[basicenv++] = *lenvp;
2304 break;
2305 }
2306 j++;
2307 }
2308 lenvp++;
2309 }
2310
2311 /*
2312 * If '-p' flag, then try to pass on allowable environment
2313 * variables. Note that by processing this first, what is
2314 * passed on the final "login:" line may over-ride the invocation
2315 * values. XXX is this correct?
2316 */
2317 if (pflag) {
2318 for (lenvp = renvp; *lenvp; lenvp++) {
2319 if (!legalenvvar(*lenvp)) {
2320 continue;
2321 }
2322 /*
2323 * If this isn't 'xxx=yyy', skip it. XXX
2324 */
2325 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2326 continue;
2327 }
2328 length = endptr + 1 - *lenvp;
2329 for (j = 0; j < basicenv; j++) {
2330 if (strncmp(envinit[j], *lenvp, length) == 0) {
2331 /*
2332 * Replace previously established value
2333 */
2334 envinit[j] = *lenvp;
2335 break;
2336 }
2337 }
2338 if (j == basicenv) {
2339 /*
2340 * It's a new definition, so add it at the end.
2341 */
2342 envinit[basicenv++] = *lenvp;
2343 }
2344 }
2345 }
2346
2347 /*
2348 * Add in all the environment variables picked up from the
2349 * argument list to "login" or from the user response to the
2350 * "login" request, if any.
2351 */
2352
2353 if (envp == NULL)
2354 goto switch_env; /* done */
2355
2356 for (j = 0, k = 0, l_index = 0;
2357 *envp != NULL && j < (MAXARGS-1);
2358 j++, envp++) {
2359
2360 /*
2361 * Scan each string provided. If it doesn't have the
2362 * format xxx=yyy, then add the string "Ln=" to the beginning.
2363 */
2364 if ((endptr = strchr(*envp, '=')) == NULL) {
2365 /*
2366 * This much to be malloc'd:
2367 * strlen(*envp) + 1 char for 'L' +
2368 * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
2369 *
2370 * total = strlen(*envp) + MAXARGSWIDTH + 3
2371 */
2372 int total = strlen(*envp) + MAXARGSWIDTH + 3;
2373 envinit[basicenv+k] = malloc(total);
2374 if (envinit[basicenv+k] == NULL) {
2375 (void) printf("%s: malloc failed\n", PROG_NAME);
2376 login_exit(1);
2377 }
2378 (void) snprintf(envinit[basicenv+k], total, "L%d=%s",
2379 l_index, *envp);
2380
2381 k++;
2382 l_index++;
2383 } else {
2384 if (!legalenvvar(*envp)) { /* this env var permited? */
2385 continue;
2386 } else {
2387
2388 /*
2389 * Check to see whether this string replaces
2390 * any previously defined string
2391 */
2392 for (i = 0, length = endptr + 1 - *envp;
2393 i < basicenv + k; i++) {
2394 if (strncmp(*envp, envinit[i], length)
2395 == 0) {
2396 envinit[i] = *envp;
2397 break;
2398 }
2399 }
2400
2401 /*
2402 * If it doesn't, place it at the end of
2403 * environment array.
2404 */
2405 if (i == basicenv+k) {
2406 envinit[basicenv+k] = *envp;
2407 k++;
2408 }
2409 }
2410 }
2411 } /* for (j = 0 ... ) */
2412
2413 switch_env:
2414 /*
2415 * Switch to the new environment.
2416 */
2417 environ = envinit;
2418 }
2419
2420 /*
2421 * print_banner - Print the banner at start up
2422 * Do not turn on DOBANNER ifdef. This is not
2423 * relevant to SunOS.
2424 */
2425
2426 static void
print_banner(void)2427 print_banner(void)
2428 {
2429 #ifdef DOBANNER
2430 uname(&un);
2431 #if i386
2432 (void) printf("UNIX System V/386 Release %s\n%s\n"
2433 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
2434 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
2435 un.release, un.nodename);
2436 #elif sun
2437 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
2438 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
2439 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
2440 "All Rights Reserved\n",
2441 un.release, un.machine, un.nodename);
2442 #else
2443 (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
2444 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
2445 un.release, un.machine, un.nodename);
2446 #endif /* i386 */
2447 #endif /* DOBANNER */
2448 }
2449
2450 /*
2451 * display_last_login_time - Advise the user the time and date
2452 * that this login-id was last used.
2453 */
2454
2455 static void
display_last_login_time(void)2456 display_last_login_time(void)
2457 {
2458 if (lastlogok) {
2459 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2460
2461 if (*ll.ll_host != '\0')
2462 (void) printf("from %.*s\n", sizeof (ll.ll_host),
2463 ll.ll_host);
2464 else
2465 (void) printf("on %.*s\n", sizeof (ll.ll_line),
2466 ll.ll_line);
2467 }
2468 }
2469
2470 /*
2471 * exec_the_shell - invoke the specified shell or start up program
2472 */
2473
2474 static void
exec_the_shell(void)2475 exec_the_shell(void)
2476 {
2477 char *endptr;
2478 int i;
2479
2480 (void) strlcat(minusnam, basename(pwd->pw_shell),
2481 sizeof (minusnam));
2482
2483 /*
2484 * Exec the shell
2485 */
2486 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2487
2488 /*
2489 * pwd->pw_shell was not an executable object file, maybe it
2490 * is a shell proceedure or a command line with arguments.
2491 * If so, turn off the SHELL= environment variable.
2492 */
2493 for (i = 0; envinit[i] != NULL; ++i) {
2494 if ((envinit[i] == shell) &&
2495 ((endptr = strchr(shell, '=')) != NULL))
2496 (*++endptr) = '\0';
2497 }
2498
2499 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2500 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2501 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
2502 }
2503
2504 (void) printf("No shell\n");
2505 }
2506
2507 /*
2508 * login_exit - Call exit() and terminate.
2509 * This function is here for PAM so cleanup can
2510 * be done before the process exits.
2511 */
2512 static void
login_exit(int exit_code)2513 login_exit(int exit_code)
2514 {
2515 if (pamh)
2516 (void) pam_end(pamh, PAM_ABORT);
2517
2518 if (audit_error)
2519 audit_failure(get_audit_id(), audit_error,
2520 pwd, remote_host, ttyn, zone_name);
2521
2522 exit(exit_code);
2523 /*NOTREACHED*/
2524 }
2525
2526 /*
2527 * Check if lenv and penv matches or not.
2528 */
2529 static int
locale_envmatch(char * lenv,char * penv)2530 locale_envmatch(char *lenv, char *penv)
2531 {
2532 while ((*lenv == *penv) && *lenv && *penv != '=') {
2533 lenv++;
2534 penv++;
2535 }
2536
2537 /*
2538 * '/' is eliminated for security reason.
2539 */
2540 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2541 return (1);
2542 return (0);
2543 }
2544
2545 static int
is_number(char * ptr)2546 is_number(char *ptr)
2547 {
2548 while (*ptr != '\0') {
2549 if (!isdigit(*ptr))
2550 return (0);
2551 ptr++;
2552 }
2553 return (1);
2554 }
2555