"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mod_setenvifplus.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
0.39_vs_0.40.
1 /* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 */
3
4 /**
5 * mod_setenvifplus.c: Apache httpd module to set environment variables
6 * See http://modsetenvifplus.sourceforge.net/ for further
7 * details about mod_setenvifplus.
8 *
9 * Copyright (C) 2020 Pascal Buchbinder
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed with
13 * this work for additional information regarding copyright ownership.
14 * The ASF licenses this file to You under the Apache License, Version 2.0
15 * (the "License"); you may not use this file except in compliance with
16 * the License. You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 */
26
27 /************************************************************************
28 * Version
29 ***********************************************************************/
30 static const char revision[] = "$Id: mod_setenvifplus.c 177 2020-07-01 05:12:41Z pbuchbinder $";
31 static const char g_revision[] = "0.40";
32
33 /************************************************************************
34 * Includes
35 ***********************************************************************/
36
37 #include <arpa/inet.h>
38
39 /* openssl */
40 #include <openssl/crypto.h>
41 #include <openssl/dh.h>
42 #include <openssl/bn.h>
43 #include <openssl/rand.h>
44 #include <openssl/evp.h>
45 #include <openssl/err.h>
46
47 /* apr */
48 #include "apr.h"
49 #include "apr_strings.h"
50 #include "apr_strmatch.h"
51 #include "apr_lib.h"
52 #include "apr_base64.h"
53
54 #define APR_WANT_STRFUNC
55 #include "apr_want.h"
56
57 /* apache */
58 //#include "ap_config.h"
59 #include "httpd.h"
60 #include "http_config.h"
61 #include "http_core.h"
62 #include "http_log.h"
63 #include "http_protocol.h"
64 #include "http_request.h"
65 #include <util_filter.h>
66 #include "util_md5.h"
67
68 /************************************************************************
69 * defines
70 ***********************************************************************/
71 #define SP_LOG_PFX(id) "mod_setenvifplus("#id"): "
72 #define SP_COOKIE_KEY "SP_COOKIE_KEY"
73 #define SP_KEY "SP_KEY"
74 #define SP_RAND_SIZE 18
75
76 enum sp_special {
77 SP_SPECIAL_NOT,
78 SP_SPECIAL_REMOTE_ADDR,
79 SP_SPECIAL_REMOTE_NET,
80 SP_SPECIAL_REMOTE_HOST,
81 SP_SPECIAL_REQUEST_URI,
82 SP_SPECIAL_REQUEST_QUERY,
83 SP_SPECIAL_REQUEST_USER,
84 SP_SPECIAL_REQUEST_METHOD,
85 SP_SPECIAL_REQUEST_PROTOCOL,
86 SP_SPECIAL_SERVER_ADDR,
87 SP_SPECIAL_SERVER_PORT,
88 SP_SPECIAL_RESPONSE_STATUS
89 };
90
91 enum sp_action {
92 SP_ACTION_SET,
93 SP_ACTION_UNSET,
94 SP_ACTION_CHANGE,
95 SP_ACTION_ADD
96 };
97
98 enum sp_cmp {
99 SP_CMP_EQ,
100 SP_CMP_NE,
101 SP_CMP_GT,
102 SP_CMP_LT
103 };
104
105 // Apache 2.4 compat
106 #if (AP_SERVER_MINORVERSION_NUMBER == 4)
107 #define SF_CONN_REMOTEIP(r) r->connection->client_ip
108 #define SF_CONN_REMOTE_ADDR(r) r->connection->client_addr
109 #else
110 #define SF_CONN_REMOTEIP(r) r->connection->remote_ip
111 #define SF_CONN_REMOTE_ADDR(r) r->connection->remote_addr
112 #endif
113
114 /************************************************************************
115 * structures
116 ***********************************************************************/
117 typedef struct {
118 const char *name; /* attribute name */
119 ap_regex_t *pnamereg; /* compiled attribute name (optional regex) */
120 const char *regex; /* regex to match against */
121 ap_regex_t *preg; /* compiled regex */
122 const apr_strmatch_pattern *pattern; /* non-regex pattern to match */
123 const char *variable; /* env var name to set (or unset) */
124 const char *value; /* env var value to set */
125 int valueSet; /* indicates, that a value has been configured by the user*/
126 enum sp_special special_type; /* is it a "special" attribute ? */
127 int icase; /* ignoring case? */
128 } sp_std_entry_t;
129
130 typedef struct {
131 const char *name;
132 ap_regex_t *preg; /* compiled regex (for SP_ACTION_CHANGE only) */
133 const char *value;
134 const char *condition;
135 enum sp_action action;
136 } sp_hdr_entry_t;
137
138 typedef struct {
139 enum sp_cmp cmp;
140 const char *left;
141 const char *right;
142 const char *variable;
143 const char *value;
144 } sp_cmp_entry_t;
145
146 typedef struct {
147 const char *header;
148 ap_regex_t *preg;
149 const char *value;
150 } sp_usr_entry_t;
151
152 typedef struct {
153 int code;
154 const char *var;
155 } sp_status_entry_t;
156
157 typedef struct {
158 const char *header;
159 const char *var;
160 } sp_hash_entry_t;
161
162 typedef struct {
163 const char *filtername;
164 const char *var;
165 int notset;
166 } sp_filter_entry_t;
167
168 typedef struct {
169 char *path;
170 apr_array_header_t *std_conditionals;
171 apr_array_header_t *std_late_conditionals;
172 apr_array_header_t *std_req_query;
173 apr_array_header_t *std_req_changequery;
174 apr_array_header_t *std_res_conditionals;
175 apr_array_header_t *std_req_header;
176 apr_array_header_t *std_late_req_header;
177 apr_array_header_t *std_res_header;
178 apr_array_header_t *user;
179 apr_array_header_t *std_removepattern_req_header;
180 apr_array_header_t *status_var;
181 apr_array_header_t *outfilter;
182 apr_array_header_t *cookie_names;
183 apr_array_header_t *header_hash;
184 apr_array_header_t* cmp;
185 apr_array_header_t* cmp_late;
186 } sp_config_t;
187
188 /* functions to replace patterns */
189 typedef struct {
190 char *identifier; /** name of the function inc. opening
191 bracket, e.g. "e64(" */
192 int len;
193 char close; /** end marker, e.g. ')' */
194 char *(*func)(request_rec *, char *); /** pointer to the function */
195 } sp_function_t;
196
197 typedef struct sp_url_escape_seq {
198 char c;
199 char *esc;
200 apr_size_t len;
201 } sp_url_escape_seq_t;
202
203 /************************************************************************
204 * globals
205 ***********************************************************************/
206 module AP_MODULE_DECLARE_DATA setenvifplus_module;
207 #define SP_ICASE_MAGIC ((void *)(&setenvifplus_module))
208
209 #ifdef SP_VAR_PFX_24
210 #define SP_VAR_PFX "%{"
211 #else
212 #define SP_VAR_PFX "${"
213 #endif
214 static char *m_sp_var_pfx = SP_VAR_PFX;
215
216 /************************************************************************
217 * private
218 ***********************************************************************/
219
220 /**
221 * Returns the client's network (IPv4/24 or IPv6/64) number.
222 */
223 static char *sp_get_remote_net(request_rec *r) {
224 apr_sockaddr_t *remote_addr = SF_CONN_REMOTE_ADDR(r);
225 if(remote_addr->family == AF_INET6) {
226 char *dst = apr_pcalloc(r->pool, INET6_ADDRSTRLEN);
227 char *ret;
228 apr_uint32_t *addr = (apr_uint32_t *)remote_addr->ipaddr_ptr;
229 apr_uint32_t dest[4];
230 if(IN6_IS_ADDR_V4MAPPED((struct in6_addr *)remote_addr->ipaddr_ptr)) {
231 dest[0] = addr[0];
232 dest[1] = addr[1];
233 dest[2] = addr[2];
234 dest[3] = addr[3] & 0x00ffffff;
235 } else {
236 dest[0] = addr[0];
237 dest[1] = addr[1];
238 dest[2] = 0;
239 dest[3] = 0;
240 }
241 ret = (char *)inet_ntop(AF_INET6, dest, dst, INET6_ADDRSTRLEN);
242 return ret;
243 } else {
244 char *dst = apr_pcalloc(r->pool, INET_ADDRSTRLEN);
245 char *ret;
246 apr_uint32_t *addr = (apr_uint32_t *)remote_addr->ipaddr_ptr;
247 apr_uint32_t dest[1];
248 dest[0] = addr[0] & 0x00ffffff;
249 ret = (char *)inet_ntop(AF_INET, dest, dst, INET_ADDRSTRLEN);
250 return ret;
251 }
252 }
253
254 /**
255 * Decryptes encrypted and base64 encoded string. See sp_enc64().
256 *
257 * @param r
258 * @param str String to decrypt
259 * @return Decrypted string or NULL on error
260 */
261 static char *sp_dec64(request_rec *r, const char *str, unsigned char *key) {
262 #if OPENSSL_VERSION_NUMBER < 0x10100000L
263 EVP_CIPHER_CTX cipher_ctx;
264 EVP_CIPHER_CTX *cipher_ctx_p = &cipher_ctx;
265 #else
266 EVP_CIPHER_CTX *cipher_ctx_p;
267 #endif
268 unsigned char *hashIn;
269 unsigned char hash[APR_MD5_DIGESTSIZE];
270 apr_md5_ctx_t md5;
271 int len = 0;
272 int buf_len = 0;
273 unsigned char *buf;
274 char *dec = (char *)apr_palloc(r->pool, 1 + apr_base64_decode_len(str));
275 int dec_len = apr_base64_decode(dec, str);
276 buf = apr_pcalloc(r->pool, dec_len);
277
278 #if OPENSSL_VERSION_NUMBER < 0x10100000L
279 EVP_CIPHER_CTX_init(cipher_ctx_p);
280 #else
281 cipher_ctx_p = EVP_CIPHER_CTX_new();
282 #endif
283 EVP_DecryptInit(cipher_ctx_p, EVP_des_ede3_cbc(), key, NULL);
284 if(!EVP_DecryptUpdate(cipher_ctx_p, (unsigned char *)&buf[buf_len], &len,
285 (const unsigned char *)dec, dec_len)) {
286 goto failed;
287 }
288 buf_len+=len;
289 if(!EVP_DecryptFinal(cipher_ctx_p, (unsigned char *)&buf[buf_len], &len)) {
290 goto failed;
291 }
292 buf_len+=len;
293
294 if(buf_len < (SP_RAND_SIZE + APR_MD5_DIGESTSIZE)) {
295 goto failed;
296 }
297 if(buf[SP_RAND_SIZE-1] != 'A' || buf[SP_RAND_SIZE-2] != 'z') {
298 goto failed;
299 }
300
301 hashIn = &buf[SP_RAND_SIZE];
302 buf = &buf[SP_RAND_SIZE + APR_MD5_DIGESTSIZE];
303 buf_len = buf_len - SP_RAND_SIZE - APR_MD5_DIGESTSIZE;
304
305 apr_md5_init(&md5);
306 apr_md5_update(&md5, buf, buf_len);
307 apr_md5_final(hash, &md5);
308
309 if(memcmp(hash, hashIn, APR_MD5_DIGESTSIZE) != 0) {
310 goto failed;
311 }
312
313 #if OPENSSL_VERSION_NUMBER < 0x10100000L
314 EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
315 #else
316 EVP_CIPHER_CTX_free(cipher_ctx_p);
317 #endif
318
319 return apr_pstrndup(r->pool, (char *)buf, buf_len);
320
321 failed:
322 #if OPENSSL_VERSION_NUMBER < 0x10100000L
323 EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
324 #else
325 EVP_CIPHER_CTX_free(cipher_ctx_p);
326 #endif
327 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
328 SP_LOG_PFX(010)"Failed to decrypt data. (%s)", str ? str : "");
329 return NULL;
330 }
331
332 /**
333 * Encrypts and base64 encodes a string. See sp_dec64().
334 *
335 * @param r
336 * @param str String to encrypt
337 * @return Encrypted string.
338 */
339 static char *sp_enc64(request_rec *r, const char *str, unsigned char *key) {
340 char *e;
341 #if OPENSSL_VERSION_NUMBER < 0x10100000L
342 EVP_CIPHER_CTX cipher_ctx;
343 EVP_CIPHER_CTX *cipher_ctx_p = &cipher_ctx;
344 #else
345 EVP_CIPHER_CTX *cipher_ctx_p;
346 #endif
347 unsigned char hash[APR_MD5_DIGESTSIZE];
348 apr_md5_ctx_t md5;
349 int len = 0;
350 int buf_len = 0;
351 int str_len = strlen(str);
352 int max_buf_len = SP_RAND_SIZE +
353 str_len +
354 EVP_CIPHER_block_size(EVP_des_ede3_cbc()) +
355 APR_MD5_DIGESTSIZE;
356 unsigned char *buf = apr_pcalloc(r->pool, max_buf_len);
357 unsigned char *rand = apr_pcalloc(r->pool, SP_RAND_SIZE);
358 #if APR_HAS_RANDOM
359 if(apr_generate_random_bytes(rand, SP_RAND_SIZE) != APR_SUCCESS) {
360 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
361 SP_LOG_PFX(030)"Can't generate random data.");
362 }
363 #else
364 if(!RAND_bytes(rand, SP_RAND_SIZE)) {
365 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
366 SP_LOG_PFX(030)"Can't generate random data.");
367 }
368 #endif
369 apr_md5_init(&md5);
370 apr_md5_update(&md5, str, str_len);
371 apr_md5_final(hash, &md5);
372 rand[SP_RAND_SIZE-1] = 'A';
373 rand[SP_RAND_SIZE-2] = 'z';
374 #if OPENSSL_VERSION_NUMBER < 0x10100000L
375 EVP_CIPHER_CTX_init(cipher_ctx_p);
376 #else
377 cipher_ctx_p = EVP_CIPHER_CTX_new();
378 #endif
379 EVP_EncryptInit(cipher_ctx_p, EVP_des_ede3_cbc(), key, NULL);
380 if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
381 rand, SP_RAND_SIZE)) {
382 goto failed;
383 }
384 buf_len+=len;
385 if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
386 hash, APR_MD5_DIGESTSIZE)) {
387 goto failed;
388 }
389 buf_len+=len;
390 if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
391 (const unsigned char *)str, str_len)) {
392 goto failed;
393 }
394 buf_len+=len;
395 if(!EVP_EncryptFinal(cipher_ctx_p, &buf[buf_len], &len)) {
396 goto failed;
397 }
398 buf_len+=len;
399 #if OPENSSL_VERSION_NUMBER < 0x10100000L
400 EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
401 #else
402 EVP_CIPHER_CTX_free(cipher_ctx_p);
403 #endif
404
405 e = (char *)apr_pcalloc(r->pool, 1 + apr_base64_encode_len(buf_len));
406 len = apr_base64_encode(e, (const char *)buf, buf_len);
407 e[len] = '\0';
408 return e;
409
410 failed:
411 #if OPENSSL_VERSION_NUMBER < 0x10100000L
412 EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
413 #else
414 EVP_CIPHER_CTX_free(cipher_ctx_p);
415 #endif
416 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
417 SP_LOG_PFX(011)"Failed to encrypt data.");
418 return "";
419 }
420
421 /**
422 * Similar to strstr but restricting the length of s1 (if not null terminated).
423 *
424 * @param s1 String to search in
425 * @param s2 Pattern to ind
426 * @param len Length of s1
427 * @return pointer to the beginning of the substring s2 within s1, or NULL
428 * if the substring is not found
429 */
430 static char *sp_strnstr(const char *s1, const char *s2, int len) {
431 const char *e1 = &s1[len-1];
432 const char *p, *q;
433 for (; *s1 && (s1 <= e1); s1++) {
434 p = s1, q = s2;
435 while(*q && *p && (q <= e1)) {
436 if (*q != *p) {
437 break;
438 }
439 p++, q++;
440 }
441 if(q > e1) {
442 return NULL;
443 }
444 if(*q == 0) {
445 return (char *)s1;
446 }
447 }
448 return 0;
449 }
450
451 static int sp_isnum(const char *x) {
452 const char *p = x;
453 if(x == NULL || x[0] == 0) {
454 return 0;
455 }
456 while(p && p[0]) {
457 if(!apr_isdigit(p[0])) {
458 return 0;
459 }
460 p++;
461 }
462 return 1;
463 }
464 /**
465 * Retuns the hex value of the character
466 *
467 * @param x
468 * @return hex value
469 */
470 static int sp_hex2c(const char *x) {
471 int i, ch;
472 ch = x[0];
473 if (apr_isdigit(ch)) {
474 i = ch - '0';
475 }else if (apr_isupper(ch)) {
476 i = ch - ('A' - 10);
477 } else {
478 i = ch - ('a' - 10);
479 }
480 i <<= 4;
481
482 ch = x[1];
483 if (apr_isdigit(ch)) {
484 i += ch - '0';
485 } else if (apr_isupper(ch)) {
486 i += ch - ('A' - 10);
487 } else {
488 i += ch - ('a' - 10);
489 }
490 return i;
491 }
492
493 #define SP_ISHEX(x) (((x >= '0') && (x <= '9')) || \
494 ((x >= 'a') && (x <= 'f')) || \
495 ((x >= 'A') && (x <= 'F')))
496
497
498 /**
499 * URL escaping/encoding (%xx, '+')
500 *
501 * @param pool To allocate encoded string from.
502 * @param x String to encode
503 * @retrun encoded string
504 */
505 static char *sp_url_encoding(apr_pool_t *pool, const char *x) {
506 int i = 0;
507 int k = 0;
508 int len = strlen(x);
509 char *c = apr_pcalloc(pool, (3 * len) + 1);
510 unsigned char *in = (unsigned char *)x;
511 while(in[i]) {
512 if(strchr("?\n /;%=\"'.:@\\&+", in[i]) != NULL) {
513 sprintf(&c[k], "%%%02x", in[i]);
514 k+=3;
515 } else {
516 c[k] = in[i];
517 k++;
518 }
519 i++;
520 }
521 c[k] = '\0';
522 return c;
523 }
524
525 /**
526 * URL unescaping/decoding (%xx, '+')
527 *
528 * @param x String to decode
529 * @param error Error message if decoding fails
530 * @return Length of the decoded string
531 */
532 static int sp_url_decoding(char *x, int *error) {
533 int i, j, ch;
534 if(x == 0) {
535 return 0;
536 }
537 if(x[0] == '\0') {
538 return 0;
539 }
540 for(i = 0, j = 0; x[i] != '\0'; i++, j++) {
541 ch = x[i];
542 if(ch == '%') {
543 if(SP_ISHEX(x[i + 1]) && SP_ISHEX(x[i + 2])) {
544 ch = sp_hex2c(&x[i + 1]);
545 i += 2;
546 } else {
547 (*error)++;
548 }
549 } else if(ch == '+') {
550 ch = ' ';
551 }
552 x[j] = ch;
553 }
554 x[j] = '\0';
555 return j;
556 }
557
558 /**
559 * If a Header name contains characters other than:
560 * -,_,[A-Z\, [a-z] and [0-9].
561 * assume the header name is a regular expression.
562 *
563 * @param p
564 * @param name
565 * @return 1 if it is a regular expression or 0 if it can be treaten as literal string
566 */
567 static int sp_is_header_regex(apr_pool_t *p, const char* name) {
568 ap_regex_t *preg = ap_pregcomp(p, "^[-A-Za-z0-9_]*$", (AP_REG_EXTENDED|AP_REG_NOSUB));
569 ap_assert(preg != NULL);
570 if(ap_regexec(preg, name, 0, NULL, 0)) {
571 return 1;
572 }
573 return 0;
574 }
575
576 /**
577 * If the input string does not take advantage of regular
578 * expression metacharacters, return a pointer to an equivalent
579 * string that can be searched using apr_strmatch(). (The
580 * returned string will often be the input string. But if
581 * the input string contains escaped characters, the returned
582 * string will be a copy with the escapes removed.)
583 */
584 static const char *sp_non_regex_pattern(apr_pool_t *p, const char *s) {
585 const char *src = s;
586 int escapes_found = 0;
587 int in_escape = 0;
588
589 while (*src) {
590 switch (*src) {
591 case '^':
592 case '.':
593 case '$':
594 case '|':
595 case '(':
596 case ')':
597 case '[':
598 case ']':
599 case '*':
600 case '+':
601 case '?':
602 case '{':
603 case '}':
604 if(!in_escape) {
605 return NULL;
606 }
607 in_escape = 0;
608 break;
609 case '\\':
610 if(!in_escape) {
611 in_escape = 1;
612 escapes_found = 1;
613 } else {
614 in_escape = 0;
615 }
616 break;
617 default:
618 if(in_escape) {
619 return NULL;
620 }
621 break;
622 }
623 src++;
624 }
625 if(!escapes_found) {
626 return s;
627 } else {
628 char *unescaped = (char *)apr_palloc(p, src - s + 1);
629 char *dst = unescaped;
630 src = s;
631 do {
632 if(*src == '\\') {
633 src++;
634 }
635 } while((*dst++ = *src++));
636 return unescaped;
637 }
638 }
639
640 /**
641 * Replaces <start>var<end> (e.g. ${var}) entries by the value of the
642 * variable within the vars table.
643 *
644 * @param r
645 * @param start_marker Defines thow the vairable beginns, e,g "${" or "b64("
646 * @param start_marker_len Lenth of the marker string
647 * @param end_marker Counterpart to the start marker, e.g. "}" or ")"
648 * @param string String to respove variables
649 * @param func Function to replace the placeholder
650 * @param envvar Name of the variable to replace the value for (itself, optional)
651 * @returns 1 (success) if all start markers could be replaced
652 */
653 static int sp_reslove_variable(request_rec *r,
654 const char *start_marker,
655 int start_marker_len,
656 const char end_marker,
657 char **string,
658 char *(*func)(request_rec *, char *),
659 const char *envvar) {
660 char startmarker = '\0';
661 char endmarker = '\0';
662 int i;
663 int start;
664 int line_end;
665 char *var_name;
666 char *new_line = *string;
667 char *line = *string;
668 const char *val;
669
670 /* functions may enclose strings which contains brackets
671 var=Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20110000 Firefox/3.6
672 ^ ^
673 header=base64(${var})
674 => if we found another start marker and that we should skip the next end marker */
675 if(sp_strnstr(start_marker, "(", start_marker_len)) {
676 startmarker = '(';
677 endmarker = ')';
678 }
679 once_again:
680 i = 0;
681 while(line[i] != 0) {
682 int found_nested = 0;
683 if(strncmp(&line[i], start_marker, start_marker_len) == 0) {
684 line_end = i;
685 i+=start_marker_len;
686 start = i;
687 // search the end_marker
688 while((line[i] != 0) &&
689 !((line[i] == end_marker) && (found_nested == 0))) {
690 if(startmarker) {
691 if(line[i] == startmarker) {
692 found_nested++;
693 }
694 if((line[i] == endmarker) && found_nested) {
695 found_nested--;
696 }
697 }
698 i++;
699 }
700 // did we miss the end? because of more start than end markers?
701 if(startmarker) {
702 if(line[i] == '\0' && line[i-1] == end_marker) {
703 i--;
704 }
705 }
706 if(line[i] != end_marker) {
707 /* no end found */
708 break;
709 } else {
710 var_name = apr_pstrndup(r->pool, &line[start], i - start);
711 val = func(r, var_name);
712 if(!val && envvar && (strcmp(var_name, envvar) == 0)) {
713 /* resolving the variable itself may fail (use might want to
714 concatenate variables)
715 => replace it by an empty value */
716 val = apr_pstrdup(r->pool, "");
717 }
718 if(val) {
719 line[line_end] = 0;
720 i++;
721 new_line = apr_pstrcat(r->pool, line, val, &line[i], NULL);
722 line = new_line;
723 goto once_again;
724 }
725 }
726 }
727 i++;
728 }
729 if(!new_line[0] || strstr(new_line, start_marker)) {
730 return 0;
731 }
732 *string = new_line;
733 return 1;
734 }
735
736 /**
737 * Function: replaces the env variable by its value.
738 *
739 * @param r
740 * @param variable
741 * @return
742 */
743 static char *sp_func_variable(request_rec *r, char *variable) {
744 return apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, variable));
745
746 }
747
748 /**
749 * Function: URL encoding
750 *
751 * @param r
752 * @param string String to encode
753 * @return Encoded string
754 */
755 static char *sp_func_eURL(request_rec *r, char *string) {
756 return sp_url_encoding(r->pool, string);
757 }
758
759 /**
760 * Function: URL decoding
761 *
762 * @param r
763 * @param string String to decode
764 * @return Decoded string
765 */
766 static char *sp_func_dURL(request_rec *r, char *string) {
767 int error;
768 char *c = apr_pstrdup(r->pool, string);
769 sp_url_decoding(c, &error);
770 return c;
771 }
772
773 /**
774 * Function: encryption
775 *
776 * @param r
777 * @param string String to encode
778 * @return Encoded string
779 */
780 static char *sp_func_enc(request_rec *r, char *string) {
781 const char *keyStr = apr_table_get(r->subprocess_env, SP_KEY);
782 unsigned char key[EVP_MAX_KEY_LENGTH];
783 if(keyStr == NULL) {
784 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
785 SP_LOG_PFX(031)"$enc() can't encrypt string, no key available."
786 " "SP_KEY" variable is missing!");
787 return "";
788 }
789 EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
790 strlen(keyStr), 1, key, NULL);
791
792 return sp_enc64(r, string, key);
793 }
794
795 /**
796 * Function: decryption
797 *
798 * @param r
799 * @param string String to decrypt
800 * @return Decrypted string
801 */
802 static char *sp_func_dec(request_rec *r, char *string) {
803 const char *keyStr = apr_table_get(r->subprocess_env, SP_KEY);
804 unsigned char key[EVP_MAX_KEY_LENGTH];
805 if(keyStr == NULL) {
806 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
807 SP_LOG_PFX(031)"$enc() can't encrypt string, no key available."
808 " "SP_KEY" variable is missing!");
809 return "";
810 }
811 EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
812 strlen(keyStr), 1, key, NULL);
813 return sp_dec64(r, string, key);
814 }
815
816
817 /**
818 * Function: base64 encoding
819 *
820 * @param r
821 * @param string String to encode
822 * @return Encoded string
823 */
824 static char *sp_func_e64(request_rec *r, char *string) {
825 return ap_pbase64encode(r->pool, string);
826 }
827
828 /**
829 * Function: base64 decoding
830 *
831 * @param r
832 * @param string String to decode
833 * @return Decoded string
834 */
835 static char *sp_func_d64(request_rec *r, char *string) {
836 return ap_pbase64decode(r->pool, string);
837 }
838
839 /**
840 * Function: creates radom characters
841 *
842 * @param r
843 * @param num Number of bytes to return
844 * @return Random string
845 */
846 static char *sp_func_RND(request_rec *r, char *num) {
847 char *buf;
848 apr_int64_t len = apr_atoi64(num);
849 unsigned char *secret = apr_pcalloc(r->pool, len);
850 #if APR_HAS_RANDOM
851 if(apr_generate_random_bytes(secret, len) != APR_SUCCESS) {
852 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
853 SP_LOG_PFX(030)"$RND() can't generate random data.");
854 }
855 #else
856 if(!RAND_bytes(secret, len)) {
857 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
858 SP_LOG_PFX(030)"$RND() can't generate random data.");
859 }
860 #endif
861 buf = (char *)apr_pcalloc(r->pool, 1 + apr_base64_encode_len(len));
862 apr_base64_encode(buf, (const char *)secret, len);
863 buf[len] = '\0';
864 return buf;
865 }
866
867 /**
868 * List of registered functions
869 */
870 static sp_function_t sp_function_list[]= {
871 { "$e64(", 5, ')', &sp_func_e64 },
872 { "$d64(", 5, ')', &sp_func_d64 },
873 { "$dURL(", 6, ')', &sp_func_dURL },
874 { "$eURL(", 6, ')', &sp_func_eURL },
875 { "$enc(", 5, ')', &sp_func_enc },
876 { "$dec(", 5, ')', &sp_func_dec },
877 { "$RND(", 5, ')', &sp_func_RND },
878 { NULL, 0, 0, NULL }
879 };
880
881 /**
882 * Implements the kown functions, e.g. base64 encoding
883 *
884 * @param r
885 * @param replaced
886 * @return Resolved string
887 */
888 static char *sp_process_functions(request_rec *r, const char *replaced) {
889 sp_function_t *e = sp_function_list;
890 char *string = apr_pstrdup(r->pool, replaced);
891 while(e && e->identifier) {
892 sp_reslove_variable(r, e->identifier, e->len, e->close, &string, e->func, NULL);
893 e++;
894 }
895 return string;
896 }
897
898 /**
899 * Resolves the variable value and (un-)sets it.
900 *
901 * @param t
902 * @param b Rule entry
903 * @param val String value to process / resolve. This value is added
904 * as the environment variable as configured by the rule entry b.
905 */
906 static void sp_match_var(request_rec *r, sp_std_entry_t *b, const char *val) {
907 if(val) {
908 ap_regmatch_t regm[AP_MAX_REG_MATCH];
909 apr_size_t val_len = strlen(val);
910 if((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
911 (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm, 0))) {
912 if(b->variable[0] == '!') {
913 apr_table_unset(r->subprocess_env, &b->variable[1]);
914 } else {
915 char *replaced = NULL;
916 if(!b->pattern) {
917 /* 1) respolve sub regex */
918 replaced = ap_pregsub(r->pool, b->value, val, AP_MAX_REG_MATCH, regm);
919 } else {
920 /* literal string (no $1 .. $9) */
921 replaced = apr_pstrdup(r->pool, b->value);
922 }
923 if(replaced) {
924 /* 2) sp_reslove_variable() resolves all ${xxx} variables */
925 if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced,
926 &sp_func_variable, b->variable)) {
927 /* 3) process functions */
928 replaced = sp_process_functions(r, replaced);
929 apr_table_set(r->subprocess_env, b->variable, replaced);
930 }
931 }
932 }
933 }
934 }
935 return;
936 }
937
938 /**
939 * Same as sp_match_var() but this method adds a request query parameter
940 */
941 static void sp_match_query(request_rec *r, sp_std_entry_t *b, const char *val) {
942 if(val) {
943 ap_regmatch_t regm[AP_MAX_REG_MATCH];
944 apr_size_t val_len = strlen(val);
945 if((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
946 (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm, 0))) {
947 char *replaced = NULL;
948 char *query = apr_pstrdup(r->pool, b->variable);
949 if(b->valueSet) {
950 if(!b->pattern) {
951 /* 1) respolve sub regex */
952 replaced = ap_pregsub(r->pool, b->value, val, AP_MAX_REG_MATCH, regm);
953 } else {
954 /* literal string (no $1 .. $9) */
955 replaced = apr_pstrdup(r->pool, b->value);
956 }
957 }
958 if(replaced) {
959 /* 2) sp_reslove_variable() resolves all ${xxx} variables */
960 if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced,
961 &sp_func_variable, b->variable)) {
962 /* 3) process functions */
963 replaced = sp_process_functions(r, replaced);
964 }
965 query = apr_pstrcat(r->pool, query, "=", replaced, NULL);
966 }
967 if(r->args) {
968 r->args = apr_pstrcat(r->pool, r->args, "&", query, NULL);
969 r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "&", query, NULL);
970 } else {
971 r->args = query;
972 r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "?", query, NULL);
973 }
974 if(r->parsed_uri.query) {
975 r->parsed_uri.query = apr_pstrcat(r->pool, r->parsed_uri.query, "&", query, NULL);
976 } else {
977 r->parsed_uri.query = query;
978 }
979 }
980 }
981 return;
982 }
983
984 /**
985 * Changes the request line query if the defined variable
986 * has been set.
987 *
988 * @param r
989 * @param vars Array of variable names
990 */
991 static void sp_query(request_rec *r, apr_array_header_t *vars) {
992 if(r->unparsed_uri == NULL) {
993 return;
994 }
995 int i;
996 char **entries = (char **)vars->elts;
997 for(i = 0; i < vars->nelts; ++i) {
998 char *variable = entries[i];
999 const char *newquery = apr_table_get(r->subprocess_env, variable);
1000 if(newquery) {
1001 if(r->args) {
1002 // search the query within unparsed_uri
1003 char *q = r->unparsed_uri;
1004 while(q && q[0]) {
1005 q = strchr(q, '?');
1006 if(q) {
1007 if(q > r->unparsed_uri) {
1008 if(q[-1] != '\\') {
1009 q[0] = '\0';
1010 } else {
1011 // escaped, search next
1012 q++;
1013 }
1014 } else {
1015 q[0] = '\0';
1016 }
1017 }
1018 }
1019 }
1020 r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "?", newquery, NULL);
1021 r->args = apr_pstrdup(r->pool, newquery);
1022 r->parsed_uri.query = apr_pstrdup(r->pool, newquery);
1023 }
1024 }
1025 return;
1026 }
1027
1028 /**
1029 * Hashes the configured variable and stores it as a request header
1030 */
1031 static void sp_hash(request_rec *r, apr_array_header_t *header_hash) {
1032 int i;
1033 sp_hash_entry_t *entries = (sp_hash_entry_t *)header_hash->elts;
1034 for(i = 0; i < header_hash->nelts; ++i) {
1035 sp_hash_entry_t *b = &entries[i];
1036 const char *var = apr_table_get(r->subprocess_env, b->var);
1037 if(var) {
1038 char *md = ap_md5_binary(r->pool, (unsigned char *)var, strlen(var));
1039 char *hash = apr_pcalloc(r->pool, 64);
1040 char *d = hash;
1041 int c = 0;
1042 while(md[0]) {
1043 d[0] = md[0];
1044 d++;
1045 c++;
1046 md++;
1047 if(c == 4 && md[0]) {
1048 c=0;
1049 d[0] = ':';
1050 d++;
1051 }
1052 }
1053 d[0] = '\0';
1054 apr_table_set(r->headers_in, b->header, hash);
1055 }
1056 }
1057 }
1058
1059 /**
1060 * Compares the variables defined by SetEnvIfCmpPlus and
1061 * adds the specified variable.
1062 *
1063 * @param r
1064 * @param cmps Rules to process
1065 */
1066 static void sp_cmp(request_rec *r, apr_array_header_t * cmps) {
1067 int i;
1068 sp_cmp_entry_t *entries = (sp_cmp_entry_t *)cmps->elts;
1069 for(i = 0; i < cmps->nelts; ++i) {
1070 sp_cmp_entry_t *b = &entries[i];
1071 const char *leftStr = apr_table_get(r->subprocess_env, b->left);
1072 const char *rightStr = apr_table_get(r->subprocess_env, b->right);
1073 if(leftStr != NULL && rightStr != NULL) {
1074 int set = 0;
1075 if(sp_isnum(leftStr) && sp_isnum(rightStr)) {
1076 int left = atoi(leftStr);
1077 int right = atoi(rightStr);
1078 switch (b->cmp) {
1079 case SP_CMP_EQ:
1080 if(left == right) {
1081 set = 1;
1082 }
1083 break;
1084 case SP_CMP_NE:
1085 if(left != right) {
1086 set = 1;
1087 }
1088 break;
1089 case SP_CMP_GT:
1090 if(left > right) {
1091 set = 1;
1092 }
1093 break;
1094 case SP_CMP_LT:
1095 if(left < right) {
1096 set = 1;
1097 }
1098 break;
1099 }
1100 } else {
1101 int c = strcasecmp(leftStr, rightStr);
1102 switch (b->cmp) {
1103 case SP_CMP_EQ:
1104 if(c == 0) {
1105 set = 1;
1106 }
1107 break;
1108 case SP_CMP_NE:
1109 if(c != 0) {
1110 set = 1;
1111 }
1112 break;
1113 case SP_CMP_GT:
1114 if(c < 0) {
1115 set = 1;
1116 }
1117 break;
1118 case SP_CMP_LT:
1119 if(c > 0) {
1120 set = 1;
1121 }
1122 break;
1123 }
1124 }
1125 if(set) {
1126 if(b->variable[0] == '!') {
1127 apr_table_unset(r->subprocess_env, &b->variable[1]);
1128 } else {
1129 char *replaced = apr_pstrdup(r->pool, b->value);
1130 sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced, &sp_func_variable, NULL);
1131 apr_table_set(r->subprocess_env, b->variable, replaced);
1132 }
1133 }
1134 }
1135 }
1136 }
1137
1138 /**
1139 * Determines the attriubte to match and calls sp_match_*() in order
1140 * to (un-)set the variable or query
1141 *
1142 * @param r
1143 * @pram headers HTTP headers (request or response)
1144 * @param conditionals Ruls to process
1145 * @param isQuery Sets a query parameter instead of an env variable
1146 * @return DECLINED
1147 */
1148 static int sp_setenv(request_rec *r, apr_table_t *headers,
1149 apr_array_header_t *conditionals, int isQuery) {
1150 int i;
1151 sp_std_entry_t *entries = (sp_std_entry_t *)conditionals->elts;
1152 void (*sp_match)(request_rec *, sp_std_entry_t *, const char *);
1153 if(isQuery) {
1154 sp_match = &sp_match_query;
1155 } else {
1156 sp_match = &sp_match_var;
1157 }
1158
1159 for(i = 0; i < conditionals->nelts; ++i) {
1160 sp_std_entry_t *b = &entries[i];
1161 const char *val = NULL;
1162
1163 switch (b->special_type) {
1164 case SP_SPECIAL_REMOTE_ADDR:
1165 val = SF_CONN_REMOTEIP(r);
1166 sp_match(r, b, val);
1167 break;
1168 case SP_SPECIAL_REMOTE_NET:
1169 val = sp_get_remote_net(r);
1170 sp_match(r, b, val);
1171 break;
1172 case SP_SPECIAL_SERVER_ADDR:
1173 val = r->connection->local_ip;
1174 sp_match(r, b, val);
1175 break;
1176 case SP_SPECIAL_SERVER_PORT:
1177 val = apr_psprintf(r->pool, "%d", r->connection->local_addr->port);
1178 sp_match(r, b, val);
1179 break;
1180 case SP_SPECIAL_REMOTE_HOST:
1181 val = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL);
1182 sp_match(r, b, val);
1183 break;
1184 case SP_SPECIAL_REQUEST_URI:
1185 val = r->uri;
1186 sp_match(r, b, val);
1187 break;
1188 case SP_SPECIAL_REQUEST_METHOD:
1189 val = r->method;
1190 sp_match(r, b, val);
1191 break;
1192 case SP_SPECIAL_REQUEST_PROTOCOL:
1193 val = r->protocol;
1194 sp_match(r, b, val);
1195 break;
1196 case SP_SPECIAL_REQUEST_QUERY:
1197 val = r->args;
1198 sp_match(r, b, val);
1199 break;
1200 case SP_SPECIAL_REQUEST_USER:
1201 val = r->user;
1202 sp_match(r, b, val);
1203 break;
1204 case SP_SPECIAL_RESPONSE_STATUS:
1205 val = apr_psprintf(r->pool, "%d", r->status);
1206 sp_match(r, b, val);
1207 break;
1208 case SP_SPECIAL_NOT:
1209 if (b->pnamereg) {
1210 /* Matching headers_in against a regex. Iterate through
1211 * the headers until we find a match or run out of
1212 * headers.
1213 */
1214 int j;
1215 const apr_array_header_t *arr = apr_table_elts(headers);
1216 const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
1217 for(j = 0; j < arr->nelts; ++j) {
1218 if(!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
1219 val = elts[j].val;
1220 sp_match(r, b, val);
1221 }
1222 }
1223 } else {
1224 /* Not matching against a regex */
1225 val = apr_table_get(headers, b->name);
1226 if(val == NULL) {
1227 val = apr_table_get(r->subprocess_env, b->name);
1228 }
1229 sp_match(r, b, val);
1230 }
1231 }
1232 }
1233 return DECLINED;
1234 }
1235
1236 /**
1237 * Evaluates a condition variable by looking it up within the
1238 * provided process variable table.
1239 *
1240 * @param subprocess_env Process environment variable table
1241 * @param conftition Variable name (optionally negotiated by a '!')
1242 * to lookup
1243 * @return 1 If the variable is set or 0 if not
1244 */
1245 static int sp_condition(apr_table_t *subprocess_env, const char *condition) {
1246 if(condition[0] == '!') {
1247 if(!apr_table_get(subprocess_env, &condition[1])) {
1248 return 1;
1249 }
1250 } else {
1251 if(apr_table_get(subprocess_env, condition)) {
1252 return 1;
1253 }
1254 }
1255 return 0;
1256 }
1257
1258 /**
1259 * Modifies the request or response headers as configured by on of the
1260 * following directives:
1261 * - ChangeRequestHeaderPlus
1262 * - ChangeResponseHeaderPlus
1263 *
1264 * @param r
1265 * @param headers Either the in or out headers table
1266 * @param change_rules Configuration to apply
1267 */
1268 static void sp_change_header(request_rec *r, apr_table_t *headers, apr_table_t *change_rules) {
1269 ap_regmatch_t regm[AP_MAX_REG_MATCH];
1270 apr_table_t *changed = apr_table_make(r->pool, 20); /* modified headers */
1271 apr_table_t *orig = apr_table_make(r->pool, 20); /* unchanged headers */
1272 int i;
1273 apr_table_entry_t *header = (apr_table_entry_t *)apr_table_elts(headers)->elts;
1274 for(i = 0; i < apr_table_elts(headers)->nelts; i++) {
1275 int match = 0;
1276 int k;
1277 apr_table_entry_t *rule = (apr_table_entry_t *)apr_table_elts(change_rules)->elts;
1278 for(k = 0; k < apr_table_elts(change_rules)->nelts; k++) {
1279 sp_hdr_entry_t *b = (sp_hdr_entry_t *)rule[k].val;
1280 if(strcasecmp(b->name, header[i].key) == 0) {
1281 if(!ap_regexec(b->preg, header[i].val, AP_MAX_REG_MATCH, regm, 0)) {
1282 char *replaced = ap_pregsub(r->pool, b->value, header[i].val, AP_MAX_REG_MATCH, regm);
1283 sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced, &sp_func_variable, NULL);
1284 replaced = sp_process_functions(r, replaced);
1285 apr_table_addn(changed, header[i].key, replaced);
1286 match = 1;
1287 // content type of the http response is stored within the request record too
1288 if((strcasecmp("content-type", b->name) == 0) &&
1289 r->content_type &&
1290 (strcasecmp(r->content_type, header[i].val) == 0)){
1291 r->content_type = replaced;
1292 }
1293 break;
1294 }
1295 }
1296 }
1297 if(!match) {
1298 apr_table_addn(orig, header[i].key, header[i].val);
1299 }
1300 }
1301 apr_table_clear(headers);
1302 header = (apr_table_entry_t *)apr_table_elts(orig)->elts;
1303 for(i = 0; i < apr_table_elts(orig)->nelts; i++) {
1304 apr_table_addn(headers, header[i].key, header[i].val);
1305 }
1306 header = (apr_table_entry_t *)apr_table_elts(changed)->elts;
1307 for(i = 0; i < apr_table_elts(changed)->nelts; i++) {
1308 apr_table_addn(headers, header[i].key, header[i].val);
1309 }
1310 }
1311
1312 /**
1313 * Removes the pattern matching the regular expression from
1314 * the header table.
1315 * Empty header is removed.
1316 *
1317 * @param r
1318 * @param header Headers to process
1319 * @param list Rules (patterns for header names)*
1320 */
1321 static void sp_removepattern(request_rec *r, apr_table_t *headers, apr_array_header_t *list) {
1322 int i;
1323 sp_hdr_entry_t *entries = (sp_hdr_entry_t *)list->elts;
1324 ap_regmatch_t ma[AP_MAX_REG_MATCH];
1325 for(i = 0; i < list->nelts; ++i) {
1326 sp_hdr_entry_t *b = &entries[i];
1327 const char *header = apr_table_get(headers, b->name);
1328 if(header) {
1329 int changed = 0;
1330 while(!ap_regexec(b->preg, header, AP_MAX_REG_MATCH, ma, 0)) {
1331 int j = 1;
1332 const char *h = &header[ma[j].rm_eo];
1333 header = apr_pstrndup(r->pool, header, ma[j].rm_so);
1334 header = apr_pstrcat(r->pool, header, h, NULL);
1335 changed++;
1336 }
1337 if(changed) {
1338 if(header == NULL || header[0] == '\0') {
1339 apr_table_unset(headers, b->name);
1340 } else {
1341 apr_table_set(headers, b->name, header);
1342 }
1343 }
1344 }
1345 }
1346 return;
1347 }
1348
1349 /**
1350 * Adds the specified output filters
1351 * @param r
1352 * @param filters
1353 */
1354 static void sp_outfilter(request_rec *r, apr_array_header_t *filters) {
1355 int i;
1356 sp_filter_entry_t *entries = (sp_filter_entry_t *)filters->elts;
1357 apr_table_t *addedfilter = NULL; // ensure we add the filter only once
1358 for(i = 0; i < filters->nelts; ++i) {
1359 sp_filter_entry_t *entry = &entries[i];
1360 const char *var = entry->var;
1361 if(entry->notset) {
1362 if(apr_table_get(r->subprocess_env, var) == NULL) {
1363 if(addedfilter == NULL) {
1364 addedfilter = apr_table_make(r->pool, 2);
1365 }
1366 if(apr_table_get(addedfilter, entry->filtername) == NULL) {
1367 apr_table_add(addedfilter, entry->filtername, "");
1368 ap_add_output_filter(entry->filtername, NULL, r, r->connection);
1369 }
1370 }
1371 } else {
1372 if(apr_table_get(r->subprocess_env, var) != NULL) {
1373 if(addedfilter == NULL) {
1374 addedfilter = apr_table_make(r->pool, 2);
1375 }
1376 if(apr_table_get(addedfilter, entry->filtername) == NULL) {
1377 apr_table_add(addedfilter, entry->filtername, "");
1378 ap_add_output_filter(entry->filtername, NULL, r, r->connection);
1379 }
1380 }
1381 }
1382 }
1383 }
1384
1385 /**
1386 * Changes the response status code if the configured
1387 * event is set.
1388 */
1389 static void sp_status(request_rec *r, apr_array_header_t *vars) {
1390 int i;
1391 sp_status_entry_t *entries = (sp_status_entry_t *)vars->elts;
1392 for(i = 0; i < vars->nelts; ++i) {
1393 sp_status_entry_t *entry = &entries[i];
1394 const char *var = entry->var;
1395 if(apr_table_get(r->subprocess_env, var) != NULL) {
1396 r->status = entry->code;
1397 }
1398 }
1399 }
1400
1401 /**
1402 * sets/unsets/adds headers to the provided headers table (which is either
1403 * the in or out headers table)
1404 *
1405 * note: Server and Date response header can't be set on locally (no
1406 * mod_proxy) served HTTP request because basic_http_header()
1407 * of http_filters.c writes direcly to the brigade removing
1408 * and Server/Date header set by mod_setenvifplus (or other)
1409 * modules.
1410 *
1411 * @param r
1412 * @param headers Either the in or out headers to modify
1413 * @param list Rules to process (std_req_header, std_late_req_header
1414 * or std_res_header)
1415 * @param isReponse Indicates if we are processing the reqest (0) or
1416 * response (1) header fields.
1417 */
1418 static int sp_header(request_rec *r, apr_table_t *headers, apr_array_header_t *list,
1419 int isResponse) {
1420 apr_table_t *change_rules = NULL;
1421 int i;
1422 sp_hdr_entry_t *entries = (sp_hdr_entry_t *)list->elts;
1423 for(i = 0; i < list->nelts; ++i) {
1424 char *val;
1425 sp_hdr_entry_t *b = &entries[i];
1426 switch (b->action) {
1427 case SP_ACTION_SET:
1428 val = apr_pstrdup(r->pool, b->value);
1429 if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &val, &sp_func_variable, NULL)) {
1430 if(b->condition) {
1431 if(sp_condition(r->subprocess_env, b->condition)) {
1432 apr_table_set(headers, b->name, val);
1433 // content type of the http response is stored within the request record too
1434 if((strcasecmp("content-type", b->name) == 0) && isResponse) {
1435 r->content_type = val;
1436 }
1437 }
1438 } else {
1439 apr_table_set(headers, b->name, val);
1440 // content type of the http response is stored within the request record too
1441 if((strcasecmp("content-type", b->name) == 0) && isResponse) {
1442 r->content_type = val;
1443 }
1444 }
1445 }
1446 break;
1447 case SP_ACTION_UNSET:
1448 if(b->condition) {
1449 if(sp_condition(r->subprocess_env, b->condition)) {
1450 apr_table_unset(headers, b->name);
1451 }
1452 } else {
1453 apr_table_unset(headers, b->name);
1454 }
1455 break;
1456 case SP_ACTION_ADD:
1457 val = apr_pstrdup(r->pool, b->value);
1458 if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &val, &sp_func_variable, NULL)) {
1459 if(b->condition) {
1460 if(sp_condition(r->subprocess_env, b->condition)) {
1461 apr_table_add(headers, b->name, val);
1462 // content type of the http response is stored within the request record too
1463 if((strcasecmp("content-type", b->name) == 0) && isResponse) {
1464 r->content_type = val;
1465 }
1466 }
1467 } else {
1468 apr_table_add(headers, b->name, val);
1469 // content type of the http response is stored within the request record too
1470 if((strcasecmp("content-type", b->name) == 0) && isResponse) {
1471 r->content_type = val;
1472 }
1473 }
1474 }
1475 break;
1476 case SP_ACTION_CHANGE:
1477 if(!change_rules) {
1478 change_rules = apr_table_make(r->pool, 1);
1479 }
1480 apr_table_addn(change_rules, apr_psprintf(r->pool, "%d", i), (char *)b);
1481 break;
1482 }
1483 }
1484 if(change_rules) {
1485 sp_change_header(r, headers, change_rules);
1486 }
1487 return DECLINED;
1488 }
1489
1490 /**
1491 * Extract the session cookie from the request.
1492 *
1493 * @param r
1494 * @param name Name of the cookie
1495 * @return Cookie or NULL if not found within the request headers
1496 */
1497 static char *sp_get_remove_cookie(request_rec *r, const char *name) {
1498 const char *cookie_h = apr_table_get(r->headers_in, "Cookie");
1499 if(cookie_h) {
1500 char *cn = apr_pstrcat(r->pool, name, "=", NULL);
1501 char *pt = ap_strcasestr(cookie_h, cn);
1502 char *p = NULL;
1503 while(pt && !p) {
1504 // ensure we found the real cookie (and not an ending b64 str)
1505 if(pt == cookie_h) {
1506 // @beginning of the header
1507 p = pt;
1508 pt = NULL;
1509 } else {
1510 char pre = pt[-1];
1511 if(pre == ' ' ||
1512 pre == ';') {
1513 // @beginnin of a cookie
1514 p = pt;
1515 pt = NULL;
1516 } else {
1517 // found patter somewhere else
1518 pt++;
1519 pt = ap_strcasestr(pt, cn);
1520 }
1521 }
1522 }
1523 if(p) {
1524 char *value = NULL;
1525 char *clean = p;
1526 if(clean > cookie_h) {
1527 clean--;
1528 while(clean > cookie_h && clean[0] == ' ') {
1529 clean[0] = '\0';
1530 clean--;
1531 }
1532 }
1533 p[0] = '\0'; /* terminate the beginning of the cookie header */
1534 p = p + strlen(cn);
1535 value = ap_getword(r->pool, (const char **)&p, ';');
1536 while(p && (p[0] == ' ')) p++;
1537 /* skip a path, if there is any */
1538 if(p && (strncasecmp(p, "$path=", strlen("$path=")) == 0)) {
1539 ap_getword(r->pool, (const char **)&p, ';');
1540 }
1541 /* restore cookie header */
1542 if(p && p[0]) {
1543 if(cookie_h[0]) {
1544 cookie_h = apr_pstrcat(r->pool, cookie_h, " ", p, NULL);
1545 } else {
1546 cookie_h = apr_pstrcat(r->pool, p, NULL);
1547 }
1548 }
1549 if(strlen(cookie_h) == 0) {
1550 apr_table_unset(r->headers_in, "cookie");
1551 } else {
1552 if((strncasecmp(cookie_h, "$Version=", strlen("$Version=")) == 0) &&
1553 (strlen(cookie_h) <= strlen("$Version=X; "))) {
1554 /* nothing left */
1555 apr_table_unset(r->headers_in, "cookie");
1556 } else {
1557 apr_table_set(r->headers_in, "Cookie", cookie_h);
1558 }
1559 }
1560 return value;
1561 }
1562 }
1563 return NULL;
1564 }
1565
1566 /**
1567 * Encrypts the specified Set-Cookie headers
1568 *
1569 * @param r
1570 * @param key
1571 * @param cookie_names List of cookies to be encrypted
1572 * @param val Set-Cookie header value to process
1573 * @param header2keep Table with the modified Set-Cookie headers
1574 * which shall replace all other Set-Cookie headers in the
1575 * response.
1576 * @param modified Indicates, the the value has been modified or not
1577 */
1578 static void sp_encSecCookie(request_rec *r, unsigned char *key,
1579 apr_array_header_t *cookie_names,
1580 const char *val,
1581 apr_table_t *headers2keep, int *modified) {
1582 int i;
1583 char **names = (char **)cookie_names->elts;
1584 for(i = 0; i < cookie_names->nelts; ++i) {
1585 char *name = apr_pstrcat(r->pool, names[i], "=", NULL);
1586 int len = strlen(name);
1587 if(strncmp(val, name, len) == 0) {
1588 char *p = apr_pstrdup(r->pool, val);
1589 char *options = strchr(p, ';');
1590 char *v = strchr(p, '=');
1591 v++;
1592 if(options) {
1593 options[0] = '\0';
1594 options++;
1595 }
1596 v = sp_enc64(r, v, key);
1597 apr_table_add(headers2keep, "Set-Cookie",
1598 apr_pstrcat(r->pool, name, v, options ? ";" : "", options, NULL));
1599 *modified = 1;
1600 return;
1601 }
1602 }
1603 // add without modification
1604 apr_table_add(headers2keep, "Set-Cookie", val);
1605 }
1606
1607 /**
1608 * Processes the outgoing Set-Cookie header fields encrypting
1609 * the specified cookies.
1610 *
1611 * @param r
1612 * @param headers Table of the outgoing HTTP headers
1613 */
1614 static void sp_setcookie(request_rec *r, apr_table_t *headers) {
1615 sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
1616 apr_array_header_t *cookie_names = sconf->cookie_names;
1617 if(cookie_names->nelts > 0) {
1618 const char *keyStr = apr_table_get(r->subprocess_env, SP_COOKIE_KEY);
1619 if(keyStr) {
1620 int i;
1621 int modified = 0;
1622 apr_table_t *headers2keep = apr_table_make(r->pool, 2);
1623 apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(headers)->elts;
1624 unsigned char key[EVP_MAX_KEY_LENGTH];
1625 EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
1626 strlen(keyStr), 1, key, NULL);
1627 for(i = 0; i < apr_table_elts(headers)->nelts; i++) {
1628 if(strcasecmp(entry[i].key, "Set-Cookie") == 0) {
1629 sp_encSecCookie(r, key, cookie_names, entry[i].val, headers2keep, &modified);
1630 }
1631 }
1632 if(modified) {
1633 apr_table_unset(headers, "Set-Cookie");
1634 entry = (apr_table_entry_t *)apr_table_elts(headers2keep)->elts;
1635 for(i = 0; i < apr_table_elts(headers2keep)->nelts; i++) {
1636 apr_table_add(headers, entry[i].key, entry[i].val);
1637 }
1638 }
1639 }
1640 }
1641 }
1642
1643 /**
1644 * Processes the incomming HTTP Cookie header decrypting
1645 * the specified cookies.
1646 *
1647 * @param r
1648 * @param conf
1649 */
1650 static void sp_cookie(request_rec *r, sp_config_t *conf) {
1651 apr_array_header_t *cookie_names = conf->cookie_names;
1652 const char *decCookies = NULL;
1653 if(cookie_names->nelts > 0) {
1654 const char *keyStr = apr_table_get(r->subprocess_env, SP_COOKIE_KEY);
1655 if(keyStr) {
1656 // rule and key available
1657 const char *cookie2 = NULL;
1658 int i;
1659 char **names = (char **)cookie_names->elts;
1660 unsigned char key[EVP_MAX_KEY_LENGTH];
1661 EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
1662 strlen(keyStr), 1, key, NULL);
1663 for(i = 0; i < cookie_names->nelts; ++i) {
1664 char **name = &names[i];
1665 char *cookie = sp_get_remove_cookie(r, *name);
1666 if(cookie) {
1667 cookie = sp_dec64(r, cookie, key);
1668 if(cookie) {
1669 if(decCookies) {
1670 decCookies = apr_pstrcat(r->pool, decCookies, "; ", *name, "=", cookie, NULL);
1671 } else {
1672 decCookies = apr_pstrcat(r->pool, *name, "=", cookie, NULL);
1673 }
1674 } else {
1675 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1676 SP_LOG_PFX(040)"Drop cookie %s.", *name);
1677 }
1678 }
1679 // TODO support cookie2 too
1680 // no cookie2 header support, drop the header if we found the configured cookie
1681 cookie2 = apr_table_get(r->headers_in, "Cookie2");
1682 if(cookie2) {
1683 char *search = apr_pstrcat(r->pool, *name, "=", NULL);
1684 if(strstr(cookie2, search) != NULL) {
1685 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1686 SP_LOG_PFX(041)"Drop cookie2 header '%s.'", cookie2);
1687 apr_table_unset(r->headers_in, "Cookie2");
1688 }
1689 }
1690 }
1691 }
1692 }
1693 if(decCookies) {
1694 const char *cookie = apr_table_get(r->headers_in, "Cookie");
1695 if(cookie) {
1696 int cookieLen = strlen(cookie);
1697 if(cookieLen > 1 && cookie[cookieLen-1] == ';') {
1698 cookie = apr_pstrcat(r->pool, cookie, " ", decCookies, NULL);
1699 } else {
1700 cookie = apr_pstrcat(r->pool, cookie, "; ", decCookies, NULL);
1701 }
1702 } else {
1703 cookie = decCookies;
1704 }
1705 apr_table_set(r->headers_in, "Cookie", cookie);
1706 }
1707 }
1708
1709 /************************************************************************
1710 * handlers
1711 ***********************************************************************/
1712
1713 static int sp_fixup(request_rec *r) {
1714 sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
1715 sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
1716 if(conf) {
1717 /* SetEnvIfPlus late */
1718 sp_setenv(r, r->headers_in, conf->std_late_conditionals, 0);
1719 /* SetEnvIfCmpPlus late */
1720 sp_cmp(r, conf->cmp_late);
1721 /* RequestHeaderPlus late */
1722 sp_header(r, r->headers_in, conf->std_late_req_header, 0);
1723 }
1724 if(sconf) {
1725 sp_cookie(r, sconf);
1726 }
1727 return DECLINED;
1728 }
1729
1730 static int sp_user_id(request_rec *r) {
1731 sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
1732 if(conf) {
1733 ap_regmatch_t regm[AP_MAX_REG_MATCH];
1734 apr_array_header_t *c = conf->user;
1735 int i;
1736 sp_usr_entry_t *entries = (sp_usr_entry_t *)c->elts;
1737 for(i = 0; i < c->nelts; ++i) {
1738 sp_usr_entry_t *b = &entries[i];
1739 const char *hdr = apr_table_get(r->headers_in, b->header);
1740 if(hdr) {
1741 if(!ap_regexec(b->preg, hdr, AP_MAX_REG_MATCH, regm, 0)) {
1742 char *replaced = ap_pregsub(r->pool, b->value, hdr, AP_MAX_REG_MATCH, regm);
1743 if(replaced) {
1744 r->user = replaced;
1745 r->ap_auth_type = (char*)apr_pstrdup(r->pool, "delegated");
1746 return OK;
1747 }
1748 }
1749 }
1750 }
1751 }
1752 return DECLINED;
1753 }
1754
1755 static int sp_header_parser(request_rec *r) {
1756 sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
1757 if(conf) {
1758 /* SetEnvIfPlus */
1759 sp_setenv(r, r->headers_in, conf->std_conditionals, 0);
1760 /* RequestHeaderPlus ChangeRequestHeaderPlus */
1761 sp_header(r, r->headers_in, conf->std_req_header, 0);
1762 /* ChangeQueryIfPlus */
1763 sp_query(r, conf->std_req_changequery);
1764 /* SetQueryIfPlus */
1765 sp_setenv(r, r->headers_in, conf->std_req_query, 1);
1766 /* SetEnvIfCmpPlus */
1767 sp_cmp(r, conf->cmp);
1768
1769 sp_removepattern(r, r->headers_in, conf->std_removepattern_req_header);
1770 }
1771 return DECLINED;
1772 }
1773
1774 static int sp_post_read_request(request_rec *r) {
1775 sp_config_t *conf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
1776 if(conf) {
1777 /* SetEnvIfPlus */
1778 sp_setenv(r, r->headers_in, conf->std_conditionals, 0);
1779 /* RequestHeaderPlus ChangeRequestHeaderPlus */
1780 sp_header(r, r->headers_in, conf->std_req_header, 0);
1781 /* ChangeQueryIfPlus */
1782 sp_query(r, conf->std_req_changequery);
1783 /* SetQueryIfPlus */
1784 sp_setenv(r, r->headers_in, conf->std_req_query, 1);
1785 /* SetEnvIfCmpPlus */
1786 sp_cmp(r, conf->cmp);
1787 /* SetHashHeaderPlus */
1788 sp_hash(r, conf->header_hash);
1789
1790 }
1791 return DECLINED;
1792 }
1793
1794 #ifdef SP_INTERNAL_TEST
1795 /** test handler printing all variables */
1796 static int sp_handler(request_rec * r) {
1797 if(strcmp(r->handler, "setenvifplus-test")) {
1798 return DECLINED;
1799 } else {
1800 int i;
1801 apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(r->subprocess_env)->elts;
1802 ap_set_content_type(r, "text/plain");
1803 for(i = 0; i < apr_table_elts(r->subprocess_env)->nelts; i++) {
1804 ap_rprintf(r, "var: <%s><%s>\n", entry[i].key, entry[i].val);
1805 }
1806 entry = (apr_table_entry_t *)apr_table_elts(r->headers_in)->elts;
1807 ap_set_content_type(r, "text/plain");
1808 apr_table_set(r->headers_out, "X-SetEnvIfPlus",
1809 apr_psprintf(r->pool, "testhandler=%s", g_revision));
1810 for(i = 0; i < apr_table_elts(r->headers_in)->nelts; i++) {
1811 ap_rprintf(r, "hdr: <%s><%s>\n", entry[i].key, entry[i].val);
1812 }
1813
1814 ap_rprintf(r, "REMOTE_ADDR: <%s>\n", SF_CONN_REMOTEIP(r));
1815 ap_rprintf(r, "REMOTE_HOST: <%s>\n", ap_get_remote_host(r->connection,
1816 r->per_dir_config, REMOTE_NAME, NULL));
1817 ap_rprintf(r, "REMOTE_NET: <%s>\n", sp_get_remote_net(r));
1818
1819 ap_rprintf(r, "SERVER_ADDR: <%s>\n", r->connection->local_ip);
1820 ap_rprintf(r, "SERVER_PORT: <%d>\n", r->connection->local_addr->port);
1821 }
1822 return OK;
1823 }
1824 #endif
1825
1826 /** finalize configuration */
1827 static int sp_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp,
1828 server_rec *bs) {
1829 ap_add_version_component(pconf, apr_psprintf(pconf, "mod_setenvifplus/%s", g_revision));
1830 #ifdef SP_INTERNAL_TEST
1831 fprintf(stdout, "\033[1mmod_setenvifplus TEST BINARY, NOT FOR PRODUCTIVE USE\033[0m\n");
1832 fflush(stdout);
1833 #endif
1834 return DECLINED;
1835 }
1836
1837 /**
1838 * Response processing
1839 * @param r
1840 * @paramheaders HTTP response headers
1841 */
1842 static void sp_out(request_rec *r, apr_table_t *headers) {
1843 sp_config_t *dconf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
1844 sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
1845
1846 if(sconf) {
1847 sp_header(r, headers, sconf->std_res_header, 1);
1848 sp_status(r, sconf->status_var);
1849 sp_outfilter(r, sconf->outfilter);
1850 }
1851 if(dconf) {
1852 sp_setenv(r, headers, dconf->std_res_conditionals, 0);
1853 sp_header(r, headers, dconf->std_res_header, 1);
1854 sp_status(r, dconf->status_var);
1855 sp_outfilter(r, dconf->outfilter);
1856 }
1857 return;
1858 }
1859
1860 static apr_status_t sp_out_err_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
1861 request_rec *r = f->r;
1862 sp_out(r, r->err_headers_out);
1863 sp_setcookie(r, r->err_headers_out);
1864 ap_remove_output_filter(f);
1865 return ap_pass_brigade(f->next, bb);
1866 }
1867
1868 static apr_status_t sp_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
1869 request_rec *r = f->r;
1870 sp_out(r, r->headers_out);
1871 sp_setcookie(r, r->headers_out);
1872 ap_remove_output_filter(f);
1873 return ap_pass_brigade(f->next, bb);
1874 }
1875
1876 /**
1877 * insert response filter
1878 */
1879 static void sp_insert_filter(request_rec *r) {
1880 ap_add_output_filter("sp_out_filter", NULL, r, r->connection);
1881 }
1882
1883 static void sp_insert_err_filter(request_rec *r) {
1884 ap_add_output_filter("sp_out_err_filter", NULL, r, r->connection);
1885 }
1886
1887 /************************************************************************
1888 * directiv handlers
1889 ***********************************************************************/
1890 static void *sp_srv_config_create(apr_pool_t *p, server_rec *s) {
1891 sp_config_t *sconf = apr_pcalloc(p, sizeof(sp_config_t));
1892 sconf->path = NULL;
1893 sconf->std_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1894 sconf->std_late_conditionals = NULL;
1895 sconf->std_req_query = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1896 sconf->std_req_changequery = apr_array_make(p, 20, sizeof(char *));
1897 sconf->std_res_conditionals = NULL;
1898 sconf->std_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1899 sconf->std_late_req_header = NULL;
1900 sconf->std_res_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1901 sconf->cmp = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
1902 sconf->user = NULL;
1903 sconf->status_var = apr_array_make(p, 2, sizeof(sp_status_entry_t));
1904 sconf->header_hash = apr_array_make(p, 2, sizeof(sp_hash_entry_t));
1905 sconf->outfilter = apr_array_make(p, 2, sizeof(sp_filter_entry_t));
1906 sconf->cookie_names = apr_array_make(p, 2, sizeof(char *));
1907 return sconf;
1908 }
1909
1910 static void *sp_srv_config_merge(apr_pool_t *p, void *basev, void *addv) {
1911 sp_config_t *sconf = apr_pcalloc(p, sizeof(sp_config_t));
1912 sp_config_t *base = basev;
1913 sp_config_t *add = addv;
1914 sconf->path = NULL;
1915 sconf->std_conditionals = apr_array_append(p, base->std_conditionals,
1916 add->std_conditionals);
1917 sconf->std_late_conditionals = NULL;
1918 sconf->std_req_query = apr_array_append(p, base->std_req_query,
1919 add->std_req_query);
1920 sconf->std_req_changequery = apr_array_append(p, base->std_req_changequery,
1921 add->std_req_changequery);
1922 sconf->std_res_conditionals = NULL;
1923 sconf->std_req_header = apr_array_append(p, base->std_req_header,
1924 add->std_req_header);
1925 sconf->std_late_req_header = NULL;
1926 sconf->std_res_header = apr_array_append(p, base->std_res_header,
1927 add->std_res_header);
1928 sconf->cmp = apr_array_append(p, base->cmp, add->cmp);
1929 sconf->status_var = apr_array_append(p, base->status_var, add->status_var);
1930 sconf->header_hash = apr_array_append(p, base->header_hash, add->header_hash);
1931 sconf->outfilter = apr_array_append(p, base->outfilter, add->outfilter);
1932 sconf->cookie_names = apr_array_append(p, base->cookie_names, add->cookie_names);
1933 return sconf;
1934 }
1935
1936 static void *sp_dir_config_create(apr_pool_t *p, char *d) {
1937 sp_config_t *dconf = apr_pcalloc(p, sizeof(sp_config_t));
1938 dconf->path = d;
1939 dconf->std_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1940 dconf->std_late_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1941 dconf->std_req_query = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1942 dconf->std_req_changequery = apr_array_make(p, 20, sizeof(char *));
1943 dconf->std_res_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
1944 dconf->std_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1945 dconf->std_late_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1946 dconf->std_res_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1947 dconf->cmp = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
1948 dconf->cmp_late = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
1949 dconf->user = apr_array_make(p, 2, sizeof(sp_usr_entry_t));
1950 dconf->std_removepattern_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
1951 dconf->status_var = apr_array_make(p, 2, sizeof(sp_status_entry_t));
1952 dconf->outfilter = apr_array_make(p, 2, sizeof(sp_filter_entry_t));
1953 return dconf;
1954 }
1955
1956 static void *sp_dir_config_merge(apr_pool_t *p, void *basev, void *addv) {
1957 sp_config_t *dconf = apr_pcalloc(p, sizeof(sp_config_t));
1958 sp_config_t *base = basev;
1959 sp_config_t *add = addv;
1960 dconf->path = add->path;
1961 dconf->std_conditionals = apr_array_append(p, base->std_conditionals,
1962 add->std_conditionals);
1963 dconf->std_late_conditionals = apr_array_append(p, base->std_late_conditionals,
1964 add->std_late_conditionals);
1965 dconf->std_req_query = apr_array_append(p, base->std_req_query,
1966 add->std_req_query);
1967 dconf->std_req_changequery = apr_array_append(p, base->std_req_changequery,
1968 add->std_req_changequery);
1969 dconf->std_res_conditionals = apr_array_append(p, base->std_res_conditionals,
1970 add->std_res_conditionals);
1971 dconf->std_req_header = apr_array_append(p, base->std_req_header,
1972 add->std_req_header);
1973 dconf->std_late_req_header = apr_array_append(p, base->std_late_req_header,
1974 add->std_late_req_header);
1975 dconf->std_res_header = apr_array_append(p, base->std_res_header,
1976 add->std_res_header);
1977 dconf->cmp = apr_array_append(p, base->cmp, add->cmp);
1978 dconf->cmp_late = apr_array_append(p, base->cmp_late, add->cmp_late);
1979 dconf->user = apr_array_append(p, base->user, add->user);
1980 dconf->status_var = apr_array_append(p, base->status_var, add->status_var);
1981 dconf->outfilter = apr_array_append(p, base->outfilter, add->outfilter);
1982 dconf->std_removepattern_req_header = apr_array_append(p, base->std_removepattern_req_header,
1983 add->std_removepattern_req_header);
1984 return dconf;
1985 }
1986
1987 const char *sp_header_cmd(cmd_parms *cmd, int argc, char *const argv[],
1988 sp_hdr_entry_t *new) {
1989 const char *action;
1990 if(argc < 2) {
1991 /* always require action and header name */
1992 return apr_psprintf(cmd->pool, "%s: takes at least two arguments",
1993 cmd->directive->directive);
1994 }
1995 action = argv[0];
1996 new->name = apr_pstrdup(cmd->pool, argv[1]);
1997 new->value = apr_pstrdup(cmd->pool, "");
1998 new->condition = NULL;
1999 if(strcasecmp(action, "set") == 0) {
2000 new->action = SP_ACTION_SET;
2001 if(argc < 3) {
2002 return apr_psprintf(cmd->pool, "%s set: takes at least three arguments",
2003 cmd->directive->directive);
2004 }
2005 new->value = apr_pstrdup(cmd->pool, argv[2]);
2006 if(argc > 3) {
2007 char *c = argv[3];
2008 if(strncasecmp(c, "env=", strlen("env=")) == 0) {
2009 new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
2010 } else {
2011 return apr_psprintf(cmd->pool, "%s set: expects 'env=' argument",
2012 cmd->directive->directive);
2013 }
2014 }
2015 } else if(strcasecmp(action, "add") == 0) {
2016 new->action = SP_ACTION_ADD;
2017 if(argc < 3) {
2018 return apr_psprintf(cmd->pool, "%s add: takes at least three arguments",
2019 cmd->directive->directive);
2020 }
2021 new->value = apr_pstrdup(cmd->pool, argv[2]);
2022 if(argc > 3) {
2023 char *c = argv[3];
2024 if(strncasecmp(c, "env=", strlen("env=")) == 0) {
2025 new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
2026 } else {
2027 return apr_psprintf(cmd->pool, "%s add: expects 'env=' argument",
2028 cmd->directive->directive);
2029 }
2030 }
2031 } else if(strcasecmp(action, "unset") == 0) {
2032 new->action = SP_ACTION_UNSET;
2033 if(argc > 2) {
2034 char *c = argv[2];
2035 if(strncasecmp(c, "env=", strlen("env=")) == 0) {
2036 new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
2037 } else {
2038 return apr_psprintf(cmd->pool, "%s unset: expects 'env=' argument",
2039 cmd->directive->directive);
2040 }
2041 }
2042 } else {
2043 return apr_psprintf(cmd->pool, "%s: unkown action",
2044 cmd->directive->directive);
2045 }
2046 return NULL;
2047 }
2048
2049 const char *sp_changeheader(cmd_parms *cmd, const char *header,
2050 const char *regex, const char *value,
2051 sp_hdr_entry_t *new) {
2052 new->action = SP_ACTION_CHANGE;
2053 new->preg = ap_pregcomp(cmd->pool, regex, (AP_REG_EXTENDED | AP_REG_ICASE));
2054 if(new->preg == NULL) {
2055 return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
2056 cmd->directive->directive, regex);
2057 }
2058 new->name = apr_pstrdup(cmd->pool, header);
2059 new->value = apr_pstrdup(cmd->pool, value);
2060 return NULL;
2061 }
2062
2063 const char *sp_changereqheader_cmd(cmd_parms *cmd, void *dcfg, const char *header,
2064 const char *regex, const char *value) {
2065 sp_hdr_entry_t *new;
2066 sp_config_t *conf;
2067 if(cmd->path) {
2068 conf = dcfg;
2069 } else {
2070 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2071 }
2072 new = apr_array_push(conf->std_req_header);
2073 return sp_changeheader(cmd, header, regex, value, new);
2074 }
2075
2076 const char *sp_removepattern_cmd(cmd_parms *cmd, void *dcfg, const char *header,
2077 const char *regex) {
2078 sp_hdr_entry_t *new;
2079 sp_config_t *conf;
2080 char *pattern = apr_psprintf(cmd->pool, ".*(%s).*", regex);
2081 if(cmd->path) {
2082 conf = dcfg;
2083 } else {
2084 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2085 }
2086 new = apr_array_push(conf->std_removepattern_req_header);
2087 new->action = SP_ACTION_CHANGE;
2088 new->preg = ap_pregcomp(cmd->pool, pattern, (AP_REG_EXTENDED | AP_REG_ICASE));
2089 if(new->preg == NULL) {
2090 return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
2091 cmd->directive->directive, pattern);
2092 }
2093 new->name = apr_pstrdup(cmd->pool, header);
2094 new->value = pattern;
2095 return NULL;
2096 }
2097
2098 const char *sp_changeresheader_cmd(cmd_parms *cmd, void *dcfg, const char *header,
2099 const char *regex, const char *value) {
2100 sp_hdr_entry_t *new;
2101 sp_config_t *conf;
2102 if(cmd->path) {
2103 conf = dcfg;
2104 } else {
2105 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2106 }
2107 new = apr_array_push(conf->std_res_header);
2108 return sp_changeheader(cmd, header, regex, value, new);
2109 }
2110
2111 const char *sp_cmp_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
2112 sp_cmp_entry_t *new;
2113 char *del;
2114 int late = 0;
2115 sp_config_t *conf;
2116 if(cmd->path) {
2117 conf = dcfg;
2118 if(argc == 5) {
2119 if(strcasecmp(argv[4], "late") == 0) {
2120 late = 1;
2121 } else {
2122 return apr_psprintf(cmd->pool, "%s: unknown parameter '%s'",
2123 cmd->directive->directive, argv[4]);
2124 }
2125 } else if(argc != 4) {
2126 return apr_psprintf(cmd->pool, "%s: requires 4 or 5 arguments",
2127 cmd->directive->directive);
2128 }
2129 } else {
2130 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2131 if(argc != 4) {
2132 return apr_psprintf(cmd->pool, "%s: requires 4 arguments",
2133 cmd->directive->directive);
2134 }
2135 }
2136 if(late) {
2137 new = apr_array_push(conf->cmp_late);
2138 } else {
2139 new = apr_array_push(conf->cmp);
2140 }
2141 new->left = apr_pstrdup(cmd->pool, argv[0]);
2142 if(strcasecmp(argv[1], "eq") == 0) {
2143 new->cmp = SP_CMP_EQ;
2144 } else if(strcasecmp(argv[1], "ne") == 0) {
2145 new->cmp = SP_CMP_NE;
2146 } else if(strcasecmp(argv[1], "lt") == 0) {
2147 new->cmp = SP_CMP_LT;
2148 } else if(strcasecmp(argv[1], "gt") == 0) {
2149 new->cmp = SP_CMP_GT;
2150 } else {
2151 return apr_psprintf(cmd->pool, "%s: invalid operator '%s",
2152 cmd->directive->directive, argv[1]);
2153 }
2154 new->right = apr_pstrdup(cmd->pool, argv[2]);
2155 new->variable = apr_pstrdup(cmd->pool, argv[3]);
2156 del = strchr(new->variable, '=');
2157 if(del) {
2158 new->value = &del[1];
2159 del[0] = '\0';
2160 } else {
2161 new->value = apr_pstrdup(cmd->pool, "");
2162 }
2163 return NULL;
2164 }
2165
2166 /**
2167 * RequestHeaderPlus
2168 */
2169 const char *sp_reqheader_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
2170 sp_hdr_entry_t *new;
2171 sp_config_t *conf;
2172 if(cmd->path) {
2173 conf = dcfg;
2174 } else {
2175 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2176 }
2177 if(strcmp(argv[argc-1], "late") == 0) {
2178 if(!cmd->path) {
2179 return apr_psprintf(cmd->pool, "%s: 'late' option not allowed here",
2180 cmd->directive->directive);
2181 }
2182 new = apr_array_push(conf->std_late_req_header);
2183 argc--;
2184 } else {
2185 new = apr_array_push(conf->std_req_header);
2186 }
2187 return sp_header_cmd(cmd, argc, argv, new);
2188 }
2189
2190 const char *sp_resheader_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
2191 sp_hdr_entry_t *new;
2192 sp_config_t *conf;
2193 if(cmd->path) {
2194 conf = dcfg;
2195 } else {
2196 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2197 }
2198 new = apr_array_push(conf->std_res_header);
2199 return sp_header_cmd(cmd, argc, argv, new);
2200 }
2201
2202 const char *sp_std_core_cmd(cmd_parms *cmd, apr_array_header_t *conditionals,
2203 const char *name, const char *regex, const char *var,
2204 int icase, int late) {
2205 const char *val;
2206 const char *simple_pattern;
2207 sp_std_entry_t *new;
2208
2209 /* create a new entry */
2210 new = apr_array_push(conditionals);
2211 new->name = name;
2212 new->regex = regex;
2213 new->icase = icase;
2214 if((simple_pattern = sp_non_regex_pattern(cmd->pool, regex))) {
2215 new->pattern = apr_strmatch_precompile(cmd->pool, simple_pattern, !icase);
2216 if(new->pattern == NULL) {
2217 return apr_psprintf(cmd->pool, "%s: strmatch pattern '%s' could not be compiled",
2218 cmd->directive->directive, simple_pattern);
2219 }
2220 new->preg = NULL;
2221 } else {
2222 new->preg = ap_pregcomp(cmd->pool, regex, (AP_REG_EXTENDED | (icase ? AP_REG_ICASE : 0)));
2223 if(new->preg == NULL) {
2224 return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
2225 cmd->directive->directive, regex);
2226 }
2227 new->pattern = NULL;
2228 }
2229 if(!strcasecmp(name, "remote_addr")) {
2230 new->special_type = SP_SPECIAL_REMOTE_ADDR;
2231 } else if(!strcasecmp(name, "remote_net")) {
2232 new->special_type = SP_SPECIAL_REMOTE_NET;
2233 } else if(!strcasecmp(name, "remote_host")) {
2234 new->special_type = SP_SPECIAL_REMOTE_HOST;
2235 } else if(!strcasecmp(name, "request_uri")) {
2236 new->special_type = SP_SPECIAL_REQUEST_URI;
2237 } else if(!strcasecmp(name, "request_path")) {
2238 new->special_type = SP_SPECIAL_REQUEST_URI;
2239 } else if(!strcasecmp(name, "request_method")) {
2240 new->special_type = SP_SPECIAL_REQUEST_METHOD;
2241 } else if(!strcasecmp(name, "request_protocol")) {
2242 new->special_type = SP_SPECIAL_REQUEST_PROTOCOL;
2243 } else if(!strcasecmp(name, "server_addr")) {
2244 new->special_type = SP_SPECIAL_SERVER_ADDR;
2245 } else if(!strcasecmp(name, "server_port")) {
2246 new->special_type = SP_SPECIAL_SERVER_PORT;
2247 } else if(!strcasecmp(name, "request_query")) {
2248 new->special_type = SP_SPECIAL_REQUEST_QUERY;
2249 } else if(!strcasecmp(name, "response_status")) {
2250 /* TODO: must be used for ResponseSetEnfIfPlus only */
2251 new->special_type = SP_SPECIAL_RESPONSE_STATUS;
2252 } else if(!strcasecmp(name, "request_user")) {
2253 if(!late) {
2254 return apr_psprintf(cmd->pool, "%s: user match works only"
2255 " when using the 'late' handler",
2256 cmd->directive->directive);
2257 }
2258 new->special_type = SP_SPECIAL_REQUEST_USER;
2259 } else {
2260 new->special_type = SP_SPECIAL_NOT;
2261 /* Handle name as a regular expression.
2262 * If name a simple header string, identify as such
2263 * (new->pnamereg = NULL) to avoid the overhead of searching
2264 * through headers_in for a regex match.
2265 */
2266 if(sp_is_header_regex(cmd->pool, name)) {
2267 new->pnamereg = ap_pregcomp(cmd->pool, name,
2268 (AP_REG_EXTENDED | AP_REG_NOSUB
2269 | (icase ? AP_REG_ICASE : 0)));
2270 if (new->pnamereg == NULL)
2271 return apr_psprintf(cmd->pool, "%s: header name '%s' could not be compiled",
2272 cmd->directive->directive, name);
2273 } else {
2274 new->pnamereg = NULL;
2275 }
2276 }
2277 val = strchr(var, '=');
2278 if(val) {
2279 new->variable = apr_pstrndup(cmd->pool, var, val-var);
2280 val++;
2281 new->value = apr_pstrdup(cmd->pool, val);
2282 new->valueSet = 1;
2283 } else {
2284 new->variable = apr_pstrdup(cmd->pool, var);
2285 new->value = apr_pstrdup(cmd->pool, "1");
2286 new->valueSet = 0;
2287 }
2288 return NULL;
2289 }
2290
2291 /**
2292 * SetEnvIfPlus
2293 * syntax:
2294 * <attribute> <regex> [!]<env-variable>[=<value>] [late]
2295 */
2296 const char *sp_std_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
2297 int icase = cmd->info == SP_ICASE_MAGIC;
2298 sp_config_t *conf;
2299 apr_array_header_t *conditionals;
2300 int late = 0;
2301 const char *name;
2302 const char *regex;
2303 const char *var;
2304 if(argc < 3) {
2305 return apr_psprintf(cmd->pool, "%s: takes at least three arguments",
2306 cmd->directive->directive);
2307 }
2308 name = argv[0];
2309 regex = argv[1];
2310 var = argv[2];
2311 /* determine scope of the directive */
2312 if(cmd->path) {
2313 if(argc > 4) {
2314 return apr_psprintf(cmd->pool, "%s: takes not more than four arguments",
2315 cmd->directive->directive);
2316 }
2317 conf = dcfg;
2318 conditionals = conf->std_conditionals;
2319 if(argc == 4) {
2320 if(strcasecmp(argv[3], "late") == 0) {
2321 late = 1;
2322 conditionals = conf->std_late_conditionals;
2323 } else {
2324 return apr_psprintf(cmd->pool, "%s: unknown parameter '%s'",
2325 cmd->directive->directive, argv[3]);
2326 }
2327 }
2328 } else {
2329 if(argc > 3) {
2330 return apr_psprintf(cmd->pool, "%s: takes not more than 3 arguments"
2331 " when used at server level",
2332 cmd->directive->directive);
2333 }
2334 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2335 conditionals = conf->std_conditionals;
2336 }
2337
2338 return sp_std_core_cmd(cmd, conditionals, name, regex, var, icase, late);
2339 }
2340
2341 /**
2342 * SetQueryIfPlus
2343 * syntax:
2344 * <attribute> <regex> <query>[=<value>]
2345 */
2346 const char *sp_std_req_query_cmd(cmd_parms *cmd, void *dcfg, const char *attribute,
2347 const char *regex, const char *query) {
2348 int icase = cmd->info == SP_ICASE_MAGIC;
2349 sp_config_t *conf;
2350 apr_array_header_t *req_query;
2351 /* determine scope of the directive */
2352 if(cmd->path) {
2353 conf = dcfg;
2354 req_query = conf->std_req_query;
2355 } else {
2356 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2357 req_query = conf->std_req_query;
2358 }
2359
2360 return sp_std_core_cmd(cmd, req_query, attribute, regex, query, icase, 0);
2361 }
2362
2363 const char *sp_std_req_changequery_cmd(cmd_parms *cmd, void *dcfg, const char *var) {
2364 sp_config_t *conf;
2365 apr_array_header_t *req_query;
2366 char **variable;
2367 /* determine scope of the directive */
2368 if(cmd->path) {
2369 conf = dcfg;
2370 req_query = conf->std_req_changequery;
2371 } else {
2372 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2373 req_query = conf->std_req_changequery;
2374 }
2375 variable = apr_array_push(req_query);
2376 *variable = apr_pstrdup(cmd->pool, var);
2377 return NULL;
2378 }
2379
2380 /**
2381 * ResponseSetEnvIfPlus
2382 * syntax:
2383 * <attribute> <regex> [!]<env-variable>[=<value>]
2384 */
2385 const char *sp_res_std_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
2386 int icase = cmd->info == SP_ICASE_MAGIC;
2387 sp_config_t *conf = dcfg;
2388 int late = 0;
2389 const char *name;
2390 const char *regex;
2391 const char *var;
2392 if(argc != 3) {
2393 return apr_psprintf(cmd->pool, "%s: takes three arguments",
2394 cmd->directive->directive);
2395 }
2396 name = argv[0];
2397 regex = argv[1];
2398 var = argv[2];
2399 return sp_std_core_cmd(cmd, conf->std_res_conditionals, name, regex, var, icase, late);
2400 }
2401
2402 const char *sp_setuser_cmd(cmd_parms *cmd, void *dcfg, const char *header,
2403 const char *regex, const char *value) {
2404 sp_usr_entry_t *new;
2405 sp_config_t *conf;
2406 if(cmd->path) {
2407 conf = dcfg;
2408 } else {
2409 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2410 }
2411 new = apr_array_push(conf->user);
2412 new->header = apr_pstrdup(cmd->pool, header);
2413 new->preg = ap_pregcomp(cmd->pool, regex, AP_REG_EXTENDED);
2414 if(new->preg == NULL) {
2415 return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
2416 cmd->directive->directive, regex);
2417 }
2418 new->value = apr_pstrdup(cmd->pool, value);
2419 return NULL;
2420 }
2421
2422 const char *sp_status_cmd(cmd_parms *cmd, void *dcfg, const char *status,
2423 const char *var) {
2424 sp_status_entry_t *new;
2425 sp_config_t *conf;
2426 int idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
2427 if(cmd->path) {
2428 conf = dcfg;
2429 } else {
2430 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2431 }
2432 new = apr_array_push(conf->status_var);
2433 new->code = atoi(status);
2434 if(new->code == 0) {
2435 return apr_psprintf(cmd->pool, "%s: invalid status code",
2436 cmd->directive->directive);
2437 }
2438 if(new->code != 500) {
2439 if(ap_index_of_response(new->code) == idx500) {
2440 return apr_psprintf(cmd->pool, "%s: unsupported HTTP response code",
2441 cmd->directive->directive);
2442 }
2443 }
2444 new->var = var;
2445 return NULL;
2446 }
2447
2448
2449 const char *sp_var_pfx_cmd(cmd_parms *cmd, void *dcfg, const char *pfx) {
2450 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2451 if (err != NULL) {
2452 return err;
2453 }
2454 if(strcmp(pfx, "$") == 0) {
2455 m_sp_var_pfx = apr_pstrdup(cmd->pool, "${");
2456 } else if(strcmp(pfx, "%") == 0) {
2457 m_sp_var_pfx = apr_pstrdup(cmd->pool, "%{");
2458 } else {
2459 return apr_psprintf(cmd->pool, "%s: unsupported variable prefix (use either %% or $)",
2460 cmd->directive->directive);
2461 }
2462 return NULL;
2463 }
2464
2465 const char *sp_outfilter_cmd(cmd_parms *cmd, void *dcfg, const char *filter,
2466 const char *var) {
2467 sp_filter_entry_t *new;
2468 sp_config_t *conf;
2469 if(cmd->path) {
2470 conf = dcfg;
2471 } else {
2472 conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2473 }
2474 new = apr_array_push(conf->outfilter);
2475 new->filtername = filter;
2476 new->var = var;
2477 new->notset = 0;
2478 if(new->var[0] == '!') {
2479 new->var = &new->var[1];
2480 new->notset = 1;
2481 }
2482 return NULL;
2483 }
2484
2485 const char *sp_cookie_cmd(cmd_parms *cmd, void *dcfg, const char *name) {
2486 char **new;
2487 sp_config_t *conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2488 new = apr_array_push(conf->cookie_names);
2489 *new = apr_pstrdup(cmd->pool, name);
2490 return NULL;
2491 }
2492
2493 const char *sp_hash_cmd(cmd_parms *cmd, void *dcfg, const char *header, const char *var) {
2494 sp_config_t *conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
2495 sp_hash_entry_t *new = apr_array_push(conf->header_hash);
2496 new->header = apr_pstrdup(cmd->pool, header);
2497 new->var = apr_pstrdup(cmd->pool, var);
2498 return NULL;
2499 }
2500
2501 static const command_rec sp_config_cmds[] = {
2502 AP_INIT_TAKE_ARGV("SetEnvIfPlus", sp_std_cmd, NULL,
2503 RSRC_CONF|ACCESS_CONF,
2504 "SetEnvIfPlus"
2505 " <attribute> <regex> [!]<env-variable>[=<value>] [late],"
2506 " defines an expression to apply to the specified request"
2507 " attribute (request header) and sets the defined"
2508 " environment variable if the expression matches."),
2509 AP_INIT_TAKE_ARGV("SetEnvIfPlusNoCase", sp_std_cmd, SP_ICASE_MAGIC,
2510 RSRC_CONF|ACCESS_CONF,
2511 "SetEnvIfPlusNoCase"
2512 " <attribute> <regex> [!]<env-variable>[=<value>] [late]"
2513 " defines an expression to apply to the specified request"
2514 " attribute (request header) and sets the defined"
2515 " environment variable if the expression matches."),
2516 AP_INIT_TAKE3("SetQueryIfPlus", sp_std_req_query_cmd, NULL,
2517 RSRC_CONF|ACCESS_CONF,
2518 "SetQueryIfPlus <attribute> <regex> <query>[=<value>]"
2519 " defines an expression to apply to the specified request"
2520 " attribute (request header) and sets the defined"
2521 " request query paramater if the expression matches."),
2522 AP_INIT_TAKE1("ChangeQueryIfPlus", sp_std_req_changequery_cmd, NULL,
2523 RSRC_CONF|ACCESS_CONF,
2524 "CahngeQueryIfPlus <env-variable>"
2525 " replaces the request query by the value of the"
2526 " specifed environment variable."),
2527 AP_INIT_TAKE_ARGV("ResponseSetEnvIfPlus", sp_res_std_cmd, NULL,
2528 ACCESS_CONF,
2529 "ResponseSetEnvIfPlus"
2530 " <attribute> <regex> [!]<env-variable>[=<value>],"
2531 " defines an expression to apply to the specified response"
2532 " attribute (response header) and sets the defined"
2533 " environment variable if the expression matches."),
2534 AP_INIT_TAKE_ARGV("ResponseSetEnvIfPlusNoCase", sp_res_std_cmd, SP_ICASE_MAGIC,
2535 ACCESS_CONF,
2536 "ResponseSetEnvIfPlusNoCase"
2537 " <attribute> <regex> [!]<env-variable>[=<value>]"
2538 " defines an expression to apply to the specified response"
2539 " attribute (response header) and sets the defined"
2540 " environment variable if the expression matches."),
2541 AP_INIT_TAKE_ARGV("RequestHeaderPlus", sp_reqheader_cmd, NULL,
2542 RSRC_CONF|ACCESS_CONF,
2543 "RequestHeaderPlus set|unset|add <header> [<value>] [env=[!]<env-variable>] [late],"
2544 " manipulates HTTP request headers. The first argument"
2545 " defines the action to take while the second argument"
2546 " specifies the header name."
2547 " The 'env=' attributes"
2548 " is used to define additional conditions (variable must be set)."
2549 " The 'late' attribute enforces late processing similar to"
2550 " the SetEnvIfPlus directive."),
2551 AP_INIT_TAKE_ARGV("HeaderPlus", sp_resheader_cmd, NULL,
2552 RSRC_CONF|ACCESS_CONF,
2553 "HeaderPlus directive is deprecated (use RequestHeaderPlus)."),
2554 AP_INIT_TAKE_ARGV("ResponseHeaderPlus", sp_resheader_cmd, NULL,
2555 RSRC_CONF|ACCESS_CONF,
2556 "ResponseHeaderPlus set|unset|add <header> [<value>] [env=[!]<env-variable>],"
2557 " manipulates HTTP request headers. The first argument"
2558 " defines the action to take while the second argument"
2559 " specifies the header name."
2560 " The 'env=' attributes"
2561 " is used to define additional conditions (variable must be set)."),
2562 AP_INIT_TAKE3("ChangeRequestHeaderPlus", sp_changereqheader_cmd, NULL,
2563 RSRC_CONF|ACCESS_CONF,
2564 "ChangeRequestHeaderPlus <header> <regex> <value>, changes the"
2565 " value of the specified request header if the regex matches."),
2566 AP_INIT_TAKE2("RequestHeaderRemovePattern", sp_removepattern_cmd, NULL,
2567 ACCESS_CONF,
2568 "RequestHeaderRemovePattern <header> <pattern>, removes the"
2569 " specified pattern from the HTTP request header."),
2570 AP_INIT_TAKE3("ChangeResponseHeaderPlus", sp_changeresheader_cmd, NULL,
2571 RSRC_CONF|ACCESS_CONF,
2572 "ChangeResponseHeaderPlus <header> <regex> <value>, changes the"
2573 " value of the specified response header if the regex matches."),
2574 AP_INIT_TAKE_ARGV("SetEnvIfCmpPlus", sp_cmp_cmd, NULL,
2575 RSRC_CONF|ACCESS_CONF,
2576 "SetEnvIfCmpPlus <env-variable1> eq|ne|gt|lt <env-variable2> [!]<env-variable>[=<value>] [late],"
2577 " sets the specifed environment variable if the specifed env-variables"
2578 " are alphabetically or numerical equal (eq), not equal (ne),"
2579 " greater (gt), less (lt). The value string may also contain other"
2580 " environment variable names surrounded by \""SP_VAR_PFX"\" and \"}\" which"
2581 " are resolved by the values of those variables."),
2582 AP_INIT_TAKE3("SetUserPlus", sp_setuser_cmd, NULL,
2583 ACCESS_CONF,
2584 "SetUserPlus <header> <regex> <value>,"
2585 " used to set the an authenticated user name using the 'value'."),
2586 AP_INIT_TAKE2("SetStatusPlus", sp_status_cmd, NULL,
2587 RSRC_CONF|ACCESS_CONF,
2588 "SetStatusPlus <code> <env-variable>,"
2589 " changes the status code of the HTTP response if the specified"
2590 " environment variable is set."),
2591 // TODO support per-directory config
2592 AP_INIT_TAKE1("CookieEncPlus", sp_cookie_cmd, NULL,
2593 RSRC_CONF,
2594 "CookieEncPlus <name>,"
2595 " encrypts the value of the specified cookies. The passphrase for"
2596 " encryption is defined by the "SP_COOKIE_KEY" variable."),
2597 AP_INIT_TAKE2("SetHashHeaderPlus", sp_hash_cmd, NULL,
2598 RSRC_CONF,
2599 "SetHashHeaderPlus <header name> <env-variable>, creates a hash"
2600 " of the value stored in the defined variable and stores it"
2601 " within the defined requst header."),
2602 AP_INIT_TAKE2("AddOutputFilterPlus", sp_outfilter_cmd, NULL,
2603 RSRC_CONF|ACCESS_CONF,
2604 "AddOutputFilterPlus <filter name> [!]<env-variable>,"
2605 " adds the ouput filter to the response if"
2606 " the specified environment variable is set."),
2607 AP_INIT_TAKE1("SetVarPrefixPlus", sp_var_pfx_cmd, NULL,
2608 RSRC_CONF,
2609 "SetVarPrefixPlus '$'|'%', defines the prefix of the"
2610 " variable place holder which is either ${...} or"
2611 " %{...} for compatibilty with Apache 2.4."
2612 " Default is '$'."),
2613 { NULL }
2614 };
2615
2616 /************************************************************************
2617 * apache register
2618 ***********************************************************************/
2619 static void sp_register_hooks(apr_pool_t * p) {
2620 static const char *pre[] = { "mod_setenvif.c", "mod_ssl.c", "mod_session.c", NULL };
2621 static const char *post[] = { "mod_headers.c", NULL };
2622 ap_hook_post_read_request(sp_post_read_request, pre, post, APR_HOOK_MIDDLE);
2623 ap_hook_header_parser(sp_header_parser, pre, post, APR_HOOK_MIDDLE);
2624 ap_hook_check_user_id(sp_user_id, pre, NULL, APR_HOOK_FIRST);
2625 ap_hook_fixups(sp_fixup, pre, post, APR_HOOK_MIDDLE);
2626 ap_hook_post_config(sp_post_config, pre, NULL, APR_HOOK_MIDDLE);
2627 #ifdef SP_INTERNAL_TEST
2628 ap_hook_handler(sp_handler, NULL, NULL, APR_HOOK_MIDDLE);
2629 #endif
2630 ap_register_output_filter("sp_out_filter", sp_out_filter, NULL, AP_FTYPE_RESOURCE);
2631 ap_register_output_filter("sp_out_err_filter", sp_out_err_filter, NULL, AP_FTYPE_RESOURCE);
2632 ap_hook_insert_filter(sp_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
2633 ap_hook_insert_error_filter(sp_insert_err_filter, NULL, NULL, APR_HOOK_MIDDLE);
2634 }
2635
2636 /************************************************************************
2637 * apache module definition
2638 ***********************************************************************/
2639 module AP_MODULE_DECLARE_DATA setenvifplus_module ={
2640 STANDARD20_MODULE_STUFF,
2641 sp_dir_config_create, /**< dir config creater */
2642 sp_dir_config_merge, /**< dir merger */
2643 sp_srv_config_create, /**< server config */
2644 sp_srv_config_merge, /**< server merger */
2645 sp_config_cmds, /**< command table */
2646 sp_register_hooks, /**< hook registery */
2647 };