~ubuntu-branches/ubuntu/breezy/pam/breezy

« back to all changes in this revision

Viewing changes to Linux-PAM/modules/pam_rhosts/pam_rhosts_auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman
  • Date: 2004-06-28 14:28:08 UTC
  • mfrom: (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040628142808-adikk7vtfg3pzcjw
Tags: 0.76-22
* Add uploaders
* Document location of repository
* Fix options containing arguments in pam_unix, Closes: #254904

Show diffs side-by-side

added added

removed removed

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