Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[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 /*
628  *                      *** Bad login logging support ***
629  */
630
631 /*
632  * badlogin()           - log to the log file 'trys'
633  *                        unsuccessful attempts
634  */
635
636 static void
637 badlogin(void)
638 {
639         int retval, count1, fildes;
640
641         /*
642          * Tries to open the log file. If succeed, lock it and write
643          * in the failed attempts
644          */
645         if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
646
647                 (void) sigset(SIGALRM, donothing);
648                 (void) alarm(L_WAITTIME);
649                 retval = lockf(fildes, F_LOCK, 0L);
650                 (void) alarm(0);
651                 (void) sigset(SIGALRM, SIG_DFL);
652                 if (retval == 0) {
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);
657                 }
658                 (void) close(fildes);
659         }
660 }
661
662
663 /*
664  * log_bad_attempts     - log each bad login attempt - called from
665  *                        login_authenticate.  Exits when the maximum attempt
666  *                        count is exceeded.
667  */
668
669 static void
670 log_bad_attempts(void)
671 {
672         time_t timenow;
673
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);
681                 trys++;
682         }
683 }
684
685
686 /*
687  * turn_on_logging      - if the logfile exist, turn on attempt logging and
688  *                        initialize the string storage area
689  */
690
691 static void
692 turn_on_logging(void)
693 {
694         struct stat dbuf;
695         int i;
696
697         if (stat(LOGINLOG, &dbuf) == 0) {
698                 writelog = 1;
699                 for (i = 0; i < LOGTRYS; i++) {
700                         if (!(log_entry[i] = malloc((size_t) ENT_SIZE))) {
701                                 writelog = 0;
702                                 break;
703                         }
704                         *log_entry[i] = '\0';
705                 }
706         }
707 }
708
709
710 /*
711  * login_conv():
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.
715  */
716 static int
717 login_conv(int num_msg, struct pam_message **msg,
718     struct pam_response **response, void *appdata_ptr)
719 {
720         struct pam_message      *m;
721         struct pam_response     *r;
722         char                    *temp;
723         int                     k, i;
724
725         if (num_msg <= 0)
726                 return (PAM_CONV_ERR);
727
728         *response = calloc(num_msg, sizeof (struct pam_response));
729         if (*response == NULL)
730                 return (PAM_BUF_ERR);
731
732         k = num_msg;
733         m = *msg;
734         r = *response;
735         while (k--) {
736
737                 switch (m->msg_style) {
738
739                 case PAM_PROMPT_ECHO_OFF:
740                         temp = getpass(m->msg);
741                         if (temp != NULL) {
742                                 r->resp = strdup(temp);
743                                 if (r->resp == NULL) {
744                                         /* free responses */
745                                         r = *response;
746                                         for (i = 0; i < num_msg; i++, r++) {
747                                                 if (r->resp)
748                                                         free(r->resp);
749                                         }
750                                         free(*response);
751                                         *response = NULL;
752                                         return (PAM_BUF_ERR);
753                                 }
754                         }
755
756                         m++;
757                         r++;
758                         break;
759
760                 case PAM_PROMPT_ECHO_ON:
761                         if (m->msg != NULL)
762                                 (void) fputs(m->msg, stdout);
763                         r->resp = calloc(1, PAM_MAX_RESP_SIZE);
764                         if (r->resp == NULL) {
765                                 /* free responses */
766                                 r = *response;
767                                 for (i = 0; i < num_msg; i++, r++) {
768                                         if (r->resp)
769                                                 free(r->resp);
770                                 }
771                                 free(*response);
772                                 *response = NULL;
773                                 return (PAM_BUF_ERR);
774                         }
775                         if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
776                                 int len;
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';
781                         } else {
782                                 login_exit(1);
783                         }
784                         m++;
785                         r++;
786                         break;
787
788                 case PAM_ERROR_MSG:
789                         if (m->msg != NULL) {
790                                 (void) fputs(m->msg, stderr);
791                                 (void) fputs("\n", stderr);
792                         }
793                         m++;
794                         r++;
795                         break;
796                 case PAM_TEXT_INFO:
797                         if (m->msg != NULL) {
798                                 (void) fputs(m->msg, stdout);
799                                 (void) fputs("\n", stdout);
800                         }
801                         m++;
802                         r++;
803                         break;
804
805                 default:
806                         break;
807                 }
808         }
809         return (PAM_SUCCESS);
810 }
811
812 /*
813  * verify_passwd        - Checks the users password
814  *                        Returns: OK if password check successful,
815  *                        ERROR if password check fails.
816  */
817
818 static int
819 verify_passwd()
820 {
821         int flags;
822         int error;
823         char *user;
824
825         /*
826          * PAM authenticates the user for us.
827          */
828
829         error = pam_authenticate(pamh, 0);
830
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);
835
836         if (error != PAM_SUCCESS) {
837                 /* something bad has happended - bye bye */
838                 if (error == PAM_ABORT) {
839                         audit_login_bad_pw();
840                         log_bad_attempts();
841                         (void) sleep(DISABLETIME);
842                         (void) printf(incorrectmsg);
843                         login_exit(1);
844                 }
845                 return (error);
846         }
847
848         if ((pwd = getpwnam(user_name)) == NULL) {
849                 audit_login_bad_pw();
850                 log_bad_attempts();
851                 return (PAM_USER_UNKNOWN);
852         }
853
854         audit_login_save_pw(pwd);
855
856         return (error);
857 }
858
859 /*
860  * quotec               - Called by getargs
861  */
862
863 static int
864 quotec(void)
865 {
866         int c, i, num;
867
868         switch (c = getc(stdin)) {
869
870                 case 'n':
871                         c = '\n';
872                         break;
873
874                 case 'r':
875                         c = '\r';
876                         break;
877
878                 case 'v':
879                         c = '\013';
880                         break;
881
882                 case 'b':
883                         c = '\b';
884                         break;
885
886                 case 't':
887                         c = '\t';
888                         break;
889
890                 case 'f':
891                         c = '\f';
892                         break;
893
894                 case '0':
895                 case '1':
896                 case '2':
897                 case '3':
898                 case '4':
899                 case '5':
900                 case '6':
901                 case '7':
902                         for (num = 0, i = 0; i < 3; i++) {
903                                 num = num * 8 + (c - '0');
904                                 if ((c = getc(stdin)) < '0' || c > '7')
905                                         break;
906                         }
907                         (void) ungetc(c, stdin);
908                         c = num & 0377;
909                         break;
910
911                 default:
912                         break;
913         }
914         return (c);
915 }
916
917 /*
918  * getargs              - returns an input line.  Exits if EOF encountered.
919  */
920 #define WHITESPACE      0
921 #define ARGUMENT        1
922
923 static char **
924 getargs(char *in_line)
925 {
926         static char envbuf[MAXLINE];
927         static char *args[MAXARGS];
928         char *ptr, **answer;
929         int c;
930         int state;
931
932         for (ptr = envbuf; ptr < &envbuf[sizeof (envbuf)]; /* cstyle */)
933                 *ptr++ = '\0';
934
935         for (answer = args; answer < &args[MAXARGS]; /* cstyle */)
936                 *answer++ = (char *)NULL;
937
938         for (ptr = envbuf, answer = &args[0], state = WHITESPACE;
939                 (c = getc(stdin)) != EOF; /* cstyle */) {
940
941                 *(in_line++) = c;
942                 switch (c) {
943
944                 case '\n':
945                         if (ptr == &envbuf[0])
946                                 return ((char **)NULL);
947                         return (&args[0]);
948
949                 case ' ':
950                 case '\t':
951                         if (state == ARGUMENT) {
952                                 *ptr++ = '\0';
953                                 state = WHITESPACE;
954                         }
955                         break;
956
957                 case '\\':
958                         c = quotec();
959
960                 default:
961                         if (state == WHITESPACE) {
962                                 *answer++ = ptr;
963                                 state = ARGUMENT;
964                         }
965                         *ptr++ = c;
966                 }
967
968                 /*
969                  * If the buffer is full, force the next character to be read to
970                  * be a <newline>.
971                  */
972                 if (ptr == &envbuf[sizeof (envbuf)-1]) {
973                         (void) ungetc('\n', stdin);
974                         (void) putc('\n', stdout);
975                 }
976         }
977
978         /*
979          * If we left loop because an EOF was received, exit immediately.
980          */
981         login_exit(0);
982         /* NOTREACHED */
983 }
984
985 /*
986  * get_user_name        - Gets the user name either passed in, or from the
987  *                        login: prompt.
988  */
989
990 static void
991 get_user_name()
992 {
993         FILE    *fp;
994         int gotname = 0;
995
996         if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
997                 char    *ptr, buffer[BUFSIZ];
998                 while ((ptr = fgets(buffer, sizeof (buffer),
999                                         fp)) != NULL) {
1000                         (void) fputs(ptr, stdout);
1001                 }
1002                 (void) fclose(fp);
1003         }
1004
1005         /*
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.
1009          */
1010
1011         if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
1012                 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
1013         else
1014                 (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
1015
1016         envp = &zero; /* XXX: is this right? */
1017 }
1018
1019
1020 /*
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
1024  */
1025
1026 static void
1027 check_for_dueling_unix(char *inputline)
1028 {
1029         if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
1030             EQN(incorrectmsg, inputline)) {
1031                 (void) printf("Looking at a login line.\n");
1032                 login_exit(8);
1033         }
1034 }
1035
1036 /*
1037  * logins_disabled -    if the file /etc/nologin exists and the user is not
1038  *                      root then do not permit them to login
1039  */
1040 static int
1041 logins_disabled(char *user_name)
1042 {
1043         FILE    *nlfd;
1044         int     c;
1045         if (!EQN("root", user_name) &&
1046                         ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
1047                 while ((c = getc(nlfd)) != EOF)
1048                         putchar(c);
1049                 (void) fflush(stdout);
1050                 sleep(5);
1051                 return (TRUE);
1052         }
1053         return (FALSE);
1054 }
1055
1056 /*
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.
1059  *
1060  */
1061
1062 static void
1063 check_for_root_user(void)
1064 {
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");
1069                         login_exit(10);
1070                 }
1071         }
1072 }
1073
1074
1075 static char *illegal[] = {
1076         "SHELL=",
1077         "HOME=",
1078         "LOGNAME=",
1079 #ifndef NO_MAIL
1080         "MAIL=",
1081 #endif
1082         "CDPATH=",
1083         "IFS=",
1084         "PATH=",
1085         0
1086 };
1087
1088 /*
1089  * legalenvvar          - Is it legal to insert this environmental variable?
1090  */
1091
1092 static int
1093 legalenvvar(char *s)
1094 {
1095         char **p;
1096
1097         for (p = illegal; *p; p++)
1098                 if (strncmp(s, *p, strlen(*p)) == 0)
1099                         return (0);
1100
1101         if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1102                 return (0);
1103
1104         return (1);
1105 }
1106
1107
1108 /*
1109  * getstr               - Get a string from standard input
1110  *                        Calls exit if read(2) fails.
1111  */
1112
1113 static void
1114 getstr(char *buf, int cnt, char *err)
1115 {
1116         char c;
1117
1118         do {
1119                 if (read(0, &c, 1) != 1)
1120                         login_exit(1);
1121                 *buf++ = c;
1122         } while (cnt > 1 && c != 0);
1123
1124         *buf = 0;
1125         err = err;      /* For lint */
1126 }
1127
1128
1129 /*
1130  * uppercaseterm        - if all input char are upper case
1131  *                        set the corresponding termio
1132  */
1133
1134 static void
1135 uppercaseterm(char *strp)
1136 {
1137         int     upper = 0;
1138         int     lower = 0;
1139         char    *sp;
1140         struct  termio  termio;
1141
1142         for (sp = strp; *sp; sp++) {
1143                 if (islower(*sp))
1144                         lower++;
1145                 else if (isupper(*sp))
1146                         upper++;
1147         }
1148
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');
1157         }
1158 }
1159
1160
1161 /*
1162  * defaults             - read defaults
1163  */
1164
1165 static void
1166 defaults(void)
1167 {
1168         extern int defcntl();
1169         int  flags;
1170         char *ptr;
1171
1172         if (defopen(Pndefault) == 0) {
1173                 /*
1174                  * ignore case
1175                  */
1176                 flags = defcntl(DC_GETFLAGS, 0);
1177                 TURNOFF(flags, DC_CASE);
1178                 defcntl(DC_SETFLAGS, flags);
1179
1180                 if ((Console = defread("CONSOLE=")) != NULL)
1181                         Console = strdup(Console);
1182
1183                 if ((Altshell = defread("ALTSHELL=")) != NULL)
1184                         Altshell = strdup(Altshell);
1185
1186                 if ((Passreq = defread("PASSREQ=")) != NULL)
1187                         Passreq = strdup(Passreq);
1188
1189                 if ((Def_tz = defread("TIMEZONE=")) != NULL)
1190                         Def_tz = strdup(Def_tz);
1191
1192                 if ((Def_hertz = defread("HZ=")) != NULL)
1193                         Def_hertz = strdup(Def_hertz);
1194
1195                 if ((Def_path   = defread("PATH=")) != NULL)
1196                         Def_path = strdup(Def_path);
1197
1198                 if ((Def_supath = defread("SUPATH=")) != NULL)
1199                         Def_supath = strdup(Def_supath);
1200
1201                 if ((ptr = defread("ULIMIT=")) != NULL)
1202                         Def_ulimit = atol(ptr);
1203
1204                 if ((ptr = defread("TIMEOUT=")) != NULL)
1205                         Def_timeout = (unsigned) atoi(ptr);
1206
1207                 if ((ptr = defread("UMASK=")) != NULL) {
1208                         long long_umask;
1209                         if (sscanf(ptr, "%lo", &long_umask) != 1)
1210                                 Umask = DEFUMASK;
1211                         else
1212                                 Umask = (mode_t) long_umask;
1213                 }
1214
1215                 if ((ptr = defread("SLEEPTIME=")) != NULL)
1216                         Sleeptime = atoi(ptr);
1217
1218                 if ((ptr = defread("SYSLOG=")) != NULL)
1219                         dosyslog = strcmp(ptr, "YES") == 0;
1220
1221                 (void) defopen((char *)NULL);
1222         }
1223 }
1224
1225
1226 /*
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
1231  */
1232
1233 static  int
1234 get_options(int argc, char *argv[])
1235 {
1236         int     c;
1237         int     errflg = 0;
1238
1239         while ((c = getopt(argc, argv, "f:h:r:pad:")) != -1) {
1240                 switch (c) {
1241                         case 'a':
1242                                 break;
1243
1244                         case 'd':
1245                                 /*
1246                                  * Must be root to pass in device name
1247                                  * otherwise we exit() as punishment for trying.
1248                                  */
1249                                 if (getuid() != 0 || geteuid() != 0) {
1250                                         login_exit(1);  /* sigh */
1251                                         /*NOTREACHED*/
1252                                 }
1253                                 ttyn = optarg;
1254                                 break;
1255
1256                         case 'h':
1257                                 if (hflag || rflag) {
1258                                         (void) fprintf(stderr,
1259                                             "Only one of -r and -h allowed\n");
1260                                         login_exit(1);
1261                                 }
1262                                 hflag++;
1263                                 SCPYN(remote_host, optarg);
1264                                 if (argv[optind]) {
1265                                         if (argv[optind][0] != '-')
1266                                                 SCPYN(terminal, argv[optind]);
1267                                         optind++;
1268                                 }
1269                                 progname = "telnet";
1270                                 break;
1271
1272                         case 'r':
1273                                 if (hflag || rflag) {
1274                                         (void) fprintf(stderr,
1275                                             "Only one of -r and -h allowed\n");
1276                                         login_exit(1);
1277                                 }
1278                                 rflag++;
1279                                 SCPYN(remote_host, optarg);
1280                                 progname = "rlogin";
1281                                 break;
1282
1283                         case 'p':
1284                                 pflag++;
1285                                 break;
1286
1287                         case 'f':
1288                                 /*
1289                                  * Must be root to bypass authentication
1290                                  * otherwise we exit() as punishment for trying.
1291                                  */
1292                                 if (getuid() != 0 || geteuid() != 0) {
1293                                         login_exit(1);  /* sigh */
1294                                         /*NOTREACHED*/
1295                                 }
1296                                 /* save fflag user name for future use */
1297                                 SCPYN(user_name, optarg);
1298                                 fflag = 1;
1299                                 break;
1300                         default:
1301                                 errflg++;
1302                                 break;
1303                 }       /* end switch */
1304         }               /* end while */
1305
1306         /*
1307          * get the prompt set by ttymon
1308          */
1309         ttyprompt = getenv("TTYPROMPT");
1310
1311         if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
1312                 /*
1313                  * if ttyprompt is set, there should be data on
1314                  * the stream already.
1315                  */
1316                 if ((envp = getargs(inputline)) != (char **)NULL) {
1317                         uppercaseterm(*envp);
1318                         /*
1319                          * don't get name if name passed as argument.
1320                          */
1321                         SCPYN(user_name, *envp++);
1322                 }
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];
1328         }
1329
1330         if (errflg)
1331                 return (-1);
1332         return (0);
1333 }
1334
1335 /*
1336  * usage                - Print usage message
1337  *
1338  */
1339
1340 static void
1341 usage(void)
1342 {
1343         (void) fprintf(stderr,
1344             "Usage:\tlogin [-h|-r] [ name [ env-var ... ]]\n");
1345 }
1346
1347 /*
1348  *                      *** Remote login support ***
1349  *
1350  */
1351
1352
1353 /*
1354  * doremoteterm         - Sets the appropriate ioctls for a remote terminal
1355  */
1356
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"
1361 };
1362
1363 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
1364
1365
1366 static void
1367 doremoteterm(char *term)
1368 {
1369         struct termios tp;
1370         char *cp = strchr(term, '/'), **cpp;
1371         char *speed;
1372
1373         (void) ioctl(0, TCGETS, &tp);
1374
1375         if (cp) {
1376                 *cp++ = '\0';
1377                 speed = cp;
1378                 cp = strchr(speed, '/');
1379
1380                 if (cp)
1381                         *cp++ = '\0';
1382
1383                 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
1384                         if (strcmp(*cpp, speed) == 0) {
1385                                 cfsetospeed(&tp, cpp-speeds);
1386                                 break;
1387                         }
1388         }
1389
1390         tp.c_lflag |= ECHO|ICANON;
1391         tp.c_iflag |= IGNPAR|ICRNL;
1392
1393         (void) ioctl(0, TCSETS, &tp);
1394
1395 }
1396
1397
1398 /*
1399  * Process_rlogin               - Does the work that rlogin and telnet
1400  *                                need done
1401  */
1402
1403 static void
1404 process_rlogin(void)
1405 {
1406
1407         getstr(rusername, sizeof (rusername), "remuser");
1408         getstr(lusername, sizeof (lusername), "locuser");
1409         getstr(terminal, sizeof (terminal), "Terminal type");
1410
1411         /* fflag has precedence over stuff passed by rlogind */
1412         if (fflag || getuid()) {
1413                 pwd = &nouser;
1414                 doremoteterm(terminal);
1415                 return;
1416         } else {
1417                 if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1418                         login_exit(1);
1419                 pwd = getpwnam(lusername);
1420                 if (pwd == NULL) {
1421                         pwd = &nouser;
1422                         doremoteterm(terminal);
1423                         return;
1424                 }
1425         }
1426
1427         doremoteterm(terminal);
1428
1429         /*
1430          * Update PAM on the user name
1431          */
1432         if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
1433                 login_exit(1);
1434
1435         if (pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
1436                 login_exit(1);
1437
1438         SCPYN(user_name, lusername);
1439         envp = &zero;
1440         lusername[0] = '\0';
1441 }
1442
1443 /*
1444  *              *** Account validation routines ***
1445  *
1446  */
1447
1448 /*
1449  * validate_account             - This is the PAM version of validate.
1450  */
1451
1452 static void
1453 validate_account()
1454 {
1455         int     error;
1456         int     n;
1457         int     flag;
1458
1459         (void) alarm(0);        /* give user time to come up with password */
1460
1461         check_log();
1462
1463         if ((Passreq != NULL) && (!strcasecmp("YES", Passreq)))
1464                 flag = PAM_DISALLOW_NULL_AUTHTOK;
1465         else
1466                 flag = 0;
1467
1468         if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
1469                 if (error == PAM_AUTHTOKEN_REQD) {
1470                         (void) printf("Choose a new password.\n");
1471
1472                         if ((error = pam_chauthtok(pamh, 0)) != PAM_SUCCESS) {
1473                                 if (dosyslog)
1474                                         syslog(LOG_CRIT,
1475                                                 "change password failure: %s",
1476                                                 pam_strerror(pamh, error));
1477                                 login_exit(1);
1478                         }
1479                 } else {
1480                         (void) printf(incorrectmsg);
1481                         if (dosyslog)
1482                                 syslog(LOG_CRIT,
1483                                         "login account failure: %s",
1484                                                 pam_strerror(pamh, error));
1485                         login_exit(1);
1486                 }
1487         }
1488 }
1489
1490 /*
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.
1494  */
1495
1496 static void
1497 check_log(void)
1498 {
1499         int fdl;
1500         long long offset;
1501
1502         offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
1503
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) &&
1507                     ll.ll_time != 0)
1508                         lastlogok = 1;
1509                 (void) close(fdl);
1510         }
1511 }
1512
1513
1514 /*
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.
1518  */
1519
1520 static int
1521 chdir_to_dir_root(void)
1522 {
1523         if (chdir(pwd->pw_dir) < 0) {
1524                 if (chdir("/") < 0) {
1525                         (void) printf("No directory!\n");
1526                         return (ERROR);
1527                 }
1528         }
1529
1530         return (OK);
1531 }
1532
1533
1534 /*
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
1540  *                        time around.
1541  */
1542
1543 static void
1544 chdir_to_dir_user(void)
1545 {
1546         if (chdir(pwd->pw_dir) < 0) {
1547                 if (chdir("/") < 0) {
1548                         (void) printf("No directory!\n");
1549                         /*
1550                          * This probably won't work since we can't get to /.
1551                          */
1552                         if (remote_host[0] && dosyslog) {
1553                                 syslog(LOG_CRIT,
1554                                 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1555                                 ttyn, HMAX, remote_host);
1556                         } else if (dosyslog) {
1557                                 syslog(LOG_CRIT,
1558                                 "REPEATED LOGIN FAILURES ON %s", ttyn);
1559                         }
1560                         closelog();
1561                         (void) sleep(DISABLETIME);
1562                         exit(1);
1563                 } else {
1564                         (void) printf("No directory! Logging in with home=/\n");
1565                         pwd->pw_dir = "/";
1566                 }
1567         }
1568 }
1569
1570
1571 /*
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
1576  */
1577
1578 static void
1579 login_authenticate()
1580 {
1581         int cnt = 1;
1582         char *user;
1583         int err;
1584
1585         do {
1586                 /* if scheme broken, then nothing to do but quit */
1587                 if (pam_get_item(pamh, PAM_USER, (void **)&user)
1588                                                         != PAM_SUCCESS)
1589                         exit(1);
1590
1591                 /*
1592                  * only get name from utility if it is not already
1593                  * supplied by pam_start or a pam_set_item.
1594                  */
1595                 if (!user || !user[0]) {
1596                         /* use call back to get user name */
1597                         get_user_name();
1598                 }
1599
1600                 err = verify_passwd();
1601                 if ((err == PAM_SUCCESS) && (chdir_to_dir_root() == OK)) {
1602                         cnt = 0;
1603                         break;
1604                 }
1605
1606                 /* scheme has kept count of retries. time to bail */
1607                 if (err == PAM_MAXTRIES)
1608                         cnt = MAXTRYS;
1609
1610                 /* only sleep after first bad passwd */
1611                 if (cnt)
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)
1617                         login_exit(1);
1618                 if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
1619                         login_exit(1);
1620         } while (cnt++ < MAXTRYS);
1621
1622         if (cnt >= MAXTRYS) {
1623                 audit_login_maxtrys();
1624                 /*
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.
1629                  */
1630                 if (writelog)
1631                         badlogin();
1632                 if (remote_host[0] && dosyslog) {
1633                         syslog(LOG_CRIT,
1634                                 "REPEATED LOGIN FAILURES ON %s FROM %.*s",
1635                                 ttyn, HMAX, remote_host);
1636                 } else if (dosyslog) {
1637                         syslog(LOG_CRIT,
1638                                 "REPEATED LOGIN FAILURES ON %s", ttyn);
1639                 }
1640                 (void) sleep(DISABLETIME);
1641                 exit(1);
1642         }
1643
1644 }
1645
1646 /*
1647  *                      *** Credential Related routines ***
1648  *
1649  */
1650
1651 /*
1652  * setup_credentials            - sets the group ID, initializes the groups
1653  *                                and sets up the secretkey.
1654  *                                Exits if a failure occurrs.
1655  */
1656
1657
1658 /*
1659  * setup_credentials            - PAM does all the work for us on this one.
1660  */
1661
1662 static void
1663 setup_credentials(void)
1664 {
1665         int     error = 0;
1666         int     flags;
1667
1668         /* set the real (and effective) GID */
1669         if (setgid(pwd->pw_gid) == -1) {
1670                 login_exit(1);
1671         }
1672
1673         /*
1674          * Initialize the supplementary group access list.
1675          */
1676         if (!user_name)
1677                 login_exit(1);
1678         if (initgroups(user_name, pwd->pw_gid) == -1) {
1679                 login_exit(1);
1680         }
1681
1682         /* XXX really should be after setgid */
1683         if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1684                 login_exit(error);
1685         }
1686
1687         /* set the real (and effective) UID */
1688         if (setuid(pwd->pw_uid) == -1) {
1689                 login_exit(1);
1690         }
1691
1692 }
1693
1694 /*
1695  *
1696  *              *** Routines to get a new user set up and running ***
1697  *
1698  *                      Things to do when starting up a new user:
1699  *                              adjust_nice
1700  *                              update_utmp_entry
1701  *                              establish_user_environment
1702  *                              print_banner
1703  *                              display_last_login_time
1704  *                              exec_the_shell
1705  *
1706  */
1707
1708
1709 /*
1710  * adjust_nice          - Set the nice (process priority) value if the
1711  *                        gecos value contains an appropriate value.
1712  */
1713
1714 static void
1715 adjust_nice(void)
1716 {
1717         int pri, mflg, i;
1718
1719         if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
1720                 pri = 0;
1721                 mflg = 0;
1722                 i = 4;
1723
1724                 if (pwd->pw_gecos[i] == '-') {
1725                         mflg++;
1726                         i++;
1727                 }
1728
1729                 while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
1730                         pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
1731
1732                 if (mflg)
1733                         pri = -pri;
1734
1735                 (void) nice(pri);
1736         }
1737 }
1738
1739 /*
1740  * update_utmp_entry    - Searchs for the correct utmp entry, making an
1741  *                        entry there if it finds one, otherwise exits.
1742  */
1743
1744 static void
1745 update_utmp_entry(int sublogin)
1746 {
1747         char    *user;
1748         int     error;
1749         static char     *errmsg = "No utmpx entry. "
1750                 "You must exec \"login\" from the lowest level \"shell\".";
1751         int        tmplen;
1752         struct utmpx  *u = (struct utmpx *)0;
1753         struct utmpx  utmp;
1754         char      *ttyntail;
1755
1756
1757         /*
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
1763          * exist.
1764          */
1765
1766         if (error = pam_open_session(pamh, 0) != PAM_SUCCESS) {
1767                 login_exit(1);
1768         }
1769
1770         if ((error = pam_get_item(pamh, PAM_USER, (void **) &user))
1771                                                         != PAM_SUCCESS) {
1772                 login_exit(1);
1773         }
1774
1775         (void) memset((void *)&utmp, 0, sizeof (utmp));
1776         (void) time(&utmp.ut_tv.tv_sec);
1777         utmp.ut_pid = getpid();
1778
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;
1784                 else
1785                         utmp.ut_syslen = sizeof (utmp.ut_host);
1786         } else {
1787                 utmp.ut_syslen = 0;
1788         }
1789
1790         SCPYN(utmp.ut_user, user);
1791
1792         /* skip over "/dev/" */
1793         ttyntail = basename(ttyn);
1794
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;
1806                         pututxline(&utmp);
1807                         break;
1808                 }
1809         }
1810         endutxent();
1811
1812         if (u == (struct utmpx *)NULL) {
1813                 if (!sublogin) {
1814                         /*
1815                          * no utmp entry already setup
1816                          * (init or rlogind/telnetd)
1817                          */
1818                         (void) puts(errmsg);
1819                         login_exit(1);
1820                 }
1821         } else {
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);
1826         }
1827 }
1828
1829
1830
1831 /*
1832  * process_chroot_logins        - Chroots to the specified subdirectory and
1833  *                                re executes login.
1834  */
1835
1836 static int
1837 process_chroot_logins(void)
1838 {
1839         /*
1840          * If the shell field starts with a '*', do a chroot to the home
1841          * directory and perform a new login.
1842          */
1843
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");
1848                         return (ERROR);
1849                 }
1850                 /*
1851                  * Set the environment flag <!sublogin> so that the next login
1852                  * knows that it is a sublogin.
1853                  */
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,
1858                         &envinit[0]);
1859                 (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
1860                 (void) printf("No /usr/bin/login or /etc/login on root\n");
1861                 login_exit(1);
1862         }
1863         return (OK);
1864 }
1865
1866 /*
1867  * establish_user_environment   - Set up the new users environment
1868  */
1869
1870 static void
1871 establish_user_environment(char **renvp)
1872 {
1873         int i, j, k, l_index, length;
1874         char *ptr;
1875         char *endptr;
1876         char **lenvp;
1877         char *krb5p;
1878         char *krb4p;
1879
1880         lenvp = environ;
1881         while (*lenvp++)
1882                 ;
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");
1887                 login_exit(8);
1888         }
1889         memcpy(envinit, newenv, sizeof (newenv));
1890
1891         /* Set up environment */
1892         if (rflag) {
1893                 ENVSTRNCAT(term, terminal);
1894         } else if (hflag) {
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);
1898         } else {
1899                 char *tp = getenv("TERM");
1900
1901                 if ((tp != NULL) && (*tp != '\0'))
1902                         ENVSTRNCAT(term, tp);
1903         }
1904
1905         ENVSTRNCAT(logname, pwd->pw_name);
1906
1907         /*
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.
1916          *
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
1927          */
1928         if (Def_tz != NULL)     /* Is there a TZ from defaults/login? */
1929                 tmp_tz = Def_tz;
1930
1931         if ((Def_tz = getenv("TZ")) != NULL) {
1932                 ENVSTRNCAT(timez, Def_tz);
1933         } else if (tmp_tz != NULL) {
1934                 Def_tz = tmp_tz;
1935                 ENVSTRNCAT(timez, Def_tz);
1936         }
1937
1938         if (Def_hertz == NULL)
1939                 (void) sprintf(hertz + strlen(hertz), "%u", HZ);
1940         else
1941                 ENVSTRNCAT(hertz, Def_hertz);
1942
1943         if (Def_path == NULL)
1944                 (void) strcat(path, DEF_PATH);
1945         else
1946                 ENVSTRNCAT(path, Def_path);
1947
1948         ENVSTRNCAT(home, pwd->pw_dir);
1949
1950         /*
1951          * Find the end of the basic environment
1952          */
1953         for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
1954                 ;
1955
1956         /*
1957          * If TZ has a value, add it.
1958          */
1959         if (strcmp(timez, "TZ=") != 0)
1960                 envinit[basicenv++] = timez;
1961
1962         if (*pwd->pw_shell == '\0') {
1963                 /*
1964                  * If possible, use the primary default shell,
1965                  * otherwise, use the secondary one.
1966                  */
1967                 if (access(SHELL, X_OK) == 0)
1968                         pwd->pw_shell = SHELL;
1969                 else
1970                         pwd->pw_shell = SHELL2;
1971         } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
1972                 envinit[basicenv++] = shell;
1973                 ENVSTRNCAT(shell, pwd->pw_shell);
1974         }
1975
1976 #ifndef NO_MAIL
1977         envinit[basicenv++] = mail;
1978         (void) strcat(mail, pwd->pw_name);
1979 #endif
1980
1981         /*
1982          * Pick up locale environment variables, if any.
1983          */
1984         lenvp = renvp;
1985         while (*lenvp != NULL) {
1986                 j = 0;
1987                 while (localeenv[j] != 0) {
1988                         /*
1989                          * locale_envmatch() returns 1 if
1990                          * *lenvp is localenev[j] and valid.
1991                          */
1992                         if (locale_envmatch(localeenv[j], *lenvp) == 1) {
1993                                 envinit[basicenv++] = *lenvp;
1994                                 break;
1995                         }
1996                         j++;
1997                 }
1998                 lenvp++;
1999         }
2000
2001         /*
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?
2006          */
2007         if (pflag) {
2008                 for (lenvp = renvp; *lenvp; lenvp++) {
2009                         if (!legalenvvar(*lenvp)) {
2010                                 continue;
2011                         }
2012                         /*
2013                          * If this isn't 'xxx=yyy', skip it.  XXX
2014                          */
2015                         if ((endptr = strchr(*lenvp, '=')) == NULL) {
2016                                 continue;
2017                         }
2018                         length = endptr + 1 - *lenvp;
2019                         for (j = 0; j < basicenv; j++) {
2020                                 if (strncmp(envinit[j], *lenvp, length) == 0) {
2021                                         /*
2022                                          * Replace previously established value
2023                                          */
2024                                         envinit[j] = *lenvp;
2025                                         break;
2026                                 }
2027                         }
2028                         if (j == basicenv) {
2029                                 /*
2030                                  * It's a new definition, so add it at the end.
2031                                  */
2032                                 envinit[basicenv++] = *lenvp;
2033                         }
2034                 }
2035         }
2036
2037         /*
2038          * Add in all the environment variables picked up from the
2039          * argument list to "login" or from the user response to the
2040          * "login" request.
2041          */
2042
2043         for (j = 0, k = 0, l_index = 0, ptr = &envblk[0];
2044                 *envp && j < (MAXARGS-1); j++, envp++) {
2045
2046                 /*
2047                  * Scan each string provided.  If it doesn't have the
2048                  * format xxx=yyy, then add the string "Ln=" to the beginning.
2049                  */
2050                 if ((endptr = strchr(*envp, '=')) == NULL) {
2051                         envinit[basicenv+k] = ptr;
2052                         (void) sprintf(ptr, "L%d=%s", l_index, *envp);
2053
2054                         /*
2055                          * Advance "ptr" to the beginning of the
2056                          * next argument.
2057                          */
2058                         while (*ptr++)
2059                                 ;
2060                         k++;
2061                         l_index++;
2062                 } else  {
2063                         if (!legalenvvar(*envp)) { /* this env var permited? */
2064                                 continue;
2065                         } else {
2066
2067                                 /*
2068                                  * Check to see whether this string replaces
2069                                  * any previously defined string
2070                                  */
2071                                 for (i = 0, length = endptr + 1 - *envp;
2072                                         i < basicenv + k; i++) {
2073                                     if (strncmp(*envp, envinit[i], length)
2074                                                 == 0) {
2075                                         envinit[i] = *envp;
2076                                         break;
2077                                     }
2078                                 }
2079
2080                                 /*
2081                                  * If it doesn't, place it at the end of
2082                                  * environment array.
2083                                  */
2084                                 if (i == basicenv+k) {
2085                                         envinit[basicenv+k] = *envp;
2086                                         k++;
2087                                 }
2088                         }
2089                 }
2090         }               /* for (j = 0 ... ) */
2091
2092         /*
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.
2098          */
2099
2100         krb5p = getenv("KRB5CCNAME");
2101
2102         if ((krb5p != NULL) && (*krb5p != '\0')) {
2103                 ENVSTRNCAT(krb5ccname, krb5p);
2104                 envinit[basicenv++] = krb5ccname;
2105         }
2106
2107         krb4p = getenv("KRBTKFILE");
2108
2109         if ((krb4p != NULL) && (*krb4p != '\0')) {
2110                 ENVSTRNCAT(krb4ccname, krb4p);
2111                 envinit[basicenv++] = krb4ccname;
2112         }
2113
2114         /*
2115          * Switch to the new environment.
2116          */
2117         environ = envinit;
2118 }
2119
2120 /*
2121  * print_banner         - Print the banner at start up
2122  *                         Do not turn on DOBANNER ifdef.  This is not
2123  *                         relevant to SunOS.
2124  */
2125
2126 static void
2127 print_banner(void)
2128 {
2129 #ifdef DOBANNER
2130         uname(&un);
2131 #if i386
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);
2136 #elif sun
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);
2142 #else
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);
2146 #endif /* i386 */
2147 #endif /* DOBANNER */
2148 }
2149
2150 /*
2151  * display_last_login_time      - Advise the user the time and date
2152  *                                that this login-id was last used.
2153  */
2154
2155 static void
2156 display_last_login_time(void)
2157 {
2158         if (lastlogok) {
2159                 (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
2160
2161                 if (*ll.ll_host != '\0')
2162                         (void) printf("from %.*s\n", sizeof (ll.ll_host),
2163                                             ll.ll_host);
2164                 else
2165                         (void) printf("on %.*s\n", sizeof (ll.ll_line),
2166                                             ll.ll_line);
2167         }
2168 }
2169
2170 /*
2171  * exec_the_shell       - invoke the specified shell or start up program
2172  */
2173
2174 static void
2175 exec_the_shell(void)
2176 {
2177         char *endptr;
2178         int i;
2179
2180         (void) strcat(minusnam, basename(pwd->pw_shell));
2181
2182         /*
2183          * Exec the shell
2184          */
2185         (void) execl(pwd->pw_shell, minusnam, (char *)0);
2186
2187         /*
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.
2191          */
2192         for (i = 0; envinit[i] != NULL; ++i) {
2193                 if ((envinit[i] == shell) &&
2194                     ((endptr = strchr(shell, '=')) != NULL))
2195                         (*++endptr) = '\0';
2196                 }
2197
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);
2201         }
2202
2203         (void) printf("No shell\n");
2204 }
2205
2206 /*
2207  * login_exit           - Call exit()  and terminate.
2208  *                        This function is here for PAM so cleanup can
2209  *                        be done before the process exits.
2210  */
2211 static void
2212 login_exit(int exit_code)
2213 {
2214         if (pamh)
2215                 pam_end(pamh, PAM_ABORT);
2216         exit(exit_code);
2217         /*NOTREACHED*/
2218 }
2219
2220 /*
2221  * Check if lenv and penv matches or not.
2222  */
2223 static int
2224 locale_envmatch(char *lenv, char *penv)
2225 {
2226         while ((*lenv == *penv) && *lenv && *penv != '=') {
2227                 lenv++;
2228                 penv++;
2229         }
2230
2231         /*
2232          * '/' is eliminated for security reason.
2233          */
2234         if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
2235                 return (1);
2236         return (0);
2237 }
2238
2239 /*
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.)
2243  */
2244
2245 #define MAX_LINELEN     256
2246 #define LOGINDEVPERM    "/etc/logindevperm"
2247 #define DIRWILD         "/*"                    /* directory wildcard */
2248 #define DIRWLDLEN       2                       /* strlen(DIRWILD) */
2249
2250 static void
2251 logindevperm(char *ttyn, uid_t uid, gid_t gid)
2252 {
2253         char *field_delims = " \t\n";
2254         char *permfile = LOGINDEVPERM;
2255         char line[MAX_LINELEN];
2256         char *console;
2257         char *mode_str;
2258         char *dev_list;
2259         char *device;
2260         char *ptr;
2261         int mode;
2262         FILE *fp;
2263         size_t l;
2264         int lineno;
2265
2266         if ((fp = fopen(permfile, "r")) == NULL)
2267                 return;
2268
2269         lineno = 0;
2270         while (fgets(line, MAX_LINELEN, fp) != NULL) {
2271                 lineno++;
2272
2273                 if ((ptr = strchr(line, '#')) != NULL)
2274                         *ptr = '\0';    /* handle comments */
2275
2276                 if ((console = strtok(line, field_delims)) == NULL)
2277                         continue;       /* ignore blank lines */
2278
2279                 if (strcmp(console, ttyn) != 0)
2280                         continue;       /* not our tty, skip */
2281
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,
2286                             lineno, line);
2287                         continue;
2288                 }
2289
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,
2295                             lineno, mode_str);
2296                         continue;
2297                 }
2298
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);
2304                         continue;
2305                 }
2306
2307                 device = strtok(dev_list, ":");
2308                 while (device != NULL) {
2309                         l = strlen(device);
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);
2314                         } else {
2315                                 /*
2316                                  * change the owner/group/permission;
2317                                  * nonexistent devices are ignored
2318                                  */
2319                                 if (chown(device, uid, gid) == -1) {
2320                                         if (errno != ENOENT) {
2321                                                 (void) fprintf(stderr, "%s: ",
2322                                                     permfile);
2323                                                 perror(device);
2324                                         }
2325                                 } else {
2326                                         if ((chmod(device, mode) == -1) &&
2327                                             (errno != ENOENT)) {
2328                                                 (void) fprintf(stderr, "%s: ",
2329                                                     permfile);
2330                                                 perror(device);
2331                                         }
2332                                 }
2333                         }
2334                         device = strtok((char *)NULL, ":");
2335                 }
2336         }
2337         (void) fclose(fp);
2338 }
2339
2340 /*
2341  * Apply owner/group/perms to all files (except "." and "..")
2342  * in a directory.
2343  */
2344 static void
2345 dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile)
2346 {
2347         DIR *dirp;
2348         struct dirent *direntp;
2349         char *name, path[MAX_LINELEN + MAXNAMELEN];
2350
2351         dirp = opendir(dir);
2352         if (dirp == NULL)
2353                 return;
2354
2355         while ((direntp = readdir(dirp)) != NULL) {
2356                 name = direntp->d_name;
2357                 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
2358                         continue;
2359
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);
2364                                 perror(path);
2365                         }
2366                 } else {
2367                         if ((chmod(path, mode) == -1) && (errno != ENOENT)) {
2368                                 (void) fprintf(stderr, "%s: ", permfile);
2369                                 perror(path);
2370                         }
2371                 }
2372         }
2373         (void) closedir(dirp);
2374 }