]> granicus.if.org Git - linux-pam/blob - modules/pam_rhosts/pam_rhosts_auth.c
Relevant BUGIDs: 490938
[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 #ifdef linux
78 # include <linux/sockios.h>
79 # ifndef __USE_MISC
80 #  define __USE_MISC
81 #  include <sys/fsuid.h>
82 # endif /* __USE_MISC */
83 #endif
84
85 #include <pwd.h>
86 #include <grp.h>
87 #include <sys/file.h>
88 #include <sys/signal.h>
89 #include <sys/stat.h>
90 #include <syslog.h>
91 #ifndef _PATH_HEQUIV
92 #define _PATH_HEQUIV "/etc/hosts.equiv"
93 #endif /* _PATH_HEQUIV */
94
95 #define PAM_SM_AUTH  /* only defines this management group */
96
97 #include <security/pam_modules.h>
98 #include <security/_pam_macros.h>
99 #include <security/_pam_modutil.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
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     } else if (strncmp("+",lhost,1) == 0) {
288         /* '+hostname' is supposed to be equivalent to 'hostname' */
289         lhost++;
290     }
291
292
293     /* Try for raw ip address first. */
294     if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
295         return (negate*(! (raddr ^ laddr)));
296
297     /* Better be a hostname. */
298     hp = gethostbyname(lhost);
299     if (hp == NULL)
300         return (0);
301     
302     /* Spin through ip addresses. */
303     for (pp = hp->h_addr_list; *pp; ++pp)
304         if (!memcmp (&raddr, *pp, sizeof (U32)))
305             return (negate);
306
307     /* No match. */
308     return (0);
309 }
310
311 /* Returns 1 on positive match, 0 on no match, -1 on negative match */
312
313 static int __icheckuser(pam_handle_t *pamh, struct _options *opts
314                         , const char *luser, const char *ruser
315                         , const char *rhost)
316 {
317     /*
318       luser is user entry from .rhosts/hosts.equiv file
319       ruser is user id on remote host
320       rhost is the remote host name
321       */
322     char *user;
323
324     /* [-+]@netgroup */
325     if (strncmp("+@",luser,2) == 0)
326         return (innetgr(&luser[2],NULL,ruser,NULL));
327
328     if (strncmp("-@",luser,2) == 0)
329         return (-innetgr(&luser[2],NULL,ruser,NULL));
330
331     /* -user */
332     if (strncmp("-",luser,1) == 0)
333         return(-(strcmp(&luser[1],ruser) == 0));
334
335     /* + */
336     if (strcmp("+",luser) == 0) {
337         (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
338         _pam_log(LOG_WARNING, "user %s has a `+' user entry", user);
339         if (opts->opt_promiscuous)
340             return(1);
341         /* If not promiscuous we handle it as a negative match */
342         return(-1);
343     }
344
345     /* simple string match */
346     return (strcmp(ruser, luser) == 0);
347 }
348
349 /*
350  * Returns 1 for blank lines (or only comment lines) and 0 otherwise
351  */
352
353 static int __isempty(char *p)
354 {
355     while (*p && isspace(*p)) {
356         ++p;
357     }
358
359     return (*p == '\0' || *p == '#') ? 1:0 ;
360 }
361
362 /*
363  * Returns 0 if positive match, 1 if _not_ ok.
364  */
365
366 static int
367 __ivaliduser (pam_handle_t *pamh, struct _options *opts,
368               FILE *hostf, U32 raddr,
369               const char *luser, const char *ruser, const char *rhost)
370 {
371     register const char *user;
372     register char *p;
373     int hcheck, ucheck;
374     char buf[MAXHOSTNAMELEN + 128];                       /* host + login */
375
376     buf[sizeof (buf)-1] = '\0';                         /* terminate line */
377
378     while (fgets(buf, sizeof(buf), hostf) != NULL) {   /* hostf file line */
379         p = buf;                              /* from beginning of file.. */
380
381         /* Skip empty or comment lines */
382         if (__isempty(p)) {
383             continue;
384         }
385
386         /* Skip lines that are too long. */
387         if (strchr(p, '\n') == NULL) {
388             int ch = getc(hostf);
389
390             while (ch != '\n' && ch != EOF)
391                 ch = getc(hostf);
392             continue;
393         }
394
395         /*
396          * If there is a hostname at the start of the line.  Set it to
397          * lower case. A leading ' ' or '\t' indicates no hostname
398          */
399
400         for (;*p && !isspace(*p); ++p) {
401             *p = tolower(*p);
402         }
403
404         /*
405          * next we want to find the permitted name for the remote user
406          */
407
408         if (*p == ' ' || *p == '\t') {
409
410             /* <nul> terminate hostname and skip spaces */
411             for (*p++='\0'; *p && isspace(*p); ++p);
412
413             user = p;                   /* this is the user's name */
414             while (*p && !isspace(*p))
415                 ++p;                    /* find end of user's name */
416         } else 
417             user = p;
418
419         *p = '\0';              /* <nul> terminate username (+host?) */
420
421         /* buf -> host(?) ; user -> username(?) */
422
423         /* First check host part */
424         hcheck=__icheckhost(pamh, opts, raddr, buf, rhost);
425
426         if (hcheck<0)
427             return(1);
428
429         if (hcheck) {
430             /* Then check user part */
431             if (! (*user))
432                 user = luser;
433
434             ucheck=__icheckuser(pamh, opts, user, ruser, rhost);
435
436             /* Positive 'host user' match? */
437             if (ucheck>0)
438                 return(0);
439
440             /* Negative 'host -user' match? */
441             if (ucheck<0)
442                 return(1);
443
444             /* Neither, go on looking for match */
445         }
446     }
447
448     return (1);
449 }
450
451 /*
452  * New .rhosts strategy: We are passed an ip address. We spin through
453  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
454  * has ip addresses, we don't have to trust a nameserver.  When it
455  * contains hostnames, we spin through the list of addresses the nameserver
456  * gives us and look for a match.
457  *
458  * Returns 0 if ok, -1 if not ok.
459  */
460
461 static int
462 pam_iruserok(pam_handle_t *pamh,
463          struct _options *opts, U32 raddr, int superuser,
464          const char *ruser, const char *luser, const char *rhost)
465 {
466     const char *cp;
467     struct stat sbuf;
468     struct passwd *pwd;
469     FILE *hostf;
470     uid_t uid;
471     int answer;
472     char pbuf[MAXPATHLEN];               /* potential buffer overrun */
473
474     if ((!superuser||opts->opt_hosts_equiv_rootok) && !opts->opt_no_hosts_equiv ) {
475
476         /* try to open system hosts.equiv file */
477         hostf = fopen (_PATH_HEQUIV, "r");
478         if (hostf) {
479             answer = __ivaliduser(pamh, opts, hostf, raddr, luser
480                                   , ruser, rhost);
481             (void) fclose(hostf);
482             if (answer == 0)
483                 return 0;      /* remote host is equivalent to localhost */
484         } /* else {
485             No hosts.equiv file on system.
486         } */
487     }
488     
489     if ( opts->opt_no_rhosts )
490         return 1;
491
492     /*
493      * Identify user's local .rhosts file
494      */
495
496     pwd = _pammodutil_getpwnam(pamh, luser);
497     if (pwd == NULL) {
498         /* 
499          * luser is assumed to be valid because of an earlier check for uid = 0
500          * we don't log this error twice. However, this shouldn't happen !
501          * --cristiang 
502          */
503         return(1);
504     }
505
506     /* check for buffer overrun */
507     if (strlen(pwd->pw_dir) + sizeof(USER_RHOSTS_FILE) + 2 >= MAXPATHLEN) {
508         if (opts->opt_debug)
509             _pam_log(LOG_DEBUG,"home directory for `%s' is too long", luser);
510         return 1;                               /* to dangerous to try */
511     }
512
513     (void) strcpy(pbuf, pwd->pw_dir);
514     (void) strcat(pbuf, USER_RHOSTS_FILE);
515
516     /*
517      * Change effective uid while _reading_ .rhosts. (not just
518      * opening).  If root and reading an NFS mounted file system,
519      * can't read files that are 0600 as .rhosts files should be.
520      */
521
522     /* We are root, this will not fail */
523 #ifdef linux
524     /* If we are on linux the better way is setfsuid */
525     uid = setfsuid(pwd->pw_uid);
526     hostf = fopen(pbuf, "r");
527 #else
528     uid = geteuid();
529     (void) seteuid(pwd->pw_uid);
530     hostf = fopen(pbuf, "r");
531 #endif
532
533     if (hostf == NULL) {
534         if (opts->opt_debug)
535             _pam_log(LOG_DEBUG,"Could not open %s file",pbuf);
536         answer = 1;
537         goto exit_function;
538     }
539
540     /*
541      * If not a regular file, or is owned by someone other than
542      * user or root or if writeable by anyone but the owner, quit.
543      */
544
545     cp = NULL;
546     if (lstat(pbuf, &sbuf) < 0 || !S_ISREG(sbuf.st_mode))
547         cp = ".rhosts not regular file";
548     else if (fstat(fileno(hostf), &sbuf) < 0)
549         cp = ".rhosts fstat failed";
550     else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
551         cp = "bad .rhosts owner";
552     else if (sbuf.st_mode & S_IWOTH)
553         cp = ".rhosts writable by other!";
554     else if (sbuf.st_mode & S_IWGRP) {
555
556         /* private group caveat */
557         if (opts->opt_private_group) {
558             struct group *grp = getgrgid(sbuf.st_gid);
559
560             if (NULL == grp || NULL == grp->gr_name
561                 || strcmp(luser,grp->gr_name)) {
562                 cp = ".rhosts writable by public group";
563             } else if (grp->gr_mem) {
564                 int gcount;
565
566                 /* require at most one member (luser) of this group */
567                 for (gcount=0; grp->gr_mem[gcount]; ++gcount) {
568                     if (strcmp(grp->gr_mem[gcount], luser)) {
569                         gcount = -1;
570                         break;
571                     }
572                 }
573                 if (gcount < 0) {
574                     cp = ".rhosts writable by other members of group";
575                 }
576             }
577         } else {
578             cp = ".rhosts writable by group";
579         }
580
581     } /* It is _NOT_ safe to append an else here...  Do so prior to
582        * S_IWGRP check */
583
584     /* If there were any problems, quit. */
585     if (cp) {
586         opts->last_error = cp;
587         answer = 1;
588         goto exit_function;
589     }
590
591     answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost);
592
593 exit_function:
594     /*
595      * Go here to exit after the fsuid/euid has been adjusted so that
596      * they are reset before we exit.
597      */
598
599 #ifdef linux
600     setfsuid(uid);
601 #else
602     (void)seteuid(uid);
603 #endif
604
605     if (hostf != NULL)
606         (void) fclose(hostf);
607
608     return answer;
609 }
610
611 static int
612 pam_ruserok (pam_handle_t *pamh,
613              struct _options *opts, const char *rhost, int superuser,
614              const char *ruser, const char *luser)
615 {
616     struct hostent *hp;
617     int answer = 1;                             /* default to failure */
618     U32 *addrs;
619     int n, i;
620
621     opts->last_error = (char *) 0;
622     hp               = gethostbyname(rhost);         /* identify host */
623
624     if (hp != NULL) {
625         /* First of all check the address length */
626         if (hp->h_length != 4) {
627             _pam_log(LOG_ALERT, "pam_rhosts module can't work with not IPv4 "
628                      "addresses");
629             return 1;                                    /* not allowed */
630         }
631
632         /* loop though address list */
633         for (n = 0; hp->h_addr_list[n]; n++);
634         D(("rhosts: %d addresses", n));
635
636         if (n) {
637             addrs = calloc (n, hp->h_length);
638             for (i = 0; i < n; i++)
639                 memcpy (addrs+i, hp->h_addr_list[i], hp->h_length);
640
641             for (i = 0; i < n && answer; i++) {
642                 D(("rhosts: address %d is %04x", i, addrs[i]));
643                 answer = pam_iruserok(pamh, opts, addrs[i], superuser,
644                                       ruser, luser, rhost);
645                          /* answer == 0 means success */
646             }
647
648             free (addrs);
649         }
650     }
651
652     return answer;
653 }
654
655 /*
656  * Internal function to do authentication
657  */
658
659 static int _pam_auth_rhosts (pam_handle_t *pamh,
660                              int flags, 
661                              int argc,
662                              const char **argv) 
663 {
664     int retval;
665     const char *luser = NULL;
666     const char *ruser = NULL, *rhost = NULL;
667     struct _options opts;
668     int as_root = 0;
669
670     /*
671      * Look at the options and set the flags accordingly.
672      */
673     memset (&opts, 0, sizeof (opts));
674     set_parameters (&opts, flags, argc, argv);
675     /*
676      * Obtain the parameters for the various items
677      */
678     for (;;) {                         /* abuse loop to avoid goto */
679
680         /* get the remotehost */
681         D(("getting rhost"));
682         retval = pam_get_rhost(pamh, &rhost, NULL);
683         (void) pam_set_item(pamh, PAM_RHOST, rhost);
684         if (retval != PAM_SUCCESS) {
685             if (opts.opt_debug) {
686                 _pam_log(LOG_DEBUG, "could not get the remote host name");
687             }
688             break;
689         }
690
691         /* get the remote user */
692         D(("getting ruser"));
693         retval = pam_get_ruser(pamh, &ruser, NULL);
694         (void) pam_set_item(pamh, PAM_RUSER, ruser);
695         if (retval != PAM_SUCCESS) {
696             if (opts.opt_debug)
697                 _pam_log(LOG_DEBUG, "could not get the remote username");
698             break;
699         }
700
701         /* get the local user */
702         D(("getting user"));
703         retval = pam_get_user(pamh, &luser, NULL);
704         if (retval != PAM_SUCCESS) {
705             if (opts.opt_debug)
706                 _pam_log(LOG_DEBUG, "could not determine name of local user");
707             break;
708         }
709
710         if (opts.superuser && !strcmp(opts.superuser, luser)) {
711             as_root = 1;
712         }
713
714         /* check if the luser uid == 0... --cristiang */
715         if (! opts.opt_no_uid_check) {
716             struct passwd *luser_pwd;
717
718             luser_pwd = _pammodutil_getpwnam(pamh, luser);
719             if (luser_pwd == NULL) {
720                 if (opts.opt_debug)
721                     _pam_log(LOG_DEBUG, "user '%s' unknown to this system",
722                              luser);
723                 retval = PAM_AUTH_ERR;
724                 break;
725             }
726             if (luser_pwd->pw_uid == 0)
727                 as_root = 1;
728             luser_pwd = NULL;                                   /* forget */
729         }
730 /*
731  * Validate the account information.
732  */
733         if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) {
734             if ( !opts.opt_suppress ) {
735                 _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s",
736                          ruser, rhost, luser, (opts.last_error==NULL) ?
737                          "access not allowed":opts.last_error);
738             }
739             retval = PAM_AUTH_ERR;
740         } else {
741             _pam_log(LOG_NOTICE, "allowed to %s@%s as %s",
742                      ruser, rhost, luser);
743         }
744         break;
745     }
746
747     return retval;
748 }
749
750 /* --- authentication management functions --- */
751
752 PAM_EXTERN
753 int pam_sm_authenticate (pam_handle_t *pamh, 
754                          int flags,
755                          int argc, 
756                          const char **argv)
757 {
758     int retval;
759
760     if (sizeof(U32) != 4) {
761         _pam_log (LOG_ALERT, "pam_rhosts module can\'t work on this hardware "
762                   "(yet)");
763         return PAM_AUTH_ERR;
764     }
765     sethostent(1);
766     retval = _pam_auth_rhosts (pamh, flags, argc, argv);
767     endhostent();
768     return retval;
769 }
770
771 PAM_EXTERN
772 int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
773                    const char **argv)
774 {
775     return PAM_SUCCESS;
776 }
777
778 /* end of module definition */
779
780
781 #ifdef PAM_STATIC
782
783 /* static module data */
784
785 struct pam_module _pam_rhosts_auth_modstruct = {
786     "pam_rhosts_auth",
787     pam_sm_authenticate,
788     pam_sm_setcred,
789     NULL,
790     NULL,
791     NULL,
792     NULL,
793 };
794
795 #endif