]> granicus.if.org Git - apache/blob - modules/aaa/mod_authnz_fcgi.c
mod_authn_socache.c: fix creation of default socache_instance.
[apache] / modules / aaa / mod_authnz_fcgi.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr_hash.h"
18 #include "apr_lib.h"
19 #include "apr_strings.h"
20
21 #include "ap_provider.h"
22 #include "httpd.h"
23 #include "http_config.h"
24 #include "http_core.h"
25 #include "http_protocol.h"
26 #include "http_request.h"
27 #include "http_log.h"
28 #include "util_script.h"
29 #include "ap_provider.h"
30 #include "mod_auth.h"
31 #include "util_fcgi.h"
32 #include "ap_mmn.h"
33
34 module AP_MODULE_DECLARE_DATA authnz_fcgi_module;
35
36 typedef struct {
37     const char *name; /* provider name */
38     const char *backend; /* backend address, as configured */
39     const char *host;
40     apr_port_t port;
41     apr_sockaddr_t *backend_addrs;
42     int is_authn;
43     int is_authz;
44 } fcgi_provider_conf;
45
46 typedef struct {
47     const char *name; /* provider name */
48     const char *default_user; /* this is user if authorizer returns
49                                * success and a user expression yields
50                                * empty string
51                                */
52     ap_expr_info_t *user_expr; /* expr to evaluate to set r->user */
53     char authoritative; /* fail request if user is rejected? */
54     char require_basic_auth; /* fail if client didn't send credentials? */
55 } fcgi_dir_conf;
56
57 typedef struct {
58     /* If an "authnz" provider successfully authenticates, record
59      * the provider name here for checking during authz.
60      */
61     const char *successful_authnz_provider;
62 } fcgi_request_notes;
63
64 static apr_hash_t *fcgi_authn_providers, *fcgi_authz_providers;
65
66 #define FCGI_IO_TIMEOUT apr_time_from_sec(30)
67
68 #ifndef NON200_RESPONSE_BUF_LEN
69 #define NON200_RESPONSE_BUF_LEN 8192
70 #endif
71
72 /* fcgi://{hostname|IPv4|IPv6}:port[/] */
73 #define FCGI_BACKEND_REGEX_STR "m%^fcgi://(.*):(\\d{1,5})/?$%"
74
75 /*
76  * utility function to connect to a peer; generally useful, but 
77  * wait for AF_UNIX support in this mod before thinking about how
78  * to make it available to other modules
79  */
80 static apr_status_t connect_to_peer(apr_socket_t **newsock,
81                                     request_rec *r,
82                                     int module_index,
83                                     apr_sockaddr_t *backend_addrs,
84                                     const char *backend_name,
85                                     apr_interval_time_t timeout)
86 {
87 #define FN_LOG_MARK __FILE__,__LINE__,module_index
88     apr_status_t rv = APR_EINVAL; /* returned if no backend addr was provided
89                                    */
90     int connected = 0;
91     apr_sockaddr_t *addr = backend_addrs;
92
93     while (addr && !connected) {
94         int loglevel = addr->next ? APLOG_DEBUG : APLOG_ERR;
95         rv = apr_socket_create(newsock, addr->family,
96                                SOCK_STREAM, 0, r->pool);
97         if (rv != APR_SUCCESS) {
98             ap_log_rerror(FN_LOG_MARK, loglevel, rv, r,
99                           APLOGNO(02494) "error creating family %d socket "
100                           "for target %s",
101                           addr->family, backend_name);
102             addr = addr->next;
103             continue;
104         }
105
106         apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1);
107         apr_socket_timeout_set(*newsock,
108                                timeout ? timeout : r->server->timeout);
109
110         rv = apr_socket_connect(*newsock, addr);
111         if (rv != APR_SUCCESS) {
112             apr_socket_close(*newsock);
113             ap_log_rerror(FN_LOG_MARK, loglevel, rv, r,
114                           APLOGNO(02495) "attempt to connect to %pI (%s) "
115                           "failed", addr, backend_name);
116             addr = addr->next;
117             continue;
118         }
119
120         connected = 1;
121     }
122
123     return rv;
124 #undef FN_LOG_MARK
125 }
126
127 static void log_provider_info(const fcgi_provider_conf *conf, request_rec *r)
128 {
129     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
130                   APLOGNO(02496) "name %s, backend %s, host %s, port %d, "
131                   "first address %pI, %c%c",
132                   conf->name,
133                   conf->backend,
134                   conf->host,
135                   (int)conf->port,
136                   conf->backend_addrs,
137                   conf->is_authn ? 'N' : '_',
138                   conf->is_authz ? 'Z' : '_');
139 }
140
141 static void setupenv(request_rec *r, const char *password, const char *apache_role)
142 {
143     ap_add_common_vars(r);
144     ap_add_cgi_vars(r);
145     apr_table_setn(r->subprocess_env, "FCGI_ROLE", AP_FCGI_AUTHORIZER_STR);
146     if (apache_role) {
147         apr_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", apache_role);
148     }
149     if (password) {
150         apr_table_setn(r->subprocess_env, "REMOTE_PASSWD", password);
151     }
152     /* Drop the variables CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED,
153      * SCRIPT_NAME and most Hop-By-Hop headers - EXCEPT we will pass
154      * PROXY_AUTH to allow CGI to perform proxy auth for httpd
155      */
156     apr_table_unset(r->subprocess_env, "CONTENT_LENGTH");
157     apr_table_unset(r->subprocess_env, "PATH_INFO");
158     apr_table_unset(r->subprocess_env, "PATH_TRANSLATED");
159     apr_table_unset(r->subprocess_env, "SCRIPT_NAME");
160     apr_table_unset(r->subprocess_env, "HTTP_KEEP_ALIVE");
161     apr_table_unset(r->subprocess_env, "HTTP_TE");
162     apr_table_unset(r->subprocess_env, "HTTP_TRAILER");
163     apr_table_unset(r->subprocess_env, "HTTP_TRANSFER_ENCODING");
164     apr_table_unset(r->subprocess_env, "HTTP_UPGRADE");
165
166     /* Connection hop-by-hop header to prevent the CGI from hanging */
167     apr_table_set(r->subprocess_env, "HTTP_CONNECTION", "close");
168 }
169
170 static apr_status_t recv_data(const fcgi_provider_conf *conf,
171                               request_rec *r,
172                               apr_socket_t *s,
173                               char *buf,
174                               apr_size_t *buflen)
175 {
176     apr_status_t rv;
177
178     rv = apr_socket_recv(s, buf, buflen);
179     if (rv != APR_SUCCESS) {
180         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
181                       APLOGNO(02497) "Couldn't read from backend %s",
182                       conf->backend);
183         return rv;
184     }
185
186 #if AP_MODULE_MAGIC_AT_LEAST(20130702,2) 
187     ap_log_rdata(APLOG_MARK, APLOG_TRACE5, r, "FastCGI data received",
188                  buf, *buflen, AP_LOG_DATA_SHOW_OFFSET);
189 #endif
190     return APR_SUCCESS;
191 }
192
193 static apr_status_t recv_data_full(const fcgi_provider_conf *conf,
194                                    request_rec *r,
195                                    apr_socket_t *s,
196                                    char *buf,
197                                    apr_size_t buflen)
198 {
199     apr_size_t readlen;
200     apr_size_t cumulative_len = 0;
201     apr_status_t rv;
202
203     do {
204         readlen = buflen - cumulative_len;
205         rv = recv_data(conf, r, s, buf + cumulative_len, &readlen);
206         if (rv != APR_SUCCESS) {
207             return rv;
208         }
209         cumulative_len += readlen;
210     } while (cumulative_len < buflen);
211
212     return APR_SUCCESS;
213 }
214
215 static apr_status_t sendv_data(const fcgi_provider_conf *conf,
216                                request_rec *r,
217                                apr_socket_t *s,
218                                struct iovec *vec,
219                                int nvec,
220                                apr_size_t *len)
221 {
222     apr_size_t to_write = 0, written = 0;
223     apr_status_t rv = APR_SUCCESS;
224     int i, offset;
225
226     for (i = 0; i < nvec; i++) {
227         to_write += vec[i].iov_len;
228 #if AP_MODULE_MAGIC_AT_LEAST(20130702,2) 
229         ap_log_rdata(APLOG_MARK, APLOG_TRACE5, r, "FastCGI data sent",
230                      vec[i].iov_base, vec[i].iov_len, AP_LOG_DATA_SHOW_OFFSET);
231 #endif
232     }
233
234     offset = 0;
235     while (to_write) {
236         apr_size_t n = 0;
237         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
238         if (rv != APR_SUCCESS) {
239             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
240                           APLOGNO(02498) "Sending data to %s failed",
241                           conf->backend);
242             break;
243         }
244         if (n > 0) {
245             written += n;
246             if (written >= to_write)
247                 break;                 /* short circuit out */
248             for (i = offset; i < nvec; ) {
249                 if (n >= vec[i].iov_len) {
250                     offset++;
251                     n -= vec[i++].iov_len;
252                 } else {
253                     vec[i].iov_len -= n;
254                     vec[i].iov_base = (char *) vec[i].iov_base + n;
255                     break;
256                 }
257             }
258         }
259     }
260
261     *len = written;
262
263     return rv;
264 }
265
266 static apr_status_t send_begin_request(request_rec *r,
267                                        const fcgi_provider_conf *conf,
268                                        apr_socket_t *s, int role,
269                                        apr_uint16_t request_id)
270 {
271     struct iovec vec[2];
272     ap_fcgi_header header;
273     unsigned char farray[AP_FCGI_HEADER_LEN];
274     ap_fcgi_begin_request_body brb;
275     unsigned char abrb[AP_FCGI_HEADER_LEN];
276     apr_size_t len;
277
278     ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id,
279                            sizeof(abrb), 0);
280     ap_fcgi_fill_in_request_body(&brb, role, 0 /* *NOT* AP_FCGI_KEEP_CONN */);
281
282     ap_fcgi_header_to_array(&header, farray);
283     ap_fcgi_begin_request_body_to_array(&brb, abrb);
284
285     vec[0].iov_base = (void *)farray;
286     vec[0].iov_len = sizeof(farray);
287     vec[1].iov_base = (void *)abrb;
288     vec[1].iov_len = sizeof(abrb);
289
290     return sendv_data(conf, r, s, vec, 2, &len);
291 }
292
293 static apr_status_t send_environment(apr_socket_t *s,
294                                      const fcgi_provider_conf *conf,
295                                      request_rec *r, apr_uint16_t request_id,
296                                      apr_pool_t *temp_pool)
297 {
298     const char *fn = "send_environment";
299     const apr_array_header_t *envarr;
300     const apr_table_entry_t *elts;
301     struct iovec vec[2];
302     ap_fcgi_header header;
303     unsigned char farray[AP_FCGI_HEADER_LEN];
304     char *body;
305     apr_status_t rv;
306     apr_size_t avail_len, len, required_len;
307     int i, next_elem, starting_elem;
308
309     envarr = apr_table_elts(r->subprocess_env);
310     elts = (const apr_table_entry_t *) envarr->elts;
311
312     if (APLOG_R_IS_LEVEL(r, APLOG_TRACE2)) {
313
314         for (i = 0; i < envarr->nelts; ++i) {
315             if (!elts[i].key) {
316                 continue;
317             }
318             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
319                           "%s: '%s': '%s'",
320                           fn, elts[i].key, 
321                           !strcmp(elts[i].key, "REMOTE_PASSWD") ?
322                               "XXXXXXXX" : elts[i].val);
323         }
324     }
325
326     /* Send envvars over in as many FastCGI records as it takes, */
327     next_elem = 0; /* starting with the first one */
328
329     avail_len = 16 * 1024; /* our limit per record, which could have been up
330                             * to AP_FCGI_MAX_CONTENT_LEN
331                             */
332
333     while (next_elem < envarr->nelts) {
334         starting_elem = next_elem;
335         required_len = ap_fcgi_encoded_env_len(r->subprocess_env,
336                                                avail_len,
337                                                &next_elem);
338
339         if (!required_len) {
340             if (next_elem < envarr->nelts) {
341                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
342                               APLOGNO(02499) "couldn't encode envvar '%s' in %"
343                               APR_SIZE_T_FMT " bytes",
344                               elts[next_elem].key, avail_len);
345                 /* skip this envvar and continue */
346                 ++next_elem;
347                 continue;
348             }
349             /* only an unused element at the end of the array */
350             break;
351         }
352
353         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
354                       APLOGNO(02500) "required len for encoding envvars: %"
355                       APR_SIZE_T_FMT ", %d/%d elems processed so far",
356                       required_len, next_elem, envarr->nelts);
357
358         body = apr_palloc(temp_pool, required_len);
359         rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
360                                 &starting_elem);
361         /* we pre-compute, so we can't run out of space */
362         ap_assert(rv == APR_SUCCESS);
363         /* compute and encode must be in sync */
364         ap_assert(starting_elem == next_elem);
365
366         ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id,
367                                (apr_uint16_t)required_len, 0);
368         ap_fcgi_header_to_array(&header, farray);
369
370         vec[0].iov_base = (void *)farray;
371         vec[0].iov_len = sizeof(farray);
372         vec[1].iov_base = body;
373         vec[1].iov_len = required_len;
374
375         rv = sendv_data(conf, r, s, vec, 2, &len);
376         apr_pool_clear(temp_pool);
377
378         if (rv) {
379             return rv;
380         }
381     }
382
383     /* Envvars sent, so say we're done */
384     ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0);
385     ap_fcgi_header_to_array(&header, farray);
386
387     vec[0].iov_base = (void *)farray;
388     vec[0].iov_len = sizeof(farray);
389
390     return sendv_data(conf, r, s, vec, 1, &len);
391 }
392
393 /*
394  * This header-state logic is from mod_proxy_fcgi.
395  */
396 enum {
397   HDR_STATE_READING_HEADERS,
398   HDR_STATE_GOT_CR,
399   HDR_STATE_GOT_CRLF,
400   HDR_STATE_GOT_CRLFCR,
401   HDR_STATE_GOT_LF,
402   HDR_STATE_DONE_WITH_HEADERS
403 };
404
405 /* Try to find the end of the script headers in the response from the back
406  * end fastcgi server. STATE holds the current header parsing state for this
407  * request.
408  *
409  * Returns 0 if it can't find the end of the headers, and 1 if it found the
410  * end of the headers. */
411 static int handle_headers(request_rec *r,
412                           int *state,
413                           char *readbuf)
414 {
415     const char *itr = readbuf;
416
417     while (*itr) {
418         if (*itr == '\r') {
419             switch (*state) {
420                 case HDR_STATE_GOT_CRLF:
421                     *state = HDR_STATE_GOT_CRLFCR;
422                     break;
423
424                 default:
425                     *state = HDR_STATE_GOT_CR;
426                     break;
427             }
428         }
429         else if (*itr == '\n') {
430             switch (*state) {
431                  case HDR_STATE_GOT_LF:
432                      *state = HDR_STATE_DONE_WITH_HEADERS;
433                      break;
434
435                  case HDR_STATE_GOT_CR:
436                      *state = HDR_STATE_GOT_CRLF;
437                      break;
438
439                  case HDR_STATE_GOT_CRLFCR:
440                      *state = HDR_STATE_DONE_WITH_HEADERS;
441                      break;
442
443                  default:
444                      *state = HDR_STATE_GOT_LF;
445                      break;
446             }
447         }
448         else {
449             *state = HDR_STATE_READING_HEADERS;
450         }
451
452         if (*state == HDR_STATE_DONE_WITH_HEADERS)
453             break;
454
455         ++itr;
456     }
457
458     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
459         return 1;
460     }
461
462     return 0;
463 }
464
465 /*
466  * handle_response() is based on mod_proxy_fcgi's dispatch()
467  */
468 static apr_status_t handle_response(const fcgi_provider_conf *conf,
469                                     request_rec *r, apr_socket_t *s,
470                                     apr_pool_t *temp_pool,
471                                     apr_uint16_t request_id,
472                                     char *rspbuf,
473                                     apr_size_t *rspbuflen)
474 {
475     apr_bucket *b;
476     apr_bucket_brigade *ob;
477     apr_size_t orspbuflen;
478     apr_status_t rv = APR_SUCCESS;
479     const char *fn = "handle_response";
480     int header_state = HDR_STATE_READING_HEADERS;
481     int seen_end_of_headers = 0, done = 0;
482
483     if (rspbuflen) {
484         orspbuflen = *rspbuflen;
485         *rspbuflen = 0; /* unless we actually read something */
486     }
487
488     ob = apr_brigade_create(r->pool, r->connection->bucket_alloc);
489
490     while (!done && rv == APR_SUCCESS) { /* Keep reading FastCGI records until
491                                           * we get AP_FCGI_END_REQUEST (done)
492                                           * or an error occurs.
493                                           */
494         apr_size_t readbuflen;
495         apr_uint16_t clen;
496         apr_uint16_t rid;
497         char readbuf[AP_IOBUFSIZE + 1];
498         unsigned char farray[AP_FCGI_HEADER_LEN];
499         unsigned char plen;
500         unsigned char type;
501         unsigned char version;
502
503         rv = recv_data_full(conf, r, s, (char *)farray, AP_FCGI_HEADER_LEN);
504         if (rv != APR_SUCCESS) {
505             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
506                           APLOGNO(02501) "%s: Error occurred before reading "
507                           "entire header", fn);
508             break;
509         }
510
511         ap_fcgi_header_fields_from_array(&version, &type, &rid, &clen, &plen,
512                                          farray);
513
514         if (version != AP_FCGI_VERSION_1) {
515             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
516                           APLOGNO(02502) "%s: Got bogus FastCGI header "
517                           "version %d", fn, (int)version);
518             rv = APR_EINVAL;
519             break;
520         }
521
522         if (rid != request_id) {
523             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
524                           APLOGNO(02503) "%s: Got bogus FastCGI header "
525                           "request id %d, expected %d",
526                           fn, rid, request_id);
527             rv = APR_EINVAL;
528             break;
529         }
530
531     recv_again: /* if we need to keep reading more of a record's content */
532
533         if (clen > sizeof(readbuf) - 1) {
534             readbuflen = sizeof(readbuf) - 1;
535         } else {
536             readbuflen = clen;
537         }
538
539         /*
540          * Now get the actual content of the record.
541          */
542         if (readbuflen != 0) {
543             rv = recv_data(conf, r, s, readbuf, &readbuflen);
544             if (rv != APR_SUCCESS) {
545                 break;
546             }
547             readbuf[readbuflen] = '\0';
548         }
549
550         switch (type) {
551         case AP_FCGI_STDOUT: /* Response headers and optional body */
552             if (clen != 0) {
553                 b = apr_bucket_transient_create(readbuf,
554                                                 readbuflen,
555                                                 r->connection->bucket_alloc);
556
557                 APR_BRIGADE_INSERT_TAIL(ob, b);
558
559                 if (!seen_end_of_headers) {
560                     int st = handle_headers(r, &header_state, readbuf);
561
562                     if (st == 1) {
563                         int status;
564
565                         seen_end_of_headers = 1;
566
567                         status =
568                             ap_scan_script_header_err_brigade_ex(r, ob,
569                                                                  NULL, 
570                                                                  APLOG_MODULE_INDEX);
571                         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
572                                       APLOGNO(02504) "%s: script header "
573                                       "parsing -> %d/%d",
574                                       fn, status, r->status);
575
576                         if (rspbuf) { /* caller wants to see response body,
577                                        * if any
578                                        */
579                             apr_status_t tmprv;
580
581                             if (rspbuflen) {
582                                 *rspbuflen = orspbuflen;
583                             }
584                             tmprv = apr_brigade_flatten(ob, rspbuf, rspbuflen);
585                             if (tmprv != APR_SUCCESS) {
586                                 /* should not occur for these bucket types;
587                                  * does not indicate overflow
588                                  */
589                                 ap_log_rerror(APLOG_MARK, APLOG_ERR, tmprv, r,
590                                               APLOGNO(02505) "%s: error "
591                                               "flattening response body",
592                                               fn);
593                             }
594                         }
595
596                         if (status != OK) {
597                             r->status = status;
598                             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
599                                           APLOGNO(02506) "%s: Error parsing "
600                                           "script headers from %s",
601                                           fn, conf->backend);
602                             rv = APR_EINVAL;
603                             break;
604                         }
605                         apr_pool_clear(temp_pool);
606                     }
607                     else {
608                         /* We're still looking for the end of the
609                          * headers, so this part of the data will need
610                          * to persist. */
611                         apr_bucket_setaside(b, temp_pool);
612                     }
613                 }
614
615                 /* If we didn't read all the data go back and get the
616                  * rest of it. */
617                 if (clen > readbuflen) {
618                     clen -= readbuflen;
619                     goto recv_again;
620                 }
621             }
622             break;
623
624         case AP_FCGI_STDERR: /* Text to log */
625             if (clen) {
626                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
627                               APLOGNO(02507) "%s: Logged from %s: '%s'",
628                               fn, conf->backend, readbuf);
629             }
630
631             if (clen > readbuflen) {
632                 clen -= readbuflen;
633                 goto recv_again; /* continue reading this record */
634             }
635             break;
636
637         case AP_FCGI_END_REQUEST:
638             done = 1;
639             break;
640
641         default:
642             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
643                           APLOGNO(02508) "%s: Got bogus FastCGI record type "
644                           "%d", fn, type);
645             break;
646         }
647
648         /*
649          * Read/discard any trailing padding.
650          */
651         if (plen) {
652             rv = recv_data_full(conf, r, s, readbuf, plen);
653             if (rv != APR_SUCCESS) {
654                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
655                               APLOGNO(02509) "%s: Error occurred reading "
656                               "padding",
657                               fn);
658                 break;
659             }
660         }
661     }
662
663     apr_brigade_cleanup(ob);
664
665     if (rv == APR_SUCCESS && !seen_end_of_headers) {
666         rv = APR_EINVAL;
667         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
668                       APLOGNO(02510) "%s: Never reached end of script headers",
669                       fn);
670     }
671
672     return rv;
673 }
674
675 /* almost from mod_fcgid */
676 static int mod_fcgid_modify_auth_header(void *vars,
677                                         const char *key, const char *val)
678 {
679     /* When the application gives a 200 response, the server ignores response
680        headers whose names aren't prefixed with Variable- prefix, and ignores
681        any response content */
682     if (strncasecmp(key, "Variable-", 9) == 0)
683         apr_table_setn(vars, key, val);
684     return 1;
685 }
686
687 static int fix_auth_header(void *vr, const char *key, const char *val)
688 {
689     request_rec *r = vr;
690
691     ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "moving %s->%s", key, val);
692     apr_table_unset(r->err_headers_out, key);
693     apr_table_setn(r->subprocess_env, key + 9, val);
694     return 1;
695 }
696
697 static void req_rsp(request_rec *r, const fcgi_provider_conf *conf,
698                     const char *password, const char *apache_role,
699                     char *rspbuf, apr_size_t *rspbuflen)
700 {
701     const char *fn = "req_rsp";
702     apr_pool_t *temp_pool;
703     apr_size_t orspbuflen;
704     apr_socket_t *s;
705     apr_status_t rv;
706     apr_table_t *saved_subprocess_env = 
707       apr_table_copy(r->pool, r->subprocess_env);
708
709     if (rspbuflen) {
710         orspbuflen = *rspbuflen;
711         *rspbuflen = 0; /* unless we actually read something */
712     }
713
714     apr_pool_create(&temp_pool, r->pool);
715
716     setupenv(r, password, apache_role);
717
718     rv = connect_to_peer(&s, r, APLOG_MODULE_INDEX, conf->backend_addrs, 
719                          conf->backend, FCGI_IO_TIMEOUT);
720     if (rv == APR_SUCCESS) {
721         apr_uint16_t request_id = 1;
722
723         rv = send_begin_request(r, conf, s, AP_FCGI_AUTHORIZER, request_id);
724         if (rv != APR_SUCCESS) {
725             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
726                           APLOGNO(02511) "%s: Failed writing request to %s",
727                           fn, conf->backend);
728         }
729
730         if (rv == APR_SUCCESS) {
731             rv = send_environment(s, conf, r, request_id, temp_pool);
732             if (rv != APR_SUCCESS) {
733                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
734                               APLOGNO(02512) "%s: Failed writing environment "
735                               "to %s", fn, conf->backend);
736             }
737         }
738
739         /* The responder owns the request body, not the authorizer.
740          * Don't even send an empty AP_FCGI_STDIN block.  libfcgi doesn't care,
741          * but it wasn't sent to authorizers by mod_fastcgi or mod_fcgi and
742          * may be unhandled by the app.  Additionally, the FastCGI spec does
743          * not mention FCGI_STDIN in the Authorizer description, though it
744          * does describe FCGI_STDIN elsewhere in more general terms than
745          * simply a wrapper for the client's request body.
746          */
747
748         if (rv == APR_SUCCESS) {
749             if (rspbuflen) {
750                 *rspbuflen = orspbuflen;
751             }
752             rv = handle_response(conf, r, s, temp_pool, request_id, rspbuf,
753                                  rspbuflen);
754             if (rv != APR_SUCCESS) {
755                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
756                               APLOGNO(02514) "%s: Failed handling response "
757                               "from %s", fn, conf->backend);
758             }
759         }
760
761         apr_socket_close(s);
762     }
763
764     apr_pool_destroy(temp_pool);
765
766     if (rv != APR_SUCCESS) {
767         /* some sort of mechanical problem */
768         r->status = HTTP_INTERNAL_SERVER_ERROR;
769     }
770     else {
771         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
772                       APLOGNO(02515) "%s: Received HTTP status %d",
773                       fn, r->status);
774     }
775
776     r->subprocess_env = saved_subprocess_env;
777
778     if (r->status == HTTP_OK) {
779         /* An Authorizer application's 200 response may include headers
780          * whose names are prefixed with Variable-, and they should be
781          * available to subsequent phases via subprocess_env (and yanked
782          * from the client response).
783          */
784         apr_table_t *vars = apr_table_make(r->pool, 10);
785         apr_table_do(mod_fcgid_modify_auth_header, vars,
786                      r->err_headers_out, NULL);
787         apr_table_do(fix_auth_header, r, vars, NULL);
788     }
789 }
790
791 static int fcgi_check_authn(request_rec *r)
792 {
793     const char *fn = "fcgi_check_authn";
794     fcgi_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
795                                                 &authnz_fcgi_module);
796     const char *password = NULL;
797     const fcgi_provider_conf *conf;
798     const char *prov;
799     const char *auth_type;
800     char rspbuf[NON200_RESPONSE_BUF_LEN + 1]; /* extra byte for '\0' */
801     apr_size_t rspbuflen = sizeof rspbuf - 1;
802     int res;
803
804     prov = dconf && dconf->name ? dconf->name : NULL;
805
806     if (!prov || !strcasecmp(prov, "None")) {
807         return DECLINED;
808     }
809
810     auth_type = ap_auth_type(r);
811
812     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
813                   APLOGNO(02516) "%s, prov %s, authoritative %s, "
814                   "require-basic %s, user expr? %s type %s",
815                   fn, prov,
816                   dconf->authoritative ? "yes" : "no",
817                   dconf->require_basic_auth ? "yes" : "no",
818                   dconf->user_expr ? "yes" : "no",
819                   auth_type);
820
821     if (auth_type && !strcasecmp(auth_type, "Basic")) {
822         if ((res = ap_get_basic_auth_pw(r, &password))) {
823             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
824                           APLOGNO(02517) "%s: couldn't retrieve basic auth "
825                           "password", fn);
826             if (dconf->require_basic_auth) {
827                 return res;
828             }
829             password = NULL;
830         }
831     }
832
833     conf = apr_hash_get(fcgi_authn_providers, prov, APR_HASH_KEY_STRING);
834     if (!conf) {
835         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
836                       APLOGNO(02518) "%s: can't find config for provider %s",
837                       fn, prov);
838         return HTTP_INTERNAL_SERVER_ERROR;
839     }
840
841     if (APLOGrdebug(r)) {
842         log_provider_info(conf, r);
843     }
844
845     req_rsp(r, conf, password, AP_FCGI_APACHE_ROLE_AUTHENTICATOR_STR,
846             rspbuf, &rspbuflen);
847
848     if (r->status == HTTP_OK) {
849         if (dconf->user_expr) {
850             const char *err;
851             const char *user = ap_expr_str_exec(r, dconf->user_expr,
852                                                 &err);
853             if (user && strlen(user)) {
854                 r->user = apr_pstrdup(r->pool, user);
855                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
856                               APLOGNO(02519) "%s: Setting user to '%s'",
857                               fn, r->user);
858             }
859             else if (user && dconf->default_user) {
860                 r->user = apr_pstrdup(r->pool, dconf->default_user);
861             }
862             else if (user) {
863                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
864                               APLOGNO(02520) "%s: Failure extracting user "
865                               "after calling authorizer: user expression "
866                               "yielded empty string (variable not set?)",
867                               fn);
868                 r->status = HTTP_INTERNAL_SERVER_ERROR;
869             }
870             else {
871                 /* unexpected error, not even an empty string was returned */
872                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
873                               APLOGNO(02521) "%s: Failure extracting user "
874                               "after calling authorizer: %s",
875                               fn, err);
876                 r->status = HTTP_INTERNAL_SERVER_ERROR;
877             }
878         }
879         if (conf->is_authz) {
880             /* combined authn/authz phase, so app won't be invoked for authz
881              *
882              * Remember that the request was successfully authorized by this
883              * provider.
884              */
885             fcgi_request_notes *rnotes = apr_palloc(r->pool, sizeof(*rnotes));
886             rnotes->successful_authnz_provider = conf->name;
887             ap_set_module_config(r->request_config, &authnz_fcgi_module,
888                                  rnotes);
889         }
890     }
891     else {
892         /* From the spec:
893          *   For Authorizer response status values other than "200" (OK), the 
894          *   Web server denies access and sends the response status, headers,
895          *   and content back to the HTTP client.
896          * But:
897          *   This only makes sense if this authorizer is authoritative.
898          */
899         if (rspbuflen > 0 && !dconf->authoritative) {
900             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
901                           APLOGNO(02522) "%s: Ignoring response body from non-"
902                           "authoritative authorizer", fn);
903         }
904         else if (rspbuflen > 0) {
905             if (rspbuflen == sizeof rspbuf - 1) {
906                 /* apr_brigade_flatten() interface :( */
907                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
908                               APLOGNO(02523) "%s: possible overflow handling "
909                               "response body", fn);
910             }
911             rspbuf[rspbuflen] = '\0'; /* we reserved an extra byte for '\0' */
912             ap_custom_response(r, r->status, rspbuf); /* API makes a copy */
913         }
914     }
915
916     return r->status == HTTP_OK ? 
917         OK : dconf->authoritative ? r->status : DECLINED;
918 }
919
920 static authn_status fcgi_check_password(request_rec *r, const char *user,
921                                         const char *password)
922 {
923     const char *fn = "fcgi_check_password";
924     const char *prov = apr_table_get(r->notes, AUTHN_PROVIDER_NAME_NOTE);
925     const fcgi_provider_conf *conf;
926
927     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
928                   APLOGNO(02524) "%s(%s, XXX): provider %s",
929                   fn, user, prov);
930
931     if (!prov) {
932         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
933                       APLOGNO(02525) "%s: provider note isn't set", fn);
934         return AUTH_GENERAL_ERROR;
935     }
936
937     conf = apr_hash_get(fcgi_authn_providers, prov, APR_HASH_KEY_STRING);
938     if (!conf) {
939         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
940                       APLOGNO(02526) "%s: can't find config for provider %s",
941                       fn, prov);
942         return AUTH_GENERAL_ERROR;
943     }
944
945     if (APLOGrdebug(r)) {
946         log_provider_info(conf, r);
947     }
948
949     req_rsp(r, conf, password, 
950             /* combined authn and authz: FCGI_APACHE_ROLE not set */
951             conf->is_authz ? NULL : AP_FCGI_APACHE_ROLE_AUTHENTICATOR_STR,
952             NULL, NULL);
953
954     if (r->status == HTTP_OK) {
955         if (conf->is_authz) {
956             /* combined authn/authz phase, so app won't be invoked for authz
957              *
958              * Remember that the request was successfully authorized by this
959              * provider.
960              */
961             fcgi_request_notes *rnotes = apr_palloc(r->pool, sizeof(*rnotes));
962             rnotes->successful_authnz_provider = conf->name;
963             ap_set_module_config(r->request_config, &authnz_fcgi_module,
964                                  rnotes);
965         }
966         return AUTH_GRANTED;
967     }
968     else if (r->status == HTTP_INTERNAL_SERVER_ERROR) {
969         return AUTH_GENERAL_ERROR;
970     }
971     else {
972         return AUTH_DENIED;
973     }
974 }
975
976 static const authn_provider fcgi_authn_provider = {
977     &fcgi_check_password,
978     NULL /* get-realm-hash not supported */
979 };
980
981 static authz_status fcgi_authz_check(request_rec *r,
982                                      const char *require_line,
983                                      const void *parsed_require_line)
984 {
985     const char *fn = "fcgi_authz_check";
986     const char *prov = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
987     const fcgi_provider_conf *conf;
988
989     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
990                   APLOGNO(02527) "%s(%s)", fn, require_line);
991
992     if (!prov) {
993         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
994                       APLOGNO(02528) "%s: provider note isn't set", fn);
995         return AUTHZ_GENERAL_ERROR;
996     }
997
998     conf = apr_hash_get(fcgi_authz_providers, prov, APR_HASH_KEY_STRING);
999     if (!conf) {
1000         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
1001                       APLOGNO(02529) "%s: can't find config for provider %s",
1002                       fn, prov);
1003         return AUTHZ_GENERAL_ERROR;
1004     }
1005
1006     if (APLOGrdebug(r)) {
1007         log_provider_info(conf, r);
1008     }
1009
1010     if (!r->user) {
1011         return AUTHZ_DENIED_NO_USER;
1012     }
1013
1014     if (conf->is_authn) {
1015         /* combined authn/authz phase, so app won't be invoked for authz
1016          *
1017          * If the provider already successfully authorized this request, 
1018          * success.
1019          */
1020         fcgi_request_notes *rnotes = ap_get_module_config(r->request_config,
1021                                                         &authnz_fcgi_module);
1022         if (rnotes
1023             && rnotes->successful_authnz_provider
1024             && !strcmp(rnotes->successful_authnz_provider, conf->name)) {
1025             return AUTHZ_GRANTED;
1026         }
1027         else {
1028             return AUTHZ_DENIED;
1029         }
1030     }
1031     else {
1032         req_rsp(r, conf, NULL, AP_FCGI_APACHE_ROLE_AUTHORIZER_STR, NULL, NULL);
1033
1034         if (r->status == HTTP_OK) {
1035             return AUTHZ_GRANTED;
1036         }
1037         else if (r->status == HTTP_INTERNAL_SERVER_ERROR) {
1038             return AUTHZ_GENERAL_ERROR;
1039         }
1040         else {
1041             return AUTHZ_DENIED;
1042         }
1043     }
1044 }
1045
1046 static const char *fcgi_authz_parse(cmd_parms *cmd, const char *require_line,
1047                                     const void **parsed_require_line)
1048 {
1049     /* Allowed form: Require [not] registered-provider-name<EOS>
1050      */
1051     if (strcmp(require_line, "")) {
1052         return "mod_authnz_fcgi doesn't support restrictions on providers "
1053                "(i.e., multiple require args)";
1054     }
1055
1056     return NULL;
1057 }
1058
1059 static const authz_provider fcgi_authz_provider = {
1060     &fcgi_authz_check,
1061     &fcgi_authz_parse,
1062 };
1063
1064 static const char *fcgi_check_authn_provider(cmd_parms *cmd,
1065                                         void *d,
1066                                         int argc,
1067                                         char *const argv[])
1068 {
1069     const char *dname = "AuthnzFcgiCheckAuthnProvider";
1070     fcgi_dir_conf *dc = d;
1071     int ca = 0;
1072
1073     if (ca >= argc) {
1074         return apr_pstrcat(cmd->pool, dname, ": No provider given", NULL);
1075     }
1076
1077     dc->name = argv[ca];
1078     ca++;
1079
1080     if (!strcasecmp(dc->name, "None")) {
1081         if (ca < argc) {
1082             return "Options aren't supported with \"None\"";
1083         }
1084     }
1085
1086     while (ca < argc) {
1087         const char *var = argv[ca], *val;
1088         int badarg = 0;
1089
1090         ca++;
1091
1092         /* at present, everything needs an argument */
1093         if (ca >= argc) {
1094             return apr_pstrcat(cmd->pool, dname, ": ", var,
1095                                "needs an argument", NULL);
1096         }
1097
1098         val = argv[ca];
1099         ca++;
1100
1101         if (!strcasecmp(var, "Authoritative")) {
1102             if (!strcasecmp(val, "On")) {
1103                 dc->authoritative = 1;
1104             }
1105             else if (!strcasecmp(val, "Off")) {
1106                 dc->authoritative = 0;
1107             }
1108             else {
1109                 badarg = 1;
1110             }
1111         }
1112         else if (!strcasecmp(var, "DefaultUser")) {
1113             dc->default_user = val;
1114         }
1115         else if (!strcasecmp(var, "RequireBasicAuth")) {
1116             if (!strcasecmp(val, "On")) {
1117                 dc->require_basic_auth = 1;
1118             }
1119             else if (!strcasecmp(val, "Off")) {
1120                 dc->require_basic_auth = 0;
1121             }
1122             else {
1123                 badarg = 1;
1124             }
1125         }
1126         else if (!strcasecmp(var, "UserExpr")) {
1127             const char *err;
1128             int flags = AP_EXPR_FLAG_DONT_VARY | AP_EXPR_FLAG_RESTRICTED
1129                 | AP_EXPR_FLAG_STRING_RESULT;
1130
1131             dc->user_expr = ap_expr_parse_cmd(cmd, val,
1132                                               flags, &err, NULL);
1133             if (err) {
1134                 return apr_psprintf(cmd->pool, "%s: Error parsing '%s': '%s'",
1135                                     dname, val, err);
1136             }
1137         }
1138         else {
1139             return apr_pstrcat(cmd->pool, dname, ": Unexpected option '",
1140                                var, "'", NULL);
1141         }
1142         if (badarg) {
1143             return apr_pstrcat(cmd->pool, dname, ": Bad argument '",
1144                                val, "' to option '", var, "'", NULL);
1145         }
1146     }
1147
1148     return NULL;
1149 }
1150
1151 /* AuthnzFcgiAuthDefineProvider {authn|authz|authnz} provider-name \
1152  *   fcgi://backendhost:backendport/
1153  */
1154 static const char *fcgi_define_provider(cmd_parms *cmd,
1155                                         void *d,
1156                                         int argc,
1157                                         char *const argv[])
1158 {
1159     const char *dname = "AuthnzFcgiDefineProvider";
1160     ap_rxplus_t *fcgi_backend_regex;
1161     apr_status_t rv;
1162     char *host;
1163     const char *err, *stype;
1164     fcgi_provider_conf *conf = apr_pcalloc(cmd->pool, sizeof(*conf));
1165     int ca = 0, rc;
1166
1167     fcgi_backend_regex = ap_rxplus_compile(cmd->pool, FCGI_BACKEND_REGEX_STR);
1168     if (!fcgi_backend_regex) {
1169         return apr_psprintf(cmd->pool,
1170                             "%s: failed to compile regexec '%s'",
1171                             dname, FCGI_BACKEND_REGEX_STR);
1172     }
1173
1174     err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1175     if (err)
1176         return err;
1177
1178     if (ca >= argc) {
1179         return apr_pstrcat(cmd->pool, dname, ": No type given", NULL);
1180     }
1181
1182     stype = argv[ca];
1183     ca++;
1184
1185     if (!strcasecmp(stype, "authn")) {
1186         conf->is_authn = 1;
1187     }
1188     else if (!strcasecmp(stype, "authz")) {
1189         conf->is_authz = 1;
1190     }
1191     else if (!strcasecmp(stype, "authnz")) {
1192         conf->is_authn = conf->is_authz = 1;
1193     }
1194     else {
1195         return apr_pstrcat(cmd->pool,
1196                            dname,
1197                            ": Invalid provider type ",
1198                            stype,
1199                            NULL);
1200     }
1201
1202     if (ca >= argc) {
1203         return apr_pstrcat(cmd->pool, dname, ": No provider name given", NULL);
1204     }
1205     conf->name = argv[ca];
1206     ca++;
1207
1208     if (ca >= argc) {
1209         return apr_pstrcat(cmd->pool, dname, ": No backend-address given",
1210                            NULL);
1211     }
1212
1213     rc = ap_rxplus_exec(cmd->pool, fcgi_backend_regex, argv[ca], NULL);
1214     if (!rc || ap_rxplus_nmatch(fcgi_backend_regex) != 3) {
1215         return apr_pstrcat(cmd->pool,
1216                            dname, ": backend-address '",
1217                            argv[ca],
1218                            "' has invalid form",
1219                            NULL);
1220     }
1221
1222     host = ap_rxplus_pmatch(cmd->pool, fcgi_backend_regex, 1);
1223     if (host[0] == '[' && host[strlen(host) - 1] == ']') {
1224         host += 1;
1225         host[strlen(host) - 1] = '\0';
1226     }
1227
1228     conf->port = atoi(ap_rxplus_pmatch(cmd->pool, fcgi_backend_regex, 2));
1229     if (conf->port > 65535) {
1230         return apr_pstrcat(cmd->pool,
1231                            dname, ": backend-address '",
1232                            argv[ca],
1233                            "' has invalid port",
1234                            NULL);
1235     }
1236
1237     conf->backend = argv[ca];
1238     conf->host = host;
1239     ca++;
1240
1241     rv = apr_sockaddr_info_get(&conf->backend_addrs, conf->host,
1242                                APR_UNSPEC, conf->port, 0, cmd->pool);
1243     if (rv != APR_SUCCESS) {
1244         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, rv, NULL,
1245                      APLOGNO(02530) "Address %s could not be resolved",
1246                      conf->backend);
1247         return apr_pstrcat(cmd->pool,
1248                            dname,
1249                            ": Error resolving backend address",
1250                            NULL);
1251     }
1252
1253     if (ca != argc) {
1254         return apr_pstrcat(cmd->pool,
1255                            dname,
1256                            ": Unexpected parameter ",
1257                            argv[ca],
1258                            NULL);
1259     }
1260
1261     if (conf->is_authn) {
1262         apr_hash_set(fcgi_authn_providers, conf->name, APR_HASH_KEY_STRING,
1263                      conf);
1264         ap_register_auth_provider(cmd->pool, AUTHN_PROVIDER_GROUP,
1265                                   conf->name,
1266                                   AUTHN_PROVIDER_VERSION,
1267                                   &fcgi_authn_provider,
1268                                   AP_AUTH_INTERNAL_PER_CONF);
1269     }
1270
1271     if (conf->is_authz) {
1272         apr_hash_set(fcgi_authz_providers, conf->name, APR_HASH_KEY_STRING,
1273                      conf);
1274         ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP,
1275                                   conf->name,
1276                                   AUTHZ_PROVIDER_VERSION,
1277                                   &fcgi_authz_provider,
1278                                   AP_AUTH_INTERNAL_PER_CONF);
1279     }
1280
1281     return NULL;
1282 }
1283
1284 static const command_rec fcgi_cmds[] = {
1285     AP_INIT_TAKE_ARGV("AuthnzFcgiDefineProvider", 
1286                       fcgi_define_provider,
1287                       NULL,
1288                       RSRC_CONF,
1289                       "Define a FastCGI authn and/or authz provider"),
1290
1291     AP_INIT_TAKE_ARGV("AuthnzFcgiCheckAuthnProvider",
1292                       fcgi_check_authn_provider,
1293                       NULL,
1294                       OR_FILEINFO,
1295                       "Enable/disable a FastCGI authorizer to handle "
1296                       "check_authn phase"),
1297
1298     {NULL}
1299 };
1300
1301 static int fcgi_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
1302                            apr_pool_t *ptemp)
1303 {
1304     fcgi_authn_providers = apr_hash_make(pconf);
1305     fcgi_authz_providers = apr_hash_make(pconf);
1306
1307     return OK;
1308 }
1309
1310 static void fcgi_register_hooks(apr_pool_t *p)
1311 {
1312     static const char * const auth_basic_runs_after_me[] = 
1313         {"mod_auth_basic.c", NULL}; /* to allow for custom response */
1314
1315     ap_hook_pre_config(fcgi_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1316     ap_hook_check_authn(fcgi_check_authn, NULL, auth_basic_runs_after_me,
1317                         APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF);
1318 }
1319
1320 static void *create_dir_conf(apr_pool_t *p, char *dummy)
1321 {
1322     fcgi_dir_conf *dconf = apr_pcalloc(p, sizeof(fcgi_dir_conf));
1323
1324     dconf->authoritative = 1;
1325     return dconf;
1326 }
1327
1328 static void *merge_dir_conf(apr_pool_t *p, void *basev, void *overridesv)
1329 {
1330     fcgi_dir_conf *a = (fcgi_dir_conf *)apr_pcalloc(p, sizeof(*a));
1331     fcgi_dir_conf *base = (fcgi_dir_conf *)basev, 
1332         *over = (fcgi_dir_conf *)overridesv;
1333
1334     /* currently we just have a single directive applicable to a 
1335      * directory, so if it is set then grab all fields from fcgi_dir_conf
1336      */
1337     if (over->name) {
1338         memcpy(a, over, sizeof(*a));
1339     }
1340     else {
1341         memcpy(a, base, sizeof(*a));
1342     }
1343     
1344     return a;
1345 }
1346
1347 AP_DECLARE_MODULE(authnz_fcgi) =
1348 {
1349     STANDARD20_MODULE_STUFF,
1350     create_dir_conf,                 /* dir config creater */
1351     merge_dir_conf,                  /* dir merger */
1352     NULL,                            /* server config */
1353     NULL,                            /* merge server config */
1354     fcgi_cmds,                       /* command apr_table_t */
1355     fcgi_register_hooks              /* register hooks */
1356 };