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