]> granicus.if.org Git - apache/blob - modules/aaa/mod_authnz_fcgi.c
Fix a comment similar to r1638072
[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, int *state,
410                           const char *readbuf, apr_size_t readlen)
411 {
412     const char *itr = readbuf;
413
414     while (readlen--) {
415         if (*itr == '\r') {
416             switch (*state) {
417                 case HDR_STATE_GOT_CRLF:
418                     *state = HDR_STATE_GOT_CRLFCR;
419                     break;
420
421                 default:
422                     *state = HDR_STATE_GOT_CR;
423                     break;
424             }
425         }
426         else if (*itr == '\n') {
427             switch (*state) {
428                  case HDR_STATE_GOT_LF:
429                      *state = HDR_STATE_DONE_WITH_HEADERS;
430                      break;
431
432                  case HDR_STATE_GOT_CR:
433                      *state = HDR_STATE_GOT_CRLF;
434                      break;
435
436                  case HDR_STATE_GOT_CRLFCR:
437                      *state = HDR_STATE_DONE_WITH_HEADERS;
438                      break;
439
440                  default:
441                      *state = HDR_STATE_GOT_LF;
442                      break;
443             }
444         }
445         else {
446             *state = HDR_STATE_READING_HEADERS;
447         }
448
449         if (*state == HDR_STATE_DONE_WITH_HEADERS)
450             break;
451
452         ++itr;
453     }
454
455     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
456         return 1;
457     }
458
459     return 0;
460 }
461
462 /*
463  * handle_response() is based on mod_proxy_fcgi's dispatch()
464  */
465 static apr_status_t handle_response(const fcgi_provider_conf *conf,
466                                     request_rec *r, apr_socket_t *s,
467                                     apr_pool_t *temp_pool,
468                                     apr_uint16_t request_id,
469                                     char *rspbuf,
470                                     apr_size_t *rspbuflen)
471 {
472     apr_bucket *b;
473     apr_bucket_brigade *ob;
474     apr_size_t orspbuflen = 0;
475     apr_status_t rv = APR_SUCCESS;
476     const char *fn = "handle_response";
477     int header_state = HDR_STATE_READING_HEADERS;
478     int seen_end_of_headers = 0, done = 0;
479
480     if (rspbuflen) {
481         orspbuflen = *rspbuflen;
482         *rspbuflen = 0; /* unless we actually read something */
483     }
484
485     ob = apr_brigade_create(r->pool, r->connection->bucket_alloc);
486
487     while (!done && rv == APR_SUCCESS) { /* Keep reading FastCGI records until
488                                           * we get AP_FCGI_END_REQUEST (done)
489                                           * or an error occurs.
490                                           */
491         apr_size_t readbuflen;
492         apr_uint16_t clen;
493         apr_uint16_t rid;
494         char readbuf[AP_IOBUFSIZE];
495         unsigned char farray[AP_FCGI_HEADER_LEN];
496         unsigned char plen;
497         unsigned char type;
498         unsigned char version;
499
500         rv = recv_data_full(conf, r, s, (char *)farray, AP_FCGI_HEADER_LEN);
501         if (rv != APR_SUCCESS) {
502             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
503                           APLOGNO(02501) "%s: Error occurred before reading "
504                           "entire header", fn);
505             break;
506         }
507
508         ap_fcgi_header_fields_from_array(&version, &type, &rid, &clen, &plen,
509                                          farray);
510
511         if (version != AP_FCGI_VERSION_1) {
512             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
513                           APLOGNO(02502) "%s: Got bogus FastCGI header "
514                           "version %d", fn, (int)version);
515             rv = APR_EINVAL;
516             break;
517         }
518
519         if (rid != request_id) {
520             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
521                           APLOGNO(02503) "%s: Got bogus FastCGI header "
522                           "request id %d, expected %d",
523                           fn, rid, request_id);
524             rv = APR_EINVAL;
525             break;
526         }
527
528     recv_again: /* if we need to keep reading more of a record's content */
529
530         if (clen > sizeof(readbuf)) {
531             readbuflen = sizeof(readbuf);
532         } else {
533             readbuflen = clen;
534         }
535
536         /*
537          * Now get the actual content of the record.
538          */
539         if (readbuflen != 0) {
540             rv = recv_data(conf, r, s, readbuf, &readbuflen);
541             if (rv != APR_SUCCESS) {
542                 break;
543             }
544         }
545
546         switch (type) {
547         case AP_FCGI_STDOUT: /* Response headers and optional body */
548             if (clen != 0) {
549                 b = apr_bucket_transient_create(readbuf,
550                                                 readbuflen,
551                                                 r->connection->bucket_alloc);
552
553                 APR_BRIGADE_INSERT_TAIL(ob, b);
554
555                 if (!seen_end_of_headers) {
556                     int st = handle_headers(r, &header_state,
557                                             readbuf, readbuflen);
558
559                     if (st == 1) {
560                         int status;
561
562                         seen_end_of_headers = 1;
563
564                         status =
565                             ap_scan_script_header_err_brigade_ex(r, ob,
566                                                                  NULL, 
567                                                                  APLOG_MODULE_INDEX);
568                         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
569                                       APLOGNO(02504) "%s: script header "
570                                       "parsing -> %d/%d",
571                                       fn, status, r->status);
572
573                         if (rspbuf) { /* caller wants to see response body,
574                                        * if any
575                                        */
576                             apr_status_t tmprv;
577
578                             if (rspbuflen) {
579                                 *rspbuflen = orspbuflen;
580                             }
581                             tmprv = apr_brigade_flatten(ob, rspbuf, rspbuflen);
582                             if (tmprv != APR_SUCCESS) {
583                                 /* should not occur for these bucket types;
584                                  * does not indicate overflow
585                                  */
586                                 ap_log_rerror(APLOG_MARK, APLOG_ERR, tmprv, r,
587                                               APLOGNO(02505) "%s: error "
588                                               "flattening response body",
589                                               fn);
590                             }
591                         }
592
593                         if (status != OK) {
594                             r->status = status;
595                             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
596                                           APLOGNO(02506) "%s: Error parsing "
597                                           "script headers from %s",
598                                           fn, conf->backend);
599                             rv = APR_EINVAL;
600                             break;
601                         }
602                         apr_pool_clear(temp_pool);
603                     }
604                     else {
605                         /* We're still looking for the end of the
606                          * headers, so this part of the data will need
607                          * to persist. */
608                         apr_bucket_setaside(b, temp_pool);
609                     }
610                 }
611
612                 /* If we didn't read all the data go back and get the
613                  * rest of it. */
614                 if (clen > readbuflen) {
615                     clen -= readbuflen;
616                     goto recv_again;
617                 }
618             }
619             break;
620
621         case AP_FCGI_STDERR: /* Text to log */
622             if (clen) {
623                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
624                               APLOGNO(02507) "%s: Logged from %s: '%s'",
625                               fn, conf->backend, readbuf);
626             }
627
628             if (clen > readbuflen) {
629                 clen -= readbuflen;
630                 goto recv_again; /* continue reading this record */
631             }
632             break;
633
634         case AP_FCGI_END_REQUEST:
635             done = 1;
636             break;
637
638         default:
639             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
640                           APLOGNO(02508) "%s: Got bogus FastCGI record type "
641                           "%d", fn, type);
642             break;
643         }
644         /* Leave on above switch's inner error. */
645         if (rv != APR_SUCCESS) {
646             break;
647         }
648
649         /*
650          * Read/discard any trailing padding.
651          */
652         if (plen) {
653             rv = recv_data_full(conf, r, s, readbuf, plen);
654             if (rv != APR_SUCCESS) {
655                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
656                               APLOGNO(02509) "%s: Error occurred reading "
657                               "padding",
658                               fn);
659                 break;
660             }
661         }
662     }
663
664     apr_brigade_cleanup(ob);
665
666     if (rv == APR_SUCCESS && !seen_end_of_headers) {
667         rv = APR_EINVAL;
668         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
669                       APLOGNO(02510) "%s: Never reached end of script headers",
670                       fn);
671     }
672
673     return rv;
674 }
675
676 /* almost from mod_fcgid */
677 static int mod_fcgid_modify_auth_header(void *vars,
678                                         const char *key, const char *val)
679 {
680     /* When the application gives a 200 response, the server ignores response
681        headers whose names aren't prefixed with Variable- prefix, and ignores
682        any response content */
683     if (ap_casecmpstrn(key, "Variable-", 9) == 0)
684         apr_table_setn(vars, key, val);
685     return 1;
686 }
687
688 static int fix_auth_header(void *vr, const char *key, const char *val)
689 {
690     request_rec *r = vr;
691
692     ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "moving %s->%s", key, val);
693     apr_table_unset(r->err_headers_out, key);
694     apr_table_setn(r->subprocess_env, key + 9, val);
695     return 1;
696 }
697
698 static void req_rsp(request_rec *r, const fcgi_provider_conf *conf,
699                     const char *password, const char *apache_role,
700                     char *rspbuf, apr_size_t *rspbuflen)
701 {
702     const char *fn = "req_rsp";
703     apr_pool_t *temp_pool;
704     apr_size_t orspbuflen = 0;
705     apr_socket_t *s;
706     apr_status_t rv;
707     apr_table_t *saved_subprocess_env = 
708       apr_table_copy(r->pool, r->subprocess_env);
709
710     if (rspbuflen) {
711         orspbuflen = *rspbuflen;
712         *rspbuflen = 0; /* unless we actually read something */
713     }
714
715     apr_pool_create(&temp_pool, r->pool);
716
717     setupenv(r, password, apache_role);
718
719     rv = connect_to_peer(&s, r, conf->backend_addrs,
720                          conf->backend, FCGI_IO_TIMEOUT);
721     if (rv == APR_SUCCESS) {
722         apr_uint16_t request_id = 1;
723
724         rv = send_begin_request(r, conf, s, AP_FCGI_AUTHORIZER, request_id);
725         if (rv != APR_SUCCESS) {
726             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
727                           APLOGNO(02511) "%s: Failed writing request to %s",
728                           fn, conf->backend);
729         }
730
731         if (rv == APR_SUCCESS) {
732             rv = send_environment(s, conf, r, request_id, temp_pool);
733             if (rv != APR_SUCCESS) {
734                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
735                               APLOGNO(02512) "%s: Failed writing environment "
736                               "to %s", fn, conf->backend);
737             }
738         }
739
740         /* The responder owns the request body, not the authorizer.
741          * Don't even send an empty AP_FCGI_STDIN block.  libfcgi doesn't care,
742          * but it wasn't sent to authorizers by mod_fastcgi or mod_fcgi and
743          * may be unhandled by the app.  Additionally, the FastCGI spec does
744          * not mention FCGI_STDIN in the Authorizer description, though it
745          * does describe FCGI_STDIN elsewhere in more general terms than
746          * simply a wrapper for the client's request body.
747          */
748
749         if (rv == APR_SUCCESS) {
750             if (rspbuflen) {
751                 *rspbuflen = orspbuflen;
752             }
753             rv = handle_response(conf, r, s, temp_pool, request_id, rspbuf,
754                                  rspbuflen);
755             if (rv != APR_SUCCESS) {
756                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
757                               APLOGNO(02514) "%s: Failed handling response "
758                               "from %s", fn, conf->backend);
759             }
760         }
761
762         apr_socket_close(s);
763     }
764
765     if (rv != APR_SUCCESS) {
766         /* some sort of mechanical problem */
767         r->status = HTTP_INTERNAL_SERVER_ERROR;
768     }
769     else {
770         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
771                       APLOGNO(02515) "%s: Received HTTP status %d",
772                       fn, r->status);
773     }
774
775     r->subprocess_env = saved_subprocess_env;
776
777     if (r->status == HTTP_OK) {
778         /* An Authorizer application's 200 response may include headers
779          * whose names are prefixed with Variable-, and they should be
780          * available to subsequent phases via subprocess_env (and yanked
781          * from the client response).
782          */
783         apr_table_t *vars = apr_table_make(temp_pool, /* not used to allocate
784                                                        * any values that end up
785                                                        * in r->(anything)
786                                                        */
787                                            10);
788         apr_table_do(mod_fcgid_modify_auth_header, vars,
789                      r->err_headers_out, NULL);
790         apr_table_do(fix_auth_header, r, vars, NULL);
791     }
792
793     apr_pool_destroy(temp_pool);
794 }
795
796 static int fcgi_check_authn(request_rec *r)
797 {
798     const char *fn = "fcgi_check_authn";
799     fcgi_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
800                                                 &authnz_fcgi_module);
801     const char *password = NULL;
802     const fcgi_provider_conf *conf;
803     const char *prov;
804     const char *auth_type;
805     char rspbuf[NON200_RESPONSE_BUF_LEN + 1]; /* extra byte for '\0' */
806     apr_size_t rspbuflen = sizeof rspbuf - 1;
807     int res;
808
809     prov = dconf && dconf->name ? dconf->name : NULL;
810
811     if (!prov || !ap_casecmpstr(prov, "None")) {
812         return DECLINED;
813     }
814
815     auth_type = ap_auth_type(r);
816
817     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
818                   APLOGNO(02516) "%s, prov %s, authoritative %s, "
819                   "require-basic %s, user expr? %s type %s",
820                   fn, prov,
821                   dconf->authoritative ? "yes" : "no",
822                   dconf->require_basic_auth ? "yes" : "no",
823                   dconf->user_expr ? "yes" : "no",
824                   auth_type);
825
826     if (auth_type && !ap_casecmpstr(auth_type, "Basic")) {
827         if ((res = ap_get_basic_auth_pw(r, &password))) {
828             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
829                           APLOGNO(02517) "%s: couldn't retrieve basic auth "
830                           "password", fn);
831             if (dconf->require_basic_auth) {
832                 return res;
833             }
834             password = NULL;
835         }
836     }
837
838     conf = apr_hash_get(fcgi_authn_providers, prov, APR_HASH_KEY_STRING);
839     if (!conf) {
840         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
841                       APLOGNO(02518) "%s: can't find config for provider %s",
842                       fn, prov);
843         return HTTP_INTERNAL_SERVER_ERROR;
844     }
845
846     if (APLOGrdebug(r)) {
847         log_provider_info(conf, r);
848     }
849
850     req_rsp(r, conf, password, AP_FCGI_APACHE_ROLE_AUTHENTICATOR_STR,
851             rspbuf, &rspbuflen);
852
853     if (r->status == HTTP_OK) {
854         if (dconf->user_expr) {
855             const char *err;
856             const char *user = ap_expr_str_exec(r, dconf->user_expr,
857                                                 &err);
858             if (user && strlen(user)) {
859                 r->user = apr_pstrdup(r->pool, user);
860                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
861                               APLOGNO(02519) "%s: Setting user to '%s'",
862                               fn, r->user);
863             }
864             else if (user && dconf->default_user) {
865                 r->user = apr_pstrdup(r->pool, dconf->default_user);
866             }
867             else if (user) {
868                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
869                               APLOGNO(02520) "%s: Failure extracting user "
870                               "after calling authorizer: user expression "
871                               "yielded empty string (variable not set?)",
872                               fn);
873                 r->status = HTTP_INTERNAL_SERVER_ERROR;
874             }
875             else {
876                 /* unexpected error, not even an empty string was returned */
877                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
878                               APLOGNO(02521) "%s: Failure extracting user "
879                               "after calling authorizer: %s",
880                               fn, err);
881                 r->status = HTTP_INTERNAL_SERVER_ERROR;
882             }
883         }
884         if (conf->is_authz) {
885             /* combined authn/authz phase, so app won't be invoked for authz
886              *
887              * Remember that the request was successfully authorized by this
888              * provider.
889              */
890             fcgi_request_notes *rnotes = apr_palloc(r->pool, sizeof(*rnotes));
891             rnotes->successful_authnz_provider = conf->name;
892             ap_set_module_config(r->request_config, &authnz_fcgi_module,
893                                  rnotes);
894         }
895     }
896     else {
897         /* From the spec:
898          *   For Authorizer response status values other than "200" (OK), the 
899          *   Web server denies access and sends the response status, headers,
900          *   and content back to the HTTP client.
901          * But:
902          *   This only makes sense if this authorizer is authoritative.
903          */
904         if (rspbuflen > 0 && !dconf->authoritative) {
905             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
906                           APLOGNO(02522) "%s: Ignoring response body from non-"
907                           "authoritative authorizer", fn);
908         }
909         else if (rspbuflen > 0) {
910             if (rspbuflen == sizeof rspbuf - 1) {
911                 /* apr_brigade_flatten() interface :( */
912                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
913                               APLOGNO(02523) "%s: possible overflow handling "
914                               "response body", fn);
915             }
916             rspbuf[rspbuflen] = '\0'; /* we reserved an extra byte for '\0' */
917             ap_custom_response(r, r->status, rspbuf); /* API makes a copy */
918         }
919     }
920
921     return r->status == HTTP_OK ? 
922         OK : dconf->authoritative ? r->status : DECLINED;
923 }
924
925 static authn_status fcgi_check_password(request_rec *r, const char *user,
926                                         const char *password)
927 {
928     const char *fn = "fcgi_check_password";
929     const char *prov = apr_table_get(r->notes, AUTHN_PROVIDER_NAME_NOTE);
930     const fcgi_provider_conf *conf;
931
932     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
933                   APLOGNO(02524) "%s(%s, XXX): provider %s",
934                   fn, user, prov);
935
936     if (!prov) {
937         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
938                       APLOGNO(02525) "%s: provider note isn't set", fn);
939         return AUTH_GENERAL_ERROR;
940     }
941
942     conf = apr_hash_get(fcgi_authn_providers, prov, APR_HASH_KEY_STRING);
943     if (!conf) {
944         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
945                       APLOGNO(02526) "%s: can't find config for provider %s",
946                       fn, prov);
947         return AUTH_GENERAL_ERROR;
948     }
949
950     if (APLOGrdebug(r)) {
951         log_provider_info(conf, r);
952     }
953
954     req_rsp(r, conf, password, 
955             /* combined authn and authz: FCGI_APACHE_ROLE not set */
956             conf->is_authz ? NULL : AP_FCGI_APACHE_ROLE_AUTHENTICATOR_STR,
957             NULL, NULL);
958
959     if (r->status == HTTP_OK) {
960         if (conf->is_authz) {
961             /* combined authn/authz phase, so app won't be invoked for authz
962              *
963              * Remember that the request was successfully authorized by this
964              * provider.
965              */
966             fcgi_request_notes *rnotes = apr_palloc(r->pool, sizeof(*rnotes));
967             rnotes->successful_authnz_provider = conf->name;
968             ap_set_module_config(r->request_config, &authnz_fcgi_module,
969                                  rnotes);
970         }
971         return AUTH_GRANTED;
972     }
973     else if (r->status == HTTP_INTERNAL_SERVER_ERROR) {
974         return AUTH_GENERAL_ERROR;
975     }
976     else {
977         return AUTH_DENIED;
978     }
979 }
980
981 static const authn_provider fcgi_authn_provider = {
982     &fcgi_check_password,
983     NULL /* get-realm-hash not supported */
984 };
985
986 static authz_status fcgi_authz_check(request_rec *r,
987                                      const char *require_line,
988                                      const void *parsed_require_line)
989 {
990     const char *fn = "fcgi_authz_check";
991     const char *prov = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
992     const fcgi_provider_conf *conf;
993
994     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
995                   APLOGNO(02527) "%s(%s)", fn, require_line);
996
997     if (!prov) {
998         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
999                       APLOGNO(02528) "%s: provider note isn't set", fn);
1000         return AUTHZ_GENERAL_ERROR;
1001     }
1002
1003     conf = apr_hash_get(fcgi_authz_providers, prov, APR_HASH_KEY_STRING);
1004     if (!conf) {
1005         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
1006                       APLOGNO(02529) "%s: can't find config for provider %s",
1007                       fn, prov);
1008         return AUTHZ_GENERAL_ERROR;
1009     }
1010
1011     if (APLOGrdebug(r)) {
1012         log_provider_info(conf, r);
1013     }
1014
1015     if (!r->user) {
1016         return AUTHZ_DENIED_NO_USER;
1017     }
1018
1019     if (conf->is_authn) {
1020         /* combined authn/authz phase, so app won't be invoked for authz
1021          *
1022          * If the provider already successfully authorized this request, 
1023          * success.
1024          */
1025         fcgi_request_notes *rnotes = ap_get_module_config(r->request_config,
1026                                                         &authnz_fcgi_module);
1027         if (rnotes
1028             && rnotes->successful_authnz_provider
1029             && !strcmp(rnotes->successful_authnz_provider, conf->name)) {
1030             return AUTHZ_GRANTED;
1031         }
1032         else {
1033             return AUTHZ_DENIED;
1034         }
1035     }
1036     else {
1037         req_rsp(r, conf, NULL, AP_FCGI_APACHE_ROLE_AUTHORIZER_STR, NULL, NULL);
1038
1039         if (r->status == HTTP_OK) {
1040             return AUTHZ_GRANTED;
1041         }
1042         else if (r->status == HTTP_INTERNAL_SERVER_ERROR) {
1043             return AUTHZ_GENERAL_ERROR;
1044         }
1045         else {
1046             return AUTHZ_DENIED;
1047         }
1048     }
1049 }
1050
1051 static const char *fcgi_authz_parse(cmd_parms *cmd, const char *require_line,
1052                                     const void **parsed_require_line)
1053 {
1054     /* Allowed form: Require [not] registered-provider-name<EOS>
1055      */
1056     if (strcmp(require_line, "")) {
1057         return "mod_authnz_fcgi doesn't support restrictions on providers "
1058                "(i.e., multiple require args)";
1059     }
1060
1061     return NULL;
1062 }
1063
1064 static const authz_provider fcgi_authz_provider = {
1065     &fcgi_authz_check,
1066     &fcgi_authz_parse,
1067 };
1068
1069 static const char *fcgi_check_authn_provider(cmd_parms *cmd,
1070                                         void *d,
1071                                         int argc,
1072                                         char *const argv[])
1073 {
1074     const char *dname = "AuthnzFcgiCheckAuthnProvider";
1075     fcgi_dir_conf *dc = d;
1076     int ca = 0;
1077
1078     if (ca >= argc) {
1079         return apr_pstrcat(cmd->pool, dname, ": No provider given", NULL);
1080     }
1081
1082     dc->name = argv[ca];
1083     ca++;
1084
1085     if (!strcasecmp(dc->name, "None")) {
1086         if (ca < argc) {
1087             return "Options aren't supported with \"None\"";
1088         }
1089     }
1090
1091     while (ca < argc) {
1092         const char *var = argv[ca], *val;
1093         int badarg = 0;
1094
1095         ca++;
1096
1097         /* at present, everything needs an argument */
1098         if (ca >= argc) {
1099             return apr_pstrcat(cmd->pool, dname, ": ", var,
1100                                "needs an argument", NULL);
1101         }
1102
1103         val = argv[ca];
1104         ca++;
1105
1106         if (!strcasecmp(var, "Authoritative")) {
1107             if (!strcasecmp(val, "On")) {
1108                 dc->authoritative = 1;
1109             }
1110             else if (!strcasecmp(val, "Off")) {
1111                 dc->authoritative = 0;
1112             }
1113             else {
1114                 badarg = 1;
1115             }
1116         }
1117         else if (!strcasecmp(var, "DefaultUser")) {
1118             dc->default_user = val;
1119         }
1120         else if (!strcasecmp(var, "RequireBasicAuth")) {
1121             if (!strcasecmp(val, "On")) {
1122                 dc->require_basic_auth = 1;
1123             }
1124             else if (!strcasecmp(val, "Off")) {
1125                 dc->require_basic_auth = 0;
1126             }
1127             else {
1128                 badarg = 1;
1129             }
1130         }
1131         else if (!strcasecmp(var, "UserExpr")) {
1132             const char *err;
1133             int flags = AP_EXPR_FLAG_DONT_VARY | AP_EXPR_FLAG_RESTRICTED
1134                 | AP_EXPR_FLAG_STRING_RESULT;
1135
1136             dc->user_expr = ap_expr_parse_cmd(cmd, val,
1137                                               flags, &err, NULL);
1138             if (err) {
1139                 return apr_psprintf(cmd->pool, "%s: Error parsing '%s': '%s'",
1140                                     dname, val, err);
1141             }
1142         }
1143         else {
1144             return apr_pstrcat(cmd->pool, dname, ": Unexpected option '",
1145                                var, "'", NULL);
1146         }
1147         if (badarg) {
1148             return apr_pstrcat(cmd->pool, dname, ": Bad argument '",
1149                                val, "' to option '", var, "'", NULL);
1150         }
1151     }
1152
1153     return NULL;
1154 }
1155
1156 /* AuthnzFcgiAuthDefineProvider {authn|authz|authnz} provider-name \
1157  *   fcgi://backendhost:backendport/
1158  */
1159 static const char *fcgi_define_provider(cmd_parms *cmd,
1160                                         void *d,
1161                                         int argc,
1162                                         char *const argv[])
1163 {
1164     const char *dname = "AuthnzFcgiDefineProvider";
1165     ap_rxplus_t *fcgi_backend_regex;
1166     apr_status_t rv;
1167     char *host;
1168     const char *err, *stype;
1169     fcgi_provider_conf *conf = apr_pcalloc(cmd->pool, sizeof(*conf));
1170     int ca = 0, rc, port;
1171
1172     fcgi_backend_regex = ap_rxplus_compile(cmd->pool, FCGI_BACKEND_REGEX_STR);
1173     if (!fcgi_backend_regex) {
1174         return apr_psprintf(cmd->pool,
1175                             "%s: failed to compile regexec '%s'",
1176                             dname, FCGI_BACKEND_REGEX_STR);
1177     }
1178
1179     err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1180     if (err)
1181         return err;
1182
1183     if (ca >= argc) {
1184         return apr_pstrcat(cmd->pool, dname, ": No type given", NULL);
1185     }
1186
1187     stype = argv[ca];
1188     ca++;
1189
1190     if (!strcasecmp(stype, "authn")) {
1191         conf->is_authn = 1;
1192     }
1193     else if (!strcasecmp(stype, "authz")) {
1194         conf->is_authz = 1;
1195     }
1196     else if (!strcasecmp(stype, "authnz")) {
1197         conf->is_authn = conf->is_authz = 1;
1198     }
1199     else {
1200         return apr_pstrcat(cmd->pool,
1201                            dname,
1202                            ": Invalid provider type ",
1203                            stype,
1204                            NULL);
1205     }
1206
1207     if (ca >= argc) {
1208         return apr_pstrcat(cmd->pool, dname, ": No provider name given", NULL);
1209     }
1210     conf->name = argv[ca];
1211     ca++;
1212
1213     if (ca >= argc) {
1214         return apr_pstrcat(cmd->pool, dname, ": No backend-address given",
1215                            NULL);
1216     }
1217
1218     rc = ap_rxplus_exec(cmd->pool, fcgi_backend_regex, argv[ca], NULL);
1219     if (!rc || ap_rxplus_nmatch(fcgi_backend_regex) != 3) {
1220         return apr_pstrcat(cmd->pool,
1221                            dname, ": backend-address '",
1222                            argv[ca],
1223                            "' has invalid form",
1224                            NULL);
1225     }
1226
1227     host = ap_rxplus_pmatch(cmd->pool, fcgi_backend_regex, 1);
1228     if (host[0] == '[' && host[strlen(host) - 1] == ']') {
1229         host += 1;
1230         host[strlen(host) - 1] = '\0';
1231     }
1232
1233     port = atoi(ap_rxplus_pmatch(cmd->pool, fcgi_backend_regex, 2));
1234     if (port > 65535) {
1235         return apr_pstrcat(cmd->pool,
1236                            dname, ": backend-address '",
1237                            argv[ca],
1238                            "' has invalid port",
1239                            NULL);
1240     }
1241
1242     conf->backend = argv[ca];
1243     conf->host = host;
1244     conf->port = port;
1245     ca++;
1246
1247     rv = apr_sockaddr_info_get(&conf->backend_addrs, conf->host,
1248                                APR_UNSPEC, conf->port, 0, cmd->pool);
1249     if (rv != APR_SUCCESS) {
1250         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, rv, NULL,
1251                      APLOGNO(02530) "Address %s could not be resolved",
1252                      conf->backend);
1253         return apr_pstrcat(cmd->pool,
1254                            dname,
1255                            ": Error resolving backend address",
1256                            NULL);
1257     }
1258
1259     if (ca != argc) {
1260         return apr_pstrcat(cmd->pool,
1261                            dname,
1262                            ": Unexpected parameter ",
1263                            argv[ca],
1264                            NULL);
1265     }
1266
1267     if (conf->is_authn) {
1268         apr_hash_set(fcgi_authn_providers, conf->name, APR_HASH_KEY_STRING,
1269                      conf);
1270         ap_register_auth_provider(cmd->pool, AUTHN_PROVIDER_GROUP,
1271                                   conf->name,
1272                                   AUTHN_PROVIDER_VERSION,
1273                                   &fcgi_authn_provider,
1274                                   AP_AUTH_INTERNAL_PER_CONF);
1275     }
1276
1277     if (conf->is_authz) {
1278         apr_hash_set(fcgi_authz_providers, conf->name, APR_HASH_KEY_STRING,
1279                      conf);
1280         ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP,
1281                                   conf->name,
1282                                   AUTHZ_PROVIDER_VERSION,
1283                                   &fcgi_authz_provider,
1284                                   AP_AUTH_INTERNAL_PER_CONF);
1285     }
1286
1287     return NULL;
1288 }
1289
1290 static const command_rec fcgi_cmds[] = {
1291     AP_INIT_TAKE_ARGV("AuthnzFcgiDefineProvider", 
1292                       fcgi_define_provider,
1293                       NULL,
1294                       RSRC_CONF,
1295                       "Define a FastCGI authn and/or authz provider"),
1296
1297     AP_INIT_TAKE_ARGV("AuthnzFcgiCheckAuthnProvider",
1298                       fcgi_check_authn_provider,
1299                       NULL,
1300                       OR_FILEINFO,
1301                       "Enable/disable a FastCGI authorizer to handle "
1302                       "check_authn phase"),
1303
1304     {NULL}
1305 };
1306
1307 static int fcgi_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
1308                            apr_pool_t *ptemp)
1309 {
1310     fcgi_authn_providers = apr_hash_make(pconf);
1311     fcgi_authz_providers = apr_hash_make(pconf);
1312
1313     return OK;
1314 }
1315
1316 static void fcgi_register_hooks(apr_pool_t *p)
1317 {
1318     static const char * const auth_basic_runs_after_me[] = 
1319         {"mod_auth_basic.c", NULL}; /* to allow for custom response */
1320
1321     ap_hook_pre_config(fcgi_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1322     ap_hook_check_authn(fcgi_check_authn, NULL, auth_basic_runs_after_me,
1323                         APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF);
1324 }
1325
1326 static void *create_dir_conf(apr_pool_t *p, char *dummy)
1327 {
1328     fcgi_dir_conf *dconf = apr_pcalloc(p, sizeof(fcgi_dir_conf));
1329
1330     dconf->authoritative = 1;
1331     return dconf;
1332 }
1333
1334 static void *merge_dir_conf(apr_pool_t *p, void *basev, void *overridesv)
1335 {
1336     fcgi_dir_conf *a = (fcgi_dir_conf *)apr_pcalloc(p, sizeof(*a));
1337     fcgi_dir_conf *base = (fcgi_dir_conf *)basev, 
1338         *over = (fcgi_dir_conf *)overridesv;
1339
1340     /* currently we just have a single directive applicable to a 
1341      * directory, so if it is set then grab all fields from fcgi_dir_conf
1342      */
1343     if (over->name) {
1344         memcpy(a, over, sizeof(*a));
1345     }
1346     else {
1347         memcpy(a, base, sizeof(*a));
1348     }
1349     
1350     return a;
1351 }
1352
1353 AP_DECLARE_MODULE(authnz_fcgi) =
1354 {
1355     STANDARD20_MODULE_STUFF,
1356     create_dir_conf,                 /* dir config creater */
1357     merge_dir_conf,                  /* dir merger */
1358     NULL,                            /* server config */
1359     NULL,                            /* merge server config */
1360     fcgi_cmds,                       /* command apr_table_t */
1361     fcgi_register_hooks              /* register hooks */
1362 };