2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: login.c /main/8 1996/10/29 13:49:30 drk $ */
24 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
25 /* All Rights Reserved */
27 /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
28 /* The copyright notice above does not evidence any */
29 /* actual or intended publication of such source code. */
31 #ident "@(#)login.c 1.63 96/02/15 SMI" /* SVr4.0 1.43.6.26 */
35 PROPRIETARY NOTICE(Combined)
37 This source code is unpublished proprietary information
38 constituting, or derived under license from AT&T's UNIX(r) System V.
39 In addition, portions of such source code were derived from Berkeley
40 4.3 BSD under license from the Regents of the University of
47 Notice of copyright on this source code product does not indicate
50 (c) 1986, 1987, 1988, 1989, 1990, 1991, 1992 Sun Microsystems, Inc
51 (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T.
53 *******************************************************************
56 /* Copyright (c) 1987, 1988 Microsoft Corporation */
57 /* All Rights Reserved */
59 /* This Module contains Proprietary Information of Microsoft */
60 /* Corporation and should be treated as Confidential. */
63 * Usage: login [ -d device ] [ name ] [ environment args ]
71 * *** Header Files ***
76 #include <sys/types.h>
77 #include <sys/param.h>
78 #include <unistd.h> /* For logfile locking */
97 #include <security/pam_appl.h>
101 * *** Defines, Macros, and String Constants ***
106 #define ISSUEFILE "/etc/issue" /* file to print before prompt */
107 #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
110 * These need to be defined for UTMP management.
111 * If we add in the utility functions later, we
114 #define __UPDATE_ENTRY 1
118 * Intervals to sleep after failed login
121 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
123 static int Sleeptime = SLEEPTIME;
126 * seconds login disabled after allowable number of unsuccessful attempts
129 #define DISABLETIME 20
134 * Login logging support
136 #define LOGINLOG "/var/adm/loginlog" /* login log file */
137 #define LNAME_SIZE 20 /* size of logged logname */
138 #define TTYN_SIZE 15 /* size of logged tty name */
139 #define TIME_SIZE 30 /* size of logged time string */
140 #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
141 #define L_WAITTIME 5 /* waittime for log file to unlock */
142 #define LOGTRYS 10 /* depth of 'try' logging */
145 * String manipulation macros: SCPYN, EQN and ENVSTRNCAT
147 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
148 #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
149 #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
150 (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
155 #define NMAX sizeof (utmp.ut_name)
156 #define HMAX sizeof (utmp.ut_host)
157 #define min(a, b) (((a) < (b)) ? (a) : (b))
160 * Various useful files and string constants
162 #define SHELL "/usr/bin/sh"
163 #define SHELL2 "/sbin/sh"
164 #define SUBLOGIN "<!sublogin>"
165 #define LASTLOG "/var/adm/lastlog"
166 #define PROG_NAME "login"
167 #define HUSHLOGIN ".hushlogin"
170 * Array and Buffer sizes
172 #define PBUFSIZE 8 /* max significant characters in a password */
178 * Miscellaneous constants
184 #define DONT_LOG_ERROR 0
189 * Counters for counting the number of failed login attempts
196 extern int defopen(char *filename);
197 extern int getsecretkey();
202 extern int audit_login_save_flags(int rflag, int hflag);
203 extern int audit_login_save_host(char *host);
204 extern int audit_login_save_ttyn(char *ttyn);
205 extern int audit_login_save_port(void);
206 extern int audit_login_success(void);
207 extern int audit_login_save_pw(struct passwd *pwd);
208 extern int audit_login_bad_pw(void);
209 extern int audit_login_maxtrys(void);
210 extern int audit_login_not_console(void);
211 extern int audit_login_bad_dialup(void);
212 extern int audit_login_maxtrys(void);
215 * utmp file variables
217 static struct utmpx utmp;
220 * The current user name
222 static char user_name[64];
223 static char minusnam[16] = "-";
226 * locale environments to be passed to shells.
228 static char *localeenv[] = {
230 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
231 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
232 static int locale_envmatch(char *lenv, char *penv);
235 * Environment variable support
237 static char shell[256] = { "SHELL=" };
238 static char home[MAXPATHLEN] = { "HOME=" };
239 static char term[64] = { "TERM=" };
240 static char logname[30] = { "LOGNAME=" };
241 static char timez[100] = { "TZ=" };
242 static char hertz[10] = { "HZ=" };
243 static char path[MAXPATHLEN] = { "PATH=" };
244 static char *newenv[10+MAXARGS] =
245 {home, path, logname, hertz, term, 0, 0};
246 static char **envinit = newenv;
248 static char envblk[MAXENV];
249 static char *zero = (char *)0;
251 static char krb5ccname[256] = { "KRB5CCNAME=" };
252 static char krb4ccname[256] = { "KRBTKFILE=" };
254 static char mail[30] = { "MAIL=/var/mail/" };
256 extern char **environ;
257 char inputline[MAXLINE];
261 * Strings used to prompt the user.
263 static char loginmsg[] = "login: ";
264 static char passwdmsg[] = "Password:";
265 static char incorrectmsg[] = "Login incorrect\n";
268 * Password file support
270 static struct passwd *pwd;
271 static char remote_host[HMAX];
274 * Illegal passwd entries.
276 static struct passwd nouser = { "", "no:password", ~ROOTUID };
281 static char *log_entry[LOGTRYS];
282 static int writelog = 0;
283 static int lastlogok = 0;
284 static struct lastlog ll;
285 static int dosyslog = 0;
288 * Default file toggles
290 static char *Pndefault = "/etc/default/login";
291 static char *Altshell = NULL;
292 static char *Console = NULL;
293 static char *Passreq = NULL;
295 static mode_t Umask = DEFUMASK;
296 static char *Def_tz = NULL;
297 static char *tmp_tz = NULL;
298 static char *Def_hertz = NULL;
299 #define SET_FSIZ 2 /* ulimit() command arg */
300 static long Def_ulimit = 0;
301 #define MAX_TIMEOUT (15 * 60)
302 #define DEF_TIMEOUT (5 * 60)
303 static unsigned Def_timeout = DEF_TIMEOUT;
304 static char *Def_path = NULL;
305 static char *Def_supath = NULL;
306 #define DEF_PATH "/usr/bin:" /* same as PATH */
307 #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
310 * ttyprompt will point to the environment variable TTYPROMPT.
311 * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
313 static char *ttyprompt = NULL;
314 static char *ttyn = NULL;
315 static struct group *grpstr;
316 static char *ttygrp = "tty";
317 static char *progname = PROG_NAME;
320 * Pass inherited environment. Used by telnetd in support of the telnet
325 * Remote login support
327 static int hflag, rflag;
328 static char rusername[NMAX+1], lusername[NMAX+1];
329 static char terminal[MAXPATHLEN];
332 * Pre-authentication flag support
336 static int login_conv(int num_msg, struct pam_message **msg,
337 struct pam_response **response, void *appdata_ptr);
339 static struct pam_conv pam_conv = {login_conv, NULL};
340 static pam_handle_t *pamh; /* Authentication handle */
343 * Function declarations
345 static void turn_on_logging(void);
346 static void defaults(void);
347 static void usage(void);
348 static void process_rlogin(void);
349 static void login_authenticate();
350 static void setup_credentials(void);
351 static void adjust_nice(void);
352 static void update_utmp_entry(int sublogin);
353 static void establish_user_environment(char **renvp);
354 static void print_banner(void);
355 static void display_last_login_time(void);
356 static void exec_the_shell(void);
357 static int process_chroot_logins(void);
358 static int chdir_to_dir_root(void);
359 static void chdir_to_dir_user(void);
360 static void logindevperm(char *, uid_t, gid_t);
361 static void dir_dev_acc(char *, uid_t, gid_t, mode_t, char *);
362 static void check_log(void);
363 static void validate_account();
364 static void doremoteterm(char *term);
365 static int get_options(int argc, char *argv[]);
366 static void uppercaseterm(char *strp);
367 static void getstr(char *buf, int cnt, char *err);
368 static int legalenvvar(char *s);
369 static void check_for_root_user(void);
370 static void check_for_dueling_unix(char inputline[]);
371 static void get_user_name(void);
372 static void login_exit(int exit_code);
373 static int logins_disabled(char *user_name);
374 static void log_bad_attempts(void);
379 * The primary flow of control is directed in this routine.
380 * Control moves in line from top to bottom calling subfunctions
381 * which perform the bulk of the work. Many of these calls exit
382 * when a fatal error is encountered and do not return to main.
388 main(int argc, char *argv[], char **renvp)
393 * Set up Defaults and flags
398 * Set up default umask
400 if (Umask > ((mode_t) 0777))
405 * Set up default timeouts and delays
407 if (Def_timeout > MAX_TIMEOUT)
408 Def_timeout = MAX_TIMEOUT;
409 if (Sleeptime < 0 || Sleeptime > 5)
410 Sleeptime = SLEEPTIME;
412 (void) alarm(Def_timeout);
415 * Ignore SIGQUIT and SIGINT and set nice to 0
417 (void) signal(SIGQUIT, SIG_IGN);
418 (void) signal(SIGINT, SIG_IGN);
422 * Set flag to disable the pid check if you find that you are
426 if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
432 if (get_options(argc, argv) == -1) {
437 audit_login_save_flags(rflag, hflag);
438 audit_login_save_host(remote_host);
441 * if devicename is not passed as argument, call ttyname(0)
449 audit_login_save_ttyn(ttyn);
450 audit_login_save_port();
453 * Call pam_start to initiate a PAM authentication operation
456 if ((pam_start(progname, user_name, &pam_conv, &pamh))
459 if ((pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
462 if ((pam_set_item(pamh, PAM_RHOST, remote_host)) != PAM_SUCCESS) {
467 * Open the log file which contains a record of successful and failed
473 * say "hi" to syslogd ..
475 openlog("login", 0, LOG_AUTH);
478 * Do special processing for -r (rlogin) flag
486 /* we are already authenticated. fill in what we must, then continue */
488 if (pwd = getpwnam(user_name))
489 audit_login_save_pw(pwd);
491 audit_login_bad_pw();
497 * Perform the primary login authentication activity.
499 login_authenticate();
502 /* change root login, then we exec another login and try again */
503 if (process_chroot_logins() != OK)
507 * If root login and not on system console then call exit(2)
509 check_for_root_user();
512 * Check to see if a shutdown is in progress, if it is and
513 * we are not root then throw the user off the system
515 if (logins_disabled(user_name) == TRUE)
518 if (pwd->pw_uid == 0) {
519 if (Def_supath != NULL)
520 Def_path = Def_supath;
522 Def_path = DEF_SUPATH;
526 * Check account expiration and passwd aging
531 * We only get here if we've been authenticated.
533 update_utmp_entry(sublogin);
536 * Now we set up the environment for the new user, which includes
537 * the users ulimit, nice value, ownership of this tty, uid, gid,
538 * and environment variables.
540 if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
541 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
544 * Set mode to r/w user & w group, owner to user and group to tty
546 (void) chmod(ttyn, S_IRUSR|S_IWUSR|S_IWGRP);
548 if ((grpstr = getgrnam(ttygrp)) == NULL)
549 (void) chown(ttyn, pwd->pw_uid, pwd->pw_gid);
551 (void) chown(ttyn, pwd->pw_uid, grpstr->gr_gid);
553 logindevperm(ttyn, pwd->pw_uid, pwd->pw_gid);
554 adjust_nice(); /* passwd file can specify nice value */
557 * Record successful login and fork process that records logout.
558 * We have to do this before setting credentials because we need
559 * to be root in order do a setaudit() and an audit().
561 audit_login_success();
563 setup_credentials(); /* Set uid/gid - exits on failure */
565 pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
568 * Set up the basic environment for the exec. This includes
569 * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
573 establish_user_environment(renvp);
575 if (pwd->pw_uid == 0)
576 if (remote_host[0] && dosyslog)
577 syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
578 ttyn, HMAX, remote_host);
580 syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
583 (void) signal(SIGQUIT, SIG_DFL);
584 (void) signal(SIGINT, SIG_DFL);
587 * Display some useful information to the new user like the banner
588 * and last login time if not a quiet login.
591 if (access(HUSHLOGIN, F_OK) != 0) {
593 display_last_login_time();
597 * Now fire off the shell of choice
610 * *** Utility functions ***
616 * donothing & catch - Signal catching functions
624 pam_end(pamh, PAM_ABORT);
639 * *** Bad login logging support ***
643 * badlogin() - log to the log file 'trys'
644 * unsuccessful attempts
650 int retval, count1, fildes;
653 * Tries to open the log file. If succeed, lock it and write
654 * in the failed attempts
656 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
658 (void) sigset(SIGALRM, donothing);
659 (void) alarm(L_WAITTIME);
660 retval = lockf(fildes, F_LOCK, 0L);
662 (void) sigset(SIGALRM, SIG_DFL);
664 for (count1 = 0; count1 < trys; count1++)
665 (void) write(fildes, log_entry[count1],
666 (unsigned) strlen(log_entry[count1]));
667 (void) lockf(fildes, F_ULOCK, 0L);
669 (void) close(fildes);
675 * log_bad_attempts - log each bad login attempt - called from
676 * login_authenticate. Exits when the maximum attempt
681 log_bad_attempts(void)
685 if (writelog == 1 && trys < LOGTRYS) {
686 (void) time(&timenow);
687 (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
688 (void) strncat(log_entry[trys], ":", (size_t) 1);
689 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
690 (void) strncat(log_entry[trys], ":", (size_t) 1);
691 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE);
698 * turn_on_logging - if the logfile exist, turn on attempt logging and
699 * initialize the string storage area
703 turn_on_logging(void)
708 if (stat(LOGINLOG, &dbuf) == 0) {
710 for (i = 0; i < LOGTRYS; i++) {
711 if (!(log_entry[i] = malloc((size_t) ENT_SIZE))) {
715 *log_entry[i] = '\0';
723 * This is the conv (conversation) function called from
724 * a PAM authentication module to print error messages
725 * or garner information from the user.
728 login_conv(int num_msg, struct pam_message **msg,
729 struct pam_response **response, void *appdata_ptr)
731 struct pam_message *m;
732 struct pam_response *r;
737 return (PAM_CONV_ERR);
739 *response = calloc(num_msg, sizeof (struct pam_response));
740 if (*response == NULL)
741 return (PAM_BUF_ERR);
748 switch (m->msg_style) {
750 case PAM_PROMPT_ECHO_OFF:
751 temp = getpass(m->msg);
753 r->resp = strdup(temp);
754 if (r->resp == NULL) {
757 for (i = 0; i < num_msg; i++, r++) {
763 return (PAM_BUF_ERR);
771 case PAM_PROMPT_ECHO_ON:
773 (void) fputs(m->msg, stdout);
774 r->resp = calloc(1, PAM_MAX_RESP_SIZE);
775 if (r->resp == NULL) {
778 for (i = 0; i < num_msg; i++, r++) {
784 return (PAM_BUF_ERR);
786 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
788 r->resp[PAM_MAX_RESP_SIZE-1] = NULL;
789 len = strlen(r->resp);
790 if (r->resp[len-1] == '\n')
791 r->resp[len-1] = '\0';
800 if (m->msg != NULL) {
801 (void) fputs(m->msg, stderr);
802 (void) fputs("\n", stderr);
808 if (m->msg != NULL) {
809 (void) fputs(m->msg, stdout);
810 (void) fputs("\n", stdout);
820 return (PAM_SUCCESS);
824 * verify_passwd - Checks the users password
825 * Returns: OK if password check successful,
826 * ERROR if password check fails.
837 * PAM authenticates the user for us.
840 error = pam_authenticate(pamh, 0);
842 /* get the user_name from the pam handle */
843 pam_get_item(pamh, PAM_USER, (void**)&user);
844 SCPYN(user_name, user);
845 check_for_dueling_unix(user_name);
847 if (error != PAM_SUCCESS) {
848 /* something bad has happended - bye bye */
849 if (error == PAM_ABORT) {
850 audit_login_bad_pw();
852 (void) sleep(DISABLETIME);
853 (void) printf(incorrectmsg);
859 if ((pwd = getpwnam(user_name)) == NULL) {
860 audit_login_bad_pw();
862 return (PAM_USER_UNKNOWN);
865 audit_login_save_pw(pwd);
871 * quotec - Called by getargs
877 register int c, i, num;
879 switch (c = getc(stdin)) {
913 for (num = 0, i = 0; i < 3; i++) {
914 num = num * 8 + (c - '0');
915 if ((c = getc(stdin)) < '0' || c > '7')
918 (void) ungetc(c, stdin);
929 * getargs - returns an input line. Exits if EOF encountered.
935 getargs(char *in_line)
937 static char envbuf[MAXLINE];
938 static char *args[MAXARGS];
939 register char *ptr, **answer;
943 for (ptr = envbuf; ptr < &envbuf[sizeof (envbuf)]; /* cstyle */)
946 for (answer = args; answer < &args[MAXARGS]; /* cstyle */)
947 *answer++ = (char *)NULL;
949 for (ptr = envbuf, answer = &args[0], state = WHITESPACE;
950 (c = getc(stdin)) != EOF; /* cstyle */) {
956 if (ptr == &envbuf[0])
957 return ((char **)NULL);
962 if (state == ARGUMENT) {
972 if (state == WHITESPACE) {
980 * If the buffer is full, force the next character to be read to
983 if (ptr == &envbuf[sizeof (envbuf)-1]) {
984 (void) ungetc('\n', stdin);
985 (void) putc('\n', stdout);
990 * If we left loop because an EOF was received, exit immediately.
997 * get_user_name - Gets the user name either passed in, or from the
1007 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1008 char *ptr, buffer[BUFSIZ];
1009 while ((ptr = fgets(buffer, sizeof (buffer),
1011 (void) fputs(ptr, stdout);
1017 * if TTYPROMPT is not set, use our own prompt
1018 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1019 * and let the module do the prompting.
1022 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1023 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1025 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1027 envp = &zero; /* XXX: is this right? */
1032 * Check_for_dueling_unix - Check to see if the another login is talking
1033 * to the line we've got open as a login port
1034 * Exits if we're talking to another unix system
1038 check_for_dueling_unix(char *inputline)
1040 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1041 EQN(incorrectmsg, inputline)) {
1042 (void) printf("Looking at a login line.\n");
1048 * logins_disabled - if the file /etc/nologin exists and the user is not
1049 * root then do not permit them to login
1052 logins_disabled(char *user_name)
1056 if (!EQN("root", user_name) &&
1057 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1058 while ((c = getc(nlfd)) != EOF)
1060 (void) fflush(stdout);
1068 * check_for_root_user - Checks if we're getting a root login on the console
1069 * Exits if root login not on system console.
1074 check_for_root_user(void)
1076 if (pwd->pw_uid == 0) {
1077 if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) {
1078 audit_login_not_console();
1079 (void) printf("Not on system console\n");
1086 static char *illegal[] = {
1100 * legalenvvar - Is it legal to insert this environmental variable?
1104 legalenvvar(char *s)
1108 for (p = illegal; *p; p++)
1109 if (strncmp(s, *p, strlen(*p)) == 0)
1112 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1120 * getstr - Get a string from standard input
1121 * Calls exit if read(2) fails.
1125 getstr(char *buf, int cnt, char *err)
1130 if (read(0, &c, 1) != 1)
1133 } while (cnt > 1 && c != 0);
1136 err = err; /* For lint */
1141 * uppercaseterm - if all input char are upper case
1142 * set the corresponding termio
1146 uppercaseterm(char *strp)
1151 struct termio termio;
1153 for (sp = strp; *sp; sp++) {
1156 else if (isupper(*sp))
1160 if (upper > 0 && lower == 0) {
1161 (void) ioctl(0, TCGETA, &termio);
1162 termio.c_iflag |= IUCLC;
1163 termio.c_oflag |= OLCUC;
1164 termio.c_lflag |= XCASE;
1165 (void) ioctl(0, TCSETAW, &termio);
1166 for (sp = strp; *sp; sp++)
1167 if (*sp >= 'A' && *sp <= 'Z') *sp += ('a' - 'A');
1173 * defaults - read defaults
1179 extern int defcntl();
1183 if (defopen(Pndefault) == 0) {
1187 flags = defcntl(DC_GETFLAGS, 0);
1188 TURNOFF(flags, DC_CASE);
1189 defcntl(DC_SETFLAGS, flags);
1191 if ((Console = defread("CONSOLE=")) != NULL)
1192 Console = strdup(Console);
1194 if ((Altshell = defread("ALTSHELL=")) != NULL)
1195 Altshell = strdup(Altshell);
1197 if ((Passreq = defread("PASSREQ=")) != NULL)
1198 Passreq = strdup(Passreq);
1200 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1201 Def_tz = strdup(Def_tz);
1203 if ((Def_hertz = defread("HZ=")) != NULL)
1204 Def_hertz = strdup(Def_hertz);
1206 if ((Def_path = defread("PATH=")) != NULL)
1207 Def_path = strdup(Def_path);
1209 if ((Def_supath = defread("SUPATH=")) != NULL)
1210 Def_supath = strdup(Def_supath);
1212 if ((ptr = defread("ULIMIT=")) != NULL)
1213 Def_ulimit = atol(ptr);
1215 if ((ptr = defread("TIMEOUT=")) != NULL)
1216 Def_timeout = (unsigned) atoi(ptr);
1218 if ((ptr = defread("UMASK=")) != NULL) {
1220 if (sscanf(ptr, "%lo", &long_umask) != 1)
1223 Umask = (mode_t) long_umask;
1226 if ((ptr = defread("SLEEPTIME=")) != NULL)
1227 Sleeptime = atoi(ptr);
1229 if ((ptr = defread("SYSLOG=")) != NULL)
1230 dosyslog = strcmp(ptr, "YES") == 0;
1232 (void) defopen((char *)NULL);
1238 * get_options(argc, argv)
1239 * - parse the cmd line.
1240 * - return 0 if successful, -1 if failed.
1241 * Calls login_exit() on misuse of -r and -h flags
1245 get_options(int argc, char *argv[])
1250 while ((c = getopt(argc, argv, "f:h:r:pad:")) != -1) {
1257 * Must be root to pass in device name
1258 * otherwise we exit() as punishment for trying.
1260 if (getuid() != 0 || geteuid() != 0) {
1261 login_exit(1); /* sigh */
1268 if (hflag || rflag) {
1269 (void) fprintf(stderr,
1270 "Only one of -r and -h allowed\n");
1274 SCPYN(remote_host, optarg);
1276 if (argv[optind][0] != '-')
1277 SCPYN(terminal, argv[optind]);
1280 progname = "telnet";
1284 if (hflag || rflag) {
1285 (void) fprintf(stderr,
1286 "Only one of -r and -h allowed\n");
1290 SCPYN(remote_host, optarg);
1291 progname = "rlogin";
1300 * Must be root to bypass authentication
1301 * otherwise we exit() as punishment for trying.
1303 if (getuid() != 0 || geteuid() != 0) {
1304 login_exit(1); /* sigh */
1307 /* save fflag user name for future use */
1308 SCPYN(user_name, optarg);
1318 * get the prompt set by ttymon
1320 ttyprompt = getenv("TTYPROMPT");
1322 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1324 * if ttyprompt is set, there should be data on
1325 * the stream already.
1327 if ((envp = getargs(inputline)) != (char **)NULL) {
1328 uppercaseterm(*envp);
1330 * don't get name if name passed as argument.
1332 SCPYN(user_name, *envp++);
1334 } else if (optind < argc) {
1335 SCPYN(user_name, argv[optind]);
1336 (void) strcpy(inputline, user_name);
1337 (void) strcat(inputline, " \n");
1338 envp = &argv[optind+1];
1347 * usage - Print usage message
1354 (void) fprintf(stderr,
1355 "Usage:\tlogin [-h|-r] [ name [ env-var ... ]]\n");
1359 * *** Remote login support ***
1365 * doremoteterm - Sets the appropriate ioctls for a remote terminal
1368 static char *speeds[] = {
1369 "0", "50", "75", "110", "134", "150", "200", "300",
1370 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
1371 "57600", "76800", "115200", "153600", "230400", "307200", "460800"
1374 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1378 doremoteterm(char *term)
1381 register char *cp = strchr(term, '/'), **cpp;
1384 (void) ioctl(0, TCGETS, &tp);
1389 cp = strchr(speed, '/');
1394 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
1395 if (strcmp(*cpp, speed) == 0) {
1396 cfsetospeed(&tp, cpp-speeds);
1401 tp.c_lflag |= ECHO|ICANON;
1402 tp.c_iflag |= IGNPAR|ICRNL;
1404 (void) ioctl(0, TCSETS, &tp);
1410 * Process_rlogin - Does the work that rlogin and telnet
1415 process_rlogin(void)
1418 getstr(rusername, sizeof (rusername), "remuser");
1419 getstr(lusername, sizeof (lusername), "locuser");
1420 getstr(terminal, sizeof (terminal), "Terminal type");
1422 /* fflag has precedence over stuff passed by rlogind */
1423 if (fflag || getuid()) {
1425 doremoteterm(terminal);
1428 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1430 pwd = getpwnam(lusername);
1433 doremoteterm(terminal);
1438 doremoteterm(terminal);
1441 * Update PAM on the user name
1443 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1446 if (pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1449 SCPYN(user_name, lusername);
1451 lusername[0] = '\0';
1455 * *** Account validation routines ***
1460 * validate_account - This is the PAM version of validate.
1470 (void) alarm(0); /* give user time to come up with password */
1474 if ((Passreq != NULL) && (!strcasecmp("YES", Passreq)))
1475 flag = PAM_DISALLOW_NULL_AUTHTOK;
1479 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1480 if (error == PAM_AUTHTOKEN_REQD) {
1481 (void) printf("Choose a new password.\n");
1483 if ((error = pam_chauthtok(pamh, 0)) != PAM_SUCCESS) {
1486 "change password failure: %s",
1487 pam_strerror(pamh, error));
1491 (void) printf(incorrectmsg);
1494 "login account failure: %s",
1495 pam_strerror(pamh, error));
1502 * Check_log - This is really a hack because PAM checks the log, but login
1503 * wants to know if the log is okay and PAM doesn't have
1504 * a module independent way of handing this info back.
1513 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1515 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) {
1516 if (llseek(fdl, offset, SEEK_SET) == offset &&
1517 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) &&
1526 * chdir_to_dir_root - Attempts to chdir us to the home directory.
1527 * defaults to "/" if it can't cd to the home
1528 * directory, and returns ERROR if it can't do that.
1532 chdir_to_dir_root(void)
1534 if (chdir(pwd->pw_dir) < 0) {
1535 if (chdir("/") < 0) {
1536 (void) printf("No directory!\n");
1546 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1547 * place us in the user's home directory just in
1548 * case it was protected and the first chdir failed.
1549 * No chdir errors should happen at this point because
1550 * all failures should have happened on the first
1555 chdir_to_dir_user(void)
1557 if (chdir(pwd->pw_dir) < 0) {
1558 if (chdir("/") < 0) {
1559 (void) printf("No directory!\n");
1561 * This probably won't work since we can't get to /.
1563 if (remote_host[0] && dosyslog) {
1565 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1566 ttyn, HMAX, remote_host);
1567 } else if (dosyslog) {
1569 "REPEATED LOGIN FAILURES ON %s", ttyn);
1572 (void) sleep(DISABLETIME);
1575 (void) printf("No directory! Logging in with home=/\n");
1583 * login_authenticate - Performs the main authentication work
1584 * 1. Prints the login prompt
1585 * 2. Requests and verifys the password
1586 * 3. Checks the port password
1590 login_authenticate()
1597 /* if scheme broken, then nothing to do but quit */
1598 if (pam_get_item(pamh, PAM_USER, (void **)&user)
1603 * only get name from utility if it is not already
1604 * supplied by pam_start or a pam_set_item.
1606 if (!user || !user[0]) {
1607 /* use call back to get user name */
1611 err = verify_passwd();
1612 if ((err == PAM_SUCCESS) && (chdir_to_dir_root() == OK)) {
1617 /* scheme has kept count of retries. time to bail */
1618 if (err == PAM_MAXTRIES)
1621 /* only sleep after first bad passwd */
1623 (void) sleep(Sleeptime);
1624 (void) printf(incorrectmsg);
1625 user_name[0] = '\0'; /* is this needed? */
1626 /* force name to be null in this case */
1627 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1629 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1631 } while (cnt++ < MAXTRYS);
1633 if (cnt >= MAXTRYS) {
1634 audit_login_maxtrys();
1636 * If logging is turned on, output the
1637 * string storage area to the log file,
1638 * and sleep for DISABLETIME
1639 * seconds before exiting.
1643 if (remote_host[0] && dosyslog) {
1645 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1646 ttyn, HMAX, remote_host);
1647 } else if (dosyslog) {
1649 "REPEATED LOGIN FAILURES ON %s", ttyn);
1651 (void) sleep(DISABLETIME);
1658 * *** Credential Related routines ***
1663 * setup_credentials - sets the group ID, initializes the groups
1664 * and sets up the secretkey.
1665 * Exits if a failure occurrs.
1670 * setup_credentials - PAM does all the work for us on this one.
1674 setup_credentials(void)
1679 /* set the real (and effective) GID */
1680 if (setgid(pwd->pw_gid) == -1) {
1685 * Initialize the supplementary group access list.
1689 if (initgroups(user_name, pwd->pw_gid) == -1) {
1693 /* XXX really should be after setgid */
1694 if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1698 /* set the real (and effective) UID */
1699 if (setuid(pwd->pw_uid) == -1) {
1707 * *** Routines to get a new user set up and running ***
1709 * Things to do when starting up a new user:
1712 * establish_user_environment
1714 * display_last_login_time
1721 * adjust_nice - Set the nice (process priority) value if the
1722 * gecos value contains an appropriate value.
1730 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1735 if (pwd->pw_gecos[i] == '-') {
1740 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1741 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1751 * update_utmp_entry - Searchs for the correct utmp entry, making an
1752 * entry there if it finds one, otherwise exits.
1756 update_utmp_entry(int sublogin)
1760 static char *errmsg = "No utmpx entry. "
1761 "You must exec \"login\" from the lowest level \"shell\".";
1763 struct utmpx *u = (struct utmpx *)0;
1769 * If we're not a sublogin then
1770 * we'll get an error back if our PID doesn't match the PID of the
1771 * entry we are updating, otherwise if its a sublogin the flags
1772 * field is set to 0, which means we just write a matching entry
1773 * (without checking the pid), or a new entry if an entry doesn't
1777 if (error = pam_open_session(pamh, 0) != PAM_SUCCESS) {
1781 if ((error = pam_get_item(pamh, PAM_USER, (void **) &user))
1786 (void) memset((void *)&utmp, 0, sizeof (utmp));
1787 (void) time(&utmp.ut_tv.tv_sec);
1788 utmp.ut_pid = getpid();
1790 if (rflag || hflag) {
1791 SCPYN(utmp.ut_host, remote_host);
1792 tmplen = strlen(remote_host) + 1;
1793 if (tmplen < sizeof (utmp.ut_host))
1794 utmp.ut_syslen = tmplen;
1796 utmp.ut_syslen = sizeof (utmp.ut_host);
1801 SCPYN(utmp.ut_user, user);
1803 /* skip over "/dev/" */
1804 ttyntail = basename(ttyn);
1806 while ((u = getutxent()) != NULL) {
1807 if ((u->ut_type == INIT_PROCESS ||
1808 u->ut_type == LOGIN_PROCESS ||
1809 u->ut_type == USER_PROCESS) &&
1810 ((sublogin && strncmp(u->ut_line, ttyntail,
1811 sizeof (u->ut_line)) == 0) ||
1812 u->ut_pid == utmp.ut_pid)) {
1813 SCPYN(utmp.ut_line, (ttyn+sizeof ("/dev/")-1));
1814 (void) memcpy(utmp.ut_id, u->ut_id,
1815 sizeof (utmp.ut_id));
1816 utmp.ut_type = USER_PROCESS;
1823 if (u == (struct utmpx *)NULL) {
1826 * no utmp entry already setup
1827 * (init or rlogind/telnetd)
1829 (void) puts(errmsg);
1833 /* Now attempt to write out this entry to the wtmp file if */
1834 /* we were successful in getting it from the utmp file and */
1835 /* the wtmp file exists. */
1836 updwtmpx(WTMPX_FILE, &utmp);
1843 * process_chroot_logins - Chroots to the specified subdirectory and
1844 * re executes login.
1848 process_chroot_logins(void)
1851 * If the shell field starts with a '*', do a chroot to the home
1852 * directory and perform a new login.
1855 if (*pwd->pw_shell == '*') {
1856 pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
1857 if (chroot(pwd->pw_dir) < 0) {
1858 (void) printf("No Root Directory\n");
1862 * Set the environment flag <!sublogin> so that the next login
1863 * knows that it is a sublogin.
1865 envinit[0] = SUBLOGIN;
1866 envinit[1] = (char *)NULL;
1867 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
1868 (void) execle("/usr/bin/login", "login", (char *)0,
1870 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1871 (void) printf("No /usr/bin/login or /etc/login on root\n");
1878 * establish_user_environment - Set up the new users environment
1882 establish_user_environment(char **renvp)
1884 int i, j, k, l_index, length;
1894 envinit = (char **) calloc(lenvp - environ + 10
1895 + MAXARGS, sizeof (char *));
1896 if (envinit == NULL) {
1897 (void) printf("Calloc failed - out of swap space.\n");
1900 memcpy(envinit, newenv, sizeof (newenv));
1902 /* Set up environment */
1904 ENVSTRNCAT(term, terminal);
1906 /* XXX: this causes an environment variable with the value */
1907 /* "" to be created later on, because terminal is "". */
1908 (void) strncpy(term, terminal, sizeof (term) - 1);
1910 char *tp = getenv("TERM");
1912 if ((tp != NULL) && (*tp != '\0'))
1913 ENVSTRNCAT(term, tp);
1916 ENVSTRNCAT(logname, pwd->pw_name);
1919 * There are three places to get timezone info. init.c sets
1920 * TZ if the file /etc/TIMEZONE contains a value for TZ.
1921 * login.c looks in the file /etc/default/login for a
1922 * variable called TIMEZONE being set. If TIMEZONE has a
1923 * value, TZ is set to that value; no environment variable
1924 * TIMEZONE is set, only TZ. If neither of these methods
1925 * work to set TZ, then the library routines will default
1926 * to using the file /usr/lib/locale/TZ/localtime.
1928 * There is a priority set up here. If /etc/TIMEZONE has
1929 * a value for TZ, that value remains top priority. If the
1930 * file /etc/default/login has TIMEZONE set, that has second
1931 * highest priority not overriding the value of TZ in
1932 * /etc/TIMEZONE. The reason for this priority is that the
1933 * file /etc/TIMEZONE is supposed to be sourced by
1934 * /etc/profile. We are doing the "sourcing" prematurely in
1935 * init.c. Additionally, a login C shell doesn't source the
1936 * file /etc/profile thus not sourcing /etc/TIMEZONE thus not
1937 * allowing an administrator to globally set TZ for all users
1939 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
1942 if ((Def_tz = getenv("TZ")) != NULL) {
1943 ENVSTRNCAT(timez, Def_tz);
1944 } else if (tmp_tz != NULL) {
1946 ENVSTRNCAT(timez, Def_tz);
1949 if (Def_hertz == NULL)
1950 (void) sprintf(hertz + strlen(hertz), "%u", HZ);
1952 ENVSTRNCAT(hertz, Def_hertz);
1954 if (Def_path == NULL)
1955 (void) strcat(path, DEF_PATH);
1957 ENVSTRNCAT(path, Def_path);
1959 ENVSTRNCAT(home, pwd->pw_dir);
1962 * Find the end of the basic environment
1964 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
1968 * If TZ has a value, add it.
1970 if (strcmp(timez, "TZ=") != 0)
1971 envinit[basicenv++] = timez;
1973 if (*pwd->pw_shell == '\0') {
1975 * If possible, use the primary default shell,
1976 * otherwise, use the secondary one.
1978 if (access(SHELL, X_OK) == 0)
1979 pwd->pw_shell = SHELL;
1981 pwd->pw_shell = SHELL2;
1982 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
1983 envinit[basicenv++] = shell;
1984 ENVSTRNCAT(shell, pwd->pw_shell);
1988 envinit[basicenv++] = mail;
1989 (void) strcat(mail, pwd->pw_name);
1993 * Pick up locale environment variables, if any.
1996 while (*lenvp != NULL) {
1998 while (localeenv[j] != 0) {
2000 * locale_envmatch() returns 1 if
2001 * *lenvp is localenev[j] and valid.
2003 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2004 envinit[basicenv++] = *lenvp;
2013 * If '-p' flag, then try to pass on allowable environment
2014 * variables. Note that by processing this first, what is
2015 * passed on the final "login:" line may over-ride the invocation
2016 * values. XXX is this correct?
2019 for (lenvp = renvp; *lenvp; lenvp++) {
2020 if (!legalenvvar(*lenvp)) {
2024 * If this isn't 'xxx=yyy', skip it. XXX
2026 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2029 length = endptr + 1 - *lenvp;
2030 for (j = 0; j < basicenv; j++) {
2031 if (strncmp(envinit[j], *lenvp, length) == 0) {
2033 * Replace previously established value
2035 envinit[j] = *lenvp;
2039 if (j == basicenv) {
2041 * It's a new definition, so add it at the end.
2043 envinit[basicenv++] = *lenvp;
2049 * Add in all the environment variables picked up from the
2050 * argument list to "login" or from the user response to the
2054 for (j = 0, k = 0, l_index = 0, ptr = &envblk[0];
2055 *envp && j < (MAXARGS-1); j++, envp++) {
2058 * Scan each string provided. If it doesn't have the
2059 * format xxx=yyy, then add the string "Ln=" to the beginning.
2061 if ((endptr = strchr(*envp, '=')) == NULL) {
2062 envinit[basicenv+k] = ptr;
2063 (void) sprintf(ptr, "L%d=%s", l_index, *envp);
2066 * Advance "ptr" to the beginning of the
2074 if (!legalenvvar(*envp)) { /* this env var permited? */
2079 * Check to see whether this string replaces
2080 * any previously defined string
2082 for (i = 0, length = endptr + 1 - *envp;
2083 i < basicenv + k; i++) {
2084 if (strncmp(*envp, envinit[i], length)
2092 * If it doesn't, place it at the end of
2093 * environment array.
2095 if (i == basicenv+k) {
2096 envinit[basicenv+k] = *envp;
2101 } /* for (j = 0 ... ) */
2104 * Add DCE/Kerberos cred name, if any.
2105 * XXX - The module specific stuff should be removed from login
2106 * program eventually. This is better placed in DCE module.
2107 * So far, we haven't come up with a way to deal with these
2108 * environment variables.
2111 krb5p = getenv("KRB5CCNAME");
2113 if ((krb5p != NULL) && (*krb5p != '\0')) {
2114 ENVSTRNCAT(krb5ccname, krb5p);
2115 envinit[basicenv++] = krb5ccname;
2118 krb4p = getenv("KRBTKFILE");
2120 if ((krb4p != NULL) && (*krb4p != '\0')) {
2121 ENVSTRNCAT(krb4ccname, krb4p);
2122 envinit[basicenv++] = krb4ccname;
2126 * Switch to the new environment.
2132 * print_banner - Print the banner at start up
2133 * Do not turn on DOBANNER ifdef. This is not
2134 * relevant to SunOS.
2143 (void) printf("UNIX System V/386 Release %s\n%s\n"
2144 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
2145 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
2146 un.release, un.nodename);
2148 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
2149 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
2150 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
2151 "All Rights Reserved\n",
2152 un.release, un.machine, un.nodename);
2154 (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
2155 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
2156 un.release, un.machine, un.nodename);
2158 #endif /* DOBANNER */
2162 * display_last_login_time - Advise the user the time and date
2163 * that this login-id was last used.
2167 display_last_login_time(void)
2170 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2172 if (*ll.ll_host != '\0')
2173 (void) printf("from %.*s\n", sizeof (ll.ll_host),
2176 (void) printf("on %.*s\n", sizeof (ll.ll_line),
2182 * exec_the_shell - invoke the specified shell or start up program
2186 exec_the_shell(void)
2191 (void) strcat(minusnam, basename(pwd->pw_shell));
2196 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2199 * pwd->pw_shell was not an executable object file, maybe it
2200 * is a shell proceedure or a command line with arguments.
2201 * If so, turn off the SHELL= environment variable.
2203 for (i = 0; envinit[i] != NULL; ++i) {
2204 if ((envinit[i] == shell) &&
2205 ((endptr = strchr(shell, '=')) != NULL))
2209 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2210 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2211 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
2214 (void) printf("No shell\n");
2218 * login_exit - Call exit() and terminate.
2219 * This function is here for PAM so cleanup can
2220 * be done before the process exits.
2223 login_exit(int exit_code)
2226 pam_end(pamh, PAM_ABORT);
2232 * Check if lenv and penv matches or not.
2235 locale_envmatch(char *lenv, char *penv)
2237 while ((*lenv == *penv) && *lenv && *penv != '=') {
2243 * '/' is eliminated for security reason.
2245 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2251 * logindevperm - change owner/group/permissions of devices
2252 * list in /etc/logindevperm. (Code derived from set_fb_attrs()
2253 * in 4.x usr/src/bin/login.c and usr/src/etc/getty/main.c.)
2256 #define MAX_LINELEN 256
2257 #define LOGINDEVPERM "/etc/logindevperm"
2258 #define DIRWILD "/*" /* directory wildcard */
2259 #define DIRWLDLEN 2 /* strlen(DIRWILD) */
2262 logindevperm(char *ttyn, uid_t uid, gid_t gid)
2264 char *field_delims = " \t\n";
2265 char *permfile = LOGINDEVPERM;
2266 char line[MAX_LINELEN];
2277 if ((fp = fopen(permfile, "r")) == NULL)
2281 while (fgets(line, MAX_LINELEN, fp) != NULL) {
2284 if ((ptr = strchr(line, '#')) != NULL)
2285 *ptr = '\0'; /* handle comments */
2287 if ((console = strtok(line, field_delims)) == NULL)
2288 continue; /* ignore blank lines */
2290 if (strcmp(console, ttyn) != 0)
2291 continue; /* not our tty, skip */
2293 mode_str = strtok((char *)NULL, field_delims);
2294 if (mode_str == NULL) {
2295 (void) fprintf(stderr,
2296 "%s: line %d, invalid entry -- %s\n", permfile,
2301 /* convert string to octal value */
2302 mode = strtol(mode_str, &ptr, 8);
2303 if (mode < 0 || mode > 0777 || *ptr != '\0') {
2304 (void) fprintf(stderr,
2305 "%s: line %d, invalid mode -- %s\n", permfile,
2310 dev_list = strtok((char *)NULL, field_delims);
2311 if (dev_list == NULL) {
2312 (void) fprintf(stderr,
2313 "%s: line %d, %s -- empty device list\n",
2314 permfile, lineno, console);
2318 device = strtok(dev_list, ":");
2319 while (device != NULL) {
2321 ptr = &device[l - DIRWLDLEN];
2322 if ((l > DIRWLDLEN) && (strcmp(ptr, DIRWILD) == 0)) {
2323 *ptr = '\0'; /* chop off wildcard */
2324 dir_dev_acc(device, uid, gid, mode, permfile);
2327 * change the owner/group/permission;
2328 * nonexistent devices are ignored
2330 if (chown(device, uid, gid) == -1) {
2331 if (errno != ENOENT) {
2332 (void) fprintf(stderr, "%s: ",
2337 if ((chmod(device, mode) == -1) &&
2338 (errno != ENOENT)) {
2339 (void) fprintf(stderr, "%s: ",
2345 device = strtok((char *)NULL, ":");
2352 * Apply owner/group/perms to all files (except "." and "..")
2356 dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile)
2359 struct dirent *direntp;
2360 char *name, path[MAX_LINELEN + MAXNAMELEN];
2362 dirp = opendir(dir);
2366 while ((direntp = readdir(dirp)) != NULL) {
2367 name = direntp->d_name;
2368 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
2371 (void) sprintf(path, "%s/%s", dir, name);
2372 if (chown(path, uid, gid) == -1) {
2373 if (errno != ENOENT) {
2374 (void) fprintf(stderr, "%s: ", permfile);
2378 if ((chmod(path, mode) == -1) && (errno != ENOENT)) {
2379 (void) fprintf(stderr, "%s: ", permfile);
2384 (void) closedir(dirp);