File: mod_auth_shadow.c

package info (click to toggle)
mod-auth-shadow 1.4-1sarge1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 108 kB
  • ctags: 35
  • sloc: ansic: 390; makefile: 74; sh: 40
file content (358 lines) | stat: -rw-r--r-- 10,794 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*
 * mod_auth_shadow.c
 *
 * An apache module to authenticate using the /etc/shadow file.
 * This module interacts with another program "validate", which
 * is setuid root.  Thus the /etc/shadow file can remain 
 * root:root 0400.
 *
 * Author: Brian Duggan <bduggan@oven.com>
 * Some code was taken from the sample code supplied with
 * _Apache Modules_ by Stein and MacEachern.  Parts of this
 * were also influenced by mod_auth.c.
 */

#include "httpd.h"  
#include "http_config.h"  
#include "http_request.h"  
#include "http_protocol.h"  
#include "http_core.h"  
#include "http_main.h" 
#include "http_log.h"  
#include <shadow.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <grp.h>
#include "validate.h"

static char* module_name = "mod_auth_shadow";

#ifndef INSTBINDIR
#error  INSTBINDIR should be defined in the makefile, as the location of the 'validate' executable
#endif

/* do not accept empty "" strings */
#define strtrue(s) (s && *s)

/* 
 * configure like so:
 * 
 * LoadModule authshadow_module modules/mod_authshadow.so
 * <Location /test>
 * AuthType Basic 
 * AuthName WhateverAuthnameYouWant
 * AuthShadow on
 * require valid-user 
 * </Location>
 */

typedef struct auth_shadow_config_struct {
    int auth_shadow_flag; /* 1 for yes, 0 for no */
} auth_shadow_config_rec;

static void *create_auth_shadow_dir_config(pool *p, char *d)
{
    auth_shadow_config_rec *sec =
    (auth_shadow_config_rec *) ap_pcalloc(p, sizeof(auth_shadow_config_rec));
    sec->auth_shadow_flag = 0;	
    return sec;
}

static const char* authshadow_flag(cmd_parms *parms, void *mconfig, int flag)
{
    auth_shadow_config_rec *s;
    s = (auth_shadow_config_rec *)mconfig;
    s->auth_shadow_flag = flag;
    return NULL;
}

static const command_rec auth_shadow_cmds[] =
{
    {"AuthShadow", authshadow_flag,
     NULL, 
     OR_AUTHCFG, FLAG,
     "On or Off depending on whether to use /etc/shadow"},
    {NULL}
};


static int authshadow_handler(request_rec *r);
static int authshadow_valid_user(request_rec *r);

module MODULE_VAR_EXPORT authshadow_module = {
    STANDARD_MODULE_STUFF, 
    NULL,                  /* module initializer                  */
    create_auth_shadow_dir_config , /* create per-dir config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    auth_shadow_cmds,      /* table of config file commands       */
    NULL,                  /* [#8] MIME-typed-dispatched handlers */
    NULL,                  /* [#1] URI to filename translation    */
    authshadow_handler,       /* [#4] validate user id from request  */
    authshadow_valid_user, /* [#5] check if the user is ok _here_ */
    NULL,                  /* [#3] check access by host address   */
    NULL,                  /* [#6] determine MIME type            */
    NULL,                  /* [#7] pre-run fixups                 */
    NULL,                  /* [#9] log a transaction              */
    NULL,                  /* [#2] header parser                  */
    NULL,                  /* child_init                          */
    NULL,                  /* child_exit                          */
    NULL                   /* [#0] post read-request              */
};


/*
 * authshadow_authorize
 *
 * See if a username/pw combination is valid.
 *
 * Returns 1 if the pw is correct, 0 if it's incorrect, -1 if there's an error.
 */

static int authshadow_authorize(const char *user, const char* pw, request_rec* r)
{
    int filedes[2];  /* fd's for pipe.  Read from 0, write to 1*/
    char validate_prog[] = INSTBINDIR "/validate";
    int ret, status;
    FILE* fp;

    if (strlen(INSTBINDIR) > 240) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
            "%s: Error -- directory name too long.",module_name);
            return(-1);
    }

    if (pipe(filedes)!=0) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
        "%s: Unable to open pipe.  Error: %d\n",module_name, errno);
        return(-1);
    }

    ret = fork();
    if (ret==-1) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
        "%s: Unable to fork. Error: %d\n",module_name, errno);
        return(-1);
    }

    if (ret==0) { /* Child */

        /* Set stdin to filedes[0] */
        dup2(filedes[0],STDIN_FILENO);

        /* ...and close the other file descriptor. */
        if (close(filedes[0])!=0) {
            fprintf(stderr,"%s: Unable to close file descriptor. Error: %d\n",module_name, errno);
            exit(1);
        }

        execl(validate_prog,
            validate_prog,
            NULL);

        /* We shouldn't reach this point */
        fprintf(stderr,"%s: Unable to exec. Error: %d\n",module_name, errno);
        exit(1);
    }

    /* Parent */

    /* We write to the pipe, then wait for the child to finish. */
    fp = fdopen(filedes[1],"w");
    if (!fp) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
        "%s: Unable to open pipe for writing: %d\n",module_name, errno);
        return(-1);
    }

    fprintf(fp, "%s\n",user);
    fprintf(fp, "%s\n",pw);
    fclose(fp);
    if (close(filedes[0])!=0) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
        "%s: Unable to close file descriptor. Error: %d\n",module_name, errno);
        return(-1);
    }

    ret = wait(&status);
    if (ret==0 || ret==-1) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
        "%s: Error while waiting for child: %d.\n",module_name, errno);
        return(-1);
    }

    if (status==0)
        return 1;  /* Correct pw */
    return 0; /* Nope */
}

/*
 * authshadow_handler
 *
 * See if the username/pw combination is valid.
 */

static int authshadow_handler(request_rec *r)
{
     int ret;
     const char *sent_pw; 
     char str[200];
     int rc = ap_get_basic_auth_pw(r, &sent_pw); 
     char user[MAX_USERNAME_LENGTH+1];
     char passwd[MAX_PW_LENGTH+1];
     int n;
     auth_shadow_config_rec *s =
        (auth_shadow_config_rec *) ap_get_module_config(r->per_dir_config, &authshadow_module);           

     if(rc != OK) return rc;
     if (s->auth_shadow_flag != 1)
        { return DECLINED; }            

     if(!(strtrue(r->connection->user) && strtrue(sent_pw))) {
         ap_note_basic_auth_failure(r);  
         ap_log_reason("Both a username and password must be provided", 
                   r->uri, r);  
         return HTTP_UNAUTHORIZED;
     }

     /* Truncate to max length. */
     n = strlen(r->connection->user);
     if (n > MAX_USERNAME_LENGTH) n=MAX_USERNAME_LENGTH;
     strncpy(user,r->connection->user,n); /* Copy to user[0..n-1] */
     user[n] = '\0';

     n = strlen(sent_pw);
     if (n > MAX_PW_LENGTH) n=MAX_PW_LENGTH;
     strncpy(passwd,sent_pw,n); /* Copy to passwd[0..n-1] */
     passwd[n] = '\0';

     ret = authshadow_authorize(user,passwd,r);
     if (ret==-1)
         return HTTP_INTERNAL_SERVER_ERROR;
     if (ret!=1) {
         ap_note_basic_auth_failure(r);  
         sprintf(str,"Invalid password entered for user %s",user);
         ap_log_reason(str,r->uri, r);  
         return HTTP_UNAUTHORIZED;
     }

     return OK;
}

/*
 * user_in_group
 *
 * See whether a given user is a member of a given group
 * (True if either the user is in the group in /etc/group
 * or if the user's primary group is this group.)
 *
 * returns: 1 for yes, 0 for no
 */
static int user_in_group (char *user, const char *groupname) {
    struct group *g;  /* Group structure */
    struct passwd *p; /* Passwd structure */
    char **m;         /* List of members */

    if (!strtrue(groupname)) {
        return 0;
    }
    g = getgrnam(groupname);

    if (!g)
        return 0; /* No such group */

    /* Check system group file. */
    m = g->gr_mem;
    if (!m)  
    {   /* Error */
        fprintf(stderr,"%s: Error reading information for group %s\n",module_name,groupname);
        return 0;
    }

    while (*m) {
        if (!strcmp(*m,user))
            return 1;
        m++;
    }

    /* Check for a matching group ID */
    p = getpwnam(user);
    if (!p) 
        return 0;
    if (p->pw_gid == g->gr_gid) 
        return 1;

    return 0;
}

/*
 *  authshadow_valid_user
 *
 *  Check the requires field to see if this is a valid user.
 */

static int authshadow_valid_user(request_rec *r)
{
       /* req_arr is the array of requires lines */
       const array_header * req_arr = ap_requires(r);
       require_line *requires;
       char *user = r->connection->user; /* The user connected. */
       int m = r->method_number;  /* The method number (e.g. M_GET, M_POST, etc..) */
       int i;
       int method_restricted = 0; 
       const char *line;          /* The requires line. */
       const char *w;             /* A word from the requires line. */
       auth_shadow_config_rec *s = (auth_shadow_config_rec *)
	       ap_get_module_config(r->per_dir_config, &authshadow_module);
	   
       if (s->auth_shadow_flag != 1)
            return DECLINED;

       if (!req_arr) {
            /* No requires lines.  Any user will do. */
            return OK;
       }
       requires = (require_line *) req_arr->elts;
        
       for (i=0; i < req_arr->nelts; i++) {
            /* Process one requires line. */

            if (!(requires[i].method_mask & (1 << m))) 
                continue;  /* The method coming through isn't restricted */
             method_restricted = 1;  /* Something was restricted */
             line = requires[i].requirement;
             w = ap_getword_white(r->pool, &line);
             if (!strcmp(w, "valid-user"))
                return OK;
             if (!strcmp(w, "user")) {
                     /* See if the user is one of the listed users. */
                     while (line[0]) {
                        w = ap_getword_conf(r->pool, &line);
                        if (!strcmp(user,w))
                            return OK;
                     }
             }
             else if (!strcmp(w,"group")) {
                /* See if the user is a member of one of the listed groups. */
                while (line[0]) {
                    w = ap_getword_conf(r->pool, &line);
                    if (user_in_group(user,w))
                        return OK;
                }
             }
       }
    if (!method_restricted)  /* This method wasn't restricted */
        return OK;
   
    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, 
           "access to %s failed.  Reason: user %s not allowed access", 
           r->uri, user);

    ap_note_basic_auth_failure(r);
    return AUTH_REQUIRED;
}