Spelling fixes
[oweals/cde.git] / cde / lib / pam / examples / login / login.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
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   */
26
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.     */
30
31 #ident  "@(#)login.c    1.63    96/02/15 SMI"   /* SVr4.0 1.43.6.26     */
32
33 /*
34
35                 PROPRIETARY NOTICE(Combined)
36
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
41 California.
42
43
44
45                 Copyright Notice
46
47 Notice of copyright on this source code product does not indicate
48 publication.
49
50         (c) 1986, 1987, 1988, 1989, 1990, 1991, 1992 Sun Microsystems, Inc
51         (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.
52                 All rights reserved.
53 *******************************************************************
54 */
55
56 /*      Copyright (c) 1987, 1988 Microsoft Corporation  */
57 /*        All Rights Reserved   */
58
59 /*      This Module contains Proprietary Information of Microsoft  */
60 /*      Corporation and should be treated as Confidential.         */
61
62 /*
63  * Usage: login [ -d device ] [ name ] [ environment args ]
64  *
65  *
66  */
67
68
69 /*
70  *
71  *                          *** Header Files ***
72  *
73  *
74  */
75
76 #include <sys/types.h>
77 #include <sys/param.h>
78 #include <unistd.h>     /* For logfile locking */
79 #include <signal.h>
80 #include <stdio.h>
81 #include <sys/stat.h>
82 #include <string.h>
83 #include <deflt.h>
84 #include <grp.h>
85 #include <fcntl.h>
86 #include <lastlog.h>
87 #include <termio.h>
88 #include <utmpx.h>
89 #include <dirent.h>
90 #include <stdlib.h>
91 #include <wait.h>
92 #include <errno.h>
93 #include <ctype.h>
94 #include <syslog.h>
95 #include <ulimit.h>
96 #include <libgen.h>
97 #include <security/pam_appl.h>
98
99 /*
100  *
101  *          *** Defines, Macros, and String Constants  ***
102  *
103  *
104  */
105
106 #define ISSUEFILE "/etc/issue"  /* file to print before prompt */
107 #define NOLOGIN "/etc/nologin"  /* file to lock users out during shutdown */
108
109 /*
110  * These need to be defined for UTMP management.
111  * If we add in the utility functions later, we
112  * can remove them.
113  */
114 #define __UPDATE_ENTRY  1
115 #define __LOGIN         2
116
117 /*
118  * Intervals to sleep after failed login
119  */
120 #ifndef SLEEPTIME
121 #define SLEEPTIME 4     /* sleeptime before login incorrect msg */
122 #endif
123 static int      Sleeptime = SLEEPTIME;
124
125 /*
126  * seconds login disabled after allowable number of unsuccessful attempts
127  */
128 #ifndef DISABLETIME
129 #define DISABLETIME     20
130 #endif
131 #define MAXTRYS         5
132
133 /*
134  * Login logging support
135  */
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 */
143
144 /*
145  * String manipulation macros: SCPYN, EQN and ENVSTRNCAT
146  */
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)); }
151
152 /*
153  * Other macros
154  */
155 #define NMAX    sizeof (utmp.ut_name)
156 #define HMAX    sizeof (utmp.ut_host)
157 #define min(a, b)       (((a) < (b)) ? (a) : (b))
158
159 /*
160  * Various useful files and string constants
161  */
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"
168
169 /*
170  * Array and Buffer sizes
171  */
172 #define PBUFSIZE 8      /* max significant characters in a password */
173 #define MAXARGS 63
174 #define MAXENV 1024
175 #define MAXLINE 2048
176
177 /*
178  * Miscellaneous constants
179  */
180 #define ROOTUID         0
181 #define ERROR           1
182 #define OK              0
183 #define LOG_ERROR       1
184 #define DONT_LOG_ERROR  0
185 #define TRUE            1
186 #define FALSE           0
187
188 /*
189  * Counters for counting the number of failed login attempts
190  */
191 static int trys = 0;
192
193 /*
194  * Externs a plenty
195  */
196 extern  int     defopen(char *filename);
197 extern  int     getsecretkey();
198
199 /*
200  * BSM hooks
201  */
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);
213
214 /*
215  * utmp file variables
216  */
217 static  struct  utmpx utmp;
218
219 /*
220  * The current user name
221  */
222 static  char    user_name[64];
223 static  char    minusnam[16] = "-";
224
225 /*
226  * locale environments to be passed to shells.
227  */
228 static char *localeenv[] = {
229         "LANG",
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);
233
234 /*
235  * Environment variable support
236  */
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;
247 static  int     basicenv;
248 static  char    envblk[MAXENV];
249 static  char    *zero = (char *)0;
250 static  char    **envp;
251 static  char    krb5ccname[256] = { "KRB5CCNAME=" };
252 static  char    krb4ccname[256] = { "KRBTKFILE=" };
253 #ifndef NO_MAIL
254 static  char    mail[30] = { "MAIL=/var/mail/" };
255 #endif
256 extern char **environ;
257 char inputline[MAXLINE];
258
259
260 /*
261  * Strings used to prompt the user.
262  */
263 static  char    loginmsg[] = "login: ";
264 static  char    passwdmsg[] = "Password:";
265 static  char    incorrectmsg[] = "Login incorrect\n";
266
267 /*
268  * Password file support
269  */
270 static  struct  passwd *pwd;
271 static  char    remote_host[HMAX];
272
273 /*
274  * Illegal passwd entries.
275  */
276 static  struct  passwd nouser = { "", "no:password", ~ROOTUID };
277
278 /*
279  * Log file support
280  */
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;
286
287 /*
288  * Default file toggles
289  */
290 static  char    *Pndefault      = "/etc/default/login";
291 static  char    *Altshell       = NULL;
292 static  char    *Console        = NULL;
293 static  char    *Passreq        = NULL;
294 #define DEFUMASK        022
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 */
308
309 /*
310  * ttyprompt will point to the environment variable TTYPROMPT.
311  * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
312  */
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;
318
319 /*
320  * Pass inherited environment.  Used by telnetd in support of the telnet
321  * ENVIRON option.
322  */
323 static  int     pflag;
324 /*
325  * Remote login support
326  */
327 static  int     hflag, rflag;
328 static  char    rusername[NMAX+1], lusername[NMAX+1];
329 static  char    terminal[MAXPATHLEN];
330
331 /*
332  * Pre-authentication flag support
333  */
334 static  int     fflag;
335
336 static int login_conv(int num_msg, struct pam_message **msg,
337     struct pam_response **response, void *appdata_ptr);
338
339 static struct pam_conv pam_conv = {login_conv, NULL};
340 static pam_handle_t *pamh;      /* Authentication handle */
341
342 /*
343  * Function declarations
344  */
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);
375
376 /*
377  *                      *** main ***
378  *
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.
383  *
384  *
385  */
386
387 void
388 main(int argc, char *argv[], char **renvp)
389 {
390         int sublogin;
391
392         /*
393          * Set up Defaults and flags
394          */
395         defaults();
396
397         /*
398          * Set up default umask
399          */
400         if (Umask > ((mode_t) 0777))
401                 Umask = DEFUMASK;
402         (void) umask(Umask);
403
404         /*
405          * Set up default timeouts and delays
406          */
407         if (Def_timeout > MAX_TIMEOUT)
408                 Def_timeout = MAX_TIMEOUT;
409         if (Sleeptime < 0 || Sleeptime > 5)
410                 Sleeptime = SLEEPTIME;
411
412         (void) alarm(Def_timeout);
413
414         /*
415          * Ignore SIGQUIT and SIGINT and set nice to 0
416          */
417         (void) signal(SIGQUIT, SIG_IGN);
418         (void) signal(SIGINT, SIG_IGN);
419         (void) nice(0);
420
421         /*
422          * Set flag to disable the pid check if you find that you are
423          * a subsystem login.
424          */
425         sublogin = 0;
426         if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
427                 sublogin = 1;
428
429         /*
430          * Parse Arguments
431          */
432         if (get_options(argc, argv) == -1) {
433                 usage();
434                 login_exit(1);
435         }
436
437         audit_login_save_flags(rflag, hflag);
438         audit_login_save_host(remote_host);
439
440         /*
441          * if devicename is not passed as argument, call ttyname(0)
442          */
443         if (ttyn == NULL) {
444                 ttyn = ttyname(0);
445                 if (ttyn == NULL)
446                         ttyn = "/dev/???";
447         }
448
449         audit_login_save_ttyn(ttyn);
450         audit_login_save_port();
451
452         /*
453          * Call pam_start to initiate a PAM authentication operation
454          */
455
456         if ((pam_start(progname, user_name, &pam_conv, &pamh))
457                 != PAM_SUCCESS)
458                 login_exit(1);
459         if ((pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
460                 login_exit(1);
461         }
462         if ((pam_set_item(pamh, PAM_RHOST, remote_host)) != PAM_SUCCESS) {
463                 login_exit(1);
464         }
465
466         /*
467          * Open the log file which contains a record of successful and failed
468          * login attempts
469          */
470         turn_on_logging();
471
472         /*
473          * say "hi" to syslogd ..
474          */
475         openlog("login", 0, LOG_AUTH);
476
477         /*
478          * Do special processing for -r (rlogin) flag
479          */
480         if (rflag)
481                 process_rlogin();
482
483         /*
484          * validate user
485          */
486         /* we are already authenticated. fill in what we must, then continue */
487         if (fflag) {
488                 if (pwd = getpwnam(user_name))
489                         audit_login_save_pw(pwd);
490                 else {
491                         audit_login_bad_pw();
492                         log_bad_attempts();
493                         login_exit(1);
494                 }
495         } else {
496                 /*
497                  * Perform the primary login authentication activity.
498                  */
499                 login_authenticate();
500         }
501
502         /* change root login, then we exec another login and try again */
503         if (process_chroot_logins() != OK)
504                 login_exit(1);
505
506         /*
507          * If root login and not on system console then call exit(2)
508          */
509         check_for_root_user();
510
511         /*
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
514          */
515         if (logins_disabled(user_name) == TRUE)
516                 login_exit(1);
517
518         if (pwd->pw_uid == 0) {
519                 if (Def_supath != NULL)
520                         Def_path = Def_supath;
521                 else
522                         Def_path = DEF_SUPATH;
523         }
524
525         /*
526          * Check account expiration and passwd aging
527          */
528         validate_account();
529
530         /*
531          * We only get here if we've been authenticated.
532          */
533         update_utmp_entry(sublogin);
534
535         /*
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.
539          */
540         if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
541                 (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
542
543         /*
544          * Set mode to r/w user & w group, owner to user and group to tty
545          */
546         (void) chmod(ttyn, S_IRUSR|S_IWUSR|S_IWGRP);
547
548         if ((grpstr = getgrnam(ttygrp)) == NULL)
549                 (void) chown(ttyn, pwd->pw_uid, pwd->pw_gid);
550         else
551                 (void) chown(ttyn, pwd->pw_uid, grpstr->gr_gid);
552
553         logindevperm(ttyn, pwd->pw_uid, pwd->pw_gid);
554         adjust_nice();          /* passwd file can specify nice value */
555
556         /*
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().
560          */
561         audit_login_success();
562
563         setup_credentials();    /* Set uid/gid - exits on failure */
564
565         pam_end(pamh, PAM_SUCCESS);     /* Done using PAM */
566
567         /*
568          * Set up the basic environment for the exec.  This includes
569          * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
570          */
571         chdir_to_dir_user();
572
573         establish_user_environment(renvp);
574
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);
579                 else if (dosyslog)
580                         syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
581         closelog();
582
583         (void) signal(SIGQUIT, SIG_DFL);
584         (void) signal(SIGINT, SIG_DFL);
585
586         /*
587          * Display some useful information to the new user like the banner
588          * and last login time if not a quiet login.
589          */
590
591         if (access(HUSHLOGIN, F_OK) != 0) {
592                 print_banner();
593                 display_last_login_time();
594         }
595
596         /*
597          * Now fire off the shell of choice
598          */
599         exec_the_shell();
600
601         /*
602          * All done
603          */
604         login_exit(1);
605         /* NOTREACHED */
606 }
607
608
609 /*
610  *                      *** Utility functions ***
611  */
612
613
614
615 /*
616  * donothing & catch    - Signal catching functions
617  */
618
619 /*ARGSUSED*/
620 static void
621 donothing(int sig)
622 {
623         if (pamh)
624                 pam_end(pamh, PAM_ABORT);
625 }
626
627 #ifdef notdef
628 static  int     intrupt;
629
630 /*ARGSUSED*/
631 static void
632 catch(int sig)
633 {
634         ++intrupt;
635 }
636 #endif
637
638 /*
639  *                      *** Bad login logging support ***
640  */
641
642 /*
643  * badlogin()           - log to the log file 'trys'
644  *                        unsuccessful attempts
645  */
646
647 static void
648 badlogin(void)
649 {
650         int retval, count1, fildes;
651
652         /*
653          * Tries to open the log file. If succeed, lock it and write
654          * in the failed attempts
655          */
656         if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
657
658                 (void) sigset(SIGALRM, donothing);
659                 (void) alarm(L_WAITTIME);
660                 retval = lockf(fildes, F_LOCK, 0L);
661                 (void) alarm(0);
662                 (void) sigset(SIGALRM, SIG_DFL);
663                 if (retval == 0) {
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);
668                 }
669                 (void) close(fildes);
670         }
671 }
672
673
674 /*
675  * log_bad_attempts     - log each bad login attempt - called from
676  *                        login_authenticate.  Exits when the maximum attempt
677  *                        count is exceeded.
678  */
679
680 static void
681 log_bad_attempts(void)
682 {
683         time_t timenow;
684
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);
692                 trys++;
693         }
694 }
695
696
697 /*
698  * turn_on_logging      - if the logfile exist, turn on attempt logging and
699  *                        initialize the string storage area
700  */
701
702 static void
703 turn_on_logging(void)
704 {
705         struct stat dbuf;
706         int i;
707
708         if (stat(LOGINLOG, &dbuf) == 0) {
709                 writelog = 1;
710                 for (i = 0; i < LOGTRYS; i++) {
711                         if (!(log_entry[i] = malloc((size_t) ENT_SIZE))) {
712                                 writelog = 0;
713                                 break;
714                         }
715                         *log_entry[i] = '\0';
716                 }
717         }
718 }
719
720
721 /*
722  * login_conv():
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.
726  */
727 static int
728 login_conv(int num_msg, struct pam_message **msg,
729     struct pam_response **response, void *appdata_ptr)
730 {
731         struct pam_message      *m;
732         struct pam_response     *r;
733         char                    *temp;
734         int                     k, i;
735
736         if (num_msg <= 0)
737                 return (PAM_CONV_ERR);
738
739         *response = calloc(num_msg, sizeof (struct pam_response));
740         if (*response == NULL)
741                 return (PAM_BUF_ERR);
742
743         k = num_msg;
744         m = *msg;
745         r = *response;
746         while (k--) {
747
748                 switch (m->msg_style) {
749
750                 case PAM_PROMPT_ECHO_OFF:
751                         temp = getpass(m->msg);
752                         if (temp != NULL) {
753                                 r->resp = strdup(temp);
754                                 if (r->resp == NULL) {
755                                         /* free responses */
756                                         r = *response;
757                                         for (i = 0; i < num_msg; i++, r++) {
758                                                 if (r->resp)
759                                                         free(r->resp);
760                                         }
761                                         free(*response);
762                                         *response = NULL;
763                                         return (PAM_BUF_ERR);
764                                 }
765                         }
766
767                         m++;
768                         r++;
769                         break;
770
771                 case PAM_PROMPT_ECHO_ON:
772                         if (m->msg != NULL)
773                                 (void) fputs(m->msg, stdout);
774                         r->resp = calloc(1, PAM_MAX_RESP_SIZE);
775                         if (r->resp == NULL) {
776                                 /* free responses */
777                                 r = *response;
778                                 for (i = 0; i < num_msg; i++, r++) {
779                                         if (r->resp)
780                                                 free(r->resp);
781                                 }
782                                 free(*response);
783                                 *response = NULL;
784                                 return (PAM_BUF_ERR);
785                         }
786                         if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
787                                 int len;
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';
792                         } else {
793                                 login_exit(1);
794                         }
795                         m++;
796                         r++;
797                         break;
798
799                 case PAM_ERROR_MSG:
800                         if (m->msg != NULL) {
801                                 (void) fputs(m->msg, stderr);
802                                 (void) fputs("\n", stderr);
803                         }
804                         m++;
805                         r++;
806                         break;
807                 case PAM_TEXT_INFO:
808                         if (m->msg != NULL) {
809                                 (void) fputs(m->msg, stdout);
810                                 (void) fputs("\n", stdout);
811                         }
812                         m++;
813                         r++;
814                         break;
815
816                 default:
817                         break;
818                 }
819         }
820         return (PAM_SUCCESS);
821 }
822
823 /*
824  * verify_passwd        - Checks the users password
825  *                        Returns: OK if password check successful,
826  *                        ERROR if password check fails.
827  */
828
829 static int
830 verify_passwd()
831 {
832         int flags;
833         int error;
834         char *user;
835
836         /*
837          * PAM authenticates the user for us.
838          */
839
840         error = pam_authenticate(pamh, 0);
841
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);
846
847         if (error != PAM_SUCCESS) {
848                 /* something bad has happended - bye bye */
849                 if (error == PAM_ABORT) {
850                         audit_login_bad_pw();
851                         log_bad_attempts();
852                         (void) sleep(DISABLETIME);
853                         (void) printf(incorrectmsg);
854                         login_exit(1);
855                 }
856                 return (error);
857         }
858
859         if ((pwd = getpwnam(user_name)) == NULL) {
860                 audit_login_bad_pw();
861                 log_bad_attempts();
862                 return (PAM_USER_UNKNOWN);
863         }
864
865         audit_login_save_pw(pwd);
866
867         return (error);
868 }
869
870 /*
871  * quotec               - Called by getargs
872  */
873
874 static int
875 quotec(void)
876 {
877         register int c, i, num;
878
879         switch (c = getc(stdin)) {
880
881                 case 'n':
882                         c = '\n';
883                         break;
884
885                 case 'r':
886                         c = '\r';
887                         break;
888
889                 case 'v':
890                         c = '\013';
891                         break;
892
893                 case 'b':
894                         c = '\b';
895                         break;
896
897                 case 't':
898                         c = '\t';
899                         break;
900
901                 case 'f':
902                         c = '\f';
903                         break;
904
905                 case '0':
906                 case '1':
907                 case '2':
908                 case '3':
909                 case '4':
910                 case '5':
911                 case '6':
912                 case '7':
913                         for (num = 0, i = 0; i < 3; i++) {
914                                 num = num * 8 + (c - '0');
915                                 if ((c = getc(stdin)) < '0' || c > '7')
916                                         break;
917                         }
918                         (void) ungetc(c, stdin);
919                         c = num & 0377;
920                         break;
921
922                 default:
923                         break;
924         }
925         return (c);
926 }
927
928 /*
929  * getargs              - returns an input line.  Exits if EOF encountered.
930  */
931 #define WHITESPACE      0
932 #define ARGUMENT        1
933
934 static char **
935 getargs(char *in_line)
936 {
937         static char envbuf[MAXLINE];
938         static char *args[MAXARGS];
939         register char *ptr, **answer;
940         register int c;
941         int state;
942
943         for (ptr = envbuf; ptr < &envbuf[sizeof (envbuf)]; /* cstyle */)
944                 *ptr++ = '\0';
945
946         for (answer = args; answer < &args[MAXARGS]; /* cstyle */)
947                 *answer++ = (char *)NULL;
948
949         for (ptr = envbuf, answer = &args[0], state = WHITESPACE;
950                 (c = getc(stdin)) != EOF; /* cstyle */) {
951
952                 *(in_line++) = c;
953                 switch (c) {
954
955                 case '\n':
956                         if (ptr == &envbuf[0])
957                                 return ((char **)NULL);
958                         return (&args[0]);
959
960                 case ' ':
961                 case '\t':
962                         if (state == ARGUMENT) {
963                                 *ptr++ = '\0';
964                                 state = WHITESPACE;
965                         }
966                         break;
967
968                 case '\\':
969                         c = quotec();
970
971                 default:
972                         if (state == WHITESPACE) {
973                                 *answer++ = ptr;
974                                 state = ARGUMENT;
975                         }
976                         *ptr++ = c;
977                 }
978
979                 /*
980                  * If the buffer is full, force the next character to be read to
981                  * be a <newline>.
982                  */
983                 if (ptr == &envbuf[sizeof (envbuf)-1]) {
984                         (void) ungetc('\n', stdin);
985                         (void) putc('\n', stdout);
986                 }
987         }
988
989         /*
990          * If we left loop because an EOF was received, exit immediately.
991          */
992         login_exit(0);
993         /* NOTREACHED */
994 }
995
996 /*
997  * get_user_name        - Gets the user name either passed in, or from the
998  *                        login: prompt.
999  */
1000
1001 static void
1002 get_user_name()
1003 {
1004         FILE    *fp;
1005         int gotname = 0;
1006
1007         if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
1008                 char    *ptr, buffer[BUFSIZ];
1009                 while ((ptr = fgets(buffer, sizeof (buffer),
1010                                         fp)) != NULL) {
1011                         (void) fputs(ptr, stdout);
1012                 }
1013                 (void) fclose(fp);
1014         }
1015
1016         /*
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.
1020          */
1021
1022         if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1023                 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1024         else
1025                 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1026
1027         envp = &zero; /* XXX: is this right? */
1028 }
1029
1030
1031 /*
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
1035  */
1036
1037 static void
1038 check_for_dueling_unix(char *inputline)
1039 {
1040         if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1041             EQN(incorrectmsg, inputline)) {
1042                 (void) printf("Looking at a login line.\n");
1043                 login_exit(8);
1044         }
1045 }
1046
1047 /*
1048  * logins_disabled -    if the file /etc/nologin exists and the user is not
1049  *                      root then do not permit them to login
1050  */
1051 static int
1052 logins_disabled(char *user_name)
1053 {
1054         FILE    *nlfd;
1055         int     c;
1056         if (!EQN("root", user_name) &&
1057                         ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1058                 while ((c = getc(nlfd)) != EOF)
1059                         putchar(c);
1060                 (void) fflush(stdout);
1061                 sleep(5);
1062                 return (TRUE);
1063         }
1064         return (FALSE);
1065 }
1066
1067 /*
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.
1070  *
1071  */
1072
1073 static void
1074 check_for_root_user(void)
1075 {
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");
1080                         login_exit(10);
1081                 }
1082         }
1083 }
1084
1085
1086 static char *illegal[] = {
1087         "SHELL=",
1088         "HOME=",
1089         "LOGNAME=",
1090 #ifndef NO_MAIL
1091         "MAIL=",
1092 #endif
1093         "CDPATH=",
1094         "IFS=",
1095         "PATH=",
1096         0
1097 };
1098
1099 /*
1100  * legalenvvar          - Is it legal to insert this environmental variable?
1101  */
1102
1103 static int
1104 legalenvvar(char *s)
1105 {
1106         register char **p;
1107
1108         for (p = illegal; *p; p++)
1109                 if (strncmp(s, *p, strlen(*p)) == 0)
1110                         return (0);
1111
1112         if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1113                 return (0);
1114
1115         return (1);
1116 }
1117
1118
1119 /*
1120  * getstr               - Get a string from standard input
1121  *                        Calls exit if read(2) fails.
1122  */
1123
1124 static void
1125 getstr(char *buf, int cnt, char *err)
1126 {
1127         char c;
1128
1129         do {
1130                 if (read(0, &c, 1) != 1)
1131                         login_exit(1);
1132                 *buf++ = c;
1133         } while (cnt > 1 && c != 0);
1134
1135         *buf = 0;
1136         err = err;      /* For lint */
1137 }
1138
1139
1140 /*
1141  * uppercaseterm        - if all input char are upper case
1142  *                        set the corresponding termio
1143  */
1144
1145 static void
1146 uppercaseterm(char *strp)
1147 {
1148         int     upper = 0;
1149         int     lower = 0;
1150         char    *sp;
1151         struct  termio  termio;
1152
1153         for (sp = strp; *sp; sp++) {
1154                 if (islower(*sp))
1155                         lower++;
1156                 else if (isupper(*sp))
1157                         upper++;
1158         }
1159
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');
1168         }
1169 }
1170
1171
1172 /*
1173  * defaults             - read defaults
1174  */
1175
1176 static void
1177 defaults(void)
1178 {
1179         extern int defcntl();
1180         register int  flags;
1181         register char *ptr;
1182
1183         if (defopen(Pndefault) == 0) {
1184                 /*
1185                  * ignore case
1186                  */
1187                 flags = defcntl(DC_GETFLAGS, 0);
1188                 TURNOFF(flags, DC_CASE);
1189                 defcntl(DC_SETFLAGS, flags);
1190
1191                 if ((Console = defread("CONSOLE=")) != NULL)
1192                         Console = strdup(Console);
1193
1194                 if ((Altshell = defread("ALTSHELL=")) != NULL)
1195                         Altshell = strdup(Altshell);
1196
1197                 if ((Passreq = defread("PASSREQ=")) != NULL)
1198                         Passreq = strdup(Passreq);
1199
1200                 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1201                         Def_tz = strdup(Def_tz);
1202
1203                 if ((Def_hertz = defread("HZ=")) != NULL)
1204                         Def_hertz = strdup(Def_hertz);
1205
1206                 if ((Def_path   = defread("PATH=")) != NULL)
1207                         Def_path = strdup(Def_path);
1208
1209                 if ((Def_supath = defread("SUPATH=")) != NULL)
1210                         Def_supath = strdup(Def_supath);
1211
1212                 if ((ptr = defread("ULIMIT=")) != NULL)
1213                         Def_ulimit = atol(ptr);
1214
1215                 if ((ptr = defread("TIMEOUT=")) != NULL)
1216                         Def_timeout = (unsigned) atoi(ptr);
1217
1218                 if ((ptr = defread("UMASK=")) != NULL) {
1219                         long long_umask;
1220                         if (sscanf(ptr, "%lo", &long_umask) != 1)
1221                                 Umask = DEFUMASK;
1222                         else
1223                                 Umask = (mode_t) long_umask;
1224                 }
1225
1226                 if ((ptr = defread("SLEEPTIME=")) != NULL)
1227                         Sleeptime = atoi(ptr);
1228
1229                 if ((ptr = defread("SYSLOG=")) != NULL)
1230                         dosyslog = strcmp(ptr, "YES") == 0;
1231
1232                 (void) defopen((char *)NULL);
1233         }
1234 }
1235
1236
1237 /*
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
1242  */
1243
1244 static  int
1245 get_options(int argc, char *argv[])
1246 {
1247         int     c;
1248         int     errflg = 0;
1249
1250         while ((c = getopt(argc, argv, "f:h:r:pad:")) != -1) {
1251                 switch (c) {
1252                         case 'a':
1253                                 break;
1254
1255                         case 'd':
1256                                 /*
1257                                  * Must be root to pass in device name
1258                                  * otherwise we exit() as punishment for trying.
1259                                  */
1260                                 if (getuid() != 0 || geteuid() != 0) {
1261                                         login_exit(1);  /* sigh */
1262                                         /*NOTREACHED*/
1263                                 }
1264                                 ttyn = optarg;
1265                                 break;
1266
1267                         case 'h':
1268                                 if (hflag || rflag) {
1269                                         (void) fprintf(stderr,
1270                                             "Only one of -r and -h allowed\n");
1271                                         login_exit(1);
1272                                 }
1273                                 hflag++;
1274                                 SCPYN(remote_host, optarg);
1275                                 if (argv[optind]) {
1276                                         if (argv[optind][0] != '-')
1277                                                 SCPYN(terminal, argv[optind]);
1278                                         optind++;
1279                                 }
1280                                 progname = "telnet";
1281                                 break;
1282
1283                         case 'r':
1284                                 if (hflag || rflag) {
1285                                         (void) fprintf(stderr,
1286                                             "Only one of -r and -h allowed\n");
1287                                         login_exit(1);
1288                                 }
1289                                 rflag++;
1290                                 SCPYN(remote_host, optarg);
1291                                 progname = "rlogin";
1292                                 break;
1293
1294                         case 'p':
1295                                 pflag++;
1296                                 break;
1297
1298                         case 'f':
1299                                 /*
1300                                  * Must be root to bypass authentication
1301                                  * otherwise we exit() as punishment for trying.
1302                                  */
1303                                 if (getuid() != 0 || geteuid() != 0) {
1304                                         login_exit(1);  /* sigh */
1305                                         /*NOTREACHED*/
1306                                 }
1307                                 /* save fflag user name for future use */
1308                                 SCPYN(user_name, optarg);
1309                                 fflag = 1;
1310                                 break;
1311                         default:
1312                                 errflg++;
1313                                 break;
1314                 }       /* end switch */
1315         }               /* end while */
1316
1317         /*
1318          * get the prompt set by ttymon
1319          */
1320         ttyprompt = getenv("TTYPROMPT");
1321
1322         if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1323                 /*
1324                  * if ttyprompt is set, there should be data on
1325                  * the stream already.
1326                  */
1327                 if ((envp = getargs(inputline)) != (char **)NULL) {
1328                         uppercaseterm(*envp);
1329                         /*
1330                          * don't get name if name passed as argument.
1331                          */
1332                         SCPYN(user_name, *envp++);
1333                 }
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];
1339         }
1340
1341         if (errflg)
1342                 return (-1);
1343         return (0);
1344 }
1345
1346 /*
1347  * usage                - Print usage message
1348  *
1349  */
1350
1351 static void
1352 usage(void)
1353 {
1354         (void) fprintf(stderr,
1355             "Usage:\tlogin [-h|-r] [ name [ env-var ... ]]\n");
1356 }
1357
1358 /*
1359  *                      *** Remote login support ***
1360  *
1361  */
1362
1363
1364 /*
1365  * doremoteterm         - Sets the appropriate ioctls for a remote terminal
1366  */
1367
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"
1372 };
1373
1374 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1375
1376
1377 static void
1378 doremoteterm(char *term)
1379 {
1380         struct termios tp;
1381         register char *cp = strchr(term, '/'), **cpp;
1382         char *speed;
1383
1384         (void) ioctl(0, TCGETS, &tp);
1385
1386         if (cp) {
1387                 *cp++ = '\0';
1388                 speed = cp;
1389                 cp = strchr(speed, '/');
1390
1391                 if (cp)
1392                         *cp++ = '\0';
1393
1394                 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
1395                         if (strcmp(*cpp, speed) == 0) {
1396                                 cfsetospeed(&tp, cpp-speeds);
1397                                 break;
1398                         }
1399         }
1400
1401         tp.c_lflag |= ECHO|ICANON;
1402         tp.c_iflag |= IGNPAR|ICRNL;
1403
1404         (void) ioctl(0, TCSETS, &tp);
1405
1406 }
1407
1408
1409 /*
1410  * Process_rlogin               - Does the work that rlogin and telnet
1411  *                                need done
1412  */
1413
1414 static void
1415 process_rlogin(void)
1416 {
1417
1418         getstr(rusername, sizeof (rusername), "remuser");
1419         getstr(lusername, sizeof (lusername), "locuser");
1420         getstr(terminal, sizeof (terminal), "Terminal type");
1421
1422         /* fflag has precedence over stuff passed by rlogind */
1423         if (fflag || getuid()) {
1424                 pwd = &nouser;
1425                 doremoteterm(terminal);
1426                 return;
1427         } else {
1428                 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1429                         login_exit(1);
1430                 pwd = getpwnam(lusername);
1431                 if (pwd == NULL) {
1432                         pwd = &nouser;
1433                         doremoteterm(terminal);
1434                         return;
1435                 }
1436         }
1437
1438         doremoteterm(terminal);
1439
1440         /*
1441          * Update PAM on the user name
1442          */
1443         if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1444                 login_exit(1);
1445
1446         if (pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1447                 login_exit(1);
1448
1449         SCPYN(user_name, lusername);
1450         envp = &zero;
1451         lusername[0] = '\0';
1452 }
1453
1454 /*
1455  *              *** Account validation routines ***
1456  *
1457  */
1458
1459 /*
1460  * validate_account             - This is the PAM version of validate.
1461  */
1462
1463 static void
1464 validate_account()
1465 {
1466         int     error;
1467         int     n;
1468         int     flag;
1469
1470         (void) alarm(0);        /* give user time to come up with password */
1471
1472         check_log();
1473
1474         if ((Passreq != NULL) && (!strcasecmp("YES", Passreq)))
1475                 flag = PAM_DISALLOW_NULL_AUTHTOK;
1476         else
1477                 flag = 0;
1478
1479         if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1480                 if (error == PAM_AUTHTOKEN_REQD) {
1481                         (void) printf("Choose a new password.\n");
1482
1483                         if ((error = pam_chauthtok(pamh, 0)) != PAM_SUCCESS) {
1484                                 if (dosyslog)
1485                                         syslog(LOG_CRIT,
1486                                                 "change password failure: %s",
1487                                                 pam_strerror(pamh, error));
1488                                 login_exit(1);
1489                         }
1490                 } else {
1491                         (void) printf(incorrectmsg);
1492                         if (dosyslog)
1493                                 syslog(LOG_CRIT,
1494                                         "login account failure: %s",
1495                                                 pam_strerror(pamh, error));
1496                         login_exit(1);
1497                 }
1498         }
1499 }
1500
1501 /*
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.
1505  */
1506
1507 static void
1508 check_log(void)
1509 {
1510         int fdl;
1511         long long offset;
1512
1513         offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1514
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) &&
1518                     ll.ll_time != 0)
1519                         lastlogok = 1;
1520                 (void) close(fdl);
1521         }
1522 }
1523
1524
1525 /*
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.
1529  */
1530
1531 static int
1532 chdir_to_dir_root(void)
1533 {
1534         if (chdir(pwd->pw_dir) < 0) {
1535                 if (chdir("/") < 0) {
1536                         (void) printf("No directory!\n");
1537                         return (ERROR);
1538                 }
1539         }
1540
1541         return (OK);
1542 }
1543
1544
1545 /*
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
1551  *                        time around.
1552  */
1553
1554 static void
1555 chdir_to_dir_user(void)
1556 {
1557         if (chdir(pwd->pw_dir) < 0) {
1558                 if (chdir("/") < 0) {
1559                         (void) printf("No directory!\n");
1560                         /*
1561                          * This probably won't work since we can't get to /.
1562                          */
1563                         if (remote_host[0] && dosyslog) {
1564                                 syslog(LOG_CRIT,
1565                                 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1566                                 ttyn, HMAX, remote_host);
1567                         } else if (dosyslog) {
1568                                 syslog(LOG_CRIT,
1569                                 "REPEATED LOGIN FAILURES ON %s", ttyn);
1570                         }
1571                         closelog();
1572                         (void) sleep(DISABLETIME);
1573                         exit(1);
1574                 } else {
1575                         (void) printf("No directory! Logging in with home=/\n");
1576                         pwd->pw_dir = "/";
1577                 }
1578         }
1579 }
1580
1581
1582 /*
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
1587  */
1588
1589 static void
1590 login_authenticate()
1591 {
1592         int cnt = 1;
1593         char *user;
1594         int err;
1595
1596         do {
1597                 /* if scheme broken, then nothing to do but quit */
1598                 if (pam_get_item(pamh, PAM_USER, (void **)&user)
1599                                                         != PAM_SUCCESS)
1600                         exit(1);
1601
1602                 /*
1603                  * only get name from utility if it is not already
1604                  * supplied by pam_start or a pam_set_item.
1605                  */
1606                 if (!user || !user[0]) {
1607                         /* use call back to get user name */
1608                         get_user_name();
1609                 }
1610
1611                 err = verify_passwd();
1612                 if ((err == PAM_SUCCESS) && (chdir_to_dir_root() == OK)) {
1613                         cnt = 0;
1614                         break;
1615                 }
1616
1617                 /* scheme has kept count of retries. time to bail */
1618                 if (err == PAM_MAXTRIES)
1619                         cnt = MAXTRYS;
1620
1621                 /* only sleep after first bad passwd */
1622                 if (cnt)
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)
1628                         login_exit(1);
1629                 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1630                         login_exit(1);
1631         } while (cnt++ < MAXTRYS);
1632
1633         if (cnt >= MAXTRYS) {
1634                 audit_login_maxtrys();
1635                 /*
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.
1640                  */
1641                 if (writelog)
1642                         badlogin();
1643                 if (remote_host[0] && dosyslog) {
1644                         syslog(LOG_CRIT,
1645                                 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1646                                 ttyn, HMAX, remote_host);
1647                 } else if (dosyslog) {
1648                         syslog(LOG_CRIT,
1649                                 "REPEATED LOGIN FAILURES ON %s", ttyn);
1650                 }
1651                 (void) sleep(DISABLETIME);
1652                 exit(1);
1653         }
1654
1655 }
1656
1657 /*
1658  *                      *** Credential Related routines ***
1659  *
1660  */
1661
1662 /*
1663  * setup_credentials            - sets the group ID, initializes the groups
1664  *                                and sets up the secretkey.
1665  *                                Exits if a failure occurrs.
1666  */
1667
1668
1669 /*
1670  * setup_credentials            - PAM does all the work for us on this one.
1671  */
1672
1673 static void
1674 setup_credentials(void)
1675 {
1676         int     error = 0;
1677         int     flags;
1678
1679         /* set the real (and effective) GID */
1680         if (setgid(pwd->pw_gid) == -1) {
1681                 login_exit(1);
1682         }
1683
1684         /*
1685          * Initialize the supplementary group access list.
1686          */
1687         if (!user_name)
1688                 login_exit(1);
1689         if (initgroups(user_name, pwd->pw_gid) == -1) {
1690                 login_exit(1);
1691         }
1692
1693         /* XXX really should be after setgid */
1694         if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1695                 login_exit(error);
1696         }
1697
1698         /* set the real (and effective) UID */
1699         if (setuid(pwd->pw_uid) == -1) {
1700                 login_exit(1);
1701         }
1702
1703 }
1704
1705 /*
1706  *
1707  *              *** Routines to get a new user set up and running ***
1708  *
1709  *                      Things to do when starting up a new user:
1710  *                              adjust_nice
1711  *                              update_utmp_entry
1712  *                              establish_user_environment
1713  *                              print_banner
1714  *                              display_last_login_time
1715  *                              exec_the_shell
1716  *
1717  */
1718
1719
1720 /*
1721  * adjust_nice          - Set the nice (process priority) value if the
1722  *                        gecos value contains an appropriate value.
1723  */
1724
1725 static void
1726 adjust_nice(void)
1727 {
1728         int pri, mflg, i;
1729
1730         if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1731                 pri = 0;
1732                 mflg = 0;
1733                 i = 4;
1734
1735                 if (pwd->pw_gecos[i] == '-') {
1736                         mflg++;
1737                         i++;
1738                 }
1739
1740                 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1741                         pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1742
1743                 if (mflg)
1744                         pri = -pri;
1745
1746                 (void) nice(pri);
1747         }
1748 }
1749
1750 /*
1751  * update_utmp_entry    - Searchs for the correct utmp entry, making an
1752  *                        entry there if it finds one, otherwise exits.
1753  */
1754
1755 static void
1756 update_utmp_entry(int sublogin)
1757 {
1758         char    *user;
1759         int     error;
1760         static char     *errmsg = "No utmpx entry. "
1761                 "You must exec \"login\" from the lowest level \"shell\".";
1762         int        tmplen;
1763         struct utmpx  *u = (struct utmpx *)0;
1764         struct utmpx  utmp;
1765         char      *ttyntail;
1766
1767
1768         /*
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
1774          * exist.
1775          */
1776
1777         if (error = pam_open_session(pamh, 0) != PAM_SUCCESS) {
1778                 login_exit(1);
1779         }
1780
1781         if ((error = pam_get_item(pamh, PAM_USER, (void **) &user))
1782                                                         != PAM_SUCCESS) {
1783                 login_exit(1);
1784         }
1785
1786         (void) memset((void *)&utmp, 0, sizeof (utmp));
1787         (void) time(&utmp.ut_tv.tv_sec);
1788         utmp.ut_pid = getpid();
1789
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;
1795                 else
1796                         utmp.ut_syslen = sizeof (utmp.ut_host);
1797         } else {
1798                 utmp.ut_syslen = 0;
1799         }
1800
1801         SCPYN(utmp.ut_user, user);
1802
1803         /* skip over "/dev/" */
1804         ttyntail = basename(ttyn);
1805
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;
1817                         pututxline(&utmp);
1818                         break;
1819                 }
1820         }
1821         endutxent();
1822
1823         if (u == (struct utmpx *)NULL) {
1824                 if (!sublogin) {
1825                         /*
1826                          * no utmp entry already setup
1827                          * (init or rlogind/telnetd)
1828                          */
1829                         (void) puts(errmsg);
1830                         login_exit(1);
1831                 }
1832         } else {
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);
1837         }
1838 }
1839
1840
1841
1842 /*
1843  * process_chroot_logins        - Chroots to the specified subdirectory and
1844  *                                re executes login.
1845  */
1846
1847 static int
1848 process_chroot_logins(void)
1849 {
1850         /*
1851          * If the shell field starts with a '*', do a chroot to the home
1852          * directory and perform a new login.
1853          */
1854
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");
1859                         return (ERROR);
1860                 }
1861                 /*
1862                  * Set the environment flag <!sublogin> so that the next login
1863                  * knows that it is a sublogin.
1864                  */
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,
1869                         &envinit[0]);
1870                 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1871                 (void) printf("No /usr/bin/login or /etc/login on root\n");
1872                 login_exit(1);
1873         }
1874         return (OK);
1875 }
1876
1877 /*
1878  * establish_user_environment   - Set up the new users environment
1879  */
1880
1881 static void
1882 establish_user_environment(char **renvp)
1883 {
1884         int i, j, k, l_index, length;
1885         char *ptr;
1886         char *endptr;
1887         char **lenvp;
1888         char *krb5p;
1889         char *krb4p;
1890
1891         lenvp = environ;
1892         while (*lenvp++)
1893                 ;
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");
1898                 login_exit(8);
1899         }
1900         memcpy(envinit, newenv, sizeof (newenv));
1901
1902         /* Set up environment */
1903         if (rflag) {
1904                 ENVSTRNCAT(term, terminal);
1905         } else if (hflag) {
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);
1909         } else {
1910                 char *tp = getenv("TERM");
1911
1912                 if ((tp != NULL) && (*tp != '\0'))
1913                         ENVSTRNCAT(term, tp);
1914         }
1915
1916         ENVSTRNCAT(logname, pwd->pw_name);
1917
1918         /*
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.
1927          *
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
1938          */
1939         if (Def_tz != NULL)     /* Is there a TZ from defaults/login? */
1940                 tmp_tz = Def_tz;
1941
1942         if ((Def_tz = getenv("TZ")) != NULL) {
1943                 ENVSTRNCAT(timez, Def_tz);
1944         } else if (tmp_tz != NULL) {
1945                 Def_tz = tmp_tz;
1946                 ENVSTRNCAT(timez, Def_tz);
1947         }
1948
1949         if (Def_hertz == NULL)
1950                 (void) sprintf(hertz + strlen(hertz), "%u", HZ);
1951         else
1952                 ENVSTRNCAT(hertz, Def_hertz);
1953
1954         if (Def_path == NULL)
1955                 (void) strcat(path, DEF_PATH);
1956         else
1957                 ENVSTRNCAT(path, Def_path);
1958
1959         ENVSTRNCAT(home, pwd->pw_dir);
1960
1961         /*
1962          * Find the end of the basic environment
1963          */
1964         for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
1965                 ;
1966
1967         /*
1968          * If TZ has a value, add it.
1969          */
1970         if (strcmp(timez, "TZ=") != 0)
1971                 envinit[basicenv++] = timez;
1972
1973         if (*pwd->pw_shell == '\0') {
1974                 /*
1975                  * If possible, use the primary default shell,
1976                  * otherwise, use the secondary one.
1977                  */
1978                 if (access(SHELL, X_OK) == 0)
1979                         pwd->pw_shell = SHELL;
1980                 else
1981                         pwd->pw_shell = SHELL2;
1982         } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
1983                 envinit[basicenv++] = shell;
1984                 ENVSTRNCAT(shell, pwd->pw_shell);
1985         }
1986
1987 #ifndef NO_MAIL
1988         envinit[basicenv++] = mail;
1989         (void) strcat(mail, pwd->pw_name);
1990 #endif
1991
1992         /*
1993          * Pick up locale environment variables, if any.
1994          */
1995         lenvp = renvp;
1996         while (*lenvp != NULL) {
1997                 j = 0;
1998                 while (localeenv[j] != 0) {
1999                         /*
2000                          * locale_envmatch() returns 1 if
2001                          * *lenvp is localenev[j] and valid.
2002                          */
2003                         if (locale_envmatch(localeenv[j], *lenvp) == 1) {
2004                                 envinit[basicenv++] = *lenvp;
2005                                 break;
2006                         }
2007                         j++;
2008                 }
2009                 lenvp++;
2010         }
2011
2012         /*
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?
2017          */
2018         if (pflag) {
2019                 for (lenvp = renvp; *lenvp; lenvp++) {
2020                         if (!legalenvvar(*lenvp)) {
2021                                 continue;
2022                         }
2023                         /*
2024                          * If this isn't 'xxx=yyy', skip it.  XXX
2025                          */
2026                         if ((endptr = strchr(*lenvp, '=')) == NULL) {
2027                                 continue;
2028                         }
2029                         length = endptr + 1 - *lenvp;
2030                         for (j = 0; j < basicenv; j++) {
2031                                 if (strncmp(envinit[j], *lenvp, length) == 0) {
2032                                         /*
2033                                          * Replace previously established value
2034                                          */
2035                                         envinit[j] = *lenvp;
2036                                         break;
2037                                 }
2038                         }
2039                         if (j == basicenv) {
2040                                 /*
2041                                  * It's a new definition, so add it at the end.
2042                                  */
2043                                 envinit[basicenv++] = *lenvp;
2044                         }
2045                 }
2046         }
2047
2048         /*
2049          * Add in all the environment variables picked up from the
2050          * argument list to "login" or from the user response to the
2051          * "login" request.
2052          */
2053
2054         for (j = 0, k = 0, l_index = 0, ptr = &envblk[0];
2055                 *envp && j < (MAXARGS-1); j++, envp++) {
2056
2057                 /*
2058                  * Scan each string provided.  If it doesn't have the
2059                  * format xxx=yyy, then add the string "Ln=" to the beginning.
2060                  */
2061                 if ((endptr = strchr(*envp, '=')) == NULL) {
2062                         envinit[basicenv+k] = ptr;
2063                         (void) sprintf(ptr, "L%d=%s", l_index, *envp);
2064
2065                         /*
2066                          * Advance "ptr" to the beginning of the
2067                          * next argument.
2068                          */
2069                         while (*ptr++)
2070                                 ;
2071                         k++;
2072                         l_index++;
2073                 } else  {
2074                         if (!legalenvvar(*envp)) { /* this env var permited? */
2075                                 continue;
2076                         } else {
2077
2078                                 /*
2079                                  * Check to see whether this string replaces
2080                                  * any previously defined string
2081                                  */
2082                                 for (i = 0, length = endptr + 1 - *envp;
2083                                         i < basicenv + k; i++) {
2084                                     if (strncmp(*envp, envinit[i], length)
2085                                                 == 0) {
2086                                         envinit[i] = *envp;
2087                                         break;
2088                                     }
2089                                 }
2090
2091                                 /*
2092                                  * If it doesn't, place it at the end of
2093                                  * environment array.
2094                                  */
2095                                 if (i == basicenv+k) {
2096                                         envinit[basicenv+k] = *envp;
2097                                         k++;
2098                                 }
2099                         }
2100                 }
2101         }               /* for (j = 0 ... ) */
2102
2103         /*
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.
2109          */
2110
2111         krb5p = getenv("KRB5CCNAME");
2112
2113         if ((krb5p != NULL) && (*krb5p != '\0')) {
2114                 ENVSTRNCAT(krb5ccname, krb5p);
2115                 envinit[basicenv++] = krb5ccname;
2116         }
2117
2118         krb4p = getenv("KRBTKFILE");
2119
2120         if ((krb4p != NULL) && (*krb4p != '\0')) {
2121                 ENVSTRNCAT(krb4ccname, krb4p);
2122                 envinit[basicenv++] = krb4ccname;
2123         }
2124
2125         /*
2126          * Switch to the new environment.
2127          */
2128         environ = envinit;
2129 }
2130
2131 /*
2132  * print_banner         - Print the banner at start up
2133  *                         Do not turn on DOBANNER ifdef.  This is not
2134  *                         relevant to SunOS.
2135  */
2136
2137 static void
2138 print_banner(void)
2139 {
2140 #ifdef DOBANNER
2141         uname(&un);
2142 #if i386
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);
2147 #elif sun
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);
2153 #else
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);
2157 #endif /* i386 */
2158 #endif /* DOBANNER */
2159 }
2160
2161 /*
2162  * display_last_login_time      - Advise the user the time and date
2163  *                                that this login-id was last used.
2164  */
2165
2166 static void
2167 display_last_login_time(void)
2168 {
2169         if (lastlogok) {
2170                 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2171
2172                 if (*ll.ll_host != '\0')
2173                         (void) printf("from %.*s\n", sizeof (ll.ll_host),
2174                                             ll.ll_host);
2175                 else
2176                         (void) printf("on %.*s\n", sizeof (ll.ll_line),
2177                                             ll.ll_line);
2178         }
2179 }
2180
2181 /*
2182  * exec_the_shell       - invoke the specified shell or start up program
2183  */
2184
2185 static void
2186 exec_the_shell(void)
2187 {
2188         char *endptr;
2189         int i;
2190
2191         (void) strcat(minusnam, basename(pwd->pw_shell));
2192
2193         /*
2194          * Exec the shell
2195          */
2196         (void) execl(pwd->pw_shell, minusnam, (char *)0);
2197
2198         /*
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.
2202          */
2203         for (i = 0; envinit[i] != NULL; ++i) {
2204                 if ((envinit[i] == shell) &&
2205                     ((endptr = strchr(shell, '=')) != NULL))
2206                         (*++endptr) = '\0';
2207                 }
2208
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);
2212         }
2213
2214         (void) printf("No shell\n");
2215 }
2216
2217 /*
2218  * login_exit           - Call exit()  and terminate.
2219  *                        This function is here for PAM so cleanup can
2220  *                        be done before the process exits.
2221  */
2222 static void
2223 login_exit(int exit_code)
2224 {
2225         if (pamh)
2226                 pam_end(pamh, PAM_ABORT);
2227         exit(exit_code);
2228         /*NOTREACHED*/
2229 }
2230
2231 /*
2232  * Check if lenv and penv matches or not.
2233  */
2234 static int
2235 locale_envmatch(char *lenv, char *penv)
2236 {
2237         while ((*lenv == *penv) && *lenv && *penv != '=') {
2238                 lenv++;
2239                 penv++;
2240         }
2241
2242         /*
2243          * '/' is eliminated for security reason.
2244          */
2245         if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2246                 return (1);
2247         return (0);
2248 }
2249
2250 /*
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.)
2254  */
2255
2256 #define MAX_LINELEN     256
2257 #define LOGINDEVPERM    "/etc/logindevperm"
2258 #define DIRWILD         "/*"                    /* directory wildcard */
2259 #define DIRWLDLEN       2                       /* strlen(DIRWILD) */
2260
2261 static void
2262 logindevperm(char *ttyn, uid_t uid, gid_t gid)
2263 {
2264         char *field_delims = " \t\n";
2265         char *permfile = LOGINDEVPERM;
2266         char line[MAX_LINELEN];
2267         char *console;
2268         char *mode_str;
2269         char *dev_list;
2270         char *device;
2271         char *ptr;
2272         int mode;
2273         FILE *fp;
2274         size_t l;
2275         int lineno;
2276
2277         if ((fp = fopen(permfile, "r")) == NULL)
2278                 return;
2279
2280         lineno = 0;
2281         while (fgets(line, MAX_LINELEN, fp) != NULL) {
2282                 lineno++;
2283
2284                 if ((ptr = strchr(line, '#')) != NULL)
2285                         *ptr = '\0';    /* handle comments */
2286
2287                 if ((console = strtok(line, field_delims)) == NULL)
2288                         continue;       /* ignore blank lines */
2289
2290                 if (strcmp(console, ttyn) != 0)
2291                         continue;       /* not our tty, skip */
2292
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,
2297                             lineno, line);
2298                         continue;
2299                 }
2300
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,
2306                             lineno, mode_str);
2307                         continue;
2308                 }
2309
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);
2315                         continue;
2316                 }
2317
2318                 device = strtok(dev_list, ":");
2319                 while (device != NULL) {
2320                         l = strlen(device);
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);
2325                         } else {
2326                                 /*
2327                                  * change the owner/group/permission;
2328                                  * nonexistent devices are ignored
2329                                  */
2330                                 if (chown(device, uid, gid) == -1) {
2331                                         if (errno != ENOENT) {
2332                                                 (void) fprintf(stderr, "%s: ",
2333                                                     permfile);
2334                                                 perror(device);
2335                                         }
2336                                 } else {
2337                                         if ((chmod(device, mode) == -1) &&
2338                                             (errno != ENOENT)) {
2339                                                 (void) fprintf(stderr, "%s: ",
2340                                                     permfile);
2341                                                 perror(device);
2342                                         }
2343                                 }
2344                         }
2345                         device = strtok((char *)NULL, ":");
2346                 }
2347         }
2348         (void) fclose(fp);
2349 }
2350
2351 /*
2352  * Apply owner/group/perms to all files (except "." and "..")
2353  * in a directory.
2354  */
2355 static void
2356 dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile)
2357 {
2358         DIR *dirp;
2359         struct dirent *direntp;
2360         char *name, path[MAX_LINELEN + MAXNAMELEN];
2361
2362         dirp = opendir(dir);
2363         if (dirp == NULL)
2364                 return;
2365
2366         while ((direntp = readdir(dirp)) != NULL) {
2367                 name = direntp->d_name;
2368                 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
2369                         continue;
2370
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);
2375                                 perror(path);
2376                         }
2377                 } else {
2378                         if ((chmod(path, mode) == -1) && (errno != ENOENT)) {
2379                                 (void) fprintf(stderr, "%s: ", permfile);
2380                                 perror(path);
2381                         }
2382                 }
2383         }
2384         (void) closedir(dirp);
2385 }