]> granicus.if.org Git - linux-pam/blob - modules/pam_rhosts/pam_rhosts_auth.c
Relevant BUGIDs:
[linux-pam] / 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  */
40
41 #include <security/_pam_aconf.h>
42
43 #define USER_RHOSTS_FILE "/.rhosts"     /* prefixed by user's home dir */
44
45 #ifdef __linux__
46 #include <endian.h>
47 #endif
48
49 #ifdef HAVE_SYS_FSUID_H
50 #include <sys/fsuid.h>
51 #endif /* HAVE_SYS_FSUID_H */
52
53 #include <sys/types.h>
54 #include <sys/uio.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <stdlib.h>
58 #include <sys/param.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <netdb.h>       /* This is supposed(?) to contain the following */
62 int innetgr(const char *, const char *, const char *,const char *);
63
64 #include <stdio.h>
65 #include <errno.h>
66 #include <sys/time.h>
67 #include <arpa/inet.h>
68
69 #ifndef MAXDNAME
70 #define MAXDNAME  256
71 #endif
72
73 #include <stdarg.h>
74 #include <ctype.h>
75
76 #include <net/if.h>
77
78 #include <pwd.h>
79 #include <grp.h>
80 #include <sys/file.h>
81 #include <sys/signal.h>
82 #include <sys/stat.h>
83 #include <stdint.h>
84 #include <syslog.h>
85 #ifndef _PATH_HEQUIV
86 #define _PATH_HEQUIV "/etc/hosts.equiv"
87 #endif /* _PATH_HEQUIV */
88
89 #define PAM_SM_AUTH  /* only defines this management group */
90
91 #include <security/pam_modules.h>
92 #include <security/_pam_macros.h>
93 #include <security/_pam_modutil.h>
94
95 /* Use the C99 type; older platforms will need this to be typedef'ed
96    elsewhere */
97 #define U32 uint32_t
98
99
100 /*
101  * Options for this module
102  */
103
104 struct _options {
105     int  opt_no_hosts_equiv;
106     int  opt_hosts_equiv_rootok;
107     int  opt_no_rhosts;
108     int  opt_debug;
109     int  opt_nowarn;
110     int  opt_disallow_null_authtok;
111     int  opt_silent;
112     int  opt_promiscuous;
113     int  opt_suppress;
114     int  opt_private_group;
115     int  opt_no_uid_check;
116     const char *superuser;
117     const char *last_error;
118 };
119
120 /* logging */
121 static void _pam_log(int err, const char *format, ...)
122 {
123     va_list args;
124
125     va_start(args, format);
126     openlog("pam_rhosts_auth", LOG_CONS|LOG_PID, LOG_AUTH);
127     vsyslog(err, format, args);
128     va_end(args);
129     closelog();
130 }
131
132 static void set_option (struct _options *opts, const char *arg)
133 {
134     if (strcmp(arg, "no_hosts_equiv") == 0) {
135         opts->opt_no_hosts_equiv = 1;
136         return;
137     }
138
139     if (strcmp(arg, "hosts_equiv_rootok") == 0) {
140         opts->opt_hosts_equiv_rootok = 1;
141         return;
142     }
143
144     if (strcmp(arg, "no_rhosts") == 0) {
145         opts->opt_no_rhosts = 1;
146         return;
147     }
148
149     if (strcmp(arg, "debug") == 0) {
150         D(("debugging enabled"));
151         opts->opt_debug = 1;
152         return;
153     }
154
155     if (strcmp(arg, "no_warn") == 0) {
156         opts->opt_nowarn = 1;
157         return;
158     }
159
160     if (strcmp(arg, "promiscuous") == 0) {
161         opts->opt_promiscuous = 1;   /* used to permit '+' in ...hosts file */
162         return;
163     }
164
165     if (strcmp(arg, "suppress") == 0) {
166         opts->opt_suppress = 1; /* used to suppress failure warning message */
167         return;
168     }
169
170     if (strcmp(arg, "privategroup") == 0) {
171         opts->opt_private_group = 1; /* used to permit group write on .rhosts
172                                         file if group has same name as owner */
173         return;
174     }
175
176     if (strcmp(arg, "no_uid_check") == 0) {
177         opts->opt_no_uid_check = 1;  /* NIS optimization */
178         return;
179     }
180
181     if (strcmp(arg, "superuser=") == 0) {
182         opts->superuser = arg+sizeof("superuser=")-1;
183         return;
184     }
185     /*
186      * All other options are ignored at the present time.
187      */
188     _pam_log(LOG_WARNING, "unrecognized option '%s'", arg);
189 }
190
191 static void set_parameters (struct _options *opts, int flags,
192                             int argc, const char **argv)
193 {
194     opts->opt_silent                = flags & PAM_SILENT;
195     opts->opt_disallow_null_authtok = flags & PAM_DISALLOW_NULL_AUTHTOK;
196
197     while (argc-- > 0) {
198         set_option (opts, *argv);
199         ++argv;
200     }
201 }
202
203 /*
204  * Obtain the name of the remote host. Currently, this is simply by
205  * requesting the contents of the PAM_RHOST item.
206  */
207
208 static int pam_get_rhost(pam_handle_t *pamh, const char **rhost
209                          , const char *prompt)
210 {
211     int retval;
212     const char   *current;
213
214     retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);
215     if (retval != PAM_SUCCESS)
216         return retval;
217
218     if (current == NULL) {
219         return PAM_AUTH_ERR;
220     }
221     *rhost = current;
222
223     return retval;        /* pass on any error from conversation */
224 }
225
226 /*
227  * Obtain the name of the remote user. Currently, this is simply by
228  * requesting the contents of the PAM_RUSER item.
229  */
230
231 static int pam_get_ruser(pam_handle_t *pamh, const char **ruser,
232                          const char *prompt)
233 {
234     int retval;
235     const char   *current;
236
237     retval = pam_get_item (pamh, PAM_RUSER, (const void **)&current);
238     if (retval != PAM_SUCCESS) {
239         return retval;
240     }
241
242     if (current == NULL) {
243         return PAM_AUTH_ERR;
244     }
245     *ruser = current;
246
247     return retval;        /* pass on any error from conversation */
248 }
249
250 /*
251  * Returns 1 if positive match, 0 if no match, -1 if negative match.
252  */
253
254 static int
255 __icheckhost (pam_handle_t *pamh, struct _options *opts, U32 raddr
256               , register char *lhost, const char *rhost)
257 {
258     struct hostent *hp;
259     U32 laddr;
260     int negate=1;    /* Multiply return with this to get -1 instead of 1 */
261     char **pp, *user;
262
263     /* Check nis netgroup.  We assume that pam has done all needed
264        paranoia checking before we are handed the rhost */
265     if (strncmp("+@",lhost,2) == 0)
266       return(innetgr(&lhost[2],rhost,NULL,NULL));
267
268     if (strncmp("-@",lhost,2) == 0)
269       return(-innetgr(&lhost[2],rhost,NULL,NULL));
270
271     /* -host */
272     if (strncmp("-",lhost,1) == 0) {
273         negate=-1;
274         lhost++;
275     } else if (strcmp("+",lhost) == 0) {
276         (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
277         D(("user %s has a `+' host entry", user));
278         if (opts->opt_promiscuous)
279             return (1);                     /* asking for trouble, but ok.. */
280         /* If not promiscuous: handle as negative */
281         return (-1);
282     } else if (strncmp("+",lhost,1) == 0) {
283         /* '+hostname' is supposed to be equivalent to 'hostname' */
284         lhost++;
285     }
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 = _pammodutil_getpwnam(pamh, 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 = _pammodutil_getgrgid(pamh, 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 = NULL;
661     const char *ruser = NULL, *rhost = NULL;
662     struct _options opts;
663     int as_root = 0;
664
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         D(("getting rhost"));
677         retval = pam_get_rhost(pamh, &rhost, NULL);
678         (void) pam_set_item(pamh, PAM_RHOST, rhost);
679         if (retval != PAM_SUCCESS) {
680             if (opts.opt_debug) {
681                 _pam_log(LOG_DEBUG, "could not get the remote host name");
682             }
683             break;
684         }
685
686         /* get the remote user */
687         D(("getting ruser"));
688         retval = pam_get_ruser(pamh, &ruser, NULL);
689         (void) pam_set_item(pamh, PAM_RUSER, ruser);
690         if (retval != PAM_SUCCESS) {
691             if (opts.opt_debug)
692                 _pam_log(LOG_DEBUG, "could not get the remote username");
693             break;
694         }
695
696         /* get the local user */
697         D(("getting user"));
698         retval = pam_get_user(pamh, &luser, NULL);
699         if (retval != PAM_SUCCESS) {
700             if (opts.opt_debug)
701                 _pam_log(LOG_DEBUG, "could not determine name of local user");
702             break;
703         }
704
705         if (opts.superuser && !strcmp(opts.superuser, luser)) {
706             as_root = 1;
707         }
708
709         /* check if the luser uid == 0... --cristiang */
710         if (! opts.opt_no_uid_check) {
711             struct passwd *luser_pwd;
712
713             luser_pwd = _pammodutil_getpwnam(pamh, luser);
714             if (luser_pwd == NULL) {
715                 if (opts.opt_debug)
716                     _pam_log(LOG_DEBUG, "user '%s' unknown to this system",
717                              luser);
718                 retval = PAM_AUTH_ERR;
719                 break;
720             }
721             if (luser_pwd->pw_uid == 0)
722                 as_root = 1;
723             luser_pwd = NULL;                                   /* forget */
724         }
725 /*
726  * Validate the account information.
727  */
728         if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) {
729             if ( !opts.opt_suppress ) {
730                 _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s",
731                          ruser, rhost, luser, (opts.last_error==NULL) ?
732                          "access not allowed":opts.last_error);
733             }
734             retval = PAM_AUTH_ERR;
735         } else {
736             _pam_log(LOG_NOTICE, "allowed to %s@%s as %s",
737                      ruser, rhost, luser);
738         }
739         break;
740     }
741
742     return retval;
743 }
744
745 /* --- authentication management functions --- */
746
747 PAM_EXTERN
748 int pam_sm_authenticate (pam_handle_t *pamh, 
749                          int flags,
750                          int argc, 
751                          const char **argv)
752 {
753     int retval;
754
755     if (sizeof(U32) != 4) {
756         _pam_log (LOG_ALERT, "pam_rhosts module can\'t work on this hardware "
757                   "(yet)");
758         return PAM_AUTH_ERR;
759     }
760     sethostent(1);
761     retval = _pam_auth_rhosts (pamh, flags, argc, argv);
762     endhostent();
763     return retval;
764 }
765
766 PAM_EXTERN
767 int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
768                    const char **argv)
769 {
770     return PAM_SUCCESS;
771 }
772
773 /* end of module definition */
774
775
776 #ifdef PAM_STATIC
777
778 /* static module data */
779
780 struct pam_module _pam_rhosts_auth_modstruct = {
781     "pam_rhosts_auth",
782     pam_sm_authenticate,
783     pam_sm_setcred,
784     NULL,
785     NULL,
786     NULL,
787     NULL,
788 };
789
790 #endif