Move everything from /var/adm to /var/log
[unleashed/lotheac.git] / usr / src / cmd / login / login.c
blob6f1d262d36ff3aecbaaf013c5b96ca911d8789d8
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>
87 #include <krb5_repository.h>
90 * *** Defines, Macros, and String Constants ***
95 #define ISSUEFILE "/etc/issue" /* file to print before prompt */
96 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
99 * These need to be defined for UTMPX management.
100 * If we add in the utility functions later, we
101 * can remove them.
103 #define __UPDATE_ENTRY 1
104 #define __LOGIN 2
107 * Intervals to sleep after failed login
109 #ifndef SLEEPTIME
110 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
111 #endif
112 static int Sleeptime = SLEEPTIME;
115 * seconds login disabled after allowable number of unsuccessful attempts
117 #ifndef DISABLETIME
118 #define DISABLETIME 20
119 #endif
120 static int Disabletime = DISABLETIME;
122 #define MAXTRYS 5
124 static int retry = MAXTRYS;
127 * Login logging support
129 #define LOGINLOG "/var/log/loginlog" /* login log file */
130 #define LNAME_SIZE 20 /* size of logged logname */
131 #define TTYN_SIZE 15 /* size of logged tty name */
132 #define TIME_SIZE 30 /* size of logged time string */
133 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
134 #define L_WAITTIME 5 /* waittime for log file to unlock */
135 #define LOGTRYS 10 /* depth of 'try' logging */
138 * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
139 * SCPYL is the safer version of SCPYN
141 #define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a))
142 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
143 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
144 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
145 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
148 * Other macros
150 #define NMAX sizeof (((struct utmpx *)0)->ut_name)
151 #define HMAX sizeof (((struct utmpx *)0)->ut_host)
152 #define min(a, b) (((a) < (b)) ? (a) : (b))
155 * Various useful files and string constants
157 #define SHELL "/bin/sh"
158 #define SUBLOGIN "<!sublogin>"
159 #define PROG_NAME "login"
160 #define HUSHLOGIN ".hushlogin"
163 * Array and Buffer sizes
165 #define PBUFSIZE 8 /* max significant characters in a password */
166 #define MAXARGS 63 /* change value below if changing this */
167 #define MAXARGSWIDTH 2 /* log10(MAXARGS) */
168 #define MAXENV 1024
169 #define MAXLINE 2048
172 * Miscellaneous constants
174 #define ROOTUID 0
175 #define ERROR 1
176 #define OK 0
177 #define LOG_ERROR 1
178 #define DONT_LOG_ERROR 0
179 #define TRUE 1
180 #define FALSE 0
183 * Counters for counting the number of failed login attempts
185 static int trys = 0;
186 static int count = 1;
189 * Externs a plenty
191 extern int getsecretkey();
194 * The current user name
196 static char user_name[NMAX];
197 static char minusnam[16] = "-";
200 * login_pid, used to find utmpx entry to update.
202 static pid_t login_pid;
205 * locale environments to be passed to shells.
207 static char *localeenv[] = {
208 "LANG",
209 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
210 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
211 static int locale_envmatch(char *, char *);
214 * Environment variable support
216 static char shell[256] = { "SHELL=" };
217 static char home[MAXPATHLEN] = { "HOME=" };
218 static char term[64] = { "TERM=" };
219 static char logname[30] = { "LOGNAME=" };
220 static char timez[100] = { "TZ=" };
221 static char hertz[10] = { "HZ=" };
222 static char path[MAXPATHLEN] = { "PATH=" };
223 static char *newenv[10+MAXARGS] =
224 {home, path, logname, hertz, term, 0, 0};
225 static char **envinit = newenv;
226 static int basicenv;
227 static char *zero = NULL;
228 static char **envp;
229 #ifndef NO_MAIL
230 static char mail[30] = { "MAIL=/var/mail/" };
231 #endif
232 extern char **environ;
233 static char inputline[MAXLINE];
235 #define MAX_ID_LEN 256
236 #define MAX_REPOSITORY_LEN 256
237 #define MAX_PAMSERVICE_LEN 256
239 static char identity[MAX_ID_LEN];
240 static char repository[MAX_REPOSITORY_LEN];
241 static char progname[MAX_PAMSERVICE_LEN];
245 * Strings used to prompt the user.
247 static char loginmsg[] = "login: ";
248 static char passwdmsg[] = "Password:";
249 static char incorrectmsg[] = "Login incorrect\n";
252 * Password file support
254 static struct passwd *pwd = NULL;
255 static char remote_host[HMAX];
256 static char zone_name[ZONENAME_MAX];
259 * Log file support
261 static char *log_entry[LOGTRYS];
262 static int writelog = 0;
263 static int dosyslog = 0;
264 static int flogin = MAXTRYS; /* flag for SYSLOG_FAILED_LOGINS */
267 * Default file toggles
269 static char *Pndefault = "/etc/default/login";
270 static char *Altshell = NULL;
271 static char *Console = NULL;
272 static int Passreqflag = 0;
274 #define DEFUMASK 022
275 static mode_t Umask = DEFUMASK;
276 static char *Def_tz = NULL;
277 static char *tmp_tz = NULL;
278 static char *Def_hertz = NULL;
279 #define SET_FSIZ 2 /* ulimit() command arg */
280 static long Def_ulimit = 0;
281 #define MAX_TIMEOUT (15 * 60)
282 #define DEF_TIMEOUT (5 * 60)
283 static unsigned Def_timeout = DEF_TIMEOUT;
284 static char *Def_path = NULL;
285 static char *Def_supath = NULL;
286 #define DEF_PATH "/usr/bin:" /* same as PATH */
287 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
290 * Defaults for updating expired passwords
292 #define DEF_ATTEMPTS 3
295 * ttyprompt will point to the environment variable TTYPROMPT.
296 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
298 static char *ttyprompt = NULL;
299 static char *ttyn = NULL;
302 * Pass inherited environment. Used by telnetd in support of the telnet
303 * ENVIRON option.
305 static boolean_t pflag = B_FALSE;
306 static boolean_t uflag = B_FALSE;
307 static boolean_t Rflag = B_FALSE;
308 static boolean_t sflag = B_FALSE;
309 static boolean_t tflag = B_FALSE;
310 static boolean_t hflag = B_FALSE;
311 static boolean_t zflag = B_FALSE;
314 * Remote login support
316 static char lusername[NMAX+1];
317 static char terminal[MAXPATHLEN];
320 * Pre-authentication flag support
322 static int fflag;
324 static char ** getargs(char *);
326 static int login_conv(int, struct pam_message **,
327 struct pam_response **, void *);
329 static struct pam_conv pam_conv = {login_conv, NULL};
330 static pam_handle_t *pamh; /* Authentication handle */
333 * Function declarations
335 static void turn_on_logging(void);
336 static void defaults(void);
337 static void usage(void);
338 static void login_authenticate();
339 static void setup_credentials(void);
340 static void adjust_nice(void);
341 static void update_utmpx_entry(int, boolean_t);
342 static void establish_user_environment(char **);
343 static void exec_the_shell(void);
344 static int process_chroot_logins(void);
345 static void chdir_to_dir_user(void);
346 static void validate_account(void);
347 static int get_options(int, char **);
348 static int legalenvvar(char *);
349 static void check_for_console(void);
350 static void check_for_dueling_unix(char *);
351 static void get_user_name(void);
352 static void login_exit(int)__NORETURN;
353 static int logins_disabled(char *);
354 static void log_bad_attempts(void);
355 static int is_number(char *);
356 static void printmotd(void);
359 * *** main ***
361 * The primary flow of control is directed in this routine.
362 * Control moves in line from top to bottom calling subfunctions
363 * which perform the bulk of the work. Many of these calls exit
364 * when a fatal error is encountered and do not return to main.
370 main(int argc, char *argv[], char **renvp)
372 int sublogin;
373 int pam_rc;
374 boolean_t silent = B_FALSE;
376 login_pid = getpid();
379 * Set up Defaults and flags
381 defaults();
382 SCPYL(progname, PROG_NAME);
385 * Set up default umask
387 if (Umask > ((mode_t)0777))
388 Umask = DEFUMASK;
389 (void) umask(Umask);
392 * Set up default timeouts and delays
394 if (Def_timeout > MAX_TIMEOUT)
395 Def_timeout = MAX_TIMEOUT;
396 if (Sleeptime < 0 || Sleeptime > 5)
397 Sleeptime = SLEEPTIME;
399 (void) alarm(Def_timeout);
402 * Ignore SIGQUIT and SIGINT and set nice to 0
404 (void) signal(SIGQUIT, SIG_IGN);
405 (void) signal(SIGINT, SIG_IGN);
406 (void) nice(0);
409 * Set flag to disable the pid check if you find that you are
410 * a subsystem login.
412 sublogin = 0;
413 if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
414 sublogin = 1;
417 * Parse Arguments
419 if (get_options(argc, argv) == -1) {
420 usage();
421 login_exit(1);
425 * if devicename is not passed as argument, call ttyname(0)
427 if (ttyn == NULL) {
428 ttyn = ttyname(0);
429 if (ttyn == NULL)
430 ttyn = "/dev/???";
434 * Call pam_start to initiate a PAM authentication operation
437 if ((pam_rc = pam_start(progname, user_name, &pam_conv, &pamh))
438 != PAM_SUCCESS) {
439 login_exit(1);
441 if ((pam_rc = pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
442 login_exit(1);
444 if ((pam_rc = pam_set_item(pamh, PAM_RHOST, remote_host)) !=
445 PAM_SUCCESS) {
446 login_exit(1);
450 * We currently only support special handling of the KRB5 PAM repository
452 if ((Rflag && strlen(repository)) &&
453 strcmp(repository, KRB5_REPOSITORY_NAME) == 0 &&
454 (uflag && strlen(identity))) {
455 krb5_repository_data_t krb5_data;
456 pam_repository_t pam_rep_data;
458 krb5_data.principal = identity;
459 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
461 pam_rep_data.type = repository;
462 pam_rep_data.scope = (void *)&krb5_data;
463 pam_rep_data.scope_len = sizeof (krb5_data);
465 (void) pam_set_item(pamh, PAM_REPOSITORY,
466 (void *)&pam_rep_data);
470 * Open the log file which contains a record of successful and failed
471 * login attempts
473 turn_on_logging();
476 * say "hi" to syslogd ..
478 openlog("login", 0, LOG_AUTH);
481 * validate user
483 /* we are already authenticated. fill in what we must, then continue */
484 if (fflag) {
485 if ((pwd = getpwnam(user_name)) == NULL) {
487 log_bad_attempts();
488 (void) printf("Login failed: unknown user '%s'.\n",
489 user_name);
490 login_exit(1);
492 } else {
494 * Perform the primary login authentication activity.
496 login_authenticate();
499 /* change root login, then we exec another login and try again */
500 if (process_chroot_logins() != OK)
501 login_exit(1);
504 * If root login and not on system console then call exit(2)
506 check_for_console();
509 * Check to see if a shutdown is in progress, if it is and
510 * we are not root then throw the user off the system
512 if (logins_disabled(user_name) == TRUE) {
513 login_exit(1);
516 if (pwd->pw_uid == 0) {
517 if (Def_supath != NULL)
518 Def_path = Def_supath;
519 else
520 Def_path = DEF_SUPATH;
524 * Check account expiration and passwd aging
526 validate_account();
529 * We only get here if we've been authenticated.
533 * Now we set up the environment for the new user, which includes
534 * the users ulimit, nice value, ownership of this tty, uid, gid,
535 * and environment variables.
537 if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
538 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
540 /* di_devperm_login() sends detailed errors to syslog */
541 if (di_devperm_login((const char *)ttyn, pwd->pw_uid, pwd->pw_gid,
542 NULL) == -1) {
543 (void) fprintf(stderr, "error processing /etc/logindevperm,"
544 " see syslog for more details\n");
547 adjust_nice(); /* passwd file can specify nice value */
549 setup_credentials(); /* Set user credentials - exits on failure */
551 if (chdir(pwd->pw_dir) == 0)
552 silent = (access(HUSHLOGIN, F_OK) == 0);
554 * NOTE: telnetd relies upon this updating of utmpx
555 * to indicate that the authentication completed successfully,
556 * pam_open_session was called and therefore they are required to
557 * call pam_close_session.
559 update_utmpx_entry(sublogin, silent);
561 /* set the real (and effective) UID */
562 if (setuid(pwd->pw_uid) == -1) {
563 login_exit(1);
567 * Set up the basic environment for the exec. This includes
568 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
570 chdir_to_dir_user();
572 establish_user_environment(renvp);
574 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
575 pamh = NULL;
577 if (pwd->pw_uid == 0) {
578 if (dosyslog) {
579 if (remote_host[0]) {
580 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
581 ttyn, HMAX, remote_host);
582 } else
583 syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
586 closelog();
588 if (!silent)
589 printmotd();
591 (void) signal(SIGQUIT, SIG_DFL);
592 (void) signal(SIGINT, SIG_DFL);
595 * Set SIGXCPU and SIGXFSZ to default disposition.
596 * Shells inherit signal disposition from parent.
597 * And the shells should have default dispositions
598 * for the two below signals.
600 (void) signal(SIGXCPU, SIG_DFL);
601 (void) signal(SIGXFSZ, SIG_DFL);
604 * Now fire off the shell of choice
606 exec_the_shell();
609 * All done
611 login_exit(1);
612 return (0);
617 * *** Utility functions ***
623 * donothing & catch - Signal catching functions
626 /*ARGSUSED*/
627 static void
628 donothing(int sig)
630 if (pamh)
631 (void) pam_end(pamh, PAM_ABORT);
634 #ifdef notdef
635 static int intrupt;
637 /*ARGSUSED*/
638 static void
639 catch(int sig)
641 ++intrupt;
643 #endif
646 * *** Bad login logging support ***
650 * badlogin() - log to the log file 'trys'
651 * unsuccessful attempts
654 static void
655 badlogin(void)
657 int retval, count1, fildes;
660 * Tries to open the log file. If succeed, lock it and write
661 * in the failed attempts
663 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
665 (void) sigset(SIGALRM, donothing);
666 (void) alarm(L_WAITTIME);
667 retval = lockf(fildes, F_LOCK, 0L);
668 (void) alarm(0);
669 (void) sigset(SIGALRM, SIG_DFL);
670 if (retval == 0) {
671 for (count1 = 0; count1 < trys; count1++)
672 (void) write(fildes, log_entry[count1],
673 (unsigned)strlen(log_entry[count1]));
674 (void) lockf(fildes, F_ULOCK, 0L);
676 (void) close(fildes);
682 * log_bad_attempts - log each bad login attempt - called from
683 * login_authenticate. Exits when the maximum attempt
684 * count is exceeded.
687 static void
688 log_bad_attempts(void)
690 time_t timenow;
692 if (trys >= LOGTRYS)
693 return;
694 if (writelog) {
695 (void) time(&timenow);
696 (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
697 (void) strncat(log_entry[trys], ":", (size_t)1);
698 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
699 (void) strncat(log_entry[trys], ":", (size_t)1);
700 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE);
701 trys++;
703 if (count > flogin) {
704 if ((pwd = getpwnam(user_name)) != NULL) {
705 if (remote_host[0]) {
706 syslog(LOG_NOTICE,
707 "Login failure on %s from %.*s, "
708 "%.*s", ttyn, HMAX, remote_host,
709 NMAX, user_name);
710 } else {
711 syslog(LOG_NOTICE,
712 "Login failure on %s, %.*s",
713 ttyn, NMAX, user_name);
715 } else {
716 if (remote_host[0]) {
717 syslog(LOG_NOTICE,
718 "Login failure on %s from %.*s",
719 ttyn, HMAX, remote_host);
720 } else {
721 syslog(LOG_NOTICE,
722 "Login failure on %s", ttyn);
730 * turn_on_logging - if the logfile exist, turn on attempt logging and
731 * initialize the string storage area
734 static void
735 turn_on_logging(void)
737 struct stat dbuf;
738 int i;
740 if (stat(LOGINLOG, &dbuf) == 0) {
741 writelog = 1;
742 for (i = 0; i < LOGTRYS; i++) {
743 if (!(log_entry[i] = malloc((size_t)ENT_SIZE))) {
744 writelog = 0;
745 break;
747 *log_entry[i] = '\0';
754 * login_conv():
755 * This is the conv (conversation) function called from
756 * a PAM authentication module to print error messages
757 * or garner information from the user.
759 /*ARGSUSED*/
760 static int
761 login_conv(int num_msg, struct pam_message **msg,
762 struct pam_response **response, void *appdata_ptr)
764 struct pam_message *m;
765 struct pam_response *r;
766 char *temp;
767 int k, i;
769 if (num_msg <= 0)
770 return (PAM_CONV_ERR);
772 *response = calloc(num_msg, sizeof (struct pam_response));
773 if (*response == NULL)
774 return (PAM_BUF_ERR);
776 k = num_msg;
777 m = *msg;
778 r = *response;
779 while (k--) {
781 switch (m->msg_style) {
783 case PAM_PROMPT_ECHO_OFF:
784 errno = 0;
785 temp = getpassphrase(m->msg);
786 if (temp != NULL) {
787 if (errno == EINTR)
788 return (PAM_CONV_ERR);
790 r->resp = strdup(temp);
791 if (r->resp == NULL) {
792 /* free responses */
793 r = *response;
794 for (i = 0; i < num_msg; i++, r++) {
795 free(r->resp);
797 free(*response);
798 *response = NULL;
799 return (PAM_BUF_ERR);
803 m++;
804 r++;
805 break;
807 case PAM_PROMPT_ECHO_ON:
808 if (m->msg != NULL)
809 (void) fputs(m->msg, stdout);
810 r->resp = calloc(1, PAM_MAX_RESP_SIZE);
811 if (r->resp == NULL) {
812 /* free responses */
813 r = *response;
814 for (i = 0; i < num_msg; i++, r++) {
815 free(r->resp);
817 free(*response);
818 *response = NULL;
819 return (PAM_BUF_ERR);
822 * The response might include environment variables
823 * information. We should store that information in
824 * envp if there is any; otherwise, envp is set to
825 * NULL.
827 bzero((void *)inputline, MAXLINE);
829 envp = getargs(inputline);
831 /* If we read in any input, process it. */
832 if (inputline[0] != '\0') {
833 int len;
835 if (envp != (char **)NULL)
837 * If getargs() did not return NULL,
838 * *envp is the first string in
839 * inputline. envp++ makes envp point
840 * to environment variables information
841 * or be NULL.
843 envp++;
845 (void) strncpy(r->resp, inputline,
846 PAM_MAX_RESP_SIZE-1);
847 r->resp[PAM_MAX_RESP_SIZE-1] = 0;
848 len = strlen(r->resp);
849 if (r->resp[len-1] == '\n')
850 r->resp[len-1] = '\0';
851 } else {
852 login_exit(1);
854 m++;
855 r++;
856 break;
858 case PAM_ERROR_MSG:
859 if (m->msg != NULL) {
860 (void) fputs(m->msg, stderr);
861 (void) fputs("\n", stderr);
863 m++;
864 r++;
865 break;
866 case PAM_TEXT_INFO:
867 if (m->msg != NULL) {
868 (void) fputs(m->msg, stdout);
869 (void) fputs("\n", stdout);
871 m++;
872 r++;
873 break;
875 default:
876 break;
879 return (PAM_SUCCESS);
883 * verify_passwd - Authenticates the user.
884 * Returns: PAM_SUCCESS if authentication successful,
885 * PAM error code if authentication fails.
888 static int
889 verify_passwd(void)
891 int error;
892 char *user;
893 int flag = (Passreqflag ? PAM_DISALLOW_NULL_AUTHTOK : 0);
896 * PAM authenticates the user for us.
898 error = pam_authenticate(pamh, flag);
900 /* get the user_name from the pam handle */
901 (void) pam_get_item(pamh, PAM_USER, (void**)&user);
903 if (user == NULL || *user == '\0')
904 return (PAM_SYSTEM_ERR);
906 SCPYL(user_name, user);
907 check_for_dueling_unix(user_name);
909 if (((pwd = getpwnam(user_name)) == NULL) &&
910 (error != PAM_USER_UNKNOWN)) {
911 return (PAM_SYSTEM_ERR);
914 return (error);
918 * quotec - Called by getargs
921 static int
922 quotec(void)
924 int c, i, num;
926 switch (c = getc(stdin)) {
928 case 'n':
929 c = '\n';
930 break;
932 case 'r':
933 c = '\r';
934 break;
936 case 'v':
937 c = '\013';
938 break;
940 case 'b':
941 c = '\b';
942 break;
944 case 't':
945 c = '\t';
946 break;
948 case 'f':
949 c = '\f';
950 break;
952 case '0':
953 case '1':
954 case '2':
955 case '3':
956 case '4':
957 case '5':
958 case '6':
959 case '7':
960 for (num = 0, i = 0; i < 3; i++) {
961 num = num * 8 + (c - '0');
962 if ((c = getc(stdin)) < '0' || c > '7')
963 break;
965 (void) ungetc(c, stdin);
966 c = num & 0377;
967 break;
969 default:
970 break;
972 return (c);
976 * getargs - returns an input line. Exits if EOF encountered.
978 #define WHITESPACE 0
979 #define ARGUMENT 1
981 static char **
982 getargs(char *input_line)
984 static char envbuf[MAXLINE];
985 static char *args[MAXARGS];
986 char *ptr, **answer;
987 int c;
988 int state;
989 char *p = input_line;
991 ptr = envbuf;
992 answer = &args[0];
993 state = WHITESPACE;
995 while ((c = getc(stdin)) != EOF && answer < &args[MAXARGS-1]) {
997 *(input_line++) = c;
999 switch (c) {
1001 case '\n':
1002 if (ptr == &envbuf[0])
1003 return ((char **)NULL);
1004 *input_line = *ptr = '\0';
1005 *answer = NULL;
1006 return (&args[0]);
1008 case ' ':
1009 case '\t':
1010 if (state == ARGUMENT) {
1011 *ptr++ = '\0';
1012 state = WHITESPACE;
1014 break;
1016 case '\\':
1017 c = quotec();
1018 /* FALLTHROUGH */
1020 default:
1021 if (state == WHITESPACE) {
1022 *answer++ = ptr;
1023 state = ARGUMENT;
1025 *ptr++ = c;
1028 /* Attempt at overflow, exit */
1029 if (input_line - p >= MAXLINE - 1 ||
1030 ptr >= &envbuf[sizeof (envbuf) - 1]) {
1031 login_exit(1);
1036 * If we left loop because an EOF was received or we've overflown
1037 * args[], exit immediately.
1039 login_exit(0);
1040 /* NOTREACHED */
1044 * get_user_name - Gets the user name either passed in, or from the
1045 * login: prompt.
1048 static void
1049 get_user_name(void)
1051 FILE *fp;
1053 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1054 char *ptr, buffer[BUFSIZ];
1055 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) {
1056 (void) fputs(ptr, stdout);
1058 (void) fclose(fp);
1062 * if TTYPROMPT is not set, use our own prompt
1063 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1064 * and let the module do the prompting.
1067 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1068 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1069 else
1070 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1072 envp = &zero; /* XXX: is this right? */
1077 * Check_for_dueling_unix - Check to see if the another login is talking
1078 * to the line we've got open as a login port
1079 * Exits if we're talking to another unix system
1082 static void
1083 check_for_dueling_unix(char *inputline)
1085 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1086 EQN(incorrectmsg, inputline)) {
1087 (void) printf("Looking at a login line.\n");
1088 login_exit(8);
1093 * logins_disabled - if the file /etc/nologin exists and the user is not
1094 * root then do not permit them to login
1096 static int
1097 logins_disabled(char *user_name)
1099 FILE *nlfd;
1100 int c;
1101 if (!EQN("root", user_name) &&
1102 ((nlfd = fopen(NOLOGIN, "r")) != NULL)) {
1103 while ((c = getc(nlfd)) != EOF)
1104 (void) putchar(c);
1105 (void) fflush(stdout);
1106 (void) sleep(5);
1107 return (TRUE);
1109 return (FALSE);
1112 #define DEFAULT_CONSOLE "/dev/console"
1115 * check_for_console - Checks if we're getting a root login on the
1116 * console, or a login from the global zone. Exits if not.
1118 * If CONSOLE is set to /dev/console in /etc/default/login, then root logins
1119 * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global
1120 * zones, but checking them does no harm.
1122 static void
1123 check_for_console(void)
1125 const char *consoles[] = { "/dev/console", "/dev/vt/", NULL };
1126 int i;
1128 if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE ||
1129 Console == NULL)
1130 return;
1132 if (strcmp(Console, DEFAULT_CONSOLE) == 0) {
1133 for (i = 0; consoles[i] != NULL; i ++) {
1134 if (strncmp(ttyn, consoles[i],
1135 strlen(consoles[i])) == 0)
1136 return;
1138 } else {
1139 if (strcmp(ttyn, Console) == 0)
1140 return;
1143 (void) printf("Not on system console\n");
1145 login_exit(10);
1150 * List of environment variables or environment variable prefixes that should
1151 * not be propagated across logins, such as when the login -p option is used.
1153 static const char *const illegal[] = {
1154 "SHELL=",
1155 "HOME=",
1156 "LOGNAME=",
1157 #ifndef NO_MAIL
1158 "MAIL=",
1159 #endif
1160 "CDPATH=",
1161 "IFS=",
1162 "PATH=",
1163 "LD_",
1164 "SMF_",
1165 NULL
1169 * legalenvvar - Is it legal to insert this environmental variable?
1172 static int
1173 legalenvvar(char *s)
1175 const char *const *p;
1177 for (p = &illegal[0]; *p; p++) {
1178 if (strncmp(s, *p, strlen(*p)) == 0)
1179 return (0);
1182 return (1);
1186 * defaults - read defaults
1189 static void
1190 defaults(void)
1192 int flags;
1193 char *ptr;
1195 if (defopen(Pndefault) == 0) {
1197 * ignore case
1199 flags = defcntl(DC_GETFLAGS, 0);
1200 TURNOFF(flags, DC_CASE);
1201 (void) defcntl(DC_SETFLAGS, flags);
1203 if ((Console = defread("CONSOLE=")) != NULL)
1204 Console = strdup(Console);
1206 if ((Altshell = defread("ALTSHELL=")) != NULL)
1207 Altshell = strdup(Altshell);
1209 if ((ptr = defread("PASSREQ=")) != NULL &&
1210 strcasecmp("YES", ptr) == 0)
1211 Passreqflag = 1;
1213 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1214 Def_tz = strdup(Def_tz);
1216 if ((Def_hertz = defread("HZ=")) != NULL)
1217 Def_hertz = strdup(Def_hertz);
1219 if ((Def_path = defread("PATH=")) != NULL)
1220 Def_path = strdup(Def_path);
1222 if ((Def_supath = defread("SUPATH=")) != NULL)
1223 Def_supath = strdup(Def_supath);
1225 if ((ptr = defread("ULIMIT=")) != NULL)
1226 Def_ulimit = atol(ptr);
1228 if ((ptr = defread("TIMEOUT=")) != NULL)
1229 Def_timeout = (unsigned)atoi(ptr);
1231 if ((ptr = defread("UMASK=")) != NULL)
1232 if (sscanf(ptr, "%lo", &Umask) != 1)
1233 Umask = DEFUMASK;
1235 if ((ptr = defread("SLEEPTIME=")) != NULL) {
1236 if (is_number(ptr))
1237 Sleeptime = atoi(ptr);
1240 if ((ptr = defread("DISABLETIME=")) != NULL) {
1241 if (is_number(ptr))
1242 Disabletime = atoi(ptr);
1245 if ((ptr = defread("SYSLOG=")) != NULL)
1246 dosyslog = strcmp(ptr, "YES") == 0;
1248 if ((ptr = defread("RETRIES=")) != NULL) {
1249 if (is_number(ptr))
1250 retry = atoi(ptr);
1253 if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) {
1254 if (is_number(ptr))
1255 flogin = atoi(ptr);
1256 else
1257 flogin = retry;
1258 } else
1259 flogin = retry;
1260 (void) defopen(NULL);
1266 * get_options(argc, argv)
1267 * - parse the cmd line.
1268 * - return 0 if successful, -1 if failed.
1269 * Calls login_exit() on misuse of -r, -h, and -z flags
1272 static int
1273 get_options(int argc, char *argv[])
1275 int c;
1276 int errflg = 0;
1277 char sflagname[NMAX+1];
1278 const char *flags_message = "Only one of -r, -h and -z allowed\n";
1280 while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
1281 switch (c) {
1282 case 'a':
1283 break;
1285 case 'd':
1287 * Must be root to pass in device name
1288 * otherwise we exit() as punishment for trying.
1290 if (getuid() != 0 || geteuid() != 0) {
1291 login_exit(1); /* sigh */
1292 /*NOTREACHED*/
1294 ttyn = optarg;
1295 break;
1297 case 'h':
1298 if (hflag || zflag) {
1299 (void) fprintf(stderr, flags_message);
1300 login_exit(1);
1302 hflag = B_TRUE;
1303 SCPYL(remote_host, optarg);
1304 if (argv[optind]) {
1305 if (argv[optind][0] != '-') {
1306 SCPYL(terminal, argv[optind]);
1307 optind++;
1308 } else {
1310 * Allow "login -h hostname -" to
1311 * skip setting up an username as "-".
1313 if (argv[optind][1] == '\0')
1314 optind++;
1318 SCPYL(progname, "telnet");
1319 break;
1321 case 'p':
1322 pflag = B_TRUE;
1323 break;
1325 case 'f':
1327 * Must be root to bypass authentication
1328 * otherwise we exit() as punishment for trying.
1330 if (getuid() != 0 || geteuid() != 0) {
1332 login_exit(1); /* sigh */
1333 /*NOTREACHED*/
1335 /* save fflag user name for future use */
1336 SCPYL(user_name, optarg);
1337 fflag = B_TRUE;
1338 break;
1339 case 'u':
1340 if (!strlen(optarg)) {
1341 (void) fprintf(stderr,
1342 "Empty string supplied with -u\n");
1343 login_exit(1);
1345 SCPYL(identity, optarg);
1346 uflag = B_TRUE;
1347 break;
1348 case 's':
1349 if (!strlen(optarg)) {
1350 (void) fprintf(stderr,
1351 "Empty string supplied with -s\n");
1352 login_exit(1);
1354 SCPYL(sflagname, optarg);
1355 sflag = B_TRUE;
1356 break;
1357 case 'R':
1358 if (!strlen(optarg)) {
1359 (void) fprintf(stderr,
1360 "Empty string supplied with -R\n");
1361 login_exit(1);
1363 SCPYL(repository, optarg);
1364 Rflag = B_TRUE;
1365 break;
1366 case 't':
1367 if (!strlen(optarg)) {
1368 (void) fprintf(stderr,
1369 "Empty string supplied with -t\n");
1370 login_exit(1);
1372 SCPYL(terminal, optarg);
1373 tflag = B_TRUE;
1374 break;
1375 case 'z':
1376 if (hflag || zflag) {
1377 (void) fprintf(stderr, flags_message);
1378 login_exit(1);
1380 (void) snprintf(zone_name, sizeof (zone_name),
1381 "zone:%s", optarg);
1382 SCPYL(progname, "zlogin");
1383 zflag = B_TRUE;
1384 break;
1385 default:
1386 errflg++;
1387 break;
1388 } /* end switch */
1389 } /* end while */
1392 * If the 's svcname' flag was used, override the progname
1393 * value that is to be used in the pam_start call.
1395 if (sflag)
1396 SCPYL(progname, sflagname);
1399 * get the prompt set by ttymon
1401 ttyprompt = getenv("TTYPROMPT");
1403 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1405 * if ttyprompt is set, there should be data on
1406 * the stream already.
1408 if ((envp = getargs(inputline)) != (char **)NULL) {
1410 * don't get name if name passed as argument.
1412 SCPYL(user_name, *envp++);
1414 } else if (optind < argc) {
1415 SCPYL(user_name, argv[optind]);
1416 (void) SCPYL(inputline, user_name);
1417 (void) strlcat(inputline, " \n", sizeof (inputline));
1418 envp = &argv[optind+1];
1420 if (!fflag)
1421 SCPYL(lusername, user_name);
1424 if (errflg)
1425 return (-1);
1426 return (0);
1430 * usage - Print usage message
1433 static void
1434 usage(void)
1436 (void) fprintf(stderr,
1437 "usage:\n"
1438 " login [-p] [-d device] [-R repository] [-s service]\n"
1439 "\t[-t terminal] [-u identity]\n"
1440 "\t[-h hostname [terminal]] [name [environ]...]\n");
1446 * *** Account validation routines ***
1451 * validate_account - This is the PAM version of validate.
1454 static void
1455 validate_account(void)
1457 int error;
1458 int flag;
1459 int tries; /* new password retries */
1461 (void) alarm(0); /* give user time to come up with password */
1463 if (Passreqflag)
1464 flag = PAM_DISALLOW_NULL_AUTHTOK;
1465 else
1466 flag = 0;
1468 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1469 if (error == PAM_NEW_AUTHTOK_REQD) {
1470 tries = 1;
1471 error = PAM_AUTHTOK_ERR;
1472 while (error == PAM_AUTHTOK_ERR &&
1473 tries <= DEF_ATTEMPTS) {
1474 if (tries > 1)
1475 (void) printf("Try again\n\n");
1477 (void) printf("Choose a new password.\n");
1479 error = pam_chauthtok(pamh,
1480 PAM_CHANGE_EXPIRED_AUTHTOK);
1481 if (error == PAM_TRY_AGAIN) {
1482 (void) sleep(1);
1483 error = pam_chauthtok(pamh,
1484 PAM_CHANGE_EXPIRED_AUTHTOK);
1486 tries++;
1489 if (error != PAM_SUCCESS) {
1490 if (dosyslog)
1491 syslog(LOG_CRIT,
1492 "change password failure: %s",
1493 pam_strerror(pamh, error));
1494 login_exit(1);
1496 } else {
1497 (void) printf(incorrectmsg);
1499 if (dosyslog)
1500 syslog(LOG_CRIT,
1501 "login account failure: %s",
1502 pam_strerror(pamh, error));
1503 login_exit(1);
1509 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1510 * place us in the user's home directory just in
1511 * case it was protected and the first chdir failed.
1512 * No chdir errors should happen at this point because
1513 * all failures should have happened on the first
1514 * time around.
1517 static void
1518 chdir_to_dir_user(void)
1520 if (chdir(pwd->pw_dir) < 0) {
1521 if (chdir("/") < 0) {
1522 (void) printf("No directory!\n");
1524 * This probably won't work since we can't get to /.
1526 if (dosyslog) {
1527 if (remote_host[0]) {
1528 syslog(LOG_CRIT,
1529 "LOGIN FAILURES ON %s FROM %.*s ",
1530 " %.*s", ttyn, HMAX,
1531 remote_host, NMAX, pwd->pw_name);
1532 } else {
1533 syslog(LOG_CRIT,
1534 "LOGIN FAILURES ON %s, %.*s",
1535 ttyn, NMAX, pwd->pw_name);
1538 closelog();
1539 (void) sleep(Disabletime);
1540 exit(1);
1541 } else {
1542 (void) printf("No directory! Logging in with home=/\n");
1543 pwd->pw_dir = "/";
1550 * login_authenticate - Performs the main authentication work
1551 * 1. Prints the login prompt
1552 * 2. Requests and verifys the password
1553 * 3. Checks the port password
1556 static void
1557 login_authenticate(void)
1559 char *user;
1560 int err;
1561 int login_successful = 0;
1563 do {
1564 /* if scheme broken, then nothing to do but quit */
1565 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS)
1566 exit(1);
1569 * only get name from utility if it is not already
1570 * supplied by pam_start or a pam_set_item.
1572 if (!user || !user[0]) {
1573 /* use call back to get user name */
1574 get_user_name();
1577 err = verify_passwd();
1580 * If root login and not on system console then call exit(2)
1582 check_for_console();
1584 switch (err) {
1585 case PAM_SUCCESS:
1586 case PAM_NEW_AUTHTOK_REQD:
1588 * Officially, pam_authenticate() shouldn't return this
1589 * but it's probably the right thing to return if
1590 * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
1591 * be forced to change password later in this code.
1593 count = 0;
1594 login_successful = 1;
1595 break;
1596 case PAM_MAXTRIES:
1597 count = retry;
1598 /*FALLTHROUGH*/
1599 case PAM_AUTH_ERR:
1600 case PAM_AUTHINFO_UNAVAIL:
1601 case PAM_USER_UNKNOWN:
1602 log_bad_attempts();
1603 break;
1604 case PAM_ABORT:
1605 log_bad_attempts();
1606 (void) sleep(Disabletime);
1607 (void) printf(incorrectmsg);
1609 login_exit(1);
1610 /*NOTREACHED*/
1611 default: /* Some other PAM error */
1612 login_exit(1);
1613 /*NOTREACHED*/
1616 if (login_successful)
1617 break;
1619 /* sleep after bad passwd */
1620 if (count)
1621 (void) sleep(Sleeptime);
1622 (void) printf(incorrectmsg);
1623 /* force name to be null in this case */
1624 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1625 login_exit(1);
1626 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1627 login_exit(1);
1628 } while (count++ < retry);
1630 if (count >= retry) {
1632 * If logging is turned on, output the
1633 * string storage area to the log file,
1634 * and sleep for Disabletime
1635 * seconds before exiting.
1637 if (writelog)
1638 badlogin();
1639 if (dosyslog) {
1640 if ((pwd = getpwnam(user_name)) != NULL) {
1641 if (remote_host[0]) {
1642 syslog(LOG_CRIT,
1643 "REPEATED LOGIN FAILURES ON %s "
1644 "FROM %.*s, %.*s",
1645 ttyn, HMAX, remote_host, NMAX,
1646 user_name);
1647 } else {
1648 syslog(LOG_CRIT,
1649 "REPEATED LOGIN FAILURES ON "
1650 "%s, %.*s",
1651 ttyn, NMAX, user_name);
1653 } else {
1654 if (remote_host[0]) {
1655 syslog(LOG_CRIT,
1656 "REPEATED LOGIN FAILURES ON %s "
1657 "FROM %.*s",
1658 ttyn, HMAX, remote_host);
1659 } else {
1660 syslog(LOG_CRIT,
1661 "REPEATED LOGIN FAILURES ON %s",
1662 ttyn);
1666 (void) sleep(Disabletime);
1667 exit(1);
1673 * *** Credential Related routines ***
1678 * setup_credentials - sets the group ID, initializes the groups
1679 * and sets up the secretkey.
1680 * Exits if a failure occurrs.
1685 * setup_credentials - PAM does all the work for us on this one.
1688 static void
1689 setup_credentials(void)
1691 int error = 0;
1693 /* set the real (and effective) GID */
1694 if (setgid(pwd->pw_gid) == -1) {
1695 login_exit(1);
1699 * Initialize the supplementary group access list.
1701 if ((user_name[0] == '\0') ||
1702 (initgroups(user_name, pwd->pw_gid) == -1)) {
1703 login_exit(1);
1706 if ((error = pam_setcred(pamh, zflag ? PAM_REINITIALIZE_CRED :
1707 PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1708 login_exit(error);
1714 * *** Routines to get a new user set up and running ***
1716 * Things to do when starting up a new user:
1717 * adjust_nice
1718 * update_utmpx_entry
1719 * establish_user_environment
1720 * exec_the_shell
1726 * adjust_nice - Set the nice (process priority) value if the
1727 * gecos value contains an appropriate value.
1730 static void
1731 adjust_nice(void)
1733 int pri, mflg, i;
1735 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1736 pri = 0;
1737 mflg = 0;
1738 i = 4;
1740 if (pwd->pw_gecos[i] == '-') {
1741 mflg++;
1742 i++;
1745 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1746 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1748 if (mflg)
1749 pri = -pri;
1751 (void) nice(pri);
1756 * update_utmpx_entry - Searchs for the correct utmpx entry, making an
1757 * entry there if it finds one, otherwise exits.
1760 static void
1761 update_utmpx_entry(int sublogin, boolean_t silent)
1763 int err;
1764 char *user;
1765 static char *errmsg = "No utmpx entry. "
1766 "You must exec \"login\" from the lowest level \"shell\".";
1767 int tmplen;
1768 struct utmpx *u = NULL;
1769 struct utmpx utmpx;
1770 char *ttyntail;
1771 int pamflags = 0;
1773 if (silent)
1774 pamflags |= PAM_SILENT;
1777 * If we're not a sublogin then
1778 * we'll get an error back if our PID doesn't match the PID of the
1779 * entry we are updating, otherwise if its a sublogin the flags
1780 * field is set to 0, which means we just write a matching entry
1781 * (without checking the pid), or a new entry if an entry doesn't
1782 * exist.
1785 if ((err = pam_open_session(pamh, pamflags)) != PAM_SUCCESS) {
1786 login_exit(1);
1789 if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) !=
1790 PAM_SUCCESS) {
1791 login_exit(1);
1794 (void) memset(&utmpx, 0, sizeof (utmpx));
1795 (void) time(&utmpx.ut_tv.tv_sec);
1796 utmpx.ut_pid = getpid();
1798 if (hflag) {
1799 SCPYN(utmpx.ut_host, remote_host);
1800 tmplen = strlen(remote_host) + 1;
1801 if (tmplen < sizeof (utmpx.ut_host))
1802 utmpx.ut_syslen = tmplen;
1803 else
1804 utmpx.ut_syslen = sizeof (utmpx.ut_host);
1805 } else if (zflag) {
1807 * If this is a login from another zone, put the
1808 * zone:<zonename> string in the utmpx entry.
1810 SCPYN(utmpx.ut_host, zone_name);
1811 tmplen = strlen(zone_name) + 1;
1812 if (tmplen < sizeof (utmpx.ut_host))
1813 utmpx.ut_syslen = tmplen;
1814 else
1815 utmpx.ut_syslen = sizeof (utmpx.ut_host);
1816 } else {
1817 utmpx.ut_syslen = 0;
1820 SCPYN(utmpx.ut_user, user);
1822 /* skip over "/dev/" */
1823 ttyntail = basename(ttyn);
1825 while ((u = getutxent()) != NULL) {
1826 if ((u->ut_type == INIT_PROCESS ||
1827 u->ut_type == LOGIN_PROCESS ||
1828 u->ut_type == USER_PROCESS) &&
1829 ((sublogin && strncmp(u->ut_line, ttyntail,
1830 sizeof (u->ut_line)) == 0) ||
1831 u->ut_pid == login_pid)) {
1832 SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1));
1833 (void) memcpy(utmpx.ut_id, u->ut_id,
1834 sizeof (utmpx.ut_id));
1835 utmpx.ut_exit.e_exit = u->ut_exit.e_exit;
1836 utmpx.ut_type = USER_PROCESS;
1837 (void) pututxline(&utmpx);
1838 break;
1841 endutxent();
1843 if (u == NULL) {
1844 if (!sublogin) {
1846 * no utmpx entry already setup
1847 * (init or telnetd)
1849 (void) puts(errmsg);
1851 login_exit(1);
1853 } else {
1854 /* Now attempt to write out this entry to the wtmp file if */
1855 /* we were successful in getting it from the utmpx file and */
1856 /* the wtmp file exists. */
1857 updwtmpx(WTMPX_FILE, &utmpx);
1864 * process_chroot_logins - Chroots to the specified subdirectory and
1865 * re executes login.
1868 static int
1869 process_chroot_logins(void)
1872 * If the shell field starts with a '*', do a chroot to the home
1873 * directory and perform a new login.
1876 if (*pwd->pw_shell == '*') {
1877 (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
1878 pamh = NULL; /* really done */
1879 if (chroot(pwd->pw_dir) < 0) {
1880 (void) printf("No Root Directory\n");
1882 return (ERROR);
1885 * Set the environment flag <!sublogin> so that the next login
1886 * knows that it is a sublogin.
1888 envinit[0] = SUBLOGIN;
1889 envinit[1] = NULL;
1890 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
1891 (void) execle("/usr/bin/login", "login", (char *)0,
1892 &envinit[0]);
1893 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1894 (void) printf("No /usr/bin/login or /etc/login on root\n");
1896 login_exit(1);
1898 return (OK);
1902 * establish_user_environment - Set up the new users enviornment
1905 static void
1906 establish_user_environment(char **renvp)
1908 int i, j, k, l_index, length, idx = 0;
1909 char *endptr;
1910 char **lenvp;
1911 char **pam_env;
1913 lenvp = environ;
1914 while (*lenvp++)
1917 /* count the number of PAM environment variables set by modules */
1918 if ((pam_env = pam_getenvlist(pamh)) != 0) {
1919 for (idx = 0; pam_env[idx] != 0; idx++)
1923 envinit = (char **)calloc(lenvp - environ + 10 + MAXARGS + idx,
1924 sizeof (char *));
1925 if (envinit == NULL) {
1926 (void) printf("Calloc failed - out of swap space.\n");
1927 login_exit(8);
1931 * add PAM environment variables first so they
1932 * can be overwritten at login's discretion.
1933 * check for illegal environment variables.
1935 idx = 0; basicenv = 0;
1936 if (pam_env != 0) {
1937 while (pam_env[idx] != 0) {
1938 if (legalenvvar(pam_env[idx])) {
1939 envinit[basicenv] = pam_env[idx];
1940 basicenv++;
1942 idx++;
1945 (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv));
1947 /* Set up environment */
1948 if (hflag) {
1949 if (strlen(terminal)) {
1950 ENVSTRNCAT(term, terminal);
1952 } else {
1953 char *tp = getenv("TERM");
1955 if ((tp != NULL) && (*tp != '\0'))
1956 ENVSTRNCAT(term, tp);
1959 ENVSTRNCAT(logname, pwd->pw_name);
1962 * There are three places to get timezone info. init.c sets
1963 * TZ if the file /etc/default/init contains a value for TZ.
1964 * login.c looks in the file /etc/default/login for a
1965 * variable called TIMEZONE being set. If TIMEZONE has a
1966 * value, TZ is set to that value; no environment variable
1967 * TIMEZONE is set, only TZ. If neither of these methods
1968 * work to set TZ, then the library routines will default
1969 * to using the file /usr/lib/locale/TZ/localtime.
1971 * There is a priority set up here. If /etc/default/init has
1972 * a value for TZ, that value remains top priority. If the
1973 * file /etc/default/login has TIMEZONE set, that has second
1974 * highest priority not overriding the value of TZ in
1975 * /etc/default/init. The reason for this priority is that the
1976 * file /etc/default/init is supposed to be sourced by
1977 * /etc/profile. We are doing the "sourcing" prematurely in
1978 * init.c. Additionally, a login C shell doesn't source the
1979 * file /etc/profile thus not sourcing /etc/default/init thus not
1980 * allowing an adminstrator to globally set TZ for all users
1982 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
1983 tmp_tz = Def_tz;
1985 if ((Def_tz = getenv("TZ")) != NULL) {
1986 ENVSTRNCAT(timez, Def_tz);
1987 } else if (tmp_tz != NULL) {
1988 Def_tz = tmp_tz;
1989 ENVSTRNCAT(timez, Def_tz);
1992 if (Def_hertz == NULL)
1993 (void) sprintf(hertz + strlen(hertz), "%lu", HZ);
1994 else
1995 ENVSTRNCAT(hertz, Def_hertz);
1997 if (Def_path == NULL)
1998 (void) strlcat(path, DEF_PATH, sizeof (path));
1999 else
2000 ENVSTRNCAT(path, Def_path);
2002 ENVSTRNCAT(home, pwd->pw_dir);
2005 * Find the end of the basic environment
2007 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
2011 * If TZ has a value, add it.
2013 if (strcmp(timez, "TZ=") != 0)
2014 envinit[basicenv++] = timez;
2016 if (*pwd->pw_shell == '\0') {
2017 pwd->pw_shell = SHELL;
2018 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
2019 envinit[basicenv++] = shell;
2020 ENVSTRNCAT(shell, pwd->pw_shell);
2023 #ifndef NO_MAIL
2024 envinit[basicenv++] = mail;
2025 (void) strlcat(mail, pwd->pw_name, sizeof (mail));
2026 #endif
2029 * Pick up locale environment variables, if any.
2031 lenvp = renvp;
2032 while (*lenvp != NULL) {
2033 j = 0;
2034 while (localeenv[j] != 0) {
2036 * locale_envmatch() returns 1 if
2037 * *lenvp is localenev[j] and valid.
2039 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2040 envinit[basicenv++] = *lenvp;
2041 break;
2043 j++;
2045 lenvp++;
2049 * If '-p' flag, then try to pass on allowable environment
2050 * variables. Note that by processing this first, what is
2051 * passed on the final "login:" line may over-ride the invocation
2052 * values. XXX is this correct?
2054 if (pflag) {
2055 for (lenvp = renvp; *lenvp; lenvp++) {
2056 if (!legalenvvar(*lenvp)) {
2057 continue;
2060 * If this isn't 'xxx=yyy', skip it. XXX
2062 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2063 continue;
2065 length = endptr + 1 - *lenvp;
2066 for (j = 0; j < basicenv; j++) {
2067 if (strncmp(envinit[j], *lenvp, length) == 0) {
2069 * Replace previously established value
2071 envinit[j] = *lenvp;
2072 break;
2075 if (j == basicenv) {
2077 * It's a new definition, so add it at the end.
2079 envinit[basicenv++] = *lenvp;
2085 * Add in all the environment variables picked up from the
2086 * argument list to "login" or from the user response to the
2087 * "login" request, if any.
2090 if (envp == NULL)
2091 goto switch_env; /* done */
2093 for (j = 0, k = 0, l_index = 0;
2094 *envp != NULL && j < (MAXARGS-1);
2095 j++, envp++) {
2098 * Scan each string provided. If it doesn't have the
2099 * format xxx=yyy, then add the string "Ln=" to the beginning.
2101 if ((endptr = strchr(*envp, '=')) == NULL) {
2103 * This much to be malloc'd:
2104 * strlen(*envp) + 1 char for 'L' +
2105 * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
2107 * total = strlen(*envp) + MAXARGSWIDTH + 3
2109 int total = strlen(*envp) + MAXARGSWIDTH + 3;
2110 envinit[basicenv+k] = malloc(total);
2111 if (envinit[basicenv+k] == NULL) {
2112 (void) printf("%s: malloc failed\n", PROG_NAME);
2113 login_exit(1);
2115 (void) snprintf(envinit[basicenv+k], total, "L%d=%s",
2116 l_index, *envp);
2118 k++;
2119 l_index++;
2120 } else {
2121 if (!legalenvvar(*envp)) { /* this env var permited? */
2122 continue;
2123 } else {
2126 * Check to see whether this string replaces
2127 * any previously defined string
2129 for (i = 0, length = endptr + 1 - *envp;
2130 i < basicenv + k; i++) {
2131 if (strncmp(*envp, envinit[i], length)
2132 == 0) {
2133 envinit[i] = *envp;
2134 break;
2139 * If it doesn't, place it at the end of
2140 * environment array.
2142 if (i == basicenv+k) {
2143 envinit[basicenv+k] = *envp;
2144 k++;
2148 } /* for (j = 0 ... ) */
2150 switch_env:
2152 * Switch to the new environment.
2154 environ = envinit;
2158 * exec_the_shell - invoke the specified shell or start up program
2161 static void
2162 exec_the_shell(void)
2164 char *endptr;
2165 int i;
2167 (void) strlcat(minusnam, basename(pwd->pw_shell),
2168 sizeof (minusnam));
2171 * Exec the shell
2173 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2176 * pwd->pw_shell was not an executable object file, maybe it
2177 * is a shell proceedure or a command line with arguments.
2178 * If so, turn off the SHELL= environment variable.
2180 for (i = 0; envinit[i] != NULL; ++i) {
2181 if ((envinit[i] == shell) &&
2182 ((endptr = strchr(shell, '=')) != NULL))
2183 (*++endptr) = '\0';
2186 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2187 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2190 (void) printf("No shell\n");
2194 * login_exit - Call exit() and terminate.
2195 * This function is here for PAM so cleanup can
2196 * be done before the process exits.
2198 static void
2199 login_exit(int exit_code)
2201 if (pamh)
2202 (void) pam_end(pamh, PAM_ABORT);
2204 exit(exit_code);
2205 /*NOTREACHED*/
2209 * Check if lenv and penv matches or not.
2211 static int
2212 locale_envmatch(char *lenv, char *penv)
2214 while ((*lenv == *penv) && *lenv && *penv != '=') {
2215 lenv++;
2216 penv++;
2220 * '/' is eliminated for security reason.
2222 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2223 return (1);
2224 return (0);
2227 static int
2228 is_number(char *ptr)
2230 while (*ptr != '\0') {
2231 if (!isdigit(*ptr))
2232 return (0);
2233 ptr++;
2235 return (1);
2238 static void
2239 interrupt_syscall(int sig)
2241 return;
2244 static void
2245 printmotd(void)
2247 int fd;
2248 char buf[2048];
2249 ssize_t nr;
2251 if ((fd = open("/etc/motd", O_RDONLY)) < 0)
2252 return;
2254 (void) signal(SIGINT, interrupt_syscall);
2256 while ((nr = read(fd, buf, sizeof(buf))) > 0 &&
2257 write(STDOUT_FILENO, buf, nr) == nr)
2260 close(fd);