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