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