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