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