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);
628 * *** Bad login logging support ***
632 * badlogin() - log to the log file 'trys'
633 * unsuccessful attempts
639 int retval, count1, fildes;
642 * Tries to open the log file. If succeed, lock it and write
643 * in the failed attempts
645 if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
647 (void) sigset(SIGALRM, donothing);
648 (void) alarm(L_WAITTIME);
649 retval = lockf(fildes, F_LOCK, 0L);
651 (void) sigset(SIGALRM, SIG_DFL);
653 for (count1 = 0; count1 < trys; count1++)
654 (void) write(fildes, log_entry[count1],
655 (unsigned) strlen(log_entry[count1]));
656 (void) lockf(fildes, F_ULOCK, 0L);
658 (void) close(fildes);
664 * log_bad_attempts - log each bad login attempt - called from
665 * login_authenticate. Exits when the maximum attempt
670 log_bad_attempts(void)
674 if (writelog == 1 && trys < LOGTRYS) {
675 (void) time(&timenow);
676 (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
677 (void) strncat(log_entry[trys], ":", (size_t) 1);
678 (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
679 (void) strncat(log_entry[trys], ":", (size_t) 1);
680 (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE);
687 * turn_on_logging - if the logfile exist, turn on attempt logging and
688 * initialize the string storage area
692 turn_on_logging(void)
697 if (stat(LOGINLOG, &dbuf) == 0) {
699 for (i = 0; i < LOGTRYS; i++) {
700 if (!(log_entry[i] = malloc((size_t) ENT_SIZE))) {
704 *log_entry[i] = '\0';
712 * This is the conv (conversation) function called from
713 * a PAM authentication module to print error messages
714 * or garner information from the user.
717 login_conv(int num_msg, struct pam_message **msg,
718 struct pam_response **response, void *appdata_ptr)
720 struct pam_message *m;
721 struct pam_response *r;
726 return (PAM_CONV_ERR);
728 *response = calloc(num_msg, sizeof (struct pam_response));
729 if (*response == NULL)
730 return (PAM_BUF_ERR);
737 switch (m->msg_style) {
739 case PAM_PROMPT_ECHO_OFF:
740 temp = getpass(m->msg);
742 r->resp = strdup(temp);
743 if (r->resp == NULL) {
746 for (i = 0; i < num_msg; i++, r++) {
752 return (PAM_BUF_ERR);
760 case PAM_PROMPT_ECHO_ON:
762 (void) fputs(m->msg, stdout);
763 r->resp = calloc(1, PAM_MAX_RESP_SIZE);
764 if (r->resp == NULL) {
767 for (i = 0; i < num_msg; i++, r++) {
773 return (PAM_BUF_ERR);
775 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
777 r->resp[PAM_MAX_RESP_SIZE-1] = NULL;
778 len = strlen(r->resp);
779 if (r->resp[len-1] == '\n')
780 r->resp[len-1] = '\0';
789 if (m->msg != NULL) {
790 (void) fputs(m->msg, stderr);
791 (void) fputs("\n", stderr);
797 if (m->msg != NULL) {
798 (void) fputs(m->msg, stdout);
799 (void) fputs("\n", stdout);
809 return (PAM_SUCCESS);
813 * verify_passwd - Checks the users password
814 * Returns: OK if password check successful,
815 * ERROR if password check fails.
826 * PAM authenticates the user for us.
829 error = pam_authenticate(pamh, 0);
831 /* get the user_name from the pam handle */
832 pam_get_item(pamh, PAM_USER, (void**)&user);
833 SCPYN(user_name, user);
834 check_for_dueling_unix(user_name);
836 if (error != PAM_SUCCESS) {
837 /* something bad has happended - bye bye */
838 if (error == PAM_ABORT) {
839 audit_login_bad_pw();
841 (void) sleep(DISABLETIME);
842 (void) printf(incorrectmsg);
848 if ((pwd = getpwnam(user_name)) == NULL) {
849 audit_login_bad_pw();
851 return (PAM_USER_UNKNOWN);
854 audit_login_save_pw(pwd);
860 * quotec - Called by getargs
868 switch (c = getc(stdin)) {
902 for (num = 0, i = 0; i < 3; i++) {
903 num = num * 8 + (c - '0');
904 if ((c = getc(stdin)) < '0' || c > '7')
907 (void) ungetc(c, stdin);
918 * getargs - returns an input line. Exits if EOF encountered.
924 getargs(char *in_line)
926 static char envbuf[MAXLINE];
927 static char *args[MAXARGS];
932 for (ptr = envbuf; ptr < &envbuf[sizeof (envbuf)]; /* cstyle */)
935 for (answer = args; answer < &args[MAXARGS]; /* cstyle */)
936 *answer++ = (char *)NULL;
938 for (ptr = envbuf, answer = &args[0], state = WHITESPACE;
939 (c = getc(stdin)) != EOF; /* cstyle */) {
945 if (ptr == &envbuf[0])
946 return ((char **)NULL);
951 if (state == ARGUMENT) {
961 if (state == WHITESPACE) {
969 * If the buffer is full, force the next character to be read to
972 if (ptr == &envbuf[sizeof (envbuf)-1]) {
973 (void) ungetc('\n', stdin);
974 (void) putc('\n', stdout);
979 * If we left loop because an EOF was received, exit immediately.
986 * get_user_name - Gets the user name either passed in, or from the
996 if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
997 char *ptr, buffer[BUFSIZ];
998 while ((ptr = fgets(buffer, sizeof (buffer),
1000 (void) fputs(ptr, stdout);
1006 * if TTYPROMPT is not set, use our own prompt
1007 * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
1008 * and let the module do the prompting.
1011 if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1012 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1014 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1016 envp = &zero; /* XXX: is this right? */
1021 * Check_for_dueling_unix - Check to see if the another login is talking
1022 * to the line we've got open as a login port
1023 * Exits if we're talking to another unix system
1027 check_for_dueling_unix(char *inputline)
1029 if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1030 EQN(incorrectmsg, inputline)) {
1031 (void) printf("Looking at a login line.\n");
1037 * logins_disabled - if the file /etc/nologin exists and the user is not
1038 * root then do not permit them to login
1041 logins_disabled(char *user_name)
1045 if (!EQN("root", user_name) &&
1046 ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1047 while ((c = getc(nlfd)) != EOF)
1049 (void) fflush(stdout);
1057 * check_for_root_user - Checks if we're getting a root login on the console
1058 * Exits if root login not on system console.
1063 check_for_root_user(void)
1065 if (pwd->pw_uid == 0) {
1066 if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) {
1067 audit_login_not_console();
1068 (void) printf("Not on system console\n");
1075 static char *illegal[] = {
1089 * legalenvvar - Is it legal to insert this environmental variable?
1093 legalenvvar(char *s)
1097 for (p = illegal; *p; p++)
1098 if (strncmp(s, *p, strlen(*p)) == 0)
1101 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1109 * getstr - Get a string from standard input
1110 * Calls exit if read(2) fails.
1114 getstr(char *buf, int cnt, char *err)
1119 if (read(0, &c, 1) != 1)
1122 } while (cnt > 1 && c != 0);
1125 err = err; /* For lint */
1130 * uppercaseterm - if all input char are upper case
1131 * set the corresponding termio
1135 uppercaseterm(char *strp)
1140 struct termio termio;
1142 for (sp = strp; *sp; sp++) {
1145 else if (isupper(*sp))
1149 if (upper > 0 && lower == 0) {
1150 (void) ioctl(0, TCGETA, &termio);
1151 termio.c_iflag |= IUCLC;
1152 termio.c_oflag |= OLCUC;
1153 termio.c_lflag |= XCASE;
1154 (void) ioctl(0, TCSETAW, &termio);
1155 for (sp = strp; *sp; sp++)
1156 if (*sp >= 'A' && *sp <= 'Z') *sp += ('a' - 'A');
1162 * defaults - read defaults
1168 extern int defcntl();
1172 if (defopen(Pndefault) == 0) {
1176 flags = defcntl(DC_GETFLAGS, 0);
1177 TURNOFF(flags, DC_CASE);
1178 defcntl(DC_SETFLAGS, flags);
1180 if ((Console = defread("CONSOLE=")) != NULL)
1181 Console = strdup(Console);
1183 if ((Altshell = defread("ALTSHELL=")) != NULL)
1184 Altshell = strdup(Altshell);
1186 if ((Passreq = defread("PASSREQ=")) != NULL)
1187 Passreq = strdup(Passreq);
1189 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1190 Def_tz = strdup(Def_tz);
1192 if ((Def_hertz = defread("HZ=")) != NULL)
1193 Def_hertz = strdup(Def_hertz);
1195 if ((Def_path = defread("PATH=")) != NULL)
1196 Def_path = strdup(Def_path);
1198 if ((Def_supath = defread("SUPATH=")) != NULL)
1199 Def_supath = strdup(Def_supath);
1201 if ((ptr = defread("ULIMIT=")) != NULL)
1202 Def_ulimit = atol(ptr);
1204 if ((ptr = defread("TIMEOUT=")) != NULL)
1205 Def_timeout = (unsigned) atoi(ptr);
1207 if ((ptr = defread("UMASK=")) != NULL) {
1209 if (sscanf(ptr, "%lo", &long_umask) != 1)
1212 Umask = (mode_t) long_umask;
1215 if ((ptr = defread("SLEEPTIME=")) != NULL)
1216 Sleeptime = atoi(ptr);
1218 if ((ptr = defread("SYSLOG=")) != NULL)
1219 dosyslog = strcmp(ptr, "YES") == 0;
1221 (void) defopen((char *)NULL);
1227 * get_options(argc, argv)
1228 * - parse the cmd line.
1229 * - return 0 if successful, -1 if failed.
1230 * Calls login_exit() on misuse of -r and -h flags
1234 get_options(int argc, char *argv[])
1239 while ((c = getopt(argc, argv, "f:h:r:pad:")) != -1) {
1246 * Must be root to pass in device name
1247 * otherwise we exit() as punishment for trying.
1249 if (getuid() != 0 || geteuid() != 0) {
1250 login_exit(1); /* sigh */
1257 if (hflag || rflag) {
1258 (void) fprintf(stderr,
1259 "Only one of -r and -h allowed\n");
1263 SCPYN(remote_host, optarg);
1265 if (argv[optind][0] != '-')
1266 SCPYN(terminal, argv[optind]);
1269 progname = "telnet";
1273 if (hflag || rflag) {
1274 (void) fprintf(stderr,
1275 "Only one of -r and -h allowed\n");
1279 SCPYN(remote_host, optarg);
1280 progname = "rlogin";
1289 * Must be root to bypass authentication
1290 * otherwise we exit() as punishment for trying.
1292 if (getuid() != 0 || geteuid() != 0) {
1293 login_exit(1); /* sigh */
1296 /* save fflag user name for future use */
1297 SCPYN(user_name, optarg);
1307 * get the prompt set by ttymon
1309 ttyprompt = getenv("TTYPROMPT");
1311 if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1313 * if ttyprompt is set, there should be data on
1314 * the stream already.
1316 if ((envp = getargs(inputline)) != (char **)NULL) {
1317 uppercaseterm(*envp);
1319 * don't get name if name passed as argument.
1321 SCPYN(user_name, *envp++);
1323 } else if (optind < argc) {
1324 SCPYN(user_name, argv[optind]);
1325 (void) strcpy(inputline, user_name);
1326 (void) strcat(inputline, " \n");
1327 envp = &argv[optind+1];
1336 * usage - Print usage message
1343 (void) fprintf(stderr,
1344 "Usage:\tlogin [-h|-r] [ name [ env-var ... ]]\n");
1348 * *** Remote login support ***
1354 * doremoteterm - Sets the appropriate ioctls for a remote terminal
1357 static char *speeds[] = {
1358 "0", "50", "75", "110", "134", "150", "200", "300",
1359 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
1360 "57600", "76800", "115200", "153600", "230400", "307200", "460800"
1363 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1367 doremoteterm(char *term)
1370 char *cp = strchr(term, '/'), **cpp;
1373 (void) ioctl(0, TCGETS, &tp);
1378 cp = strchr(speed, '/');
1383 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
1384 if (strcmp(*cpp, speed) == 0) {
1385 cfsetospeed(&tp, cpp-speeds);
1390 tp.c_lflag |= ECHO|ICANON;
1391 tp.c_iflag |= IGNPAR|ICRNL;
1393 (void) ioctl(0, TCSETS, &tp);
1399 * Process_rlogin - Does the work that rlogin and telnet
1404 process_rlogin(void)
1407 getstr(rusername, sizeof (rusername), "remuser");
1408 getstr(lusername, sizeof (lusername), "locuser");
1409 getstr(terminal, sizeof (terminal), "Terminal type");
1411 /* fflag has precedence over stuff passed by rlogind */
1412 if (fflag || getuid()) {
1414 doremoteterm(terminal);
1417 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1419 pwd = getpwnam(lusername);
1422 doremoteterm(terminal);
1427 doremoteterm(terminal);
1430 * Update PAM on the user name
1432 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1435 if (pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1438 SCPYN(user_name, lusername);
1440 lusername[0] = '\0';
1444 * *** Account validation routines ***
1449 * validate_account - This is the PAM version of validate.
1459 (void) alarm(0); /* give user time to come up with password */
1463 if ((Passreq != NULL) && (!strcasecmp("YES", Passreq)))
1464 flag = PAM_DISALLOW_NULL_AUTHTOK;
1468 if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1469 if (error == PAM_AUTHTOKEN_REQD) {
1470 (void) printf("Choose a new password.\n");
1472 if ((error = pam_chauthtok(pamh, 0)) != PAM_SUCCESS) {
1475 "change password failure: %s",
1476 pam_strerror(pamh, error));
1480 (void) printf(incorrectmsg);
1483 "login account failure: %s",
1484 pam_strerror(pamh, error));
1491 * Check_log - This is really a hack because PAM checks the log, but login
1492 * wants to know if the log is okay and PAM doesn't have
1493 * a module independent way of handing this info back.
1502 offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1504 if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) {
1505 if (llseek(fdl, offset, SEEK_SET) == offset &&
1506 read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) &&
1515 * chdir_to_dir_root - Attempts to chdir us to the home directory.
1516 * defaults to "/" if it can't cd to the home
1517 * directory, and returns ERROR if it can't do that.
1521 chdir_to_dir_root(void)
1523 if (chdir(pwd->pw_dir) < 0) {
1524 if (chdir("/") < 0) {
1525 (void) printf("No directory!\n");
1535 * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
1536 * place us in the user's home directory just in
1537 * case it was protected and the first chdir failed.
1538 * No chdir errors should happen at this point because
1539 * all failures should have happened on the first
1544 chdir_to_dir_user(void)
1546 if (chdir(pwd->pw_dir) < 0) {
1547 if (chdir("/") < 0) {
1548 (void) printf("No directory!\n");
1550 * This probably won't work since we can't get to /.
1552 if (remote_host[0] && dosyslog) {
1554 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1555 ttyn, HMAX, remote_host);
1556 } else if (dosyslog) {
1558 "REPEATED LOGIN FAILURES ON %s", ttyn);
1561 (void) sleep(DISABLETIME);
1564 (void) printf("No directory! Logging in with home=/\n");
1572 * login_authenticate - Performs the main authentication work
1573 * 1. Prints the login prompt
1574 * 2. Requests and verifys the password
1575 * 3. Checks the port password
1579 login_authenticate()
1586 /* if scheme broken, then nothing to do but quit */
1587 if (pam_get_item(pamh, PAM_USER, (void **)&user)
1592 * only get name from utility if it is not already
1593 * supplied by pam_start or a pam_set_item.
1595 if (!user || !user[0]) {
1596 /* use call back to get user name */
1600 err = verify_passwd();
1601 if ((err == PAM_SUCCESS) && (chdir_to_dir_root() == OK)) {
1606 /* scheme has kept count of retries. time to bail */
1607 if (err == PAM_MAXTRIES)
1610 /* only sleep after first bad passwd */
1612 (void) sleep(Sleeptime);
1613 (void) printf(incorrectmsg);
1614 user_name[0] = '\0'; /* is this needed? */
1615 /* force name to be null in this case */
1616 if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
1618 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1620 } while (cnt++ < MAXTRYS);
1622 if (cnt >= MAXTRYS) {
1623 audit_login_maxtrys();
1625 * If logging is turned on, output the
1626 * string storage area to the log file,
1627 * and sleep for DISABLETIME
1628 * seconds before exiting.
1632 if (remote_host[0] && dosyslog) {
1634 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1635 ttyn, HMAX, remote_host);
1636 } else if (dosyslog) {
1638 "REPEATED LOGIN FAILURES ON %s", ttyn);
1640 (void) sleep(DISABLETIME);
1647 * *** Credential Related routines ***
1652 * setup_credentials - sets the group ID, initializes the groups
1653 * and sets up the secretkey.
1654 * Exits if a failure occurrs.
1659 * setup_credentials - PAM does all the work for us on this one.
1663 setup_credentials(void)
1668 /* set the real (and effective) GID */
1669 if (setgid(pwd->pw_gid) == -1) {
1674 * Initialize the supplementary group access list.
1678 if (initgroups(user_name, pwd->pw_gid) == -1) {
1682 /* XXX really should be after setgid */
1683 if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1687 /* set the real (and effective) UID */
1688 if (setuid(pwd->pw_uid) == -1) {
1696 * *** Routines to get a new user set up and running ***
1698 * Things to do when starting up a new user:
1701 * establish_user_environment
1703 * display_last_login_time
1710 * adjust_nice - Set the nice (process priority) value if the
1711 * gecos value contains an appropriate value.
1719 if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1724 if (pwd->pw_gecos[i] == '-') {
1729 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1730 pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1740 * update_utmp_entry - Searchs for the correct utmp entry, making an
1741 * entry there if it finds one, otherwise exits.
1745 update_utmp_entry(int sublogin)
1749 static char *errmsg = "No utmpx entry. "
1750 "You must exec \"login\" from the lowest level \"shell\".";
1752 struct utmpx *u = (struct utmpx *)0;
1758 * If we're not a sublogin then
1759 * we'll get an error back if our PID doesn't match the PID of the
1760 * entry we are updating, otherwise if its a sublogin the flags
1761 * field is set to 0, which means we just write a matching entry
1762 * (without checking the pid), or a new entry if an entry doesn't
1766 if (error = pam_open_session(pamh, 0) != PAM_SUCCESS) {
1770 if ((error = pam_get_item(pamh, PAM_USER, (void **) &user))
1775 (void) memset((void *)&utmp, 0, sizeof (utmp));
1776 (void) time(&utmp.ut_tv.tv_sec);
1777 utmp.ut_pid = getpid();
1779 if (rflag || hflag) {
1780 SCPYN(utmp.ut_host, remote_host);
1781 tmplen = strlen(remote_host) + 1;
1782 if (tmplen < sizeof (utmp.ut_host))
1783 utmp.ut_syslen = tmplen;
1785 utmp.ut_syslen = sizeof (utmp.ut_host);
1790 SCPYN(utmp.ut_user, user);
1792 /* skip over "/dev/" */
1793 ttyntail = basename(ttyn);
1795 while ((u = getutxent()) != NULL) {
1796 if ((u->ut_type == INIT_PROCESS ||
1797 u->ut_type == LOGIN_PROCESS ||
1798 u->ut_type == USER_PROCESS) &&
1799 ((sublogin && strncmp(u->ut_line, ttyntail,
1800 sizeof (u->ut_line)) == 0) ||
1801 u->ut_pid == utmp.ut_pid)) {
1802 SCPYN(utmp.ut_line, (ttyn+sizeof ("/dev/")-1));
1803 (void) memcpy(utmp.ut_id, u->ut_id,
1804 sizeof (utmp.ut_id));
1805 utmp.ut_type = USER_PROCESS;
1812 if (u == (struct utmpx *)NULL) {
1815 * no utmp entry already setup
1816 * (init or rlogind/telnetd)
1818 (void) puts(errmsg);
1822 /* Now attempt to write out this entry to the wtmp file if */
1823 /* we were successful in getting it from the utmp file and */
1824 /* the wtmp file exists. */
1825 updwtmpx(WTMPX_FILE, &utmp);
1832 * process_chroot_logins - Chroots to the specified subdirectory and
1833 * re executes login.
1837 process_chroot_logins(void)
1840 * If the shell field starts with a '*', do a chroot to the home
1841 * directory and perform a new login.
1844 if (*pwd->pw_shell == '*') {
1845 pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
1846 if (chroot(pwd->pw_dir) < 0) {
1847 (void) printf("No Root Directory\n");
1851 * Set the environment flag <!sublogin> so that the next login
1852 * knows that it is a sublogin.
1854 envinit[0] = SUBLOGIN;
1855 envinit[1] = (char *)NULL;
1856 (void) printf("Subsystem root: %s\n", pwd->pw_dir);
1857 (void) execle("/usr/bin/login", "login", (char *)0,
1859 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1860 (void) printf("No /usr/bin/login or /etc/login on root\n");
1867 * establish_user_environment - Set up the new users environment
1871 establish_user_environment(char **renvp)
1873 int i, j, k, l_index, length;
1883 envinit = (char **) calloc(lenvp - environ + 10
1884 + MAXARGS, sizeof (char *));
1885 if (envinit == NULL) {
1886 (void) printf("Calloc failed - out of swap space.\n");
1889 memcpy(envinit, newenv, sizeof (newenv));
1891 /* Set up environment */
1893 ENVSTRNCAT(term, terminal);
1895 /* XXX: this causes an environment variable with the value */
1896 /* "" to be created later on, because terminal is "". */
1897 (void) strncpy(term, terminal, sizeof (term) - 1);
1899 char *tp = getenv("TERM");
1901 if ((tp != NULL) && (*tp != '\0'))
1902 ENVSTRNCAT(term, tp);
1905 ENVSTRNCAT(logname, pwd->pw_name);
1908 * There are three places to get timezone info. init.c sets
1909 * TZ if the file /etc/TIMEZONE contains a value for TZ.
1910 * login.c looks in the file /etc/default/login for a
1911 * variable called TIMEZONE being set. If TIMEZONE has a
1912 * value, TZ is set to that value; no environment variable
1913 * TIMEZONE is set, only TZ. If neither of these methods
1914 * work to set TZ, then the library routines will default
1915 * to using the file /usr/lib/locale/TZ/localtime.
1917 * There is a priority set up here. If /etc/TIMEZONE has
1918 * a value for TZ, that value remains top priority. If the
1919 * file /etc/default/login has TIMEZONE set, that has second
1920 * highest priority not overriding the value of TZ in
1921 * /etc/TIMEZONE. The reason for this priority is that the
1922 * file /etc/TIMEZONE is supposed to be sourced by
1923 * /etc/profile. We are doing the "sourcing" prematurely in
1924 * init.c. Additionally, a login C shell doesn't source the
1925 * file /etc/profile thus not sourcing /etc/TIMEZONE thus not
1926 * allowing an administrator to globally set TZ for all users
1928 if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
1931 if ((Def_tz = getenv("TZ")) != NULL) {
1932 ENVSTRNCAT(timez, Def_tz);
1933 } else if (tmp_tz != NULL) {
1935 ENVSTRNCAT(timez, Def_tz);
1938 if (Def_hertz == NULL)
1939 (void) sprintf(hertz + strlen(hertz), "%u", HZ);
1941 ENVSTRNCAT(hertz, Def_hertz);
1943 if (Def_path == NULL)
1944 (void) strcat(path, DEF_PATH);
1946 ENVSTRNCAT(path, Def_path);
1948 ENVSTRNCAT(home, pwd->pw_dir);
1951 * Find the end of the basic environment
1953 for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
1957 * If TZ has a value, add it.
1959 if (strcmp(timez, "TZ=") != 0)
1960 envinit[basicenv++] = timez;
1962 if (*pwd->pw_shell == '\0') {
1964 * If possible, use the primary default shell,
1965 * otherwise, use the secondary one.
1967 if (access(SHELL, X_OK) == 0)
1968 pwd->pw_shell = SHELL;
1970 pwd->pw_shell = SHELL2;
1971 } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
1972 envinit[basicenv++] = shell;
1973 ENVSTRNCAT(shell, pwd->pw_shell);
1977 envinit[basicenv++] = mail;
1978 (void) strcat(mail, pwd->pw_name);
1982 * Pick up locale environment variables, if any.
1985 while (*lenvp != NULL) {
1987 while (localeenv[j] != 0) {
1989 * locale_envmatch() returns 1 if
1990 * *lenvp is localenev[j] and valid.
1992 if (locale_envmatch(localeenv[j], *lenvp) == 1) {
1993 envinit[basicenv++] = *lenvp;
2002 * If '-p' flag, then try to pass on allowable environment
2003 * variables. Note that by processing this first, what is
2004 * passed on the final "login:" line may over-ride the invocation
2005 * values. XXX is this correct?
2008 for (lenvp = renvp; *lenvp; lenvp++) {
2009 if (!legalenvvar(*lenvp)) {
2013 * If this isn't 'xxx=yyy', skip it. XXX
2015 if ((endptr = strchr(*lenvp, '=')) == NULL) {
2018 length = endptr + 1 - *lenvp;
2019 for (j = 0; j < basicenv; j++) {
2020 if (strncmp(envinit[j], *lenvp, length) == 0) {
2022 * Replace previously established value
2024 envinit[j] = *lenvp;
2028 if (j == basicenv) {
2030 * It's a new definition, so add it at the end.
2032 envinit[basicenv++] = *lenvp;
2038 * Add in all the environment variables picked up from the
2039 * argument list to "login" or from the user response to the
2043 for (j = 0, k = 0, l_index = 0, ptr = &envblk[0];
2044 *envp && j < (MAXARGS-1); j++, envp++) {
2047 * Scan each string provided. If it doesn't have the
2048 * format xxx=yyy, then add the string "Ln=" to the beginning.
2050 if ((endptr = strchr(*envp, '=')) == NULL) {
2051 envinit[basicenv+k] = ptr;
2052 (void) sprintf(ptr, "L%d=%s", l_index, *envp);
2055 * Advance "ptr" to the beginning of the
2063 if (!legalenvvar(*envp)) { /* this env var permited? */
2068 * Check to see whether this string replaces
2069 * any previously defined string
2071 for (i = 0, length = endptr + 1 - *envp;
2072 i < basicenv + k; i++) {
2073 if (strncmp(*envp, envinit[i], length)
2081 * If it doesn't, place it at the end of
2082 * environment array.
2084 if (i == basicenv+k) {
2085 envinit[basicenv+k] = *envp;
2090 } /* for (j = 0 ... ) */
2093 * Add DCE/Kerberos cred name, if any.
2094 * XXX - The module specific stuff should be removed from login
2095 * program eventually. This is better placed in DCE module.
2096 * So far, we haven't come up with a way to deal with these
2097 * environment variables.
2100 krb5p = getenv("KRB5CCNAME");
2102 if ((krb5p != NULL) && (*krb5p != '\0')) {
2103 ENVSTRNCAT(krb5ccname, krb5p);
2104 envinit[basicenv++] = krb5ccname;
2107 krb4p = getenv("KRBTKFILE");
2109 if ((krb4p != NULL) && (*krb4p != '\0')) {
2110 ENVSTRNCAT(krb4ccname, krb4p);
2111 envinit[basicenv++] = krb4ccname;
2115 * Switch to the new environment.
2121 * print_banner - Print the banner at start up
2122 * Do not turn on DOBANNER ifdef. This is not
2123 * relevant to SunOS.
2132 (void) printf("UNIX System V/386 Release %s\n%s\n"
2133 "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
2134 "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
2135 un.release, un.nodename);
2137 (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
2138 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
2139 "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
2140 "All Rights Reserved\n",
2141 un.release, un.machine, un.nodename);
2143 (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
2144 "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
2145 un.release, un.machine, un.nodename);
2147 #endif /* DOBANNER */
2151 * display_last_login_time - Advise the user the time and date
2152 * that this login-id was last used.
2156 display_last_login_time(void)
2159 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2161 if (*ll.ll_host != '\0')
2162 (void) printf("from %.*s\n", sizeof (ll.ll_host),
2165 (void) printf("on %.*s\n", sizeof (ll.ll_line),
2171 * exec_the_shell - invoke the specified shell or start up program
2175 exec_the_shell(void)
2180 (void) strcat(minusnam, basename(pwd->pw_shell));
2185 (void) execl(pwd->pw_shell, minusnam, (char *)0);
2188 * pwd->pw_shell was not an executable object file, maybe it
2189 * is a shell proceedure or a command line with arguments.
2190 * If so, turn off the SHELL= environment variable.
2192 for (i = 0; envinit[i] != NULL; ++i) {
2193 if ((envinit[i] == shell) &&
2194 ((endptr = strchr(shell, '=')) != NULL))
2198 if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
2199 (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
2200 (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
2203 (void) printf("No shell\n");
2207 * login_exit - Call exit() and terminate.
2208 * This function is here for PAM so cleanup can
2209 * be done before the process exits.
2212 login_exit(int exit_code)
2215 pam_end(pamh, PAM_ABORT);
2221 * Check if lenv and penv matches or not.
2224 locale_envmatch(char *lenv, char *penv)
2226 while ((*lenv == *penv) && *lenv && *penv != '=') {
2232 * '/' is eliminated for security reason.
2234 if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2240 * logindevperm - change owner/group/permissions of devices
2241 * list in /etc/logindevperm. (Code derived from set_fb_attrs()
2242 * in 4.x usr/src/bin/login.c and usr/src/etc/getty/main.c.)
2245 #define MAX_LINELEN 256
2246 #define LOGINDEVPERM "/etc/logindevperm"
2247 #define DIRWILD "/*" /* directory wildcard */
2248 #define DIRWLDLEN 2 /* strlen(DIRWILD) */
2251 logindevperm(char *ttyn, uid_t uid, gid_t gid)
2253 char *field_delims = " \t\n";
2254 char *permfile = LOGINDEVPERM;
2255 char line[MAX_LINELEN];
2266 if ((fp = fopen(permfile, "r")) == NULL)
2270 while (fgets(line, MAX_LINELEN, fp) != NULL) {
2273 if ((ptr = strchr(line, '#')) != NULL)
2274 *ptr = '\0'; /* handle comments */
2276 if ((console = strtok(line, field_delims)) == NULL)
2277 continue; /* ignore blank lines */
2279 if (strcmp(console, ttyn) != 0)
2280 continue; /* not our tty, skip */
2282 mode_str = strtok((char *)NULL, field_delims);
2283 if (mode_str == NULL) {
2284 (void) fprintf(stderr,
2285 "%s: line %d, invalid entry -- %s\n", permfile,
2290 /* convert string to octal value */
2291 mode = strtol(mode_str, &ptr, 8);
2292 if (mode < 0 || mode > 0777 || *ptr != '\0') {
2293 (void) fprintf(stderr,
2294 "%s: line %d, invalid mode -- %s\n", permfile,
2299 dev_list = strtok((char *)NULL, field_delims);
2300 if (dev_list == NULL) {
2301 (void) fprintf(stderr,
2302 "%s: line %d, %s -- empty device list\n",
2303 permfile, lineno, console);
2307 device = strtok(dev_list, ":");
2308 while (device != NULL) {
2310 ptr = &device[l - DIRWLDLEN];
2311 if ((l > DIRWLDLEN) && (strcmp(ptr, DIRWILD) == 0)) {
2312 *ptr = '\0'; /* chop off wildcard */
2313 dir_dev_acc(device, uid, gid, mode, permfile);
2316 * change the owner/group/permission;
2317 * nonexistent devices are ignored
2319 if (chown(device, uid, gid) == -1) {
2320 if (errno != ENOENT) {
2321 (void) fprintf(stderr, "%s: ",
2326 if ((chmod(device, mode) == -1) &&
2327 (errno != ENOENT)) {
2328 (void) fprintf(stderr, "%s: ",
2334 device = strtok((char *)NULL, ":");
2341 * Apply owner/group/perms to all files (except "." and "..")
2345 dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile)
2348 struct dirent *direntp;
2349 char *name, path[MAX_LINELEN + MAXNAMELEN];
2351 dirp = opendir(dir);
2355 while ((direntp = readdir(dirp)) != NULL) {
2356 name = direntp->d_name;
2357 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
2360 (void) sprintf(path, "%s/%s", dir, name);
2361 if (chown(path, uid, gid) == -1) {
2362 if (errno != ENOENT) {
2363 (void) fprintf(stderr, "%s: ", permfile);
2367 if ((chmod(path, mode) == -1) && (errno != ENOENT)) {
2368 (void) fprintf(stderr, "%s: ", permfile);
2373 (void) closedir(dirp);