Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[games.git] / contrib / libpam / modules / pam_rhosts / pam_rhosts_auth.c
1 /*----------------------------------------------------------------------
2  * Modified for Linux-PAM by Al Longyear <longyear@netcom.com> 96/5/5
3  * Modifications, Cristian Gafton 97/2/8
4  * Modifications, Peter Allgeyer 97/3
5  * Modifications (netgroups and fixes), Nicolai Langfeldt 97/3/21
6  * Security fix: 97/10/2 - gethostbyname called repeatedly without care
7  * Modification (added privategroup option) Andrew <morgan@transmeta.com>
8  *----------------------------------------------------------------------
9  * Copyright (c) 1983, 1993, 1994
10  *      The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  * $FreeBSD: src/contrib/libpam/modules/pam_rhosts/pam_rhosts_auth.c,v 1.3.2.2 2001/06/11 15:28:24 markm Exp $
40  * $DragonFly: src/contrib/libpam/modules/pam_rhosts/Attic/pam_rhosts_auth.c,v 1.2 2003/06/17 04:24:03 dillon Exp $
41  */
42
43 #define _BSD_SOURCE
44
45 #define USER_RHOSTS_FILE "/.rhosts"     /* prefixed by user's home dir */
46
47 #ifdef linux
48 #include <endian.h>
49 #endif
50
51 #ifdef NEED_FSUID_H
52 #include <sys/fsuid.h>
53 #endif /* NEED_FSUID_H */
54
55 #include <sys/types.h>
56 #include <sys/uio.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <sys/param.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <netdb.h>       /* This is supposed(?) to contain the following */
64 int innetgr(const char *, const char *, const char *,const char *);
65
66 #include <stdio.h>
67 #include <errno.h>
68 #include <sys/time.h>
69 #include <arpa/inet.h>
70
71 #ifndef MAXDNAME
72 #define MAXDNAME  256
73 #endif
74
75 #include <stdarg.h>
76 #include <ctype.h>
77
78 #include <net/if.h>
79 #ifdef linux
80 # include <linux/sockios.h>
81 # ifndef __USE_MISC
82 #  define __USE_MISC
83 #  include <sys/fsuid.h>
84 # endif /* __USE_MISC */
85 #endif
86
87 #include <pwd.h>
88 #include <grp.h>
89 #include <sys/file.h>
90 #include <sys/signal.h>
91 #include <sys/stat.h>
92 #include <syslog.h>
93 #ifndef _PATH_HEQUIV
94 #define _PATH_HEQUIV "/etc/hosts.equiv"
95 #endif /* _PATH_HEQUIV */
96
97 #define PAM_SM_AUTH  /* only defines this management group */
98
99 #include <security/pam_modules.h>
100 #include <security/_pam_macros.h>
101
102 /* to the best of my knowledge, all modern UNIX boxes have 32 bit integers */
103 #define U32 unsigned int
104
105
106 /*
107  * Options for this module
108  */
109
110 struct _options {
111     int  opt_no_hosts_equiv;
112     int  opt_hosts_equiv_rootok;
113     int  opt_no_rhosts;
114     int  opt_debug;
115     int  opt_nowarn;
116     int  opt_disallow_null_authtok;
117     int  opt_silent;
118     int  opt_promiscuous;
119     int  opt_suppress;
120     int  opt_private_group;
121     int  opt_no_uid_check;
122     const char *superuser;
123     const char *last_error;
124 };
125
126 /* logging */
127 static void _pam_log(int err, const char *format, ...)
128 {
129     va_list args;
130
131     va_start(args, format);
132     openlog("pam_rhosts_auth", LOG_CONS|LOG_PID, LOG_AUTH);
133     vsyslog(err, format, args);
134     va_end(args);
135     closelog();
136 }
137
138 static void set_option (struct _options *opts, const char *arg)
139 {
140     if (strcmp(arg, "no_hosts_equiv") == 0) {
141         opts->opt_no_hosts_equiv = 1;
142         return;
143     }
144
145     if (strcmp(arg, "hosts_equiv_rootok") == 0) {
146         opts->opt_hosts_equiv_rootok = 1;
147         return;
148     }
149
150     if (strcmp(arg, "no_rhosts") == 0) {
151         opts->opt_no_rhosts = 1;
152         return;
153     }
154
155     if (strcmp(arg, "debug") == 0) {
156         D(("debugging enabled"));
157         opts->opt_debug = 1;
158         return;
159     }
160
161     if (strcmp(arg, "no_warn") == 0) {
162         opts->opt_nowarn = 1;
163         return;
164     }
165
166     if (strcmp(arg, "promiscuous") == 0) {
167         opts->opt_promiscuous = 1;   /* used to permit '+' in ...hosts file */
168         return;
169     }
170
171     if (strcmp(arg, "suppress") == 0) {
172         opts->opt_suppress = 1; /* used to suppress failure warning message */
173         return;
174     }
175
176     if (strcmp(arg, "privategroup") == 0) {
177         opts->opt_private_group = 1; /* used to permit group write on .rhosts
178                                         file if group has same name as owner */
179         return;
180     }
181
182     if (strcmp(arg, "no_uid_check") == 0) {
183         opts->opt_no_uid_check = 1;  /* NIS optimization */
184         return;
185     }
186
187     if (strcmp(arg, "superuser=") == 0) {
188         opts->superuser = arg+sizeof("superuser=")-1;
189         return;
190     }
191     /*
192      * All other options are ignored at the present time.
193      */
194     _pam_log(LOG_WARNING, "unrecognized option '%s'", arg);
195 }
196
197 static void set_parameters (struct _options *opts, int flags,
198                             int argc, const char **argv)
199 {
200     opts->opt_silent                = flags & PAM_SILENT;
201     opts->opt_disallow_null_authtok = flags & PAM_DISALLOW_NULL_AUTHTOK;
202
203     while (argc-- > 0) {
204         set_option (opts, *argv);
205         ++argv;
206     }
207 }
208
209 /*
210  * Obtain the name of the remote host. Currently, this is simply by
211  * requesting the contents of the PAM_RHOST item.
212  */
213
214 static int pam_get_rhost(pam_handle_t *pamh, const char **rhost
215                          , const char *prompt)
216 {
217     int retval;
218     const char   *current;
219
220     retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);
221     if (retval != PAM_SUCCESS)
222         return retval;
223
224     if (current == NULL) {
225         return PAM_AUTH_ERR;
226     }
227     *rhost = current;
228
229     return retval;        /* pass on any error from conversation */
230 }
231
232 /*
233  * Obtain the name of the remote user. Currently, this is simply by
234  * requesting the contents of the PAM_RUSER item.
235  */
236
237 static int pam_get_ruser(pam_handle_t *pamh, const char **ruser
238                          , const char *prompt)
239 {
240     int retval;
241     const char   *current;
242
243     retval = pam_get_item (pamh, PAM_RUSER, (const void **)&current);
244     if (retval != PAM_SUCCESS)
245         return retval;
246
247     if (current == NULL) {
248         return PAM_AUTH_ERR;
249     }
250     *ruser = current;
251
252     return retval;        /* pass on any error from conversation */
253 }
254
255 /*
256  * Returns 1 if positive match, 0 if no match, -1 if negative match.
257  */
258
259 static int
260 __icheckhost (pam_handle_t *pamh, struct _options *opts, U32 raddr
261               , register char *lhost, const char *rhost)
262 {
263     struct hostent *hp;
264     U32 laddr;
265     int negate=1;    /* Multiply return with this to get -1 instead of 1 */
266     char **pp, *user;
267
268     /* Check nis netgroup.  We assume that pam has done all needed
269        paranoia checking before we are handed the rhost */
270     if (strncmp("+@",lhost,2) == 0)
271       return(innetgr(&lhost[2],rhost,NULL,NULL));
272
273     if (strncmp("-@",lhost,2) == 0)
274       return(-innetgr(&lhost[2],rhost,NULL,NULL));
275
276     /* -host */
277     if (strncmp("-",lhost,1) == 0) {
278         negate=-1;
279         lhost++;
280     } else if (strcmp("+",lhost) == 0) {
281         (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
282         D(("user %s has a `+' host entry", user));
283         if (opts->opt_promiscuous)
284             return (1);                     /* asking for trouble, but ok.. */
285         /* If not promiscuous: handle as negative */
286         return (-1);
287     }
288
289     /* Try for raw ip address first. */
290     if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
291         return (negate*(! (raddr ^ laddr)));
292
293     /* Better be a hostname. */
294     hp = gethostbyname(lhost);
295     if (hp == NULL)
296         return (0);
297     
298     /* Spin through ip addresses. */
299     for (pp = hp->h_addr_list; *pp; ++pp)
300         if (!memcmp (&raddr, *pp, sizeof (U32)))
301             return (negate);
302
303     /* No match. */
304     return (0);
305 }
306
307 /* Returns 1 on positive match, 0 on no match, -1 on negative match */
308
309 static int __icheckuser(pam_handle_t *pamh, struct _options *opts
310                         , const char *luser, const char *ruser
311                         , const char *rhost)
312 {
313     /*
314       luser is user entry from .rhosts/hosts.equiv file
315       ruser is user id on remote host
316       rhost is the remote host name
317       */
318     char *user;
319
320     /* [-+]@netgroup */
321     if (strncmp("+@",luser,2) == 0)
322         return (innetgr(&luser[2],NULL,ruser,NULL));
323
324     if (strncmp("-@",luser,2) == 0)
325         return (-innetgr(&luser[2],NULL,ruser,NULL));
326
327     /* -user */
328     if (strncmp("-",luser,1) == 0)
329         return(-(strcmp(&luser[1],ruser) == 0));
330
331     /* + */
332     if (strcmp("+",luser) == 0) {
333         (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
334         _pam_log(LOG_WARNING, "user %s has a `+' user entry", user);
335         if (opts->opt_promiscuous)
336             return(1);
337         /* If not promiscuous we handle it as a negative match */
338         return(-1);
339     }
340
341     /* simple string match */
342     return (strcmp(ruser, luser) == 0);
343 }
344
345 /*
346  * Returns 1 for blank lines (or only comment lines) and 0 otherwise
347  */
348
349 static int __isempty(char *p)
350 {
351     while (*p && isspace(*p)) {
352         ++p;
353     }
354
355     return (*p == '\0' || *p == '#') ? 1:0 ;
356 }
357
358 /*
359  * Returns 0 if positive match, 1 if _not_ ok.
360  */
361
362 static int
363 __ivaliduser (pam_handle_t *pamh, struct _options *opts,
364               FILE *hostf, U32 raddr,
365               const char *luser, const char *ruser, const char *rhost)
366 {
367     register const char *user;
368     register char *p;
369     int hcheck, ucheck;
370     char buf[MAXHOSTNAMELEN + 128];                       /* host + login */
371
372     buf[sizeof (buf)-1] = '\0';                         /* terminate line */
373
374     while (fgets(buf, sizeof(buf), hostf) != NULL) {   /* hostf file line */
375         p = buf;                              /* from beginning of file.. */
376
377         /* Skip empty or comment lines */
378         if (__isempty(p)) {
379             continue;
380         }
381
382         /* Skip lines that are too long. */
383         if (strchr(p, '\n') == NULL) {
384             int ch = getc(hostf);
385
386             while (ch != '\n' && ch != EOF)
387                 ch = getc(hostf);
388             continue;
389         }
390
391         /*
392          * If there is a hostname at the start of the line.  Set it to
393          * lower case. A leading ' ' or '\t' indicates no hostname
394          */
395
396         for (;*p && !isspace(*p); ++p) {
397             *p = tolower(*p);
398         }
399
400         /*
401          * next we want to find the permitted name for the remote user
402          */
403
404         if (*p == ' ' || *p == '\t') {
405
406             /* <nul> terminate hostname and skip spaces */
407             for (*p++='\0'; *p && isspace(*p); ++p);
408
409             user = p;                   /* this is the user's name */
410             while (*p && !isspace(*p))
411                 ++p;                    /* find end of user's name */
412         } else 
413             user = p;
414
415         *p = '\0';              /* <nul> terminate username (+host?) */
416
417         /* buf -> host(?) ; user -> username(?) */
418
419         /* First check host part */
420         hcheck=__icheckhost(pamh, opts, raddr, buf, rhost);
421
422         if (hcheck<0)
423             return(1);
424
425         if (hcheck) {
426             /* Then check user part */
427             if (! (*user))
428                 user = luser;
429
430             ucheck=__icheckuser(pamh, opts, user, ruser, rhost);
431
432             /* Positive 'host user' match? */
433             if (ucheck>0)
434                 return(0);
435
436             /* Negative 'host -user' match? */
437             if (ucheck<0)
438                 return(1);
439
440             /* Neither, go on looking for match */
441         }
442     }
443
444     return (1);
445 }
446
447 /*
448  * New .rhosts strategy: We are passed an ip address. We spin through
449  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
450  * has ip addresses, we don't have to trust a nameserver.  When it
451  * contains hostnames, we spin through the list of addresses the nameserver
452  * gives us and look for a match.
453  *
454  * Returns 0 if ok, -1 if not ok.
455  */
456
457 static int
458 pam_iruserok(pam_handle_t *pamh,
459          struct _options *opts, U32 raddr, int superuser,
460          const char *ruser, const char *luser, const char *rhost)
461 {
462     const char *cp;
463     struct stat sbuf;
464     struct passwd *pwd;
465     FILE *hostf;
466     uid_t uid;
467     int answer;
468     char pbuf[MAXPATHLEN];               /* potential buffer overrun */
469
470     if ((!superuser||opts->opt_hosts_equiv_rootok) && !opts->opt_no_hosts_equiv ) {
471
472         /* try to open system hosts.equiv file */
473         hostf = fopen (_PATH_HEQUIV, "r");
474         if (hostf) {
475             answer = __ivaliduser(pamh, opts, hostf, raddr, luser
476                                   , ruser, rhost);
477             (void) fclose(hostf);
478             if (answer == 0)
479                 return 0;      /* remote host is equivalent to localhost */
480         } /* else {
481             No hosts.equiv file on system.
482         } */
483     }
484     
485     if ( opts->opt_no_rhosts )
486         return 1;
487
488     /*
489      * Identify user's local .rhosts file
490      */
491
492     pwd = getpwnam(luser);
493     if (pwd == NULL) {
494         /* 
495          * luser is assumed to be valid because of an earlier check for uid = 0
496          * we don't log this error twice. However, this shouldn't happen !
497          * --cristiang 
498          */
499         return(1);
500     }
501
502     /* check for buffer overrun */
503     if (strlen(pwd->pw_dir) + sizeof(USER_RHOSTS_FILE) + 2 >= MAXPATHLEN) {
504         if (opts->opt_debug)
505             _pam_log(LOG_DEBUG,"home directory for `%s' is too long", luser);
506         return 1;                               /* to dangerous to try */
507     }
508
509     (void) strcpy(pbuf, pwd->pw_dir);
510     (void) strcat(pbuf, USER_RHOSTS_FILE);
511
512     /*
513      * Change effective uid while _reading_ .rhosts. (not just
514      * opening).  If root and reading an NFS mounted file system,
515      * can't read files that are 0600 as .rhosts files should be.
516      */
517
518     /* We are root, this will not fail */
519 #ifdef linux
520     /* If we are on linux the better way is setfsuid */
521     uid = setfsuid(pwd->pw_uid);
522     hostf = fopen(pbuf, "r");
523 #else
524     uid = geteuid();
525     (void) seteuid(pwd->pw_uid);
526     hostf = fopen(pbuf, "r");
527 #endif
528
529     if (hostf == NULL) {
530         if (opts->opt_debug)
531             _pam_log(LOG_DEBUG,"Could not open %s file",pbuf);
532         answer = 1;
533         goto exit_function;
534     }
535
536     /*
537      * If not a regular file, or is owned by someone other than
538      * user or root or if writeable by anyone but the owner, quit.
539      */
540
541     cp = NULL;
542     if (lstat(pbuf, &sbuf) < 0 || !S_ISREG(sbuf.st_mode))
543         cp = ".rhosts not regular file";
544     else if (fstat(fileno(hostf), &sbuf) < 0)
545         cp = ".rhosts fstat failed";
546     else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
547         cp = "bad .rhosts owner";
548     else if (sbuf.st_mode & S_IWOTH)
549         cp = ".rhosts writable by other!";
550     else if (sbuf.st_mode & S_IWGRP) {
551
552         /* private group caveat */
553         if (opts->opt_private_group) {
554             struct group *grp = getgrgid(sbuf.st_gid);
555
556             if (NULL == grp || NULL == grp->gr_name
557                 || strcmp(luser,grp->gr_name)) {
558                 cp = ".rhosts writable by public group";
559             } else if (grp->gr_mem) {
560                 int gcount;
561
562                 /* require at most one member (luser) of this group */
563                 for (gcount=0; grp->gr_mem[gcount]; ++gcount) {
564                     if (strcmp(grp->gr_mem[gcount], luser)) {
565                         gcount = -1;
566                         break;
567                     }
568                 }
569                 if (gcount < 0) {
570                     cp = ".rhosts writable by other members of group";
571                 }
572             }
573         } else {
574             cp = ".rhosts writable by group";
575         }
576
577     } /* It is _NOT_ safe to append an else here...  Do so prior to
578        * S_IWGRP check */
579
580     /* If there were any problems, quit. */
581     if (cp) {
582         opts->last_error = cp;
583         answer = 1;
584         goto exit_function;
585     }
586
587     answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost);
588
589 exit_function:
590     /*
591      * Go here to exit after the fsuid/euid has been adjusted so that
592      * they are reset before we exit.
593      */
594
595 #ifdef linux
596     setfsuid(uid);
597 #else
598     (void)seteuid(uid);
599 #endif
600
601     if (hostf != NULL)
602         (void) fclose(hostf);
603
604     return answer;
605 }
606
607 static int
608 pam_ruserok (pam_handle_t *pamh,
609              struct _options *opts, const char *rhost, int superuser,
610              const char *ruser, const char *luser)
611 {
612     struct hostent *hp;
613     int answer = 1;                             /* default to failure */
614     U32 *addrs;
615     int n, i;
616
617     opts->last_error = (char *) 0;
618     hp               = gethostbyname(rhost);         /* identify host */
619
620     if (hp != NULL) {
621         /* First of all check the address length */
622         if (hp->h_length != 4) {
623             _pam_log(LOG_ALERT, "pam_rhosts module can't work with not IPv4 "
624                      "addresses");
625             return 1;                                    /* not allowed */
626         }
627
628         /* loop though address list */
629         for (n = 0; hp->h_addr_list[n]; n++);
630         D(("rhosts: %d addresses", n));
631
632         if (n) {
633             addrs = calloc (n, hp->h_length);
634             for (i = 0; i < n; i++)
635                 memcpy (addrs+i, hp->h_addr_list[i], hp->h_length);
636
637             for (i = 0; i < n && answer; i++) {
638                 D(("rhosts: address %d is %04x", i, addrs[i]));
639                 answer = pam_iruserok(pamh, opts, addrs[i], superuser,
640                                       ruser, luser, rhost);
641                          /* answer == 0 means success */
642             }
643
644             free (addrs);
645         }
646     }
647
648     return answer;
649 }
650
651 /*
652  * Internal function to do authentication
653  */
654
655 static int _pam_auth_rhosts (pam_handle_t *pamh,
656                              int flags, 
657                              int argc,
658                              const char **argv) 
659 {
660     int retval;
661     const char *luser;
662     const char *ruser,*rhost;
663     struct _options opts;
664     int as_root = 0;
665     /*
666      * Look at the options and set the flags accordingly.
667      */
668     memset (&opts, 0, sizeof (opts));
669     set_parameters (&opts, flags, argc, argv);
670     /*
671      * Obtain the parameters for the various items
672      */
673     for (;;) {                         /* abuse loop to avoid goto */
674
675         /* get the remotehost */
676         retval = pam_get_rhost(pamh, &rhost, NULL);
677         (void) pam_set_item(pamh, PAM_RHOST, rhost);
678         if (retval != PAM_SUCCESS) {
679             if (opts.opt_debug) {
680                 _pam_log(LOG_DEBUG, "could not get the remote host name");
681             }
682             break;
683         }
684
685         /* get the remote user */
686         retval = pam_get_ruser(pamh, &ruser, NULL);
687         (void) pam_set_item(pamh, PAM_RUSER, ruser);
688         if (retval != PAM_SUCCESS) {
689             if (opts.opt_debug)
690                 _pam_log(LOG_DEBUG, "could not get the remote username");
691             break;
692         }
693
694         /* get the local user */
695         retval = pam_get_user(pamh, &luser, NULL);
696
697         if (retval != PAM_SUCCESS) {
698             if (opts.opt_debug)
699                 _pam_log(LOG_DEBUG, "could not determine name of local user");
700             break;
701         }
702
703         if (opts.superuser && !strcmp(opts.superuser, luser)) {
704             as_root = 1;
705         }
706
707         /* check if the luser uid == 0... --cristiang */
708         if (! opts.opt_no_uid_check) {
709             struct passwd *luser_pwd;
710
711             luser_pwd = getpwnam(luser);
712             if (luser_pwd == NULL) {
713                 if (opts.opt_debug)
714                     _pam_log(LOG_DEBUG, "user '%s' unknown to this system",
715                              luser);
716                 retval = PAM_AUTH_ERR;
717                 break;
718             }
719             if (luser_pwd->pw_uid == 0)
720                 as_root = 1;
721             luser_pwd = NULL;                                   /* forget */
722         }
723 /*
724  * Validate the account information.
725  */
726         if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) {
727             if ( !opts.opt_suppress ) {
728                 _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s",
729                          ruser, rhost, luser, (opts.last_error==NULL) ?
730                          "access not allowed":opts.last_error);
731             }
732             retval = PAM_AUTH_ERR;
733         } else {
734             _pam_log(LOG_NOTICE, "allowed to %s@%s as %s",
735                      ruser, rhost, luser);
736         }
737         break;
738     }
739
740     return retval;
741 }
742
743 /* --- authentication management functions --- */
744
745 PAM_EXTERN
746 int pam_sm_authenticate (pam_handle_t *pamh, 
747                          int flags,
748                          int argc, 
749                          const char **argv)
750 {
751     int retval;
752
753     if (sizeof(U32) != 4) {
754         _pam_log (LOG_ALERT, "pam_rhosts module can\'t work on this hardware "
755                   "(yet)");
756         return PAM_AUTH_ERR;
757     }
758     sethostent(1);
759     retval = _pam_auth_rhosts (pamh, flags, argc, argv);
760     endhostent();
761     return retval;
762 }
763
764 PAM_EXTERN
765 int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
766                    const char **argv)
767 {
768     return PAM_SUCCESS;
769 }
770
771 /* end of module definition */
772
773
774 #ifdef PAM_STATIC
775
776 /* static module data */
777
778 struct pam_module _pam_rhosts_auth_modstruct = {
779     "pam_rhosts_auth",
780     pam_sm_authenticate,
781     pam_sm_setcred,
782     NULL,
783     NULL,
784     NULL,
785     NULL,
786 };
787
788 #endif