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

« back to all changes in this revision

Viewing changes to Linux-PAM/modules/pam_group/pam_group.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
/* pam_group module */
 
2
 
 
3
/*
 
4
 * $Id: pam_group.c,v 1.7 2004/01/12 06:47:14 hartmans Exp $
 
5
 *
 
6
 * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
 
7
 */
 
8
 
 
9
const static char rcsid[] =
 
10
"$Id: pam_group.c,v 1.7 2004/01/12 06:47:14 hartmans Exp $;\n"
 
11
"Version 0.5 for Linux-PAM\n"
 
12
"Copyright (c) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n";
 
13
 
 
14
#define _BSD_SOURCE
 
15
 
 
16
#include <sys/file.h>
 
17
#include <stdio.h>
 
18
#include <stdlib.h>
 
19
#include <ctype.h>
 
20
#include <unistd.h>
 
21
#include <stdarg.h>
 
22
#include <time.h>
 
23
#include <syslog.h>
 
24
#include <string.h>
 
25
 
 
26
#include <grp.h>
 
27
#include <sys/types.h>
 
28
#include <sys/stat.h>
 
29
#include <fcntl.h>
 
30
 
 
31
#ifdef DEFAULT_CONF_FILE
 
32
# define PAM_GROUP_CONF         DEFAULT_CONF_FILE /* from external define */
 
33
#else
 
34
# define PAM_GROUP_CONF         "/etc/security/group.conf"
 
35
#endif
 
36
#define PAM_GROUP_BUFLEN        1000
 
37
#define FIELD_SEPARATOR         ';'   /* this is new as of .02 */
 
38
 
 
39
#ifdef TRUE 
 
40
# undef TRUE 
 
41
#endif 
 
42
#ifdef FALSE 
 
43
# undef FALSE 
 
44
#endif
 
45
 
 
46
typedef enum { FALSE, TRUE } boolean;
 
47
typedef enum { AND, OR } operator;
 
48
 
 
49
/*
 
50
 * here, we make definitions for the externally accessible functions
 
51
 * in this file (these definitions are required for static modules
 
52
 * but strongly encouraged generally) they are used to instruct the
 
53
 * modules include file to define their prototypes.
 
54
 */
 
55
 
 
56
#define PAM_SM_AUTH
 
57
 
 
58
#include <security/pam_modules.h>
 
59
#include <security/_pam_macros.h>
 
60
 
 
61
/* --- static functions for checking whether the user should be let in --- */
 
62
 
 
63
static void _log_err(const char *format, ... )
 
64
{
 
65
    va_list args;
 
66
 
 
67
    va_start(args, format);
 
68
    openlog("pam_group", LOG_CONS|LOG_PID, LOG_AUTH);
 
69
    vsyslog(LOG_CRIT, format, args);
 
70
    va_end(args);
 
71
    closelog();
 
72
}
 
73
 
 
74
static void shift_bytes(char *mem, int from, int by)
 
75
{
 
76
    while (by-- > 0) {
 
77
        *mem = mem[from];
 
78
        ++mem;
 
79
    }
 
80
}
 
81
 
 
82
static int read_field(int fd, char **buf, int *from, int *to)
 
83
{
 
84
    /* is buf set ? */
 
85
 
 
86
    if (! *buf) {
 
87
        *buf = (char *) malloc(PAM_GROUP_BUFLEN);
 
88
        if (! *buf) {
 
89
            _log_err("out of memory");
 
90
            return -1;
 
91
        }
 
92
        *from = *to = 0;
 
93
        fd = open(PAM_GROUP_CONF, O_RDONLY);
 
94
    }
 
95
 
 
96
    /* do we have a file open ? return error */
 
97
 
 
98
    if (fd < 0 && *to <= 0) {
 
99
        _log_err( PAM_GROUP_CONF " not opened");
 
100
        memset(*buf, 0, PAM_GROUP_BUFLEN);
 
101
        _pam_drop(*buf);
 
102
        return -1;
 
103
    }
 
104
 
 
105
    /* check if there was a newline last time */
 
106
 
 
107
    if ((*to > *from) && (*to > 0)
 
108
        && ((*buf)[*from] == '\0')) {   /* previous line ended */
 
109
        (*from)++;
 
110
        (*buf)[0] = '\0';
 
111
        return fd;
 
112
    }
 
113
 
 
114
    /* ready for more data: first shift the buffer's remaining data */
 
115
 
 
116
    *to -= *from;
 
117
    shift_bytes(*buf, *from, *to);
 
118
    *from = 0;
 
119
    (*buf)[*to] = '\0';
 
120
 
 
121
    while (fd >= 0 && *to < PAM_GROUP_BUFLEN) {
 
122
        int i;
 
123
 
 
124
        /* now try to fill the remainder of the buffer */
 
125
 
 
126
        i = read(fd, *to + *buf, PAM_GROUP_BUFLEN - *to);
 
127
        if (i < 0) {
 
128
            _log_err("error reading " PAM_GROUP_CONF);
 
129
            return -1;
 
130
        } else if (!i) {
 
131
            close(fd);
 
132
            fd = -1;          /* end of file reached */
 
133
        } else
 
134
            *to += i;
 
135
    
 
136
        /*
 
137
         * contract the buffer. Delete any comments, and replace all
 
138
         * multiple spaces with single commas
 
139
         */
 
140
 
 
141
        i = 0;
 
142
#ifdef DEBUG_DUMP
 
143
        D(("buffer=<%s>",*buf));
 
144
#endif
 
145
        while (i < *to) {
 
146
            int j, c = 0;
 
147
            if ((*buf)[i] == ',') {
 
148
                for (j=++i; j<*to && (*buf)[j] == ','; ++j);
 
149
                if (j!=i) {
 
150
                    shift_bytes(i + (*buf), j-i, (*to) - j);
 
151
                    *to -= j-i;
 
152
                }
 
153
            }
 
154
            switch ((*buf)[i]) {
 
155
            case '#':
 
156
                for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
 
157
                if (j >= *to) {
 
158
                    (*buf)[*to = ++i] = '\0';
 
159
                } else if (c == '\n') {
 
160
                    shift_bytes(i + (*buf), j-i, (*to) - j);
 
161
                    *to -= j-i;
 
162
                    ++i;
 
163
                } else {
 
164
                    _log_err("internal error in " __FILE__
 
165
                             " at line %d", __LINE__ );
 
166
                    return -1;
 
167
                }
 
168
                break;
 
169
            case '\\':
 
170
                if ((*buf)[i+1] == '\n') {
 
171
                    shift_bytes(i + *buf, 2, *to - (i+2));
 
172
                    *to -= 2;
 
173
                } else {
 
174
                    ++i;   /* we don't escape non-newline characters */
 
175
                }
 
176
                break;
 
177
            case '!':
 
178
            case ' ':
 
179
            case '\t':
 
180
                if ((*buf)[i] != '!')
 
181
                    (*buf)[i] = ',';
 
182
                /* delete any trailing spaces */
 
183
                for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
 
184
                                         || c == '\t' ); ++j);
 
185
                shift_bytes(i + *buf, j-i, (*to)-j );
 
186
                *to -= j-i;
 
187
                break;
 
188
            default:
 
189
                ++i;
 
190
            }
 
191
        }
 
192
    }
 
193
 
 
194
    (*buf)[*to] = '\0';
 
195
 
 
196
    /* now return the next field (set the from/to markers) */
 
197
    {
 
198
        int i;
 
199
 
 
200
        for (i=0; i<*to; ++i) {
 
201
            switch ((*buf)[i]) {
 
202
            case '#':
 
203
            case '\n':               /* end of the line/file */
 
204
                (*buf)[i] = '\0';
 
205
                *from = i;
 
206
                return fd;
 
207
            case FIELD_SEPARATOR:    /* end of the field */
 
208
                (*buf)[i] = '\0';
 
209
            *from = ++i;
 
210
            return fd;
 
211
            }
 
212
        }
 
213
        *from = i;
 
214
        (*buf)[*from] = '\0';
 
215
    }
 
216
 
 
217
    if (*to <= 0) {
 
218
        D(("[end of text]"));
 
219
        *buf = NULL;
 
220
    }
 
221
    return fd;
 
222
}
 
223
 
 
224
/* read a member from a field */
 
225
 
 
226
static int logic_member(const char *string, int *at)
 
227
{
 
228
     int len,c,to;
 
229
     int done=0;
 
230
     int token=0;
 
231
 
 
232
     len=0;
 
233
     to=*at;
 
234
     do {
 
235
          c = string[to++];
 
236
 
 
237
          switch (c) {
 
238
 
 
239
          case '\0':
 
240
               --to;
 
241
               done = 1;
 
242
               break;
 
243
 
 
244
          case '&':
 
245
          case '|':
 
246
          case '!':
 
247
               if (token) {
 
248
                    --to;
 
249
               }
 
250
               done = 1;
 
251
               break;
 
252
 
 
253
          default:
 
254
               if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
 
255
                    || c == '-' || c == '.' || c == '/'
 
256
                   || c == ':') {
 
257
                    token = 1;
 
258
               } else if (token) {
 
259
                    --to;
 
260
                    done = 1;
 
261
               } else {
 
262
                    ++*at;
 
263
               }
 
264
          }
 
265
     } while (!done);
 
266
 
 
267
     return to - *at;
 
268
}
 
269
 
 
270
typedef enum { VAL, OP } expect;
 
271
 
 
272
static boolean logic_field(const void *me, const char *x, int rule,
 
273
                           boolean (*agrees)(const void *, const char *
 
274
                                             , int, int))
 
275
{
 
276
     boolean left=FALSE, right, not=FALSE;
 
277
     operator oper=OR;
 
278
     int at=0, l;
 
279
     expect next=VAL;
 
280
 
 
281
     while ((l = logic_member(x,&at))) {
 
282
          int c = x[at];
 
283
 
 
284
          if (next == VAL) {
 
285
               if (c == '!')
 
286
                    not = !not;
 
287
               else if (isalnum(c) || c == '*' || c == ':' || c == '/') {
 
288
                    right = not ^ agrees(me, x+at, l, rule);
 
289
                    if (oper == AND)
 
290
                         left &= right;
 
291
                    else
 
292
                         left |= right;
 
293
                    next = OP;
 
294
               } else {
 
295
                    _log_err("garbled syntax; expected name (rule #%d)", rule);
 
296
                    return FALSE;
 
297
               }
 
298
          } else {   /* OP */
 
299
               switch (c) {
 
300
               case '&':
 
301
                    oper = AND;
 
302
                    break;
 
303
               case '|':
 
304
                    oper = OR;
 
305
                    break;
 
306
               default:
 
307
                    _log_err("garbled syntax; expected & or | (rule #%d)"
 
308
                             , rule);
 
309
                    D(("%c at %d",c,at));
 
310
                    return FALSE;
 
311
               }
 
312
               next = VAL;
 
313
          }
 
314
          at += l;
 
315
     }
 
316
 
 
317
     return left;
 
318
}
 
319
 
 
320
static boolean is_same(const void *A, const char *b, int len, int rule)
 
321
{
 
322
     int i;
 
323
     const char *a;
 
324
 
 
325
     a = A;
 
326
     for (i=0; len > 0; ++i, --len) {
 
327
          if (b[i] != a[i]) {
 
328
               if (b[i++] == '*') {
 
329
                    return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
 
330
               } else
 
331
                    return FALSE;
 
332
          }
 
333
     }
 
334
     return ( !len );
 
335
}
 
336
 
 
337
typedef struct {
 
338
     int day;             /* array of 7 bits, one set for today */
 
339
     int minute;            /* integer, hour*100+minute for now */
 
340
} TIME;
 
341
 
 
342
struct day {
 
343
     const char *d;
 
344
     int bit;
 
345
} static const days[11] = {
 
346
     { "su", 01 },
 
347
     { "mo", 02 },
 
348
     { "tu", 04 },
 
349
     { "we", 010 },
 
350
     { "th", 020 },
 
351
     { "fr", 040 },
 
352
     { "sa", 0100 },
 
353
     { "wk", 076 },
 
354
     { "wd", 0101 },
 
355
     { "al", 0177 },
 
356
     { NULL, 0 }
 
357
};
 
358
 
 
359
static TIME time_now(void)
 
360
{
 
361
     struct tm *local;
 
362
     time_t the_time;
 
363
     TIME this;
 
364
 
 
365
     the_time = time((time_t *)0);                /* get the current time */
 
366
     local = localtime(&the_time);
 
367
     this.day = days[local->tm_wday].bit;
 
368
     this.minute = local->tm_hour*100 + local->tm_min;
 
369
 
 
370
     D(("day: 0%o, time: %.4d", this.day, this.minute));
 
371
     return this;
 
372
}
 
373
 
 
374
/* take the current date and see if the range "date" passes it */
 
375
static boolean check_time(const void *AT, const char *times, int len, int rule)
 
376
{
 
377
     boolean not,pass;
 
378
     int marked_day, time_start, time_end;
 
379
     const TIME *at;
 
380
     int i,j=0;
 
381
 
 
382
     at = AT;
 
383
     D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times));
 
384
 
 
385
     if (times == NULL) {
 
386
          /* this should not happen */
 
387
          _log_err("internal error: " __FILE__ " line %d", __LINE__);
 
388
          return FALSE;
 
389
     }
 
390
 
 
391
     if (times[j] == '!') {
 
392
          ++j;
 
393
          not = TRUE;
 
394
     } else {
 
395
          not = FALSE;
 
396
     }
 
397
 
 
398
     for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
 
399
          int this_day=-1;
 
400
 
 
401
          D(("%c%c ?", times[j], times[j+1]));
 
402
          for (i=0; days[i].d != NULL; ++i) {
 
403
               if (tolower(times[j]) == days[i].d[0]
 
404
                   && tolower(times[j+1]) == days[i].d[1] ) {
 
405
                    this_day = days[i].bit;
 
406
                    break;
 
407
               }
 
408
          }
 
409
          j += 2;
 
410
          if (this_day == -1) {
 
411
               _log_err("bad day specified (rule #%d)", rule);
 
412
               return FALSE;
 
413
          }
 
414
          marked_day ^= this_day;
 
415
     }
 
416
     if (marked_day == 0) {
 
417
          _log_err("no day specified");
 
418
          return FALSE;
 
419
     }
 
420
     D(("day range = 0%o", marked_day));
 
421
 
 
422
     time_start = 0;
 
423
     for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
 
424
          time_start *= 10;
 
425
          time_start += times[i+j]-'0';       /* is this portable? */
 
426
     }
 
427
     j += i;
 
428
 
 
429
     if (times[j] == '-') {
 
430
          time_end = 0;
 
431
          for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
 
432
               time_end *= 10;
 
433
               time_end += times[i+j]-'0';    /* is this portable? */
 
434
          }
 
435
          j += i;
 
436
     } else
 
437
          time_end = -1;
 
438
 
 
439
     D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
 
440
     if (i != 5 || time_end == -1) {
 
441
          _log_err("no/bad times specified (rule #%d)", rule);
 
442
          return TRUE;
 
443
     }
 
444
     D(("times(%d to %d)", time_start,time_end));
 
445
     D(("marked_day = 0%o", marked_day));
 
446
 
 
447
     /* compare with the actual time now */
 
448
 
 
449
     pass = FALSE;
 
450
     if (time_start < time_end) {    /* start < end ? --> same day */
 
451
          if ((at->day & marked_day) && (at->minute >= time_start)
 
452
              && (at->minute < time_end)) {
 
453
               D(("time is listed"));
 
454
               pass = TRUE;
 
455
          }
 
456
     } else {                                    /* spans two days */
 
457
          if ((at->day & marked_day) && (at->minute >= time_start)) {
 
458
               D(("caught on first day"));
 
459
               pass = TRUE;
 
460
          } else {
 
461
               marked_day <<= 1;
 
462
               marked_day |= (marked_day & 0200) ? 1:0;
 
463
               D(("next day = 0%o", marked_day));
 
464
               if ((at->day & marked_day) && (at->minute <= time_end)) {
 
465
                    D(("caught on second day"));
 
466
                    pass = TRUE;
 
467
               }
 
468
          }
 
469
     }
 
470
 
 
471
     return (not ^ pass);
 
472
}
 
473
 
 
474
static int find_member(const char *string, int *at)
 
475
{
 
476
     int len,c,to;
 
477
     int done=0;
 
478
     int token=0;
 
479
 
 
480
     len=0;
 
481
     to=*at;
 
482
     do {
 
483
          c = string[to++];
 
484
 
 
485
          switch (c) {
 
486
 
 
487
          case '\0':
 
488
               --to;
 
489
               done = 1;
 
490
               break;
 
491
 
 
492
          case '&':
 
493
          case '|':
 
494
          case '!':
 
495
               if (token) {
 
496
                    --to;
 
497
               }
 
498
               done = 1;
 
499
               break;
 
500
 
 
501
          default:
 
502
               if (isalpha(c) || isdigit(c) || c == '_' || c == '*'
 
503
                    || c == '-') {
 
504
                    token = 1;
 
505
               } else if (token) {
 
506
                    --to;
 
507
                    done = 1;
 
508
               } else {
 
509
                    ++*at;
 
510
               }
 
511
          }
 
512
     } while (!done);
 
513
 
 
514
     return to - *at;
 
515
}
 
516
 
 
517
#define GROUP_BLK 10
 
518
#define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK)
 
519
 
 
520
static int mkgrplist(char *buf, gid_t **list, int len)
 
521
{
 
522
     int l,at=0;
 
523
     int blks;
 
524
 
 
525
     blks = blk_size(len);
 
526
     D(("cf. blks=%d and len=%d", blks,len));
 
527
 
 
528
     while ((l = find_member(buf,&at))) {
 
529
          int edge;
 
530
 
 
531
          if (len >= blks) {
 
532
               gid_t *tmp;
 
533
 
 
534
               D(("allocating new block"));
 
535
               tmp = (gid_t *) realloc((*list)
 
536
                                       , sizeof(gid_t) * (blks += GROUP_BLK));
 
537
               if (tmp != NULL) {
 
538
                    (*list) = tmp;
 
539
               } else {
 
540
                    _log_err("out of memory for group list");
 
541
                    free(*list);
 
542
                    (*list) = NULL;
 
543
                    return -1;
 
544
               }
 
545
          }
 
546
 
 
547
          /* '\0' terminate the entry */
 
548
 
 
549
          edge = (buf[at+l]) ? 1:0;
 
550
          buf[at+l] = '\0';
 
551
          D(("found group: %s",buf+at));
 
552
 
 
553
          /* this is where we convert a group name to a gid_t */
 
554
#ifdef WANT_PWDB
 
555
          {
 
556
              int retval;
 
557
              const struct pwdb *pw=NULL;
 
558
 
 
559
              retval = pwdb_locate("group", PWDB_DEFAULT, buf+at
 
560
                                   , PWDB_ID_UNKNOWN, &pw);
 
561
              if (retval != PWDB_SUCCESS) {
 
562
                  _log_err("bad group: %s; %s", buf+at, pwdb_strerror(retval));
 
563
              } else {
 
564
                  const struct pwdb_entry *pwe=NULL;
 
565
 
 
566
                  D(("group %s exists", buf+at));
 
567
                  retval = pwdb_get_entry(pw, "gid", &pwe);
 
568
                  if (retval == PWDB_SUCCESS) {
 
569
                      D(("gid = %d [%p]",* (const gid_t *) pwe->value,list));
 
570
                      (*list)[len++] = * (const gid_t *) pwe->value;
 
571
                      pwdb_entry_delete(&pwe);                  /* tidy up */
 
572
                  } else {
 
573
                      _log_err("%s group entry is bad; %s"
 
574
                               , pwdb_strerror(retval));
 
575
                  }
 
576
                  pw = NULL;          /* break link - cached for later use */
 
577
              }
 
578
          }
 
579
#else
 
580
          {
 
581
              const struct group *grp;
 
582
 
 
583
              grp = getgrnam(buf+at);
 
584
              if (grp == NULL) {
 
585
                  _log_err("bad group: %s", buf+at);
 
586
              } else {
 
587
                  D(("group %s exists", buf+at));
 
588
                  (*list)[len++] = grp->gr_gid;
 
589
              }
 
590
          }
 
591
#endif
 
592
 
 
593
          /* next entry along */
 
594
 
 
595
          at += l + edge;
 
596
     }
 
597
     D(("returning with [%p/len=%d]->%p",list,len,*list));
 
598
     return len;
 
599
}
 
600
 
 
601
 
 
602
static int check_account(const char *service, const char *tty
 
603
     , const char *user)
 
604
{
 
605
    int from=0,to=0,fd=-1;
 
606
    char *buffer=NULL;
 
607
    int count=0;
 
608
    TIME here_and_now;
 
609
    int retval=PAM_SUCCESS;
 
610
    gid_t *grps;
 
611
    int no_grps;
 
612
 
 
613
    /*
 
614
     * first we get the current list of groups - the application
 
615
     * will have previously done an initgroups(), or equivalent.
 
616
     */
 
617
 
 
618
    D(("counting supplementary groups"));
 
619
    no_grps = getgroups(0, NULL);      /* find the current number of groups */
 
620
    if (no_grps > 0) {
 
621
        grps = calloc( blk_size(no_grps) , sizeof(gid_t) );
 
622
        D(("copying current list into grps [%d big]",blk_size(no_grps)));
 
623
        (void) getgroups(no_grps, grps);
 
624
#ifdef DEBUG
 
625
        {
 
626
            int z;
 
627
            for (z=0; z<no_grps; ++z) {
 
628
                D(("gid[%d]=%d", z, grps[z]));
 
629
            }
 
630
        }
 
631
#endif
 
632
    } else {
 
633
        D(("no supplementary groups known"));
 
634
        no_grps = 0;
 
635
        grps = NULL;
 
636
    }
 
637
 
 
638
    here_and_now = time_now();                         /* find current time */
 
639
 
 
640
    /* parse the rules in the configuration file */
 
641
    do {
 
642
        int good=TRUE;
 
643
 
 
644
        /* here we get the service name field */
 
645
 
 
646
        fd = read_field(fd,&buffer,&from,&to);
 
647
        if (!buffer || !buffer[0]) {
 
648
            /* empty line .. ? */
 
649
            continue;
 
650
        }
 
651
        ++count;
 
652
        D(("working on rule #%d",count));
 
653
 
 
654
        good = logic_field(service, buffer, count, is_same);
 
655
        D(("with service: %s", good ? "passes":"fails" ));
 
656
 
 
657
        /* here we get the terminal name field */
 
658
 
 
659
        fd = read_field(fd,&buffer,&from,&to);
 
660
        if (!buffer || !buffer[0]) {
 
661
            _log_err(PAM_GROUP_CONF "; no tty entry #%d", count);
 
662
            continue;
 
663
        }
 
664
        good &= logic_field(tty, buffer, count, is_same);
 
665
        D(("with tty: %s", good ? "passes":"fails" ));
 
666
 
 
667
        /* here we get the username field */
 
668
 
 
669
        fd = read_field(fd,&buffer,&from,&to);
 
670
        if (!buffer || !buffer[0]) {
 
671
            _log_err(PAM_GROUP_CONF "; no user entry #%d", count);
 
672
            continue;
 
673
        }
 
674
        good &= logic_field(user, buffer, count, is_same);
 
675
        D(("with user: %s", good ? "passes":"fails" ));
 
676
 
 
677
        /* here we get the time field */
 
678
 
 
679
        fd = read_field(fd,&buffer,&from,&to);
 
680
        if (!buffer || !buffer[0]) {
 
681
            _log_err(PAM_GROUP_CONF "; no time entry #%d", count);
 
682
            continue;
 
683
        }
 
684
 
 
685
        good &= logic_field(&here_and_now, buffer, count, check_time);
 
686
        D(("with time: %s", good ? "passes":"fails" ));
 
687
 
 
688
        fd = read_field(fd,&buffer,&from,&to);
 
689
        if (!buffer || !buffer[0]) {
 
690
            _log_err(PAM_GROUP_CONF "; no listed groups for rule #%d"
 
691
                     , count);
 
692
            continue;
 
693
        }
 
694
 
 
695
        /*
 
696
         * so we have a list of groups, we need to turn it into
 
697
         * something to send to setgroups(2)
 
698
         */
 
699
 
 
700
        if (good) {
 
701
            D(("adding %s to gid list", buffer));
 
702
            good = mkgrplist(buffer, &grps, no_grps);
 
703
            if (good < 0) {
 
704
                no_grps = 0;
 
705
            } else {
 
706
                no_grps = good;
 
707
            }
 
708
        }
 
709
 
 
710
        /* check the line is terminated correctly */
 
711
 
 
712
        fd = read_field(fd,&buffer,&from,&to);
 
713
        if (buffer && buffer[0]) {
 
714
            _log_err(PAM_GROUP_CONF "; poorly terminated rule #%d", count);
 
715
        }
 
716
 
 
717
        if (good > 0) {
 
718
            D(("rule #%d passed, added %d groups", count, good));
 
719
        } else if (good < 0) {
 
720
            retval = PAM_BUF_ERR;
 
721
        } else {
 
722
            D(("rule #%d failed", count));
 
723
        }
 
724
 
 
725
    } while (buffer);
 
726
 
 
727
    /* now set the groups for the user */
 
728
 
 
729
    if (no_grps > 0) {
 
730
        int err;
 
731
        D(("trying to set %d groups", no_grps));
 
732
#ifdef DEBUG
 
733
        for (err=0; err<no_grps; ++err) {
 
734
            D(("gid[%d]=%d", err, grps[err]));
 
735
        }
 
736
#endif
 
737
        if ((err = setgroups(no_grps, grps))) {
 
738
            D(("but couldn't set groups %d", err));
 
739
            _log_err("unable to set the group membership for user (err=%d)"
 
740
                     , err);
 
741
            retval = PAM_CRED_ERR;
 
742
        }
 
743
    }
 
744
 
 
745
    if (grps) {                                          /* tidy up */
 
746
        memset(grps, 0, sizeof(gid_t) * blk_size(no_grps));
 
747
        _pam_drop(grps);
 
748
        no_grps = 0;
 
749
    }
 
750
 
 
751
    return retval;
 
752
}
 
753
 
 
754
/* --- public authentication management functions --- */
 
755
 
 
756
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags
 
757
                                   , int argc, const char **argv)
 
758
{
 
759
    return PAM_IGNORE;
 
760
}
 
761
 
 
762
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
 
763
                              , int argc, const char **argv)
 
764
{
 
765
    const char *service=NULL, *tty=NULL;
 
766
    const char *user=NULL;
 
767
    int retval;
 
768
    unsigned setting;
 
769
 
 
770
    /* only interested in establishing credentials */
 
771
    /* PAM docs say that an empty flag is to be treated as PAM_ESTABLISH_CRED.
 
772
       Some people just pass PAM_SILENT, so cope with it, too. */
 
773
 
 
774
    setting = flags;
 
775
    if (!((setting & PAM_ESTABLISH_CRED) || (setting & PAM_REINITIALIZE_CRED)
 
776
          || (setting == 0) || (setting == PAM_SILENT) )) {
 
777
        D(("ignoring call - not for establishing credentials"));
 
778
        return PAM_SUCCESS;            /* don't fail because of this */
 
779
    }
 
780
 
 
781
    /* set service name */
 
782
 
 
783
    if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
 
784
        != PAM_SUCCESS || service == NULL) {
 
785
        _log_err("cannot find the current service name");
 
786
        return PAM_ABORT;
 
787
    }
 
788
 
 
789
    /* set username */
 
790
 
 
791
    if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
 
792
        || *user == '\0') {
 
793
        _log_err("cannot determine the user's name");
 
794
        return PAM_USER_UNKNOWN;
 
795
    }
 
796
 
 
797
    /* set tty name */
 
798
 
 
799
    if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
 
800
        || tty == NULL) {
 
801
        D(("PAM_TTY not set, probing stdin"));
 
802
        tty = ttyname(STDIN_FILENO);
 
803
        if (tty == NULL) {
 
804
            _log_err("couldn't get the tty name");
 
805
            return PAM_ABORT;
 
806
        }
 
807
        if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
 
808
            _log_err("couldn't set tty name");
 
809
            return PAM_ABORT;
 
810
        }
 
811
    }
 
812
 
 
813
    if (strncmp("/dev/",tty,5) == 0) {          /* strip leading /dev/ */
 
814
        tty += 5;
 
815
    }
 
816
 
 
817
    /* good, now we have the service name, the user and the terminal name */
 
818
 
 
819
    D(("service=%s", service));
 
820
    D(("user=%s", user));
 
821
    D(("tty=%s", tty));
 
822
 
 
823
#ifdef WANT_PWDB
 
824
 
 
825
    /* We initialize the pwdb library and check the account */
 
826
    retval = pwdb_start();                             /* initialize */
 
827
    if (retval == PWDB_SUCCESS) {
 
828
        retval = check_account(service,tty,user);      /* get groups */
 
829
        (void) pwdb_end();                                /* tidy up */
 
830
    } else {
 
831
        D(("failed to initialize pwdb; %s", pwdb_strerror(retval)));
 
832
        _log_err("unable to initialize libpwdb");
 
833
        retval = PAM_ABORT;
 
834
    }
 
835
 
 
836
#else /* WANT_PWDB */
 
837
    retval = check_account(service,tty,user);          /* get groups */
 
838
#endif /* WANT_PWDB */
 
839
 
 
840
    return retval;
 
841
}
 
842
 
 
843
/* end of module definition */
 
844
 
 
845
#ifdef PAM_STATIC
 
846
 
 
847
/* static module data */
 
848
 
 
849
struct pam_module _pam_group_modstruct = {
 
850
    "pam_group",
 
851
    pam_sm_authenticate,
 
852
    pam_sm_setcred,
 
853
    NULL,
 
854
    NULL,
 
855
    NULL,
 
856
    NULL
 
857
};
 
858
#endif