dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / login / login.c
blob9731f0249b767c091a0eaab79a3610ff3e453e26
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
33 * All Rights Reserved
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
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
46 * explained here:
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 */
65 #include <signal.h>
66 #include <stdio.h>
67 #include <sys/stat.h>
68 #include <string.h>
69 #include <deflt.h>
70 #include <grp.h>
71 #include <fcntl.h>
72 #include <termio.h>
73 #include <utmpx.h>
74 #include <stdlib.h>
75 #include <wait.h>
76 #include <errno.h>
77 #include <ctype.h>
78 #include <syslog.h>
79 #include <ulimit.h>
80 #include <libgen.h>
81 #include <pwd.h>
82 #include <security/pam_appl.h>
83 #include <strings.h>
84 #include <libdevinfo.h>
85 #include <zone.h>
86 #include "login_audit.h"
88 #include <krb5_repository.h>
91 * *** Defines, Macros, and String Constants ***
96 #define ISSUEFILE "/etc/issue" /* file to print before prompt */
97 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
100 * These need to be defined for UTMPX management.
101 * If we add in the utility functions later, we
102 * can remove them.
104 #define __UPDATE_ENTRY 1
105 #define __LOGIN 2
108 * Intervals to sleep after failed login
110 #ifndef SLEEPTIME
111 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
112 #endif
113 static int Sleeptime = SLEEPTIME;
116 * seconds login disabled after allowable number of unsuccessful attempts
118 #ifndef DISABLETIME
119 #define DISABLETIME 20
120 #endif
121 static int Disabletime = DISABLETIME;
123 #define MAXTRYS 5
125 static int retry = MAXTRYS;
128 * Login logging support
130 #define LOGINLOG "/var/adm/loginlog" /* login log file */
131 #define LNAME_SIZE 20 /* size of logged logname */
132 #define TTYN_SIZE 15 /* size of logged tty name */
133 #define TIME_SIZE 30 /* size of logged time string */
134 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
135 #define L_WAITTIME 5 /* waittime for log file to unlock */
136 #define LOGTRYS 10 /* depth of 'try' logging */
139 * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
140 * SCPYL is the safer version of SCPYN
142 #define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a))
143 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
144 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
145 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
146 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
149 * Other macros
151 #define NMAX sizeof (((struct utmpx *)0)->ut_name)
152 #define HMAX sizeof (((struct utmpx *)0)->ut_host)
153 #define min(a, b) (((a) < (b)) ? (a) : (b))
156 * Various useful files and string constants
158 #define SHELL "/usr/bin/sh"
159 #define SHELL2 "/sbin/sh"
160 #define SUBLOGIN "<!sublogin>"
161 #define PROG_NAME "login"
162 #define HUSHLOGIN ".hushlogin"
165 * Array and Buffer sizes
167 #define PBUFSIZE 8 /* max significant characters in a password */
168 #define MAXARGS 63 /* change value below if changing this */
169 #define MAXARGSWIDTH 2 /* log10(MAXARGS) */
170 #define MAXENV 1024
171 #define MAXLINE 2048
174 * Miscellaneous constants
176 #define ROOTUID 0
177 #define ERROR 1
178 #define OK 0
179 #define LOG_ERROR 1
180 #define DONT_LOG_ERROR 0
181 #define TRUE 1
182 #define FALSE 0
185 * Counters for counting the number of failed login attempts
187 static int trys = 0;
188 static int count = 1;
191 * error value for login_exit() audit output (0 == no audit record)
193 static int audit_error = 0;
196 * Externs a plenty
198 extern int getsecretkey();
201 * The current user name
203 static char user_name[NMAX];
204 static char minusnam[16] = "-";
207 * login_pid, used to find utmpx entry to update.
209 static pid_t login_pid;
212 * locale environments to be passed to shells.
214 static char *localeenv[] = {
215 "LANG",
216 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
217 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
218 static int locale_envmatch(char *, char *);
221 * Environment variable support
223 static char shell[256] = { "SHELL=" };
224 static char home[MAXPATHLEN] = { "HOME=" };
225 static char term[64] = { "TERM=" };
226 static char logname[30] = { "LOGNAME=" };
227 static char timez[100] = { "TZ=" };
228 static char hertz[10] = { "HZ=" };
229 static char path[MAXPATHLEN] = { "PATH=" };
230 static char *newenv[10+MAXARGS] =
231 {home, path, logname, hertz, term, 0, 0};
232 static char **envinit = newenv;
233 static int basicenv;
234 static char *zero = NULL;
235 static char **envp;
236 #ifndef NO_MAIL
237 static char mail[30] = { "MAIL=/var/mail/" };
238 #endif
239 extern char **environ;
240 static char inputline[MAXLINE];
242 #define MAX_ID_LEN 256
243 #define MAX_REPOSITORY_LEN 256
244 #define MAX_PAMSERVICE_LEN 256
246 static char identity[MAX_ID_LEN];
247 static char repository[MAX_REPOSITORY_LEN];
248 static char progname[MAX_PAMSERVICE_LEN];
252 * Strings used to prompt the user.
254 static char loginmsg[] = "login: ";
255 static char passwdmsg[] = "Password:";
256 static char incorrectmsg[] = "Login incorrect\n";
259 * Password file support
261 static struct passwd *pwd = NULL;
262 static char remote_host[HMAX];
263 static char zone_name[ZONENAME_MAX];
266 * Log file support
268 static char *log_entry[LOGTRYS];
269 static int writelog = 0;
270 static int dosyslog = 0;
271 static int flogin = MAXTRYS; /* flag for SYSLOG_FAILED_LOGINS */
274 * Default file toggles
276 static char *Pndefault = "/etc/default/login";
277 static char *Altshell = NULL;
278 static char *Console = NULL;
279 static int Passreqflag = 0;
281 #define DEFUMASK 022
282 static mode_t Umask = DEFUMASK;
283 static char *Def_tz = NULL;
284 static char *tmp_tz = NULL;
285 static char *Def_hertz = NULL;
286 #define SET_FSIZ 2 /* ulimit() command arg */
287 static long Def_ulimit = 0;
288 #define MAX_TIMEOUT (15 * 60)
289 #define DEF_TIMEOUT (5 * 60)
290 static unsigned Def_timeout = DEF_TIMEOUT;
291 static char *Def_path = NULL;
292 static char *Def_supath = NULL;
293 #define DEF_PATH "/usr/bin:" /* same as PATH */
294 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
297 * Defaults for updating expired passwords
299 #define DEF_ATTEMPTS 3
302 * ttyprompt will point to the environment variable TTYPROMPT.
303 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
305 static char *ttyprompt = NULL;
306 static char *ttyn = NULL;
309 * Pass inherited environment. Used by telnetd in support of the telnet
310 * ENVIRON option.
312 static boolean_t pflag = B_FALSE;
313 static boolean_t uflag = B_FALSE;
314 static boolean_t Rflag = B_FALSE;
315 static boolean_t sflag = B_FALSE;
316 static boolean_t tflag = B_FALSE;
317 static boolean_t hflag = B_FALSE;
318 static boolean_t zflag = B_FALSE;
321 * Remote login support
323 static char lusername[NMAX+1];
324 static char terminal[MAXPATHLEN];
327 * Pre-authentication flag support
329 static int fflag;
331 static char ** getargs(char *);
333 static int login_conv(int, struct pam_message **,
334 struct pam_response **, void *);
336 static struct pam_conv pam_conv = {login_conv, NULL};
337 static pam_handle_t *pamh; /* Authentication handle */
340 * Function declarations
342 static void turn_on_logging(void);
343 static void defaults(void);
344 static void usage(void);
345 static void login_authenticate();
346 static void setup_credentials(void);
347 static void adjust_nice(void);
348 static void update_utmpx_entry(int, boolean_t);
349 static void establish_user_environment(char **);
350 static void exec_the_shell(void);
351 static int process_chroot_logins(void);
352 static void chdir_to_dir_user(void);
353 static void validate_account(void);
354 static int get_options(int, char **);
355 static int legalenvvar(char *);
356 static void check_for_console(void);
357 static void check_for_dueling_unix(char *);
358 static void get_user_name(void);
359 static uint_t get_audit_id(void);
360 static void login_exit(int)__NORETURN;
361 static int logins_disabled(char *);
362 static void log_bad_attempts(void);
363 static int is_number(char *);
364 static void printmotd(void);
367 * *** main ***
369 * The primary flow of control is directed in this routine.
370 * Control moves in line from top to bottom calling subfunctions
371 * which perform the bulk of the work. Many of these calls exit
372 * when a fatal error is encountered and do not return to main.
378 main(int argc, char *argv[], char **renvp)
380 int sublogin;
381 int pam_rc;
382 boolean_t silent = B_FALSE;
384 login_pid = getpid();
387 * Set up Defaults and flags
389 defaults();
390 SCPYL(progname, PROG_NAME);
393 * Set up default umask
395 if (Umask > ((mode_t)0777))
396 Umask = DEFUMASK;
397 (void) umask(Umask);
400 * Set up default timeouts and delays
402 if (Def_timeout > MAX_TIMEOUT)
403 Def_timeout = MAX_TIMEOUT;
404 if (Sleeptime < 0 || Sleeptime > 5)
405 Sleeptime = SLEEPTIME;
407 (void) alarm(Def_timeout);
410 * Ignore SIGQUIT and SIGINT and set nice to 0
412 (void) signal(SIGQUIT, SIG_IGN);
413 (void) signal(SIGINT, SIG_IGN);
414 (void) nice(0);
417 * Set flag to disable the pid check if you find that you are
418 * a subsystem login.
420 sublogin = 0;
421 if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
422 sublogin = 1;
425 * Parse Arguments
427 if (get_options(argc, argv) == -1) {
428 usage();
429 audit_error = ADT_FAIL_VALUE_BAD_CMD;
430 login_exit(1);
434 * if devicename is not passed as argument, call ttyname(0)
436 if (ttyn == NULL) {
437 ttyn = ttyname(0);
438 if (ttyn == NULL)
439 ttyn = "/dev/???";
443 * Call pam_start to initiate a PAM authentication operation
446 if ((pam_rc = pam_start(progname, user_name, &pam_conv, &pamh))
447 != PAM_SUCCESS) {
448 audit_error = ADT_FAIL_PAM + pam_rc;
449 login_exit(1);
451 if ((pam_rc = pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
452 audit_error = ADT_FAIL_PAM + pam_rc;
453 login_exit(1);
455 if ((pam_rc = pam_set_item(pamh, PAM_RHOST, remote_host)) !=
456 PAM_SUCCESS) {
457 audit_error = ADT_FAIL_PAM + pam_rc;
458 login_exit(1);
462 * We currently only support special handling of the KRB5 PAM repository
464 if ((Rflag && strlen(repository)) &&
465 strcmp(repository, KRB5_REPOSITORY_NAME) == 0 &&
466 (uflag && strlen(identity))) {
467 krb5_repository_data_t krb5_data;
468 pam_repository_t pam_rep_data;
470 krb5_data.principal = identity;
471 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
473 pam_rep_data.type = repository;
474 pam_rep_data.scope = (void *)&krb5_data;
475 pam_rep_data.scope_len = sizeof (krb5_data);
477 (void) pam_set_item(pamh, PAM_REPOSITORY,
478 (void *)&pam_rep_data);
482 * Open the log file which contains a record of successful and failed
483 * login attempts
485 turn_on_logging();
488 * say "hi" to syslogd ..
490 openlog("login", 0, LOG_AUTH);
493 * validate user
495 /* we are already authenticated. fill in what we must, then continue */
496 if (fflag) {
497 if ((pwd = getpwnam(user_name)) == NULL) {
498 audit_error = ADT_FAIL_VALUE_USERNAME;
500 log_bad_attempts();
501 (void) printf("Login failed: unknown user '%s'.\n",
502 user_name);
503 login_exit(1);
505 } else {
507 * Perform the primary login authentication activity.
509 login_authenticate();
512 /* change root login, then we exec another login and try again */
513 if (process_chroot_logins() != OK)
514 login_exit(1);
517 * If root login and not on system console then call exit(2)
519 check_for_console();
522 * Check to see if a shutdown is in progress, if it is and
523 * we are not root then throw the user off the system
525 if (logins_disabled(user_name) == TRUE) {
526 audit_error = ADT_FAIL_VALUE_LOGIN_DISABLED;
527 login_exit(1);
530 if (pwd->pw_uid == 0) {
531 if (Def_supath != NULL)
532 Def_path = Def_supath;
533 else
534 Def_path = DEF_SUPATH;
538 * Check account expiration and passwd aging
540 validate_account();
543 * We only get here if we've been authenticated.
547 * Now we set up the environment for the new user, which includes
548 * the users ulimit, nice value, ownership of this tty, uid, gid,
549 * and environment variables.
551 if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
552 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
554 /* di_devperm_login() sends detailed errors to syslog */
555 if (di_devperm_login((const char *)ttyn, pwd->pw_uid, pwd->pw_gid,
556 NULL) == -1) {
557 (void) fprintf(stderr, "error processing /etc/logindevperm,"
558 " see syslog for more details\n");
561 adjust_nice(); /* passwd file can specify nice value */
563 setup_credentials(); /* Set user credentials - exits on failure */
565 if (chdir(pwd->pw_dir) == 0)
566 silent = (access(HUSHLOGIN, F_OK) == 0);
568 * NOTE: telnetd relies upon this updating of utmpx
569 * to indicate that the authentication completed successfully,
570 * pam_open_session was called and therefore they are required to
571 * call pam_close_session.
573 update_utmpx_entry(sublogin, silent);
575 /* set the real (and effective) UID */
576 if (setuid(pwd->pw_uid) == -1) {
577 login_exit(1);
581 * Set up the basic environment for the exec. This includes
582 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
584 chdir_to_dir_user();
586 establish_user_environment(renvp);
588 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
589 pamh = NULL;
591 if (pwd->pw_uid == 0) {
592 if (dosyslog) {
593 if (remote_host[0]) {
594 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
595 ttyn, HMAX, remote_host);
596 } else
597 syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
600 closelog();
602 if (!silent)
603 printmotd();
605 (void) signal(SIGQUIT, SIG_DFL);
606 (void) signal(SIGINT, SIG_DFL);
609 * Set SIGXCPU and SIGXFSZ to default disposition.
610 * Shells inherit signal disposition from parent.
611 * And the shells should have default dispositions
612 * for the two below signals.
614 (void) signal(SIGXCPU, SIG_DFL);
615 (void) signal(SIGXFSZ, SIG_DFL);
618 * Now fire off the shell of choice
620 exec_the_shell();
623 * All done
625 login_exit(1);
626 return (0);
631 * *** Utility functions ***
637 * donothing & catch - Signal catching functions
640 /*ARGSUSED*/
641 static void
642 donothing(int sig)
644 if (pamh)
645 (void) pam_end(pamh, PAM_ABORT);
648 #ifdef notdef
649 static int intrupt;
651 /*ARGSUSED*/
652 static void
653 catch(int sig)
655 ++intrupt;
657 #endif
660 * *** Bad login logging support ***
664 * badlogin() - log to the log file 'trys'
665 * unsuccessful attempts
668 static void
669 badlogin(void)
671 int retval, count1, fildes;
674 * Tries to open the log file. If succeed, lock it and write
675 * in the failed attempts
677 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
679 (void) sigset(SIGALRM, donothing);
680 (void) alarm(L_WAITTIME);
681 retval = lockf(fildes, F_LOCK, 0L);
682 (void) alarm(0);
683 (void) sigset(SIGALRM, SIG_DFL);
684 if (retval == 0) {
685 for (count1 = 0; count1 < trys; count1++)
686 (void) write(fildes, log_entry[count1],
687 (unsigned)strlen(log_entry[count1]));
688 (void) lockf(fildes, F_ULOCK, 0L);
690 (void) close(fildes);
696 * log_bad_attempts - log each bad login attempt - called from
697 * login_authenticate. Exits when the maximum attempt
698 * count is exceeded.
701 static void
702 log_bad_attempts(void)
704 time_t timenow;
706 if (trys >= LOGTRYS)
707 return;
708 if (writelog) {
709 (void) time(&timenow);
710 (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
711 (void) strncat(log_entry[trys], ":", (size_t)1);
712 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
713 (void) strncat(log_entry[trys], ":", (size_t)1);
714 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE);
715 trys++;
717 if (count > flogin) {
718 if ((pwd = getpwnam(user_name)) != NULL) {
719 if (remote_host[0]) {
720 syslog(LOG_NOTICE,
721 "Login failure on %s from %.*s, "
722 "%.*s", ttyn, HMAX, remote_host,
723 NMAX, user_name);
724 } else {
725 syslog(LOG_NOTICE,
726 "Login failure on %s, %.*s",
727 ttyn, NMAX, user_name);
729 } else {
730 if (remote_host[0]) {
731 syslog(LOG_NOTICE,
732 "Login failure on %s from %.*s",
733 ttyn, HMAX, remote_host);
734 } else {
735 syslog(LOG_NOTICE,
736 "Login failure on %s", ttyn);
744 * turn_on_logging - if the logfile exist, turn on attempt logging and
745 * initialize the string storage area
748 static void
749 turn_on_logging(void)
751 struct stat dbuf;
752 int i;
754 if (stat(LOGINLOG, &dbuf) == 0) {
755 writelog = 1;
756 for (i = 0; i < LOGTRYS; i++) {
757 if (!(log_entry[i] = malloc((size_t)ENT_SIZE))) {
758 writelog = 0;
759 break;
761 *log_entry[i] = '\0';
768 * login_conv():
769 * This is the conv (conversation) function called from
770 * a PAM authentication module to print error messages
771 * or garner information from the user.
773 /*ARGSUSED*/
774 static int
775 login_conv(int num_msg, struct pam_message **msg,
776 struct pam_response **response, void *appdata_ptr)
778 struct pam_message *m;
779 struct pam_response *r;
780 char *temp;
781 int k, i;
783 if (num_msg <= 0)
784 return (PAM_CONV_ERR);
786 *response = calloc(num_msg, sizeof (struct pam_response));
787 if (*response == NULL)
788 return (PAM_BUF_ERR);
790 k = num_msg;
791 m = *msg;
792 r = *response;
793 while (k--) {
795 switch (m->msg_style) {
797 case PAM_PROMPT_ECHO_OFF:
798 errno = 0;
799 temp = getpassphrase(m->msg);
800 if (temp != NULL) {
801 if (errno == EINTR)
802 return (PAM_CONV_ERR);
804 r->resp = strdup(temp);
805 if (r->resp == NULL) {
806 /* free responses */
807 r = *response;
808 for (i = 0; i < num_msg; i++, r++) {
809 free(r->resp);
811 free(*response);
812 *response = NULL;
813 return (PAM_BUF_ERR);
817 m++;
818 r++;
819 break;
821 case PAM_PROMPT_ECHO_ON:
822 if (m->msg != NULL)
823 (void) fputs(m->msg, stdout);
824 r->resp = calloc(1, PAM_MAX_RESP_SIZE);
825 if (r->resp == NULL) {
826 /* free responses */
827 r = *response;
828 for (i = 0; i < num_msg; i++, r++) {
829 free(r->resp);
831 free(*response);
832 *response = NULL;
833 return (PAM_BUF_ERR);
836 * The response might include environment variables
837 * information. We should store that information in
838 * envp if there is any; otherwise, envp is set to
839 * NULL.
841 bzero((void *)inputline, MAXLINE);
843 envp = getargs(inputline);
845 /* If we read in any input, process it. */
846 if (inputline[0] != '\0') {
847 int len;
849 if (envp != (char **)NULL)
851 * If getargs() did not return NULL,
852 * *envp is the first string in
853 * inputline. envp++ makes envp point
854 * to environment variables information
855 * or be NULL.
857 envp++;
859 (void) strncpy(r->resp, inputline,
860 PAM_MAX_RESP_SIZE-1);
861 r->resp[PAM_MAX_RESP_SIZE-1] = 0;
862 len = strlen(r->resp);
863 if (r->resp[len-1] == '\n')
864 r->resp[len-1] = '\0';
865 } else {
866 login_exit(1);
868 m++;
869 r++;
870 break;
872 case PAM_ERROR_MSG:
873 if (m->msg != NULL) {
874 (void) fputs(m->msg, stderr);
875 (void) fputs("\n", stderr);
877 m++;
878 r++;
879 break;
880 case PAM_TEXT_INFO:
881 if (m->msg != NULL) {
882 (void) fputs(m->msg, stdout);
883 (void) fputs("\n", stdout);
885 m++;
886 r++;
887 break;
889 default:
890 break;
893 return (PAM_SUCCESS);
897 * verify_passwd - Authenticates the user.
898 * Returns: PAM_SUCCESS if authentication successful,
899 * PAM error code if authentication fails.
902 static int
903 verify_passwd(void)
905 int error;
906 char *user;
907 int flag = (Passreqflag ? PAM_DISALLOW_NULL_AUTHTOK : 0);
910 * PAM authenticates the user for us.
912 error = pam_authenticate(pamh, flag);
914 /* get the user_name from the pam handle */
915 (void) pam_get_item(pamh, PAM_USER, (void**)&user);
917 if (user == NULL || *user == '\0')
918 return (PAM_SYSTEM_ERR);
920 SCPYL(user_name, user);
921 check_for_dueling_unix(user_name);
923 if (((pwd = getpwnam(user_name)) == NULL) &&
924 (error != PAM_USER_UNKNOWN)) {
925 return (PAM_SYSTEM_ERR);
928 return (error);
932 * quotec - Called by getargs
935 static int
936 quotec(void)
938 int c, i, num;
940 switch (c = getc(stdin)) {
942 case 'n':
943 c = '\n';
944 break;
946 case 'r':
947 c = '\r';
948 break;
950 case 'v':
951 c = '\013';
952 break;
954 case 'b':
955 c = '\b';
956 break;
958 case 't':
959 c = '\t';
960 break;
962 case 'f':
963 c = '\f';
964 break;
966 case '0':
967 case '1':
968 case '2':
969 case '3':
970 case '4':
971 case '5':
972 case '6':
973 case '7':
974 for (num = 0, i = 0; i < 3; i++) {
975 num = num * 8 + (c - '0');
976 if ((c = getc(stdin)) < '0' || c > '7')
977 break;
979 (void) ungetc(c, stdin);
980 c = num & 0377;
981 break;
983 default:
984 break;
986 return (c);
990 * getargs - returns an input line. Exits if EOF encountered.
992 #define WHITESPACE 0
993 #define ARGUMENT 1
995 static char **
996 getargs(char *input_line)
998 static char envbuf[MAXLINE];
999 static char *args[MAXARGS];
1000 char *ptr, **answer;
1001 int c;
1002 int state;
1003 char *p = input_line;
1005 ptr = envbuf;
1006 answer = &args[0];
1007 state = WHITESPACE;
1009 while ((c = getc(stdin)) != EOF && answer < &args[MAXARGS-1]) {
1011 *(input_line++) = c;
1013 switch (c) {
1015 case '\n':
1016 if (ptr == &envbuf[0])
1017 return ((char **)NULL);
1018 *input_line = *ptr = '\0';
1019 *answer = NULL;
1020 return (&args[0]);
1022 case ' ':
1023 case '\t':
1024 if (state == ARGUMENT) {
1025 *ptr++ = '\0';
1026 state = WHITESPACE;
1028 break;
1030 case '\\':
1031 c = quotec();
1033 default:
1034 if (state == WHITESPACE) {
1035 *answer++ = ptr;
1036 state = ARGUMENT;
1038 *ptr++ = c;
1041 /* Attempt at overflow, exit */
1042 if (input_line - p >= MAXLINE - 1 ||
1043 ptr >= &envbuf[sizeof (envbuf) - 1]) {
1044 audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW;
1045 login_exit(1);
1050 * If we left loop because an EOF was received or we've overflown
1051 * args[], exit immediately.
1053 login_exit(0);
1054 /* NOTREACHED */
1058 * get_user_name - Gets the user name either passed in, or from the
1059 * login: prompt.
1062 static void
1063 get_user_name(void)
1065 FILE *fp;
1067 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1068 char *ptr, buffer[BUFSIZ];
1069 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
1070 (void) fputs(ptr, stdout);
1072 (void) fclose(fp);
1076 * if TTYPROMPT is not set, use our own prompt
1077 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1078 * and let the module do the prompting.
1081 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1082 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1083 else
1084 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1086 envp = &zero; /* XXX: is this right? */
1091 * Check_for_dueling_unix - Check to see if the another login is talking
1092 * to the line we've got open as a login port
1093 * Exits if we're talking to another unix system
1096 static void
1097 check_for_dueling_unix(char *inputline)
1099 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1100 EQN(incorrectmsg, inputline)) {
1101 (void) printf("Looking at a login line.\n");
1102 login_exit(8);
1107 * logins_disabled - if the file /etc/nologin exists and the user is not
1108 * root then do not permit them to login
1110 static int
1111 logins_disabled(char *user_name)
1113 FILE *nlfd;
1114 int c;
1115 if (!EQN("root", user_name) &&
1116 ((nlfd = fopen(NOLOGIN, "r")) != NULL)) {
1117 while ((c = getc(nlfd)) != EOF)
1118 (void) putchar(c);
1119 (void) fflush(stdout);
1120 (void) sleep(5);
1121 return (TRUE);
1123 return (FALSE);
1126 #define DEFAULT_CONSOLE "/dev/console"
1129 * check_for_console - Checks if we're getting a root login on the
1130 * console, or a login from the global zone. Exits if not.
1132 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins
1133 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global
1134 * zones, but checking them does no harm.
1136 static void
1137 check_for_console(void)
1139 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL };
1140 int i;
1142 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE ||
1143 Console == NULL)
1144 return;
1146 if (strcmp(Console, DEFAULT_CONSOLE) == 0) {
1147 for (i = 0; consoles[i] != NULL; i ++) {
1148 if (strncmp(ttyn, consoles[i],
1149 strlen(consoles[i])) == 0)
1150 return;
1152 } else {
1153 if (strcmp(ttyn, Console) == 0)
1154 return;
1157 (void) printf("Not on system console\n");
1159 audit_error = ADT_FAIL_VALUE_CONSOLE;
1160 login_exit(10);
1165 * List of environment variables or environment variable prefixes that should
1166 * not be propagated across logins, such as when the login -p option is used.
1168 static const char *const illegal[] = {
1169 "SHELL=",
1170 "HOME=",
1171 "LOGNAME=",
1172 #ifndef NO_MAIL
1173 "MAIL=",
1174 #endif
1175 "CDPATH=",
1176 "IFS=",
1177 "PATH=",
1178 "LD_",
1179 "SMF_",
1180 NULL
1184 * legalenvvar - Is it legal to insert this environmental variable?
1187 static int
1188 legalenvvar(char *s)
1190 const char *const *p;
1192 for (p = &illegal[0]; *p; p++) {
1193 if (strncmp(s, *p, strlen(*p)) == 0)
1194 return (0);
1197 return (1);
1201 * defaults - read defaults
1204 static void
1205 defaults(void)
1207 int flags;
1208 char *ptr;
1210 if (defopen(Pndefault) == 0) {
1212 * ignore case
1214 flags = defcntl(DC_GETFLAGS, 0);
1215 TURNOFF(flags, DC_CASE);
1216 (void) defcntl(DC_SETFLAGS, flags);
1218 if ((Console = defread("CONSOLE=")) != NULL)
1219 Console = strdup(Console);
1221 if ((Altshell = defread("ALTSHELL=")) != NULL)
1222 Altshell = strdup(Altshell);
1224 if ((ptr = defread("PASSREQ=")) != NULL &&
1225 strcasecmp("YES", ptr) == 0)
1226 Passreqflag = 1;
1228 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1229 Def_tz = strdup(Def_tz);
1231 if ((Def_hertz = defread("HZ=")) != NULL)
1232 Def_hertz = strdup(Def_hertz);
1234 if ((Def_path = defread("PATH=")) != NULL)
1235 Def_path = strdup(Def_path);
1237 if ((Def_supath = defread("SUPATH=")) != NULL)
1238 Def_supath = strdup(Def_supath);
1240 if ((ptr = defread("ULIMIT=")) != NULL)
1241 Def_ulimit = atol(ptr);
1243 if ((ptr = defread("TIMEOUT=")) != NULL)
1244 Def_timeout = (unsigned)atoi(ptr);
1246 if ((ptr = defread("UMASK=")) != NULL)
1247 if (sscanf(ptr, "%lo", &Umask) != 1)
1248 Umask = DEFUMASK;
1250 if ((ptr = defread("SLEEPTIME=")) != NULL) {
1251 if (is_number(ptr))
1252 Sleeptime = atoi(ptr);
1255 if ((ptr = defread("DISABLETIME=")) != NULL) {
1256 if (is_number(ptr))
1257 Disabletime = atoi(ptr);
1260 if ((ptr = defread("SYSLOG=")) != NULL)
1261 dosyslog = strcmp(ptr, "YES") == 0;
1263 if ((ptr = defread("RETRIES=")) != NULL) {
1264 if (is_number(ptr))
1265 retry = atoi(ptr);
1268 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) {
1269 if (is_number(ptr))
1270 flogin = atoi(ptr);
1271 else
1272 flogin = retry;
1273 } else
1274 flogin = retry;
1275 (void) defopen(NULL);
1281 * get_options(argc, argv)
1282 * - parse the cmd line.
1283 * - return 0 if successful, -1 if failed.
1284 * Calls login_exit() on misuse of -r, -h, and -z flags
1287 static int
1288 get_options(int argc, char *argv[])
1290 int c;
1291 int errflg = 0;
1292 char sflagname[NMAX+1];
1293 const char *flags_message = "Only one of -r, -h and -z allowed\n";
1295 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
1296 switch (c) {
1297 case 'a':
1298 break;
1300 case 'd':
1302 * Must be root to pass in device name
1303 * otherwise we exit() as punishment for trying.
1305 if (getuid() != 0 || geteuid() != 0) {
1306 audit_error = ADT_FAIL_VALUE_DEVICE_PERM;
1307 login_exit(1); /* sigh */
1308 /*NOTREACHED*/
1310 ttyn = optarg;
1311 break;
1313 case 'h':
1314 if (hflag || zflag) {
1315 (void) fprintf(stderr, flags_message);
1316 login_exit(1);
1318 hflag = B_TRUE;
1319 SCPYL(remote_host, optarg);
1320 if (argv[optind]) {
1321 if (argv[optind][0] != '-') {
1322 SCPYL(terminal, argv[optind]);
1323 optind++;
1324 } else {
1326 * Allow "login -h hostname -" to
1327 * skip setting up an username as "-".
1329 if (argv[optind][1] == '\0')
1330 optind++;
1334 SCPYL(progname, "telnet");
1335 break;
1337 case 'p':
1338 pflag = B_TRUE;
1339 break;
1341 case 'f':
1343 * Must be root to bypass authentication
1344 * otherwise we exit() as punishment for trying.
1346 if (getuid() != 0 || geteuid() != 0) {
1347 audit_error = ADT_FAIL_VALUE_AUTH_BYPASS;
1349 login_exit(1); /* sigh */
1350 /*NOTREACHED*/
1352 /* save fflag user name for future use */
1353 SCPYL(user_name, optarg);
1354 fflag = B_TRUE;
1355 break;
1356 case 'u':
1357 if (!strlen(optarg)) {
1358 (void) fprintf(stderr,
1359 "Empty string supplied with -u\n");
1360 login_exit(1);
1362 SCPYL(identity, optarg);
1363 uflag = B_TRUE;
1364 break;
1365 case 's':
1366 if (!strlen(optarg)) {
1367 (void) fprintf(stderr,
1368 "Empty string supplied with -s\n");
1369 login_exit(1);
1371 SCPYL(sflagname, optarg);
1372 sflag = B_TRUE;
1373 break;
1374 case 'R':
1375 if (!strlen(optarg)) {
1376 (void) fprintf(stderr,
1377 "Empty string supplied with -R\n");
1378 login_exit(1);
1380 SCPYL(repository, optarg);
1381 Rflag = B_TRUE;
1382 break;
1383 case 't':
1384 if (!strlen(optarg)) {
1385 (void) fprintf(stderr,
1386 "Empty string supplied with -t\n");
1387 login_exit(1);
1389 SCPYL(terminal, optarg);
1390 tflag = B_TRUE;
1391 break;
1392 case 'z':
1393 if (hflag || zflag) {
1394 (void) fprintf(stderr, flags_message);
1395 login_exit(1);
1397 (void) snprintf(zone_name, sizeof (zone_name),
1398 "zone:%s", optarg);
1399 SCPYL(progname, "zlogin");
1400 zflag = B_TRUE;
1401 break;
1402 default:
1403 errflg++;
1404 break;
1405 } /* end switch */
1406 } /* end while */
1409 * If the 's svcname' flag was used, override the progname
1410 * value that is to be used in the pam_start call.
1412 if (sflag)
1413 SCPYL(progname, sflagname);
1416 * get the prompt set by ttymon
1418 ttyprompt = getenv("TTYPROMPT");
1420 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1422 * if ttyprompt is set, there should be data on
1423 * the stream already.
1425 if ((envp = getargs(inputline)) != (char **)NULL) {
1427 * don't get name if name passed as argument.
1429 SCPYL(user_name, *envp++);
1431 } else if (optind < argc) {
1432 SCPYL(user_name, argv[optind]);
1433 (void) SCPYL(inputline, user_name);
1434 (void) strlcat(inputline, " \n", sizeof (inputline));
1435 envp = &argv[optind+1];
1437 if (!fflag)
1438 SCPYL(lusername, user_name);
1441 if (errflg)
1442 return (-1);
1443 return (0);
1447 * usage - Print usage message
1450 static void
1451 usage(void)
1453 (void) fprintf(stderr,
1454 "usage:\n"
1455 " login [-p] [-d device] [-R repository] [-s service]\n"
1456 "\t[-t terminal] [-u identity]\n"
1457 "\t[-h hostname [terminal]] [name [environ]...]\n");
1463 * *** Account validation routines ***
1468 * validate_account - This is the PAM version of validate.
1471 static void
1472 validate_account(void)
1474 int error;
1475 int flag;
1476 int tries; /* new password retries */
1478 (void) alarm(0); /* give user time to come up with password */
1480 if (Passreqflag)
1481 flag = PAM_DISALLOW_NULL_AUTHTOK;
1482 else
1483 flag = 0;
1485 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1486 if (error == PAM_NEW_AUTHTOK_REQD) {
1487 tries = 1;
1488 error = PAM_AUTHTOK_ERR;
1489 while (error == PAM_AUTHTOK_ERR &&
1490 tries <= DEF_ATTEMPTS) {
1491 if (tries > 1)
1492 (void) printf("Try again\n\n");
1494 (void) printf("Choose a new password.\n");
1496 error = pam_chauthtok(pamh,
1497 PAM_CHANGE_EXPIRED_AUTHTOK);
1498 if (error == PAM_TRY_AGAIN) {
1499 (void) sleep(1);
1500 error = pam_chauthtok(pamh,
1501 PAM_CHANGE_EXPIRED_AUTHTOK);
1503 tries++;
1506 if (error != PAM_SUCCESS) {
1507 if (dosyslog)
1508 syslog(LOG_CRIT,
1509 "change password failure: %s",
1510 pam_strerror(pamh, error));
1511 audit_error = ADT_FAIL_PAM + error;
1512 login_exit(1);
1513 } else {
1514 audit_success(ADT_passwd, pwd, zone_name);
1516 } else {
1517 (void) printf(incorrectmsg);
1519 if (dosyslog)
1520 syslog(LOG_CRIT,
1521 "login account failure: %s",
1522 pam_strerror(pamh, error));
1523 audit_error = ADT_FAIL_PAM + error;
1524 login_exit(1);
1530 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1531 * place us in the user's home directory just in
1532 * case it was protected and the first chdir failed.
1533 * No chdir errors should happen at this point because
1534 * all failures should have happened on the first
1535 * time around.
1538 static void
1539 chdir_to_dir_user(void)
1541 if (chdir(pwd->pw_dir) < 0) {
1542 if (chdir("/") < 0) {
1543 (void) printf("No directory!\n");
1545 * This probably won't work since we can't get to /.
1547 if (dosyslog) {
1548 if (remote_host[0]) {
1549 syslog(LOG_CRIT,
1550 "LOGIN FAILURES ON %s FROM %.*s ",
1551 " %.*s", ttyn, HMAX,
1552 remote_host, NMAX, pwd->pw_name);
1553 } else {
1554 syslog(LOG_CRIT,
1555 "LOGIN FAILURES ON %s, %.*s",
1556 ttyn, NMAX, pwd->pw_name);
1559 closelog();
1560 (void) sleep(Disabletime);
1561 exit(1);
1562 } else {
1563 (void) printf("No directory! Logging in with home=/\n");
1564 pwd->pw_dir = "/";
1571 * login_authenticate - Performs the main authentication work
1572 * 1. Prints the login prompt
1573 * 2. Requests and verifys the password
1574 * 3. Checks the port password
1577 static void
1578 login_authenticate(void)
1580 char *user;
1581 int err;
1582 int login_successful = 0;
1584 do {
1585 /* if scheme broken, then nothing to do but quit */
1586 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS)
1587 exit(1);
1590 * only get name from utility if it is not already
1591 * supplied by pam_start or a pam_set_item.
1593 if (!user || !user[0]) {
1594 /* use call back to get user name */
1595 get_user_name();
1598 err = verify_passwd();
1601 * If root login and not on system console then call exit(2)
1603 check_for_console();
1605 switch (err) {
1606 case PAM_SUCCESS:
1607 case PAM_NEW_AUTHTOK_REQD:
1609 * Officially, pam_authenticate() shouldn't return this
1610 * but it's probably the right thing to return if
1611 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
1612 * be forced to change password later in this code.
1614 count = 0;
1615 login_successful = 1;
1616 break;
1617 case PAM_MAXTRIES:
1618 count = retry;
1619 /*FALLTHROUGH*/
1620 case PAM_AUTH_ERR:
1621 case PAM_AUTHINFO_UNAVAIL:
1622 case PAM_USER_UNKNOWN:
1623 audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd,
1624 remote_host, ttyn, zone_name);
1625 log_bad_attempts();
1626 break;
1627 case PAM_ABORT:
1628 log_bad_attempts();
1629 (void) sleep(Disabletime);
1630 (void) printf(incorrectmsg);
1632 audit_error = ADT_FAIL_PAM + err;
1633 login_exit(1);
1634 /*NOTREACHED*/
1635 default: /* Some other PAM error */
1636 audit_error = ADT_FAIL_PAM + err;
1637 login_exit(1);
1638 /*NOTREACHED*/
1641 if (login_successful)
1642 break;
1644 /* sleep after bad passwd */
1645 if (count)
1646 (void) sleep(Sleeptime);
1647 (void) printf(incorrectmsg);
1648 /* force name to be null in this case */
1649 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1650 login_exit(1);
1651 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1652 login_exit(1);
1653 } while (count++ < retry);
1655 if (count >= retry) {
1656 audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd,
1657 remote_host, ttyn, zone_name);
1659 * If logging is turned on, output the
1660 * string storage area to the log file,
1661 * and sleep for Disabletime
1662 * seconds before exiting.
1664 if (writelog)
1665 badlogin();
1666 if (dosyslog) {
1667 if ((pwd = getpwnam(user_name)) != NULL) {
1668 if (remote_host[0]) {
1669 syslog(LOG_CRIT,
1670 "REPEATED LOGIN FAILURES ON %s "
1671 "FROM %.*s, %.*s",
1672 ttyn, HMAX, remote_host, NMAX,
1673 user_name);
1674 } else {
1675 syslog(LOG_CRIT,
1676 "REPEATED LOGIN FAILURES ON "
1677 "%s, %.*s",
1678 ttyn, NMAX, user_name);
1680 } else {
1681 if (remote_host[0]) {
1682 syslog(LOG_CRIT,
1683 "REPEATED LOGIN FAILURES ON %s "
1684 "FROM %.*s",
1685 ttyn, HMAX, remote_host);
1686 } else {
1687 syslog(LOG_CRIT,
1688 "REPEATED LOGIN FAILURES ON %s",
1689 ttyn);
1693 (void) sleep(Disabletime);
1694 exit(1);
1700 * *** Credential Related routines ***
1705 * setup_credentials - sets the group ID, initializes the groups
1706 * and sets up the secretkey.
1707 * Exits if a failure occurrs.
1712 * setup_credentials - PAM does all the work for us on this one.
1715 static void
1716 setup_credentials(void)
1718 int error = 0;
1720 /* set the real (and effective) GID */
1721 if (setgid(pwd->pw_gid) == -1) {
1722 login_exit(1);
1726 * Initialize the supplementary group access list.
1728 if ((user_name[0] == '\0') ||
1729 (initgroups(user_name, pwd->pw_gid) == -1)) {
1730 audit_error = ADT_FAIL_VALUE_PROGRAM;
1731 login_exit(1);
1734 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED :
1735 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1736 audit_error = ADT_FAIL_PAM + error;
1737 login_exit(error);
1741 * Record successful login and fork process that records logout.
1742 * We have to do this after setting credentials because pam_setcred()
1743 * loads key audit info into the cred, but before setuid() so audit
1744 * system calls will work.
1746 audit_success(get_audit_id(), pwd, zone_name);
1749 static uint_t
1750 get_audit_id(void)
1752 if (hflag)
1753 return (ADT_telnet);
1754 else if (zflag)
1755 return (ADT_zlogin);
1757 return (ADT_login);
1762 * *** Routines to get a new user set up and running ***
1764 * Things to do when starting up a new user:
1765 * adjust_nice
1766 * update_utmpx_entry
1767 * establish_user_environment
1768 * exec_the_shell
1774 * adjust_nice - Set the nice (process priority) value if the
1775 * gecos value contains an appropriate value.
1778 static void
1779 adjust_nice(void)
1781 int pri, mflg, i;
1783 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1784 pri = 0;
1785 mflg = 0;
1786 i = 4;
1788 if (pwd->pw_gecos[i] == '-') {
1789 mflg++;
1790 i++;
1793 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1794 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1796 if (mflg)
1797 pri = -pri;
1799 (void) nice(pri);
1804 * update_utmpx_entry - Searchs for the correct utmpx entry, making an
1805 * entry there if it finds one, otherwise exits.
1808 static void
1809 update_utmpx_entry(int sublogin, boolean_t silent)
1811 int err;
1812 char *user;
1813 static char *errmsg = "No utmpx entry. "
1814 "You must exec \"login\" from the lowest level \"shell\".";
1815 int tmplen;
1816 struct utmpx *u = NULL;
1817 struct utmpx utmpx;
1818 char *ttyntail;
1819 int pamflags = 0;
1821 if (silent)
1822 pamflags |= PAM_SILENT;
1825 * If we're not a sublogin then
1826 * we'll get an error back if our PID doesn't match the PID of the
1827 * entry we are updating, otherwise if its a sublogin the flags
1828 * field is set to 0, which means we just write a matching entry
1829 * (without checking the pid), or a new entry if an entry doesn't
1830 * exist.
1833 if ((err = pam_open_session(pamh, pamflags)) != PAM_SUCCESS) {
1834 audit_error = ADT_FAIL_PAM + err;
1835 login_exit(1);
1838 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) !=
1839 PAM_SUCCESS) {
1840 audit_error = ADT_FAIL_PAM + err;
1841 login_exit(1);
1844 (void) memset(&utmpx, 0, sizeof (utmpx));
1845 (void) time(&utmpx.ut_tv.tv_sec);
1846 utmpx.ut_pid = getpid();
1848 if (hflag) {
1849 SCPYN(utmpx.ut_host, remote_host);
1850 tmplen = strlen(remote_host) + 1;
1851 if (tmplen < sizeof (utmpx.ut_host))
1852 utmpx.ut_syslen = tmplen;
1853 else
1854 utmpx.ut_syslen = sizeof (utmpx.ut_host);
1855 } else if (zflag) {
1857 * If this is a login from another zone, put the
1858 * zone:<zonename> string in the utmpx entry.
1860 SCPYN(utmpx.ut_host, zone_name);
1861 tmplen = strlen(zone_name) + 1;
1862 if (tmplen < sizeof (utmpx.ut_host))
1863 utmpx.ut_syslen = tmplen;
1864 else
1865 utmpx.ut_syslen = sizeof (utmpx.ut_host);
1866 } else {
1867 utmpx.ut_syslen = 0;
1870 SCPYN(utmpx.ut_user, user);
1872 /* skip over "/dev/" */
1873 ttyntail = basename(ttyn);
1875 while ((u = getutxent()) != NULL) {
1876 if ((u->ut_type == INIT_PROCESS ||
1877 u->ut_type == LOGIN_PROCESS ||
1878 u->ut_type == USER_PROCESS) &&
1879 ((sublogin && strncmp(u->ut_line, ttyntail,
1880 sizeof (u->ut_line)) == 0) ||
1881 u->ut_pid == login_pid)) {
1882 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1));
1883 (void) memcpy(utmpx.ut_id, u->ut_id,
1884 sizeof (utmpx.ut_id));
1885 utmpx.ut_exit.e_exit = u->ut_exit.e_exit;
1886 utmpx.ut_type = USER_PROCESS;
1887 (void) pututxline(&utmpx);
1888 break;
1891 endutxent();
1893 if (u == NULL) {
1894 if (!sublogin) {
1896 * no utmpx entry already setup
1897 * (init or telnetd)
1899 (void) puts(errmsg);
1901 audit_error = ADT_FAIL_VALUE_PROGRAM;
1902 login_exit(1);
1904 } else {
1905 /* Now attempt to write out this entry to the wtmp file if */
1906 /* we were successful in getting it from the utmpx file and */
1907 /* the wtmp file exists. */
1908 updwtmpx(WTMPX_FILE, &utmpx);
1915 * process_chroot_logins - Chroots to the specified subdirectory and
1916 * re executes login.
1919 static int
1920 process_chroot_logins(void)
1923 * If the shell field starts with a '*', do a chroot to the home
1924 * directory and perform a new login.
1927 if (*pwd->pw_shell == '*') {
1928 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
1929 pamh = NULL; /* really done */
1930 if (chroot(pwd->pw_dir) < 0) {
1931 (void) printf("No Root Directory\n");
1933 audit_failure(get_audit_id(),
1934 ADT_FAIL_VALUE_CHDIR_FAILED,
1935 pwd, remote_host, ttyn, zone_name);
1937 return (ERROR);
1940 * Set the environment flag <!sublogin> so that the next login
1941 * knows that it is a sublogin.
1943 envinit[0] = SUBLOGIN;
1944 envinit[1] = NULL;
1945 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
1946 (void) execle("/usr/bin/login", "login", (char *)0,
1947 &envinit[0]);
1948 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1949 (void) printf("No /usr/bin/login or /etc/login on root\n");
1951 audit_error = ADT_FAIL_VALUE_PROGRAM;
1953 login_exit(1);
1955 return (OK);
1959 * establish_user_environment - Set up the new users enviornment
1962 static void
1963 establish_user_environment(char **renvp)
1965 int i, j, k, l_index, length, idx = 0;
1966 char *endptr;
1967 char **lenvp;
1968 char **pam_env;
1970 lenvp = environ;
1971 while (*lenvp++)
1974 /* count the number of PAM environment variables set by modules */
1975 if ((pam_env = pam_getenvlist(pamh)) != 0) {
1976 for (idx = 0; pam_env[idx] != 0; idx++)
1980 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx,
1981 sizeof (char *));
1982 if (envinit == NULL) {
1983 (void) printf("Calloc failed - out of swap space.\n");
1984 login_exit(8);
1988 * add PAM environment variables first so they
1989 * can be overwritten at login's discretion.
1990 * check for illegal environment variables.
1992 idx = 0; basicenv = 0;
1993 if (pam_env != 0) {
1994 while (pam_env[idx] != 0) {
1995 if (legalenvvar(pam_env[idx])) {
1996 envinit[basicenv] = pam_env[idx];
1997 basicenv++;
1999 idx++;
2002 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv));
2004 /* Set up environment */
2005 if (hflag) {
2006 if (strlen(terminal)) {
2007 ENVSTRNCAT(term, terminal);
2009 } else {
2010 char *tp = getenv("TERM");
2012 if ((tp != NULL) && (*tp != '\0'))
2013 ENVSTRNCAT(term, tp);
2016 ENVSTRNCAT(logname, pwd->pw_name);
2019 * There are three places to get timezone info. init.c sets
2020 * TZ if the file /etc/default/init contains a value for TZ.
2021 * login.c looks in the file /etc/default/login for a
2022 * variable called TIMEZONE being set. If TIMEZONE has a
2023 * value, TZ is set to that value; no environment variable
2024 * TIMEZONE is set, only TZ. If neither of these methods
2025 * work to set TZ, then the library routines will default
2026 * to using the file /usr/lib/locale/TZ/localtime.
2028 * There is a priority set up here. If /etc/default/init has
2029 * a value for TZ, that value remains top priority. If the
2030 * file /etc/default/login has TIMEZONE set, that has second
2031 * highest priority not overriding the value of TZ in
2032 * /etc/default/init. The reason for this priority is that the
2033 * file /etc/default/init is supposed to be sourced by
2034 * /etc/profile. We are doing the "sourcing" prematurely in
2035 * init.c. Additionally, a login C shell doesn't source the
2036 * file /etc/profile thus not sourcing /etc/default/init thus not
2037 * allowing an adminstrator to globally set TZ for all users
2039 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
2040 tmp_tz = Def_tz;
2042 if ((Def_tz = getenv("TZ")) != NULL) {
2043 ENVSTRNCAT(timez, Def_tz);
2044 } else if (tmp_tz != NULL) {
2045 Def_tz = tmp_tz;
2046 ENVSTRNCAT(timez, Def_tz);
2049 if (Def_hertz == NULL)
2050 (void) sprintf(hertz + strlen(hertz), "%lu", HZ);
2051 else
2052 ENVSTRNCAT(hertz, Def_hertz);
2054 if (Def_path == NULL)
2055 (void) strlcat(path, DEF_PATH, sizeof (path));
2056 else
2057 ENVSTRNCAT(path, Def_path);
2059 ENVSTRNCAT(home, pwd->pw_dir);
2062 * Find the end of the basic environment
2064 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
2068 * If TZ has a value, add it.
2070 if (strcmp(timez, "TZ=") != 0)
2071 envinit[basicenv++] = timez;
2073 if (*pwd->pw_shell == '\0') {
2075 * If possible, use the primary default shell,
2076 * otherwise, use the secondary one.
2078 if (access(SHELL, X_OK) == 0)
2079 pwd->pw_shell = SHELL;
2080 else
2081 pwd->pw_shell = SHELL2;
2082 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
2083 envinit[basicenv++] = shell;
2084 ENVSTRNCAT(shell, pwd->pw_shell);
2087 #ifndef NO_MAIL
2088 envinit[basicenv++] = mail;
2089 (void) strlcat(mail, pwd->pw_name, sizeof (mail));
2090 #endif
2093 * Pick up locale environment variables, if any.
2095 lenvp = renvp;
2096 while (*lenvp != NULL) {
2097 j = 0;
2098 while (localeenv[j] != 0) {
2100 * locale_envmatch() returns 1 if
2101 * *lenvp is localenev[j] and valid.
2103 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2104 envinit[basicenv++] = *lenvp;
2105 break;
2107 j++;
2109 lenvp++;
2113 * If '-p' flag, then try to pass on allowable environment
2114 * variables. Note that by processing this first, what is
2115 * passed on the final "login:" line may over-ride the invocation
2116 * values. XXX is this correct?
2118 if (pflag) {
2119 for (lenvp = renvp; *lenvp; lenvp++) {
2120 if (!legalenvvar(*lenvp)) {
2121 continue;
2124 * If this isn't 'xxx=yyy', skip it. XXX
2126 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2127 continue;
2129 length = endptr + 1 - *lenvp;
2130 for (j = 0; j < basicenv; j++) {
2131 if (strncmp(envinit[j], *lenvp, length) == 0) {
2133 * Replace previously established value
2135 envinit[j] = *lenvp;
2136 break;
2139 if (j == basicenv) {
2141 * It's a new definition, so add it at the end.
2143 envinit[basicenv++] = *lenvp;
2149 * Add in all the environment variables picked up from the
2150 * argument list to "login" or from the user response to the
2151 * "login" request, if any.
2154 if (envp == NULL)
2155 goto switch_env; /* done */
2157 for (j = 0, k = 0, l_index = 0;
2158 *envp != NULL && j < (MAXARGS-1);
2159 j++, envp++) {
2162 * Scan each string provided. If it doesn't have the
2163 * format xxx=yyy, then add the string "Ln=" to the beginning.
2165 if ((endptr = strchr(*envp, '=')) == NULL) {
2167 * This much to be malloc'd:
2168 * strlen(*envp) + 1 char for 'L' +
2169 * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
2171 * total = strlen(*envp) + MAXARGSWIDTH + 3
2173 int total = strlen(*envp) + MAXARGSWIDTH + 3;
2174 envinit[basicenv+k] = malloc(total);
2175 if (envinit[basicenv+k] == NULL) {
2176 (void) printf("%s: malloc failed\n", PROG_NAME);
2177 login_exit(1);
2179 (void) snprintf(envinit[basicenv+k], total, "L%d=%s",
2180 l_index, *envp);
2182 k++;
2183 l_index++;
2184 } else {
2185 if (!legalenvvar(*envp)) { /* this env var permited? */
2186 continue;
2187 } else {
2190 * Check to see whether this string replaces
2191 * any previously defined string
2193 for (i = 0, length = endptr + 1 - *envp;
2194 i < basicenv + k; i++) {
2195 if (strncmp(*envp, envinit[i], length)
2196 == 0) {
2197 envinit[i] = *envp;
2198 break;
2203 * If it doesn't, place it at the end of
2204 * environment array.
2206 if (i == basicenv+k) {
2207 envinit[basicenv+k] = *envp;
2208 k++;
2212 } /* for (j = 0 ... ) */
2214 switch_env:
2216 * Switch to the new environment.
2218 environ = envinit;
2222 * exec_the_shell - invoke the specified shell or start up program
2225 static void
2226 exec_the_shell(void)
2228 char *endptr;
2229 int i;
2231 (void) strlcat(minusnam, basename(pwd->pw_shell),
2232 sizeof (minusnam));
2235 * Exec the shell
2237 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2240 * pwd->pw_shell was not an executable object file, maybe it
2241 * is a shell proceedure or a command line with arguments.
2242 * If so, turn off the SHELL= environment variable.
2244 for (i = 0; envinit[i] != NULL; ++i) {
2245 if ((envinit[i] == shell) &&
2246 ((endptr = strchr(shell, '=')) != NULL))
2247 (*++endptr) = '\0';
2250 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2251 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2252 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
2255 (void) printf("No shell\n");
2259 * login_exit - Call exit() and terminate.
2260 * This function is here for PAM so cleanup can
2261 * be done before the process exits.
2263 static void
2264 login_exit(int exit_code)
2266 if (pamh)
2267 (void) pam_end(pamh, PAM_ABORT);
2269 if (audit_error)
2270 audit_failure(get_audit_id(), audit_error,
2271 pwd, remote_host, ttyn, zone_name);
2273 exit(exit_code);
2274 /*NOTREACHED*/
2278 * Check if lenv and penv matches or not.
2280 static int
2281 locale_envmatch(char *lenv, char *penv)
2283 while ((*lenv == *penv) && *lenv && *penv != '=') {
2284 lenv++;
2285 penv++;
2289 * '/' is eliminated for security reason.
2291 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2292 return (1);
2293 return (0);
2296 static int
2297 is_number(char *ptr)
2299 while (*ptr != '\0') {
2300 if (!isdigit(*ptr))
2301 return (0);
2302 ptr++;
2304 return (1);
2307 static void
2308 interrupt_syscall(int sig)
2310 return;
2313 static void
2314 printmotd(void)
2316 int fd;
2317 char buf[2048];
2318 ssize_t nr;
2320 if ((fd = open("/etc/motd", O_RDONLY)) < 0)
2321 return;
2323 (void) signal(SIGINT, interrupt_syscall);
2325 while ((nr = read(fd, buf, sizeof(buf))) > 0 &&
2326 write(STDOUT_FILENO, buf, nr) == nr)
2329 close(fd);