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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 /* Copyright (c) 1987, 1988 Microsoft Corporation */
41 /* All Rights Reserved */
44 * For a complete reference to login(1), see the manual page. However,
45 * login has accreted some intentionally undocumented options, which are
48 * -a: This legacy flag appears to be unused.
50 * -f <username>: This flag was introduced by PSARC 1995/039 in support
51 * of Kerberos. But it's not used by Sun's Kerberos implementation.
52 * It is however employed by zlogin(1), since it allows one to tell
53 * login: "This user is authenticated." In the case of zlogin that's
54 * true because the zone always trusts the global zone.
56 * -z <zonename>: This flag is passed to login when zlogin(1) executes a
57 * zone login. This tells login(1) to skip it's normal CONSOLE check
58 * (i.e. that the root login must be on /dev/console) and tells us the
59 * name of the zone from which the login is occurring.
62 #include <sys/types.h>
63 #include <sys/param.h>
64 #include <unistd.h> /* For logfile locking */
83 #include <security/pam_appl.h>
85 #include <libdevinfo.h>
87 #include "login_audit.h"
89 #include <krb5_repository.h>
92 * *** Defines, Macros, and String Constants ***
97 #define ISSUEFILE "/etc/issue" /* file to print before prompt */
98 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
101 * These need to be defined for UTMPX management.
102 * If we add in the utility functions later, we
105 #define __UPDATE_ENTRY 1
109 * Intervals to sleep after failed login
112 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
114 static int Sleeptime
= SLEEPTIME
;
117 * seconds login disabled after allowable number of unsuccessful attempts
120 #define DISABLETIME 20
122 static int Disabletime
= DISABLETIME
;
126 static int retry
= MAXTRYS
;
129 * Login logging support
131 #define LOGINLOG "/var/adm/loginlog" /* login log file */
132 #define LNAME_SIZE 20 /* size of logged logname */
133 #define TTYN_SIZE 15 /* size of logged tty name */
134 #define TIME_SIZE 30 /* size of logged time string */
135 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
136 #define L_WAITTIME 5 /* waittime for log file to unlock */
137 #define LOGTRYS 10 /* depth of 'try' logging */
140 * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
141 * SCPYL is the safer version of SCPYN
143 #define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a))
144 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
145 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
146 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
147 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
152 #define NMAX sizeof (((struct utmpx *)0)->ut_name)
153 #define HMAX sizeof (((struct utmpx *)0)->ut_host)
154 #define min(a, b) (((a) < (b)) ? (a) : (b))
157 * Various useful files and string constants
159 #define SHELL "/usr/bin/sh"
160 #define SHELL2 "/sbin/sh"
161 #define SUBLOGIN "<!sublogin>"
162 #define LASTLOG "/var/adm/lastlog"
163 #define PROG_NAME "login"
164 #define HUSHLOGIN ".hushlogin"
167 * Array and Buffer sizes
169 #define PBUFSIZE 8 /* max significant characters in a password */
170 #define MAXARGS 63 /* change value below if changing this */
171 #define MAXARGSWIDTH 2 /* log10(MAXARGS) */
176 * Miscellaneous constants
182 #define DONT_LOG_ERROR 0
187 * Counters for counting the number of failed login attempts
190 static int count
= 1;
193 * error value for login_exit() audit output (0 == no audit record)
195 static int audit_error
= 0;
200 extern int getsecretkey();
203 * The current user name
205 static char user_name
[NMAX
];
206 static char minusnam
[16] = "-";
209 * login_pid, used to find utmpx entry to update.
211 static pid_t login_pid
;
214 * locale environments to be passed to shells.
216 static char *localeenv
[] = {
218 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
219 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
220 static int locale_envmatch(char *, char *);
223 * Environment variable support
225 static char shell
[256] = { "SHELL=" };
226 static char home
[MAXPATHLEN
] = { "HOME=" };
227 static char term
[64] = { "TERM=" };
228 static char logname
[30] = { "LOGNAME=" };
229 static char timez
[100] = { "TZ=" };
230 static char hertz
[10] = { "HZ=" };
231 static char path
[MAXPATHLEN
] = { "PATH=" };
232 static char *newenv
[10+MAXARGS
] =
233 {home
, path
, logname
, hertz
, term
, 0, 0};
234 static char **envinit
= newenv
;
236 static char *zero
= (char *)0;
239 static char mail
[30] = { "MAIL=/var/mail/" };
241 extern char **environ
;
242 static char inputline
[MAXLINE
];
244 #define MAX_ID_LEN 256
245 #define MAX_REPOSITORY_LEN 256
246 #define MAX_PAMSERVICE_LEN 256
248 static char identity
[MAX_ID_LEN
];
249 static char repository
[MAX_REPOSITORY_LEN
];
250 static char progname
[MAX_PAMSERVICE_LEN
];
254 * Strings used to prompt the user.
256 static char loginmsg
[] = "login: ";
257 static char passwdmsg
[] = "Password:";
258 static char incorrectmsg
[] = "Login incorrect\n";
261 * Password file support
263 static struct passwd
*pwd
= NULL
;
264 static char remote_host
[HMAX
];
265 static char zone_name
[ZONENAME_MAX
];
268 * Illegal passwd entries.
270 static struct passwd nouser
= { "", "no:password", (uid_t
)-1 };
275 static char *log_entry
[LOGTRYS
];
276 static int writelog
= 0;
277 static int lastlogok
= 0;
278 static struct lastlog ll
;
279 static int dosyslog
= 0;
280 static int flogin
= MAXTRYS
; /* flag for SYSLOG_FAILED_LOGINS */
283 * Default file toggles
285 static char *Pndefault
= "/etc/default/login";
286 static char *Altshell
= NULL
;
287 static char *Console
= NULL
;
288 static int Passreqflag
= 0;
291 static mode_t Umask
= DEFUMASK
;
292 static char *Def_tz
= NULL
;
293 static char *tmp_tz
= NULL
;
294 static char *Def_hertz
= NULL
;
295 #define SET_FSIZ 2 /* ulimit() command arg */
296 static long Def_ulimit
= 0;
297 #define MAX_TIMEOUT (15 * 60)
298 #define DEF_TIMEOUT (5 * 60)
299 static unsigned Def_timeout
= DEF_TIMEOUT
;
300 static char *Def_path
= NULL
;
301 static char *Def_supath
= NULL
;
302 #define DEF_PATH "/usr/bin:" /* same as PATH */
303 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
306 * Defaults for updating expired passwords
308 #define DEF_ATTEMPTS 3
311 * ttyprompt will point to the environment variable TTYPROMPT.
312 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
314 static char *ttyprompt
= NULL
;
315 static char *ttyn
= NULL
;
318 * Pass inherited environment. Used by telnetd in support of the telnet
321 static boolean_t pflag
= B_FALSE
;
322 static boolean_t uflag
= B_FALSE
;
323 static boolean_t Rflag
= B_FALSE
;
324 static boolean_t sflag
= B_FALSE
;
325 static boolean_t Uflag
= B_FALSE
;
326 static boolean_t tflag
= B_FALSE
;
327 static boolean_t hflag
= B_FALSE
;
328 static boolean_t rflag
= B_FALSE
;
329 static boolean_t zflag
= B_FALSE
;
332 * Remote login support
334 static char rusername
[NMAX
+1], lusername
[NMAX
+1];
335 static char terminal
[MAXPATHLEN
];
338 * Pre-authentication flag support
342 static char ** getargs(char *);
344 static int login_conv(int, struct pam_message
**,
345 struct pam_response
**, void *);
347 static struct pam_conv pam_conv
= {login_conv
, NULL
};
348 static pam_handle_t
*pamh
; /* Authentication handle */
351 * Function declarations
353 static void turn_on_logging(void);
354 static void defaults(void);
355 static void usage(void);
356 static void process_rlogin(void);
357 static void login_authenticate();
358 static void setup_credentials(void);
359 static void adjust_nice(void);
360 static void update_utmpx_entry(int);
361 static void establish_user_environment(char **);
362 static void print_banner(void);
363 static void display_last_login_time(void);
364 static void exec_the_shell(void);
365 static int process_chroot_logins(void);
366 static void chdir_to_dir_user(void);
367 static void check_log(void);
368 static void validate_account(void);
369 static void doremoteterm(char *);
370 static int get_options(int, char **);
371 static void getstr(char *, int, char *);
372 static int legalenvvar(char *);
373 static void check_for_console(void);
374 static void check_for_dueling_unix(char *);
375 static void get_user_name(void);
376 static uint_t
get_audit_id(void);
377 static void login_exit(int)__NORETURN
;
378 static int logins_disabled(char *);
379 static void log_bad_attempts(void);
380 static int is_number(char *);
385 * The primary flow of control is directed in this routine.
386 * Control moves in line from top to bottom calling subfunctions
387 * which perform the bulk of the work. Many of these calls exit
388 * when a fatal error is encountered and do not return to main.
394 main(int argc
, char *argv
[], char **renvp
)
399 login_pid
= getpid();
402 * Set up Defaults and flags
405 SCPYL(progname
, PROG_NAME
);
408 * Set up default umask
410 if (Umask
> ((mode_t
)0777))
415 * Set up default timeouts and delays
417 if (Def_timeout
> MAX_TIMEOUT
)
418 Def_timeout
= MAX_TIMEOUT
;
419 if (Sleeptime
< 0 || Sleeptime
> 5)
420 Sleeptime
= SLEEPTIME
;
422 (void) alarm(Def_timeout
);
425 * Ignore SIGQUIT and SIGINT and set nice to 0
427 (void) signal(SIGQUIT
, SIG_IGN
);
428 (void) signal(SIGINT
, SIG_IGN
);
432 * Set flag to disable the pid check if you find that you are
436 if (*renvp
&& strcmp(*renvp
, SUBLOGIN
) == 0)
442 if (get_options(argc
, argv
) == -1) {
444 audit_error
= ADT_FAIL_VALUE_BAD_CMD
;
449 * if devicename is not passed as argument, call ttyname(0)
458 * Call pam_start to initiate a PAM authentication operation
461 if ((pam_rc
= pam_start(progname
, user_name
, &pam_conv
, &pamh
))
463 audit_error
= ADT_FAIL_PAM
+ pam_rc
;
466 if ((pam_rc
= pam_set_item(pamh
, PAM_TTY
, ttyn
)) != PAM_SUCCESS
) {
467 audit_error
= ADT_FAIL_PAM
+ pam_rc
;
470 if ((pam_rc
= pam_set_item(pamh
, PAM_RHOST
, remote_host
)) !=
472 audit_error
= ADT_FAIL_PAM
+ pam_rc
;
477 * We currently only support special handling of the KRB5 PAM repository
479 if ((Rflag
&& strlen(repository
)) &&
480 strcmp(repository
, KRB5_REPOSITORY_NAME
) == 0 &&
481 (uflag
&& strlen(identity
))) {
482 krb5_repository_data_t krb5_data
;
483 pam_repository_t pam_rep_data
;
485 krb5_data
.principal
= identity
;
486 krb5_data
.flags
= SUNW_PAM_KRB5_ALREADY_AUTHENTICATED
;
488 pam_rep_data
.type
= repository
;
489 pam_rep_data
.scope
= (void *)&krb5_data
;
490 pam_rep_data
.scope_len
= sizeof (krb5_data
);
492 (void) pam_set_item(pamh
, PAM_REPOSITORY
,
493 (void *)&pam_rep_data
);
497 * Open the log file which contains a record of successful and failed
503 * say "hi" to syslogd ..
505 openlog("login", 0, LOG_AUTH
);
508 * Do special processing for -r (rlogin) flag
516 /* we are already authenticated. fill in what we must, then continue */
518 if ((pwd
= getpwnam(user_name
)) == NULL
) {
519 audit_error
= ADT_FAIL_VALUE_USERNAME
;
522 (void) printf("Login failed: unknown user '%s'.\n",
528 * Perform the primary login authentication activity.
530 login_authenticate();
533 /* change root login, then we exec another login and try again */
534 if (process_chroot_logins() != OK
)
538 * If root login and not on system console then call exit(2)
543 * Check to see if a shutdown is in progress, if it is and
544 * we are not root then throw the user off the system
546 if (logins_disabled(user_name
) == TRUE
) {
547 audit_error
= ADT_FAIL_VALUE_LOGIN_DISABLED
;
551 if (pwd
->pw_uid
== 0) {
552 if (Def_supath
!= NULL
)
553 Def_path
= Def_supath
;
555 Def_path
= DEF_SUPATH
;
559 * Check account expiration and passwd aging
564 * We only get here if we've been authenticated.
568 * Now we set up the environment for the new user, which includes
569 * the users ulimit, nice value, ownership of this tty, uid, gid,
570 * and environment variables.
572 if (Def_ulimit
> 0L && ulimit(SET_FSIZ
, Def_ulimit
) < 0L)
573 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit
);
575 /* di_devperm_login() sends detailed errors to syslog */
576 if (di_devperm_login((const char *)ttyn
, pwd
->pw_uid
, pwd
->pw_gid
,
578 (void) fprintf(stderr
, "error processing /etc/logindevperm,"
579 " see syslog for more details\n");
582 adjust_nice(); /* passwd file can specify nice value */
584 setup_credentials(); /* Set user credentials - exits on failure */
587 * NOTE: telnetd and rlogind rely upon this updating of utmpx
588 * to indicate that the authentication completed successfully,
589 * pam_open_session was called and therefore they are required to
590 * call pam_close_session.
592 update_utmpx_entry(sublogin
);
594 /* set the real (and effective) UID */
595 if (setuid(pwd
->pw_uid
) == -1) {
600 * Set up the basic environment for the exec. This includes
601 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
605 establish_user_environment(renvp
);
607 (void) pam_end(pamh
, PAM_SUCCESS
); /* Done using PAM */
610 if (pwd
->pw_uid
== 0) {
612 if (remote_host
[0]) {
613 syslog(LOG_NOTICE
, "ROOT LOGIN %s FROM %.*s",
614 ttyn
, HMAX
, remote_host
);
616 syslog(LOG_NOTICE
, "ROOT LOGIN %s", ttyn
);
621 (void) signal(SIGQUIT
, SIG_DFL
);
622 (void) signal(SIGINT
, SIG_DFL
);
625 * Display some useful information to the new user like the banner
626 * and last login time if not a quiet login.
629 if (access(HUSHLOGIN
, F_OK
) != 0) {
631 display_last_login_time();
635 * Set SIGXCPU and SIGXFSZ to default disposition.
636 * Shells inherit signal disposition from parent.
637 * And the shells should have default dispositions
638 * for the two below signals.
640 (void) signal(SIGXCPU
, SIG_DFL
);
641 (void) signal(SIGXFSZ
, SIG_DFL
);
644 * Now fire off the shell of choice
657 * *** Utility functions ***
663 * donothing & catch - Signal catching functions
671 (void) pam_end(pamh
, PAM_ABORT
);
686 * *** Bad login logging support ***
690 * badlogin() - log to the log file 'trys'
691 * unsuccessful attempts
697 int retval
, count1
, fildes
;
700 * Tries to open the log file. If succeed, lock it and write
701 * in the failed attempts
703 if ((fildes
= open(LOGINLOG
, O_APPEND
|O_WRONLY
)) != -1) {
705 (void) sigset(SIGALRM
, donothing
);
706 (void) alarm(L_WAITTIME
);
707 retval
= lockf(fildes
, F_LOCK
, 0L);
709 (void) sigset(SIGALRM
, SIG_DFL
);
711 for (count1
= 0; count1
< trys
; count1
++)
712 (void) write(fildes
, log_entry
[count1
],
713 (unsigned)strlen(log_entry
[count1
]));
714 (void) lockf(fildes
, F_ULOCK
, 0L);
716 (void) close(fildes
);
722 * log_bad_attempts - log each bad login attempt - called from
723 * login_authenticate. Exits when the maximum attempt
728 log_bad_attempts(void)
735 (void) time(&timenow
);
736 (void) strncat(log_entry
[trys
], user_name
, LNAME_SIZE
);
737 (void) strncat(log_entry
[trys
], ":", (size_t)1);
738 (void) strncat(log_entry
[trys
], ttyn
, TTYN_SIZE
);
739 (void) strncat(log_entry
[trys
], ":", (size_t)1);
740 (void) strncat(log_entry
[trys
], ctime(&timenow
), TIME_SIZE
);
743 if (count
> flogin
) {
744 if ((pwd
= getpwnam(user_name
)) != NULL
) {
745 if (remote_host
[0]) {
747 "Login failure on %s from %.*s, "
748 "%.*s", ttyn
, HMAX
, remote_host
,
752 "Login failure on %s, %.*s",
753 ttyn
, NMAX
, user_name
);
756 if (remote_host
[0]) {
758 "Login failure on %s from %.*s",
759 ttyn
, HMAX
, remote_host
);
762 "Login failure on %s", ttyn
);
770 * turn_on_logging - if the logfile exist, turn on attempt logging and
771 * initialize the string storage area
775 turn_on_logging(void)
780 if (stat(LOGINLOG
, &dbuf
) == 0) {
782 for (i
= 0; i
< LOGTRYS
; i
++) {
783 if (!(log_entry
[i
] = malloc((size_t)ENT_SIZE
))) {
787 *log_entry
[i
] = '\0';
795 * This is the conv (conversation) function called from
796 * a PAM authentication module to print error messages
797 * or garner information from the user.
801 login_conv(int num_msg
, struct pam_message
**msg
,
802 struct pam_response
**response
, void *appdata_ptr
)
804 struct pam_message
*m
;
805 struct pam_response
*r
;
810 return (PAM_CONV_ERR
);
812 *response
= calloc(num_msg
, sizeof (struct pam_response
));
813 if (*response
== NULL
)
814 return (PAM_BUF_ERR
);
821 switch (m
->msg_style
) {
823 case PAM_PROMPT_ECHO_OFF
:
825 temp
= getpassphrase(m
->msg
);
828 return (PAM_CONV_ERR
);
830 r
->resp
= strdup(temp
);
831 if (r
->resp
== NULL
) {
834 for (i
= 0; i
< num_msg
; i
++, r
++) {
840 return (PAM_BUF_ERR
);
848 case PAM_PROMPT_ECHO_ON
:
850 (void) fputs(m
->msg
, stdout
);
851 r
->resp
= calloc(1, PAM_MAX_RESP_SIZE
);
852 if (r
->resp
== NULL
) {
855 for (i
= 0; i
< num_msg
; i
++, r
++) {
861 return (PAM_BUF_ERR
);
864 * The response might include environment variables
865 * information. We should store that information in
866 * envp if there is any; otherwise, envp is set to
869 bzero((void *)inputline
, MAXLINE
);
871 envp
= getargs(inputline
);
873 /* If we read in any input, process it. */
874 if (inputline
[0] != '\0') {
877 if (envp
!= (char **)NULL
)
879 * If getargs() did not return NULL,
880 * *envp is the first string in
881 * inputline. envp++ makes envp point
882 * to environment variables information
887 (void) strncpy(r
->resp
, inputline
,
888 PAM_MAX_RESP_SIZE
-1);
889 r
->resp
[PAM_MAX_RESP_SIZE
-1] = NULL
;
890 len
= strlen(r
->resp
);
891 if (r
->resp
[len
-1] == '\n')
892 r
->resp
[len
-1] = '\0';
901 if (m
->msg
!= NULL
) {
902 (void) fputs(m
->msg
, stderr
);
903 (void) fputs("\n", stderr
);
909 if (m
->msg
!= NULL
) {
910 (void) fputs(m
->msg
, stdout
);
911 (void) fputs("\n", stdout
);
921 return (PAM_SUCCESS
);
925 * verify_passwd - Authenticates the user.
926 * Returns: PAM_SUCCESS if authentication successful,
927 * PAM error code if authentication fails.
935 int flag
= (Passreqflag
? PAM_DISALLOW_NULL_AUTHTOK
: 0);
938 * PAM authenticates the user for us.
940 error
= pam_authenticate(pamh
, flag
);
942 /* get the user_name from the pam handle */
943 (void) pam_get_item(pamh
, PAM_USER
, (void**)&user
);
945 if (user
== NULL
|| *user
== '\0')
946 return (PAM_SYSTEM_ERR
);
948 SCPYL(user_name
, user
);
949 check_for_dueling_unix(user_name
);
951 if (((pwd
= getpwnam(user_name
)) == NULL
) &&
952 (error
!= PAM_USER_UNKNOWN
)) {
953 return (PAM_SYSTEM_ERR
);
960 * quotec - Called by getargs
968 switch (c
= getc(stdin
)) {
1002 for (num
= 0, i
= 0; i
< 3; i
++) {
1003 num
= num
* 8 + (c
- '0');
1004 if ((c
= getc(stdin
)) < '0' || c
> '7')
1007 (void) ungetc(c
, stdin
);
1018 * getargs - returns an input line. Exits if EOF encountered.
1020 #define WHITESPACE 0
1024 getargs(char *input_line
)
1026 static char envbuf
[MAXLINE
];
1027 static char *args
[MAXARGS
];
1028 char *ptr
, **answer
;
1031 char *p
= input_line
;
1037 while ((c
= getc(stdin
)) != EOF
&& answer
< &args
[MAXARGS
-1]) {
1039 *(input_line
++) = c
;
1044 if (ptr
== &envbuf
[0])
1045 return ((char **)NULL
);
1046 *input_line
= *ptr
= '\0';
1052 if (state
== ARGUMENT
) {
1062 if (state
== WHITESPACE
) {
1069 /* Attempt at overflow, exit */
1070 if (input_line
- p
>= MAXLINE
- 1 ||
1071 ptr
>= &envbuf
[sizeof (envbuf
) - 1]) {
1072 audit_error
= ADT_FAIL_VALUE_INPUT_OVERFLOW
;
1078 * If we left loop because an EOF was received or we've overflown
1079 * args[], exit immediately.
1086 * get_user_name - Gets the user name either passed in, or from the
1095 if ((fp
= fopen(ISSUEFILE
, "r")) != NULL
) {
1096 char *ptr
, buffer
[BUFSIZ
];
1097 while ((ptr
= fgets(buffer
, sizeof (buffer
), fp
)) != NULL
) {
1098 (void) fputs(ptr
, stdout
);
1104 * if TTYPROMPT is not set, use our own prompt
1105 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1106 * and let the module do the prompting.
1109 if ((ttyprompt
== NULL
) || (*ttyprompt
== '\0'))
1110 (void) pam_set_item(pamh
, PAM_USER_PROMPT
, (void *)loginmsg
);
1112 (void) pam_set_item(pamh
, PAM_USER_PROMPT
, (void *)ttyprompt
);
1114 envp
= &zero
; /* XXX: is this right? */
1119 * Check_for_dueling_unix - Check to see if the another login is talking
1120 * to the line we've got open as a login port
1121 * Exits if we're talking to another unix system
1125 check_for_dueling_unix(char *inputline
)
1127 if (EQN(loginmsg
, inputline
) || EQN(passwdmsg
, inputline
) ||
1128 EQN(incorrectmsg
, inputline
)) {
1129 (void) printf("Looking at a login line.\n");
1135 * logins_disabled - if the file /etc/nologin exists and the user is not
1136 * root then do not permit them to login
1139 logins_disabled(char *user_name
)
1143 if (!EQN("root", user_name
) &&
1144 ((nlfd
= fopen(NOLOGIN
, "r")) != (FILE *)NULL
)) {
1145 while ((c
= getc(nlfd
)) != EOF
)
1147 (void) fflush(stdout
);
1154 #define DEFAULT_CONSOLE "/dev/console"
1157 * check_for_console - Checks if we're getting a root login on the
1158 * console, or a login from the global zone. Exits if not.
1160 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins
1161 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global
1162 * zones, but checking them does no harm.
1165 check_for_console(void)
1167 const char *consoles
[] = { "/dev/console", "/dev/vt/", NULL
};
1170 if (pwd
== NULL
|| pwd
->pw_uid
!= 0 || zflag
!= B_FALSE
||
1174 if (strcmp(Console
, DEFAULT_CONSOLE
) == 0) {
1175 for (i
= 0; consoles
[i
] != NULL
; i
++) {
1176 if (strncmp(ttyn
, consoles
[i
],
1177 strlen(consoles
[i
])) == 0)
1181 if (strcmp(ttyn
, Console
) == 0)
1185 (void) printf("Not on system console\n");
1187 audit_error
= ADT_FAIL_VALUE_CONSOLE
;
1193 * List of environment variables or environment variable prefixes that should
1194 * not be propagated across logins, such as when the login -p option is used.
1196 static const char *const illegal
[] = {
1212 * legalenvvar - Is it legal to insert this environmental variable?
1216 legalenvvar(char *s
)
1218 const char *const *p
;
1220 for (p
= &illegal
[0]; *p
; p
++) {
1221 if (strncmp(s
, *p
, strlen(*p
)) == 0)
1230 * getstr - Get a string from standard input
1231 * Calls exit if read(2) fails.
1235 getstr(char *buf
, int cnt
, char *err
)
1240 if (read(0, &c
, 1) != 1)
1243 } while (--cnt
> 1 && c
!= 0);
1246 err
= err
; /* For lint */
1251 * defaults - read defaults
1260 if (defopen(Pndefault
) == 0) {
1264 flags
= defcntl(DC_GETFLAGS
, 0);
1265 TURNOFF(flags
, DC_CASE
);
1266 (void) defcntl(DC_SETFLAGS
, flags
);
1268 if ((Console
= defread("CONSOLE=")) != NULL
)
1269 Console
= strdup(Console
);
1271 if ((Altshell
= defread("ALTSHELL=")) != NULL
)
1272 Altshell
= strdup(Altshell
);
1274 if ((ptr
= defread("PASSREQ=")) != NULL
&&
1275 strcasecmp("YES", ptr
) == 0)
1278 if ((Def_tz
= defread("TIMEZONE=")) != NULL
)
1279 Def_tz
= strdup(Def_tz
);
1281 if ((Def_hertz
= defread("HZ=")) != NULL
)
1282 Def_hertz
= strdup(Def_hertz
);
1284 if ((Def_path
= defread("PATH=")) != NULL
)
1285 Def_path
= strdup(Def_path
);
1287 if ((Def_supath
= defread("SUPATH=")) != NULL
)
1288 Def_supath
= strdup(Def_supath
);
1290 if ((ptr
= defread("ULIMIT=")) != NULL
)
1291 Def_ulimit
= atol(ptr
);
1293 if ((ptr
= defread("TIMEOUT=")) != NULL
)
1294 Def_timeout
= (unsigned)atoi(ptr
);
1296 if ((ptr
= defread("UMASK=")) != NULL
)
1297 if (sscanf(ptr
, "%lo", &Umask
) != 1)
1300 if ((ptr
= defread("SLEEPTIME=")) != NULL
) {
1302 Sleeptime
= atoi(ptr
);
1305 if ((ptr
= defread("DISABLETIME=")) != NULL
) {
1307 Disabletime
= atoi(ptr
);
1310 if ((ptr
= defread("SYSLOG=")) != NULL
)
1311 dosyslog
= strcmp(ptr
, "YES") == 0;
1313 if ((ptr
= defread("RETRIES=")) != NULL
) {
1318 if ((ptr
= defread("SYSLOG_FAILED_LOGINS=")) != NULL
) {
1325 (void) defopen((char *)NULL
);
1331 * get_options(argc, argv)
1332 * - parse the cmd line.
1333 * - return 0 if successful, -1 if failed.
1334 * Calls login_exit() on misuse of -r, -h, and -z flags
1338 get_options(int argc
, char *argv
[])
1342 char sflagname
[NMAX
+1];
1343 const char *flags_message
= "Only one of -r, -h and -z allowed\n";
1345 while ((c
= getopt(argc
, argv
, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
1352 * Must be root to pass in device name
1353 * otherwise we exit() as punishment for trying.
1355 if (getuid() != 0 || geteuid() != 0) {
1356 audit_error
= ADT_FAIL_VALUE_DEVICE_PERM
;
1357 login_exit(1); /* sigh */
1364 if (hflag
|| rflag
|| zflag
) {
1365 (void) fprintf(stderr
, flags_message
);
1369 SCPYL(remote_host
, optarg
);
1371 if (argv
[optind
][0] != '-') {
1372 SCPYL(terminal
, argv
[optind
]);
1376 * Allow "login -h hostname -" to
1377 * skip setting up an username as "-".
1379 if (argv
[optind
][1] == '\0')
1384 SCPYL(progname
, "telnet");
1388 if (hflag
|| rflag
|| zflag
) {
1389 (void) fprintf(stderr
, flags_message
);
1393 SCPYL(remote_host
, optarg
);
1394 SCPYL(progname
, "rlogin");
1403 * Must be root to bypass authentication
1404 * otherwise we exit() as punishment for trying.
1406 if (getuid() != 0 || geteuid() != 0) {
1407 audit_error
= ADT_FAIL_VALUE_AUTH_BYPASS
;
1409 login_exit(1); /* sigh */
1412 /* save fflag user name for future use */
1413 SCPYL(user_name
, optarg
);
1417 if (!strlen(optarg
)) {
1418 (void) fprintf(stderr
,
1419 "Empty string supplied with -u\n");
1422 SCPYL(identity
, optarg
);
1426 if (!strlen(optarg
)) {
1427 (void) fprintf(stderr
,
1428 "Empty string supplied with -s\n");
1431 SCPYL(sflagname
, optarg
);
1435 if (!strlen(optarg
)) {
1436 (void) fprintf(stderr
,
1437 "Empty string supplied with -R\n");
1440 SCPYL(repository
, optarg
);
1444 if (!strlen(optarg
)) {
1445 (void) fprintf(stderr
,
1446 "Empty string supplied with -t\n");
1449 SCPYL(terminal
, optarg
);
1454 * Kerberized rlogind may fork us with
1455 * -U "" if the rlogin client used the "-a"
1456 * option to send a NULL username. This is done
1457 * to force login to prompt for a user/password.
1458 * However, if Kerberos auth was used, we dont need
1459 * to prompt, so we will accept the option and
1460 * handle the situation later.
1462 SCPYL(rusername
, optarg
);
1466 if (hflag
|| rflag
|| zflag
) {
1467 (void) fprintf(stderr
, flags_message
);
1470 (void) snprintf(zone_name
, sizeof (zone_name
),
1472 SCPYL(progname
, "zlogin");
1482 * If the 's svcname' flag was used, override the progname
1483 * value that is to be used in the pam_start call.
1486 SCPYL(progname
, sflagname
);
1489 * get the prompt set by ttymon
1491 ttyprompt
= getenv("TTYPROMPT");
1493 if ((ttyprompt
!= NULL
) && (*ttyprompt
!= '\0')) {
1495 * if ttyprompt is set, there should be data on
1496 * the stream already.
1498 if ((envp
= getargs(inputline
)) != (char **)NULL
) {
1500 * don't get name if name passed as argument.
1502 SCPYL(user_name
, *envp
++);
1504 } else if (optind
< argc
) {
1505 SCPYL(user_name
, argv
[optind
]);
1506 (void) SCPYL(inputline
, user_name
);
1507 (void) strlcat(inputline
, " \n", sizeof (inputline
));
1508 envp
= &argv
[optind
+1];
1511 SCPYL(lusername
, user_name
);
1520 * usage - Print usage message
1526 (void) fprintf(stderr
,
1528 " login [-p] [-d device] [-R repository] [-s service]\n"
1529 "\t[-t terminal] [-u identity] [-U ruser]\n"
1530 "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n");
1535 * doremoteterm - Sets the appropriate ioctls for a remote terminal
1537 static char *speeds
[] = {
1538 "0", "50", "75", "110", "134", "150", "200", "300",
1539 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
1540 "57600", "76800", "115200", "153600", "230400", "307200", "460800",
1544 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1548 doremoteterm(char *term
)
1551 char *cp
= strchr(term
, '/'), **cpp
;
1554 (void) ioctl(0, TCGETS
, &tp
);
1559 cp
= strchr(speed
, '/');
1564 for (cpp
= speeds
; cpp
< &speeds
[NSPEEDS
]; cpp
++)
1565 if (strcmp(*cpp
, speed
) == 0) {
1566 (void) cfsetospeed(&tp
, cpp
-speeds
);
1571 tp
.c_lflag
|= ECHO
|ICANON
;
1572 tp
.c_iflag
|= IGNPAR
|ICRNL
;
1574 (void) ioctl(0, TCSETS
, &tp
);
1579 * Process_rlogin - Does the work that rlogin and telnet
1583 process_rlogin(void)
1586 * If a Kerberized rlogin was initiated, then these fields
1587 * must be read by rlogin daemon itself and passed down via
1590 if (!Uflag
&& !strlen(rusername
))
1591 getstr(rusername
, sizeof (rusername
), "remuser");
1592 if (!strlen(lusername
))
1593 getstr(lusername
, sizeof (lusername
), "locuser");
1594 if (!tflag
&& !strlen(terminal
))
1595 getstr(terminal
, sizeof (terminal
), "Terminal type");
1597 if (strlen(terminal
))
1598 doremoteterm(terminal
);
1600 /* fflag has precedence over stuff passed by rlogind */
1601 if (fflag
|| getuid()) {
1605 if (pam_set_item(pamh
, PAM_USER
, lusername
) != PAM_SUCCESS
)
1608 pwd
= getpwnam(lusername
);
1616 * Update PAM on the user name
1618 if (strlen(lusername
) &&
1619 pam_set_item(pamh
, PAM_USER
, lusername
) != PAM_SUCCESS
)
1622 if (strlen(rusername
) &&
1623 pam_set_item(pamh
, PAM_RUSER
, rusername
) != PAM_SUCCESS
)
1626 SCPYL(user_name
, lusername
);
1628 lusername
[0] = '\0';
1632 * *** Account validation routines ***
1637 * validate_account - This is the PAM version of validate.
1641 validate_account(void)
1645 int tries
; /* new password retries */
1647 (void) alarm(0); /* give user time to come up with password */
1652 flag
= PAM_DISALLOW_NULL_AUTHTOK
;
1656 if ((error
= pam_acct_mgmt(pamh
, flag
)) != PAM_SUCCESS
) {
1657 if (error
== PAM_NEW_AUTHTOK_REQD
) {
1659 error
= PAM_AUTHTOK_ERR
;
1660 while (error
== PAM_AUTHTOK_ERR
&&
1661 tries
<= DEF_ATTEMPTS
) {
1663 (void) printf("Try again\n\n");
1665 (void) printf("Choose a new password.\n");
1667 error
= pam_chauthtok(pamh
,
1668 PAM_CHANGE_EXPIRED_AUTHTOK
);
1669 if (error
== PAM_TRY_AGAIN
) {
1671 error
= pam_chauthtok(pamh
,
1672 PAM_CHANGE_EXPIRED_AUTHTOK
);
1677 if (error
!= PAM_SUCCESS
) {
1680 "change password failure: %s",
1681 pam_strerror(pamh
, error
));
1682 audit_error
= ADT_FAIL_PAM
+ error
;
1685 audit_success(ADT_passwd
, pwd
, zone_name
);
1688 (void) printf(incorrectmsg
);
1692 "login account failure: %s",
1693 pam_strerror(pamh
, error
));
1694 audit_error
= ADT_FAIL_PAM
+ error
;
1701 * Check_log - This is really a hack because PAM checks the log, but login
1702 * wants to know if the log is okay and PAM doesn't have
1703 * a module independent way of handing this info back.
1712 offset
= (long long) pwd
->pw_uid
* (long long) sizeof (struct lastlog
);
1714 if ((fdl
= open(LASTLOG
, O_RDWR
|O_CREAT
, 0444)) >= 0) {
1715 if (llseek(fdl
, offset
, SEEK_SET
) == offset
&&
1716 read(fdl
, (char *)&ll
, sizeof (ll
)) == sizeof (ll
) &&
1724 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1725 * place us in the user's home directory just in
1726 * case it was protected and the first chdir failed.
1727 * No chdir errors should happen at this point because
1728 * all failures should have happened on the first
1733 chdir_to_dir_user(void)
1735 if (chdir(pwd
->pw_dir
) < 0) {
1736 if (chdir("/") < 0) {
1737 (void) printf("No directory!\n");
1739 * This probably won't work since we can't get to /.
1742 if (remote_host
[0]) {
1744 "LOGIN FAILURES ON %s FROM %.*s ",
1745 " %.*s", ttyn
, HMAX
,
1746 remote_host
, NMAX
, pwd
->pw_name
);
1749 "LOGIN FAILURES ON %s, %.*s",
1750 ttyn
, NMAX
, pwd
->pw_name
);
1754 (void) sleep(Disabletime
);
1757 (void) printf("No directory! Logging in with home=/\n");
1765 * login_authenticate - Performs the main authentication work
1766 * 1. Prints the login prompt
1767 * 2. Requests and verifys the password
1768 * 3. Checks the port password
1772 login_authenticate(void)
1776 int login_successful
= 0;
1779 /* if scheme broken, then nothing to do but quit */
1780 if (pam_get_item(pamh
, PAM_USER
, (void **)&user
) != PAM_SUCCESS
)
1784 * only get name from utility if it is not already
1785 * supplied by pam_start or a pam_set_item.
1787 if (!user
|| !user
[0]) {
1788 /* use call back to get user name */
1792 err
= verify_passwd();
1795 * If root login and not on system console then call exit(2)
1797 check_for_console();
1801 case PAM_NEW_AUTHTOK_REQD
:
1803 * Officially, pam_authenticate() shouldn't return this
1804 * but it's probably the right thing to return if
1805 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
1806 * be forced to change password later in this code.
1809 login_successful
= 1;
1815 case PAM_AUTHINFO_UNAVAIL
:
1816 case PAM_USER_UNKNOWN
:
1817 audit_failure(get_audit_id(), ADT_FAIL_PAM
+ err
, pwd
,
1818 remote_host
, ttyn
, zone_name
);
1823 (void) sleep(Disabletime
);
1824 (void) printf(incorrectmsg
);
1826 audit_error
= ADT_FAIL_PAM
+ err
;
1829 default: /* Some other PAM error */
1830 audit_error
= ADT_FAIL_PAM
+ err
;
1835 if (login_successful
)
1838 /* sleep after bad passwd */
1840 (void) sleep(Sleeptime
);
1841 (void) printf(incorrectmsg
);
1842 /* force name to be null in this case */
1843 if (pam_set_item(pamh
, PAM_USER
, NULL
) != PAM_SUCCESS
)
1845 if (pam_set_item(pamh
, PAM_RUSER
, NULL
) != PAM_SUCCESS
)
1847 } while (count
++ < retry
);
1849 if (count
>= retry
) {
1850 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES
, pwd
,
1851 remote_host
, ttyn
, zone_name
);
1853 * If logging is turned on, output the
1854 * string storage area to the log file,
1855 * and sleep for Disabletime
1856 * seconds before exiting.
1861 if ((pwd
= getpwnam(user_name
)) != NULL
) {
1862 if (remote_host
[0]) {
1864 "REPEATED LOGIN FAILURES ON %s "
1866 ttyn
, HMAX
, remote_host
, NMAX
,
1870 "REPEATED LOGIN FAILURES ON "
1872 ttyn
, NMAX
, user_name
);
1875 if (remote_host
[0]) {
1877 "REPEATED LOGIN FAILURES ON %s "
1879 ttyn
, HMAX
, remote_host
);
1882 "REPEATED LOGIN FAILURES ON %s",
1887 (void) sleep(Disabletime
);
1894 * *** Credential Related routines ***
1899 * setup_credentials - sets the group ID, initializes the groups
1900 * and sets up the secretkey.
1901 * Exits if a failure occurrs.
1906 * setup_credentials - PAM does all the work for us on this one.
1910 setup_credentials(void)
1914 /* set the real (and effective) GID */
1915 if (setgid(pwd
->pw_gid
) == -1) {
1920 * Initialize the supplementary group access list.
1922 if ((user_name
[0] == '\0') ||
1923 (initgroups(user_name
, pwd
->pw_gid
) == -1)) {
1924 audit_error
= ADT_FAIL_VALUE_PROGRAM
;
1928 if ((error
= pam_setcred(pamh
, zflag
? PAM_REINITIALIZE_CRED
:
1929 PAM_ESTABLISH_CRED
)) != PAM_SUCCESS
) {
1930 audit_error
= ADT_FAIL_PAM
+ error
;
1935 * Record successful login and fork process that records logout.
1936 * We have to do this after setting credentials because pam_setcred()
1937 * loads key audit info into the cred, but before setuid() so audit
1938 * system calls will work.
1940 audit_success(get_audit_id(), pwd
, zone_name
);
1947 return (ADT_rlogin
);
1949 return (ADT_telnet
);
1951 return (ADT_zlogin
);
1958 * *** Routines to get a new user set up and running ***
1960 * Things to do when starting up a new user:
1962 * update_utmpx_entry
1963 * establish_user_environment
1965 * display_last_login_time
1972 * adjust_nice - Set the nice (process priority) value if the
1973 * gecos value contains an appropriate value.
1981 if (strncmp("pri=", pwd
->pw_gecos
, 4) == 0) {
1986 if (pwd
->pw_gecos
[i
] == '-') {
1991 while (pwd
->pw_gecos
[i
] >= '0' && pwd
->pw_gecos
[i
] <= '9')
1992 pri
= (pri
* 10) + pwd
->pw_gecos
[i
++] - '0';
2002 * update_utmpx_entry - Searchs for the correct utmpx entry, making an
2003 * entry there if it finds one, otherwise exits.
2007 update_utmpx_entry(int sublogin
)
2011 static char *errmsg
= "No utmpx entry. "
2012 "You must exec \"login\" from the lowest level \"shell\".";
2014 struct utmpx
*u
= (struct utmpx
*)0;
2019 * If we're not a sublogin then
2020 * we'll get an error back if our PID doesn't match the PID of the
2021 * entry we are updating, otherwise if its a sublogin the flags
2022 * field is set to 0, which means we just write a matching entry
2023 * (without checking the pid), or a new entry if an entry doesn't
2027 if ((err
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
2028 audit_error
= ADT_FAIL_PAM
+ err
;
2032 if ((err
= pam_get_item(pamh
, PAM_USER
, (void **) &user
)) !=
2034 audit_error
= ADT_FAIL_PAM
+ err
;
2038 (void) memset((void *)&utmpx
, 0, sizeof (utmpx
));
2039 (void) time(&utmpx
.ut_tv
.tv_sec
);
2040 utmpx
.ut_pid
= getpid();
2042 if (rflag
|| hflag
) {
2043 SCPYN(utmpx
.ut_host
, remote_host
);
2044 tmplen
= strlen(remote_host
) + 1;
2045 if (tmplen
< sizeof (utmpx
.ut_host
))
2046 utmpx
.ut_syslen
= tmplen
;
2048 utmpx
.ut_syslen
= sizeof (utmpx
.ut_host
);
2051 * If this is a login from another zone, put the
2052 * zone:<zonename> string in the utmpx entry.
2054 SCPYN(utmpx
.ut_host
, zone_name
);
2055 tmplen
= strlen(zone_name
) + 1;
2056 if (tmplen
< sizeof (utmpx
.ut_host
))
2057 utmpx
.ut_syslen
= tmplen
;
2059 utmpx
.ut_syslen
= sizeof (utmpx
.ut_host
);
2061 utmpx
.ut_syslen
= 0;
2064 SCPYN(utmpx
.ut_user
, user
);
2066 /* skip over "/dev/" */
2067 ttyntail
= basename(ttyn
);
2069 while ((u
= getutxent()) != NULL
) {
2070 if ((u
->ut_type
== INIT_PROCESS
||
2071 u
->ut_type
== LOGIN_PROCESS
||
2072 u
->ut_type
== USER_PROCESS
) &&
2073 ((sublogin
&& strncmp(u
->ut_line
, ttyntail
,
2074 sizeof (u
->ut_line
)) == 0) ||
2075 u
->ut_pid
== login_pid
)) {
2076 SCPYN(utmpx
.ut_line
, (ttyn
+sizeof ("/dev/")-1));
2077 (void) memcpy(utmpx
.ut_id
, u
->ut_id
,
2078 sizeof (utmpx
.ut_id
));
2079 utmpx
.ut_exit
.e_exit
= u
->ut_exit
.e_exit
;
2080 utmpx
.ut_type
= USER_PROCESS
;
2081 (void) pututxline(&utmpx
);
2087 if (u
== (struct utmpx
*)NULL
) {
2090 * no utmpx entry already setup
2091 * (init or rlogind/telnetd)
2093 (void) puts(errmsg
);
2095 audit_error
= ADT_FAIL_VALUE_PROGRAM
;
2099 /* Now attempt to write out this entry to the wtmp file if */
2100 /* we were successful in getting it from the utmpx file and */
2101 /* the wtmp file exists. */
2102 updwtmpx(WTMPX_FILE
, &utmpx
);
2109 * process_chroot_logins - Chroots to the specified subdirectory and
2110 * re executes login.
2114 process_chroot_logins(void)
2117 * If the shell field starts with a '*', do a chroot to the home
2118 * directory and perform a new login.
2121 if (*pwd
->pw_shell
== '*') {
2122 (void) pam_end(pamh
, PAM_SUCCESS
); /* Done using PAM */
2123 pamh
= NULL
; /* really done */
2124 if (chroot(pwd
->pw_dir
) < 0) {
2125 (void) printf("No Root Directory\n");
2127 audit_failure(get_audit_id(),
2128 ADT_FAIL_VALUE_CHDIR_FAILED
,
2129 pwd
, remote_host
, ttyn
, zone_name
);
2134 * Set the environment flag <!sublogin> so that the next login
2135 * knows that it is a sublogin.
2137 envinit
[0] = SUBLOGIN
;
2138 envinit
[1] = (char *)NULL
;
2139 (void) printf("Subsystem root: %s\n", pwd
->pw_dir
);
2140 (void) execle("/usr/bin/login", "login", (char *)0,
2142 (void) execle("/etc/login", "login", (char *)0, &envinit
[0]);
2143 (void) printf("No /usr/bin/login or /etc/login on root\n");
2145 audit_error
= ADT_FAIL_VALUE_PROGRAM
;
2153 * establish_user_environment - Set up the new users enviornment
2157 establish_user_environment(char **renvp
)
2159 int i
, j
, k
, l_index
, length
, idx
= 0;
2168 /* count the number of PAM environment variables set by modules */
2169 if ((pam_env
= pam_getenvlist(pamh
)) != 0) {
2170 for (idx
= 0; pam_env
[idx
] != 0; idx
++)
2174 envinit
= (char **)calloc(lenvp
- environ
+ 10 + MAXARGS
+ idx
,
2176 if (envinit
== NULL
) {
2177 (void) printf("Calloc failed - out of swap space.\n");
2182 * add PAM environment variables first so they
2183 * can be overwritten at login's discretion.
2184 * check for illegal environment variables.
2186 idx
= 0; basicenv
= 0;
2188 while (pam_env
[idx
] != 0) {
2189 if (legalenvvar(pam_env
[idx
])) {
2190 envinit
[basicenv
] = pam_env
[idx
];
2196 (void) memcpy(&envinit
[basicenv
], newenv
, sizeof (newenv
));
2198 /* Set up environment */
2200 ENVSTRNCAT(term
, terminal
);
2202 if (strlen(terminal
)) {
2203 ENVSTRNCAT(term
, terminal
);
2206 char *tp
= getenv("TERM");
2208 if ((tp
!= NULL
) && (*tp
!= '\0'))
2209 ENVSTRNCAT(term
, tp
);
2212 ENVSTRNCAT(logname
, pwd
->pw_name
);
2215 * There are three places to get timezone info. init.c sets
2216 * TZ if the file /etc/default/init contains a value for TZ.
2217 * login.c looks in the file /etc/default/login for a
2218 * variable called TIMEZONE being set. If TIMEZONE has a
2219 * value, TZ is set to that value; no environment variable
2220 * TIMEZONE is set, only TZ. If neither of these methods
2221 * work to set TZ, then the library routines will default
2222 * to using the file /usr/lib/locale/TZ/localtime.
2224 * There is a priority set up here. If /etc/default/init has
2225 * a value for TZ, that value remains top priority. If the
2226 * file /etc/default/login has TIMEZONE set, that has second
2227 * highest priority not overriding the value of TZ in
2228 * /etc/default/init. The reason for this priority is that the
2229 * file /etc/default/init is supposed to be sourced by
2230 * /etc/profile. We are doing the "sourcing" prematurely in
2231 * init.c. Additionally, a login C shell doesn't source the
2232 * file /etc/profile thus not sourcing /etc/default/init thus not
2233 * allowing an adminstrator to globally set TZ for all users
2235 if (Def_tz
!= NULL
) /* Is there a TZ from defaults/login? */
2238 if ((Def_tz
= getenv("TZ")) != NULL
) {
2239 ENVSTRNCAT(timez
, Def_tz
);
2240 } else if (tmp_tz
!= NULL
) {
2242 ENVSTRNCAT(timez
, Def_tz
);
2245 if (Def_hertz
== NULL
)
2246 (void) sprintf(hertz
+ strlen(hertz
), "%lu", HZ
);
2248 ENVSTRNCAT(hertz
, Def_hertz
);
2250 if (Def_path
== NULL
)
2251 (void) strlcat(path
, DEF_PATH
, sizeof (path
));
2253 ENVSTRNCAT(path
, Def_path
);
2255 ENVSTRNCAT(home
, pwd
->pw_dir
);
2258 * Find the end of the basic environment
2260 for (basicenv
= 0; envinit
[basicenv
] != NULL
; basicenv
++)
2264 * If TZ has a value, add it.
2266 if (strcmp(timez
, "TZ=") != 0)
2267 envinit
[basicenv
++] = timez
;
2269 if (*pwd
->pw_shell
== '\0') {
2271 * If possible, use the primary default shell,
2272 * otherwise, use the secondary one.
2274 if (access(SHELL
, X_OK
) == 0)
2275 pwd
->pw_shell
= SHELL
;
2277 pwd
->pw_shell
= SHELL2
;
2278 } else if (Altshell
!= NULL
&& strcmp(Altshell
, "YES") == 0) {
2279 envinit
[basicenv
++] = shell
;
2280 ENVSTRNCAT(shell
, pwd
->pw_shell
);
2284 envinit
[basicenv
++] = mail
;
2285 (void) strlcat(mail
, pwd
->pw_name
, sizeof (mail
));
2289 * Pick up locale environment variables, if any.
2292 while (*lenvp
!= NULL
) {
2294 while (localeenv
[j
] != 0) {
2296 * locale_envmatch() returns 1 if
2297 * *lenvp is localenev[j] and valid.
2299 if (locale_envmatch(localeenv
[j
], *lenvp
) == 1) {
2300 envinit
[basicenv
++] = *lenvp
;
2309 * If '-p' flag, then try to pass on allowable environment
2310 * variables. Note that by processing this first, what is
2311 * passed on the final "login:" line may over-ride the invocation
2312 * values. XXX is this correct?
2315 for (lenvp
= renvp
; *lenvp
; lenvp
++) {
2316 if (!legalenvvar(*lenvp
)) {
2320 * If this isn't 'xxx=yyy', skip it. XXX
2322 if ((endptr
= strchr(*lenvp
, '=')) == NULL
) {
2325 length
= endptr
+ 1 - *lenvp
;
2326 for (j
= 0; j
< basicenv
; j
++) {
2327 if (strncmp(envinit
[j
], *lenvp
, length
) == 0) {
2329 * Replace previously established value
2331 envinit
[j
] = *lenvp
;
2335 if (j
== basicenv
) {
2337 * It's a new definition, so add it at the end.
2339 envinit
[basicenv
++] = *lenvp
;
2345 * Add in all the environment variables picked up from the
2346 * argument list to "login" or from the user response to the
2347 * "login" request, if any.
2351 goto switch_env
; /* done */
2353 for (j
= 0, k
= 0, l_index
= 0;
2354 *envp
!= NULL
&& j
< (MAXARGS
-1);
2358 * Scan each string provided. If it doesn't have the
2359 * format xxx=yyy, then add the string "Ln=" to the beginning.
2361 if ((endptr
= strchr(*envp
, '=')) == NULL
) {
2363 * This much to be malloc'd:
2364 * strlen(*envp) + 1 char for 'L' +
2365 * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
2367 * total = strlen(*envp) + MAXARGSWIDTH + 3
2369 int total
= strlen(*envp
) + MAXARGSWIDTH
+ 3;
2370 envinit
[basicenv
+k
] = malloc(total
);
2371 if (envinit
[basicenv
+k
] == NULL
) {
2372 (void) printf("%s: malloc failed\n", PROG_NAME
);
2375 (void) snprintf(envinit
[basicenv
+k
], total
, "L%d=%s",
2381 if (!legalenvvar(*envp
)) { /* this env var permited? */
2386 * Check to see whether this string replaces
2387 * any previously defined string
2389 for (i
= 0, length
= endptr
+ 1 - *envp
;
2390 i
< basicenv
+ k
; i
++) {
2391 if (strncmp(*envp
, envinit
[i
], length
)
2399 * If it doesn't, place it at the end of
2400 * environment array.
2402 if (i
== basicenv
+k
) {
2403 envinit
[basicenv
+k
] = *envp
;
2408 } /* for (j = 0 ... ) */
2412 * Switch to the new environment.
2418 * print_banner - Print the banner at start up
2419 * Do not turn on DOBANNER ifdef. This is not
2420 * relevant to SunOS.
2429 (void) printf("UNIX System V/386 Release %s\n%s\n"
2430 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
2431 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
2432 un
.release
, un
.nodename
);
2434 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
2435 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
2436 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
2437 "All Rights Reserved\n",
2438 un
.release
, un
.machine
, un
.nodename
);
2440 (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
2441 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
2442 un
.release
, un
.machine
, un
.nodename
);
2444 #endif /* DOBANNER */
2448 * display_last_login_time - Advise the user the time and date
2449 * that this login-id was last used.
2453 display_last_login_time(void)
2456 (void) printf("Last login: %.*s ", 24-5, ctime(&ll
.ll_time
));
2458 if (*ll
.ll_host
!= '\0')
2459 (void) printf("from %.*s\n", sizeof (ll
.ll_host
),
2462 (void) printf("on %.*s\n", sizeof (ll
.ll_line
),
2468 * exec_the_shell - invoke the specified shell or start up program
2472 exec_the_shell(void)
2477 (void) strlcat(minusnam
, basename(pwd
->pw_shell
),
2483 (void) execl(pwd
->pw_shell
, minusnam
, (char *)0);
2486 * pwd->pw_shell was not an executable object file, maybe it
2487 * is a shell proceedure or a command line with arguments.
2488 * If so, turn off the SHELL= environment variable.
2490 for (i
= 0; envinit
[i
] != NULL
; ++i
) {
2491 if ((envinit
[i
] == shell
) &&
2492 ((endptr
= strchr(shell
, '=')) != NULL
))
2496 if (access(pwd
->pw_shell
, R_OK
|X_OK
) == 0) {
2497 (void) execl(SHELL
, "sh", pwd
->pw_shell
, (char *)0);
2498 (void) execl(SHELL2
, "sh", pwd
->pw_shell
, (char *)0);
2501 (void) printf("No shell\n");
2505 * login_exit - Call exit() and terminate.
2506 * This function is here for PAM so cleanup can
2507 * be done before the process exits.
2510 login_exit(int exit_code
)
2513 (void) pam_end(pamh
, PAM_ABORT
);
2516 audit_failure(get_audit_id(), audit_error
,
2517 pwd
, remote_host
, ttyn
, zone_name
);
2524 * Check if lenv and penv matches or not.
2527 locale_envmatch(char *lenv
, char *penv
)
2529 while ((*lenv
== *penv
) && *lenv
&& *penv
!= '=') {
2535 * '/' is eliminated for security reason.
2537 if (*lenv
== '\0' && *penv
== '=' && *(penv
+ 1) != '/')
2543 is_number(char *ptr
)
2545 while (*ptr
!= '\0') {