Apply typo fixes provided by Debian packagers.
[shibboleth/cpp-sp.git] / apache / mod_shib.cpp
1 /**\r
2  * Licensed to the University Corporation for Advanced Internet\r
3  * Development, Inc. (UCAID) under one or more contributor license\r
4  * agreements. See the NOTICE file distributed with this work for\r
5  * additional information regarding copyright ownership.\r
6  *\r
7  * UCAID licenses this file to you under the Apache License,\r
8  * Version 2.0 (the "License"); you may not use this file except\r
9  * in compliance with the License. You may obtain a copy of the\r
10  * License at\r
11  *\r
12  * http://www.apache.org/licenses/LICENSE-2.0\r
13  *\r
14  * Unless required by applicable law or agreed to in writing,\r
15  * software distributed under the License is distributed on an\r
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\r
17  * either express or implied. See the License for the specific\r
18  * language governing permissions and limitations under the License.\r
19  */\r
20 \r
21 /**\r
22  * mod_shib.cpp\r
23  *\r
24  * Apache module implementation.\r
25  */\r
26 \r
27 #define SHIBSP_LITE\r
28 \r
29 #ifdef SOLARIS2\r
30 #undef _XOPEN_SOURCE    // causes gethostname conflict in unistd.h\r
31 #endif\r
32 \r
33 #ifdef WIN32\r
34 # define _CRT_NONSTDC_NO_DEPRECATE 1\r
35 # define _CRT_SECURE_NO_DEPRECATE 1\r
36 #endif\r
37 \r
38 #include <shibsp/exceptions.h>\r
39 #include <shibsp/AbstractSPRequest.h>\r
40 #include <shibsp/AccessControl.h>\r
41 #include <shibsp/GSSRequest.h>\r
42 #include <shibsp/RequestMapper.h>\r
43 #include <shibsp/SPConfig.h>\r
44 #include <shibsp/ServiceProvider.h>\r
45 #include <shibsp/SessionCache.h>\r
46 #include <shibsp/attribute/Attribute.h>\r
47 \r
48 #include <xercesc/util/XMLUniDefs.hpp>\r
49 #include <xercesc/util/regx/RegularExpression.hpp>\r
50 #include <xmltooling/XMLToolingConfig.h>\r
51 #include <xmltooling/util/NDC.h>\r
52 #include <xmltooling/util/ParserPool.h>\r
53 #include <xmltooling/util/Threads.h>\r
54 #include <xmltooling/util/XMLConstants.h>\r
55 #include <xmltooling/util/XMLHelper.h>\r
56 \r
57 #ifdef WIN32\r
58 # include <winsock2.h>\r
59 # include <ws2tcpip.h>\r
60 #endif\r
61 \r
62 #undef _XPG4_2\r
63 \r
64 #include <set>\r
65 #include <memory>\r
66 #include <fstream>\r
67 #include <stdexcept>\r
68 #include <boost/lexical_cast.hpp>\r
69 \r
70 // Apache specific header files\r
71 #include <httpd.h>\r
72 #include <http_config.h>\r
73 #include <http_protocol.h>\r
74 #include <http_main.h>\r
75 #define CORE_PRIVATE\r
76 #include <http_core.h>\r
77 #include <http_log.h>\r
78 #include <http_request.h>\r
79 \r
80 #ifndef SHIB_APACHE_13\r
81 #include <apr_buckets.h>\r
82 #include <apr_strings.h>\r
83 #include <apr_pools.h>\r
84 #endif\r
85 \r
86 #ifdef SHIB_APACHE_24\r
87 #include <mod_auth.h>\r
88 #endif\r
89 \r
90 #include <cstddef>\r
91 #ifdef HAVE_UNISTD_H\r
92 #include <unistd.h>             // for getpid()\r
93 #endif\r
94 \r
95 using namespace shibsp;\r
96 using namespace xmltooling;\r
97 using namespace boost;\r
98 using namespace std;\r
99 using xercesc::RegularExpression;\r
100 using xercesc::XMLException;\r
101 \r
102 #ifdef APLOG_USE_MODULE\r
103     extern "C" module AP_MODULE_DECLARE_DATA mod_shib;\r
104     static int* const aplog_module_index = &(mod_shib.module_index);\r
105 #else\r
106     extern "C" module MODULE_VAR_EXPORT mod_shib;\r
107 #endif\r
108 \r
109 namespace {\r
110     char* g_szSHIBConfig = nullptr;\r
111     char* g_szSchemaDir = nullptr;\r
112     char* g_szPrefix = nullptr;\r
113     SPConfig* g_Config = nullptr;\r
114     string g_unsetHeaderValue,g_spoofKey;\r
115     bool g_checkSpoofing = true;\r
116     bool g_catchAll = false;\r
117 #ifndef SHIB_APACHE_13\r
118     char* g_szGSSContextKey = "mod_auth_gssapi:gss_ctx";\r
119 #endif\r
120     static const char* g_UserDataKey = "urn:mace:shibboleth:Apache:shib_check_user";\r
121 }\r
122 \r
123 /* Apache 2.2.x headers must be accumulated and set in the output filter.\r
124    Apache 2.0.49+ supports the filter method.\r
125    Apache 1.3.x and lesser 2.0.x must write the headers directly. */\r
126 \r
127 #if (defined(SHIB_APACHE_20) || defined(SHIB_APACHE_22) || defined(SHIB_APACHE_24)) && AP_MODULE_MAGIC_AT_LEAST(20020903,6)\r
128 #define SHIB_DEFERRED_HEADERS\r
129 #endif\r
130 \r
131 /********************************************************************************/\r
132 // Basic Apache Configuration code.\r
133 //\r
134 \r
135 // per-server module configuration structure\r
136 struct shib_server_config\r
137 {\r
138     char* szScheme;\r
139     int bCompatValidUser;\r
140 };\r
141 \r
142 // creates the per-server configuration\r
143 extern "C" void* create_shib_server_config(SH_AP_POOL* p, server_rec* s)\r
144 {\r
145     shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));\r
146     sc->szScheme = nullptr;\r
147     sc->bCompatValidUser = -1;\r
148     return sc;\r
149 }\r
150 \r
151 // overrides server configuration in virtual servers\r
152 extern "C" void* merge_shib_server_config (SH_AP_POOL* p, void* base, void* sub)\r
153 {\r
154     shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));\r
155     shib_server_config* parent=(shib_server_config*)base;\r
156     shib_server_config* child=(shib_server_config*)sub;\r
157 \r
158     if (child->szScheme)\r
159         sc->szScheme=ap_pstrdup(p,child->szScheme);\r
160     else if (parent->szScheme)\r
161         sc->szScheme=ap_pstrdup(p,parent->szScheme);\r
162     else\r
163         sc->szScheme=nullptr;\r
164 \r
165     sc->bCompatValidUser = ((child->bCompatValidUser==-1) ? parent->bCompatValidUser : child->bCompatValidUser);\r
166 \r
167     return sc;\r
168 }\r
169 \r
170 // per-dir module configuration structure\r
171 struct shib_dir_config\r
172 {\r
173     SH_AP_TABLE* tSettings; // generic table of extensible settings\r
174 \r
175     // RM Configuration\r
176 #ifdef SHIB_APACHE_24\r
177     int bRequestMapperAuthz;// support RequestMapper AccessControl plugins\r
178 #else\r
179     char* szAuthGrpFile;    // Auth GroupFile name\r
180         char* szAccessControl;  // path to "external" AccessControl plugin file\r
181     int bRequireAll;        // all "known" require directives must match, otherwise OR logic\r
182     int bAuthoritative;     // allow htaccess plugin to DECLINE when authz fails\r
183     int bCompatWith24;      // support 2.4-reserved require logic for compatibility\r
184 #endif\r
185 \r
186     // Content Configuration\r
187     char* szApplicationId;  // Shib applicationId value\r
188     char* szRequireWith;    // require a session using a specific initiator?\r
189     char* szRedirectToSSL;  // redirect non-SSL requests to SSL port\r
190     int bOff;               // flat-out disable all Shib processing\r
191     int bBasicHijack;       // activate for AuthType Basic?\r
192     int bRequireSession;    // require a session?\r
193     int bExportAssertion;   // export SAML assertion to the environment?\r
194     int bUseEnvVars;        // use environment?\r
195     int bUseHeaders;        // use headers?\r
196     int bExpireRedirects;   // expire redirects?\r
197 };\r
198 \r
199 // creates per-directory config structure\r
200 extern "C" void* create_shib_dir_config (SH_AP_POOL* p, char* d)\r
201 {\r
202     shib_dir_config* dc=(shib_dir_config*)ap_pcalloc(p,sizeof(shib_dir_config));\r
203     dc->tSettings = nullptr;\r
204 #ifdef SHIB_APACHE_24\r
205     dc->bRequestMapperAuthz = -1;\r
206 #else\r
207     dc->szAuthGrpFile = nullptr;\r
208         dc->szAccessControl = nullptr;\r
209     dc->bRequireAll = -1;\r
210     dc->bAuthoritative = -1;\r
211     dc->bCompatWith24 = -1;\r
212 #endif\r
213     dc->szApplicationId = nullptr;\r
214     dc->szRequireWith = nullptr;\r
215     dc->szRedirectToSSL = nullptr;\r
216     dc->bOff = -1;\r
217     dc->bBasicHijack = -1;\r
218     dc->bRequireSession = -1;\r
219     dc->bExportAssertion = -1;\r
220     dc->bUseEnvVars = -1;\r
221     dc->bUseHeaders = -1;\r
222     dc->bExpireRedirects = -1;\r
223     return dc;\r
224 }\r
225 \r
226 // overrides server configuration in directories\r
227 extern "C" void* merge_shib_dir_config (SH_AP_POOL* p, void* base, void* sub)\r
228 {\r
229     shib_dir_config* dc=(shib_dir_config*)ap_pcalloc(p,sizeof(shib_dir_config));\r
230     shib_dir_config* parent=(shib_dir_config*)base;\r
231     shib_dir_config* child=(shib_dir_config*)sub;\r
232 \r
233     // The child supersedes any matching table settings in the parent.\r
234     dc->tSettings = nullptr;\r
235     if (parent->tSettings)\r
236         dc->tSettings = ap_copy_table(p, parent->tSettings);\r
237     if (child->tSettings) {\r
238         if (dc->tSettings)\r
239             ap_overlap_tables(dc->tSettings, child->tSettings, AP_OVERLAP_TABLES_SET);\r
240         else\r
241             dc->tSettings = ap_copy_table(p, child->tSettings);\r
242     }\r
243 \r
244 #ifdef SHIB_APACHE_24\r
245     dc->bRequestMapperAuthz = ((child->bRequestMapperAuthz==-1) ? parent->bRequestMapperAuthz : child->bRequestMapperAuthz);\r
246 #else\r
247     if (child->szAuthGrpFile)\r
248         dc->szAuthGrpFile=ap_pstrdup(p,child->szAuthGrpFile);\r
249     else if (parent->szAuthGrpFile)\r
250         dc->szAuthGrpFile=ap_pstrdup(p,parent->szAuthGrpFile);\r
251     else\r
252         dc->szAuthGrpFile=nullptr;\r
253 \r
254         if (child->szAccessControl)\r
255         dc->szAccessControl=ap_pstrdup(p,child->szAccessControl);\r
256     else if (parent->szAccessControl)\r
257         dc->szAccessControl=ap_pstrdup(p,parent->szAccessControl);\r
258     else\r
259         dc->szAccessControl=nullptr;\r
260 #endif\r
261 \r
262     if (child->szApplicationId)\r
263         dc->szApplicationId=ap_pstrdup(p,child->szApplicationId);\r
264     else if (parent->szApplicationId)\r
265         dc->szApplicationId=ap_pstrdup(p,parent->szApplicationId);\r
266     else\r
267         dc->szApplicationId=nullptr;\r
268 \r
269     if (child->szRequireWith)\r
270         dc->szRequireWith=ap_pstrdup(p,child->szRequireWith);\r
271     else if (parent->szRequireWith)\r
272         dc->szRequireWith=ap_pstrdup(p,parent->szRequireWith);\r
273     else\r
274         dc->szRequireWith=nullptr;\r
275 \r
276     if (child->szRedirectToSSL)\r
277         dc->szRedirectToSSL=ap_pstrdup(p,child->szRedirectToSSL);\r
278     else if (parent->szRedirectToSSL)\r
279         dc->szRedirectToSSL=ap_pstrdup(p,parent->szRedirectToSSL);\r
280     else\r
281         dc->szRedirectToSSL=nullptr;\r
282 \r
283     dc->bOff = ((child->bOff==-1) ? parent->bOff : child->bOff);\r
284     dc->bBasicHijack = ((child->bBasicHijack==-1) ? parent->bBasicHijack : child->bBasicHijack);\r
285     dc->bRequireSession = ((child->bRequireSession==-1) ? parent->bRequireSession : child->bRequireSession);\r
286     dc->bExportAssertion = ((child->bExportAssertion==-1) ? parent->bExportAssertion : child->bExportAssertion);\r
287 #ifndef SHIB_APACHE_24\r
288     dc->bRequireAll = ((child->bRequireAll==-1) ? parent->bRequireAll : child->bRequireAll);\r
289     dc->bAuthoritative = ((child->bAuthoritative==-1) ? parent->bAuthoritative : child->bAuthoritative);\r
290     dc->bCompatWith24 = ((child->bCompatWith24==-1) ? parent->bCompatWith24 : child->bCompatWith24);\r
291 #endif\r
292     dc->bUseEnvVars = ((child->bUseEnvVars==-1) ? parent->bUseEnvVars : child->bUseEnvVars);\r
293     dc->bUseHeaders = ((child->bUseHeaders==-1) ? parent->bUseHeaders : child->bUseHeaders);\r
294     dc->bExpireRedirects = ((child->bExpireRedirects==-1) ? parent->bExpireRedirects : child->bExpireRedirects);\r
295     return dc;\r
296 }\r
297 \r
298 class ShibTargetApache; // forward decl\r
299 \r
300 // per-request module structure\r
301 struct shib_request_config\r
302 {\r
303     SH_AP_TABLE* env;        // environment vars\r
304 #ifdef SHIB_DEFERRED_HEADERS\r
305     SH_AP_TABLE* hdr_out;    // headers to browser\r
306 #endif\r
307 #ifndef SHIB_APACHE_13\r
308     ShibTargetApache* sta;  // SP per-request structure wrapped around Apache's request\r
309 #endif\r
310 };\r
311 \r
312 // create or return a request record\r
313 static shib_request_config* get_request_config(request_rec *r)\r
314 {\r
315     shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
316     if (rc) {\r
317         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "get_request_config called redundantly");\r
318     }\r
319     else {\r
320         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "get_request_config created per-request structure");\r
321         rc = (shib_request_config*)ap_pcalloc(r->pool,sizeof(shib_request_config));\r
322         memset(rc, 0, sizeof(shib_request_config));\r
323         ap_set_module_config(r->request_config, &mod_shib, rc);\r
324     }\r
325     return rc;\r
326 }\r
327 \r
328 class ShibTargetApache : public AbstractSPRequest\r
329 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)\r
330     , public GSSRequest\r
331 #endif\r
332 {\r
333   mutable string m_body;\r
334   mutable bool m_gotBody,m_firsttime;\r
335   mutable vector<string> m_certs;\r
336   set<string> m_allhttp;\r
337 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)\r
338   mutable gss_name_t m_gssname;\r
339 #endif\r
340 \r
341 public:\r
342   bool m_handler;\r
343   request_rec* m_req;\r
344   shib_dir_config* m_dc;\r
345   shib_server_config* m_sc;\r
346   shib_request_config* m_rc;\r
347 \r
348   ShibTargetApache(request_rec* req) : AbstractSPRequest(SHIBSP_LOGCAT ".Apache"),\r
349         m_gotBody(false),m_firsttime(true),\r
350 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)\r
351         m_gssname(GSS_C_NO_NAME),\r
352 #endif\r
353         m_handler(false), m_req(req), m_dc(nullptr), m_sc(nullptr), m_rc(nullptr) {\r
354   }\r
355   virtual ~ShibTargetApache() {\r
356 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)\r
357     if (m_gssname != GSS_C_NO_NAME) {\r
358         OM_uint32 minor;\r
359         gss_release_name(&minor, &m_gssname);\r
360     }\r
361 #endif\r
362   }\r
363 \r
364   bool isInitialized() const {\r
365       return (m_sc != nullptr);\r
366   }\r
367 \r
368   bool init(bool handler, bool check_user) {\r
369     m_handler = handler;\r
370     if (m_sc)\r
371         return !check_user; // only initialize once\r
372     m_sc = (shib_server_config*)ap_get_module_config(m_req->server->module_config, &mod_shib);\r
373     m_dc = (shib_dir_config*)ap_get_module_config(m_req->per_dir_config, &mod_shib);\r
374     m_rc = (shib_request_config*)ap_get_module_config(m_req->request_config, &mod_shib);\r
375 \r
376     setRequestURI(m_req->unparsed_uri);\r
377 \r
378     if (check_user && m_dc->bUseHeaders == 1) {\r
379         // Try and see if this request was already processed, to skip spoof checking.\r
380         if (!ap_is_initial_req(m_req)) {\r
381             m_firsttime = false;\r
382         }\r
383         else if (!g_spoofKey.empty()) {\r
384             const char* hdr = ap_table_get(m_req->headers_in, "Shib-Spoof-Check");\r
385             if (hdr && g_spoofKey == hdr)\r
386                 m_firsttime = false;\r
387         }\r
388         if (!m_firsttime)\r
389             log(SPDebug, "shib_check_user running more than once");\r
390     }\r
391     return true;\r
392   }\r
393 \r
394   const char* getScheme() const {\r
395     return m_sc->szScheme ? m_sc->szScheme : ap_http_method(m_req);\r
396   }\r
397   bool isSecure() const {\r
398       return HTTPRequest::isSecure();\r
399   }\r
400   const char* getHostname() const {\r
401 #ifdef SHIB_APACHE_24\r
402       return ap_get_server_name_for_url(m_req);\r
403 #else\r
404       return ap_get_server_name(m_req);\r
405 #endif\r
406   }\r
407   int getPort() const {\r
408     return ap_get_server_port(m_req);\r
409   }\r
410   const char* getMethod() const {\r
411     return m_req->method;\r
412   }\r
413   string getContentType() const {\r
414     const char* type = ap_table_get(m_req->headers_in, "Content-Type");\r
415     return type ? type : "";\r
416   }\r
417   long getContentLength() const {\r
418       // Apache won't expose content length until the body's read.\r
419       if (!m_gotBody) {\r
420           getRequestBody();\r
421       }\r
422       return m_body.length();\r
423   }\r
424   string getRemoteAddr() const {\r
425     string ret = AbstractSPRequest::getRemoteAddr();\r
426     if (!ret.empty())\r
427         return ret;\r
428 #ifdef SHIB_APACHE_24\r
429     return m_req->useragent_ip;\r
430 #else\r
431     return m_req->connection->remote_ip;\r
432 #endif\r
433   }\r
434   void log(SPLogLevel level, const string& msg) const {\r
435     AbstractSPRequest::log(level,msg);\r
436     ap_log_rerror(\r
437         APLOG_MARK,\r
438         (level == SPDebug ? APLOG_DEBUG :\r
439         (level == SPInfo ? APLOG_INFO :\r
440         (level == SPWarn ? APLOG_WARNING :\r
441         (level == SPError ? APLOG_ERR : APLOG_CRIT))))|APLOG_NOERRNO,\r
442         SH_AP_R(m_req),\r
443         "%s",\r
444         msg.c_str()\r
445         );\r
446   }\r
447   const char* getQueryString() const { return m_req->args; }\r
448   const char* getRequestBody() const {\r
449     if (m_gotBody || m_req->method_number==M_GET)\r
450         return m_body.c_str();\r
451 #ifdef SHIB_APACHE_13\r
452     // Read the posted data\r
453     if (ap_setup_client_block(m_req, REQUEST_CHUNKED_DECHUNK) != OK) {\r
454         m_gotBody=true;\r
455         log(SPError, "Apache function (setup_client_block) failed while reading request body.");\r
456         return m_body.c_str();\r
457     }\r
458     if (!ap_should_client_block(m_req)) {\r
459         m_gotBody=true;\r
460         log(SPError, "Apache function (should_client_block) failed while reading request body.");\r
461         return m_body.c_str();\r
462     }\r
463     if (m_req->remaining > 1024*1024)\r
464         throw opensaml::SecurityPolicyException("Blocked request body larger than 1M size limit.");\r
465     m_gotBody=true;\r
466     int len;\r
467     char buff[HUGE_STRING_LEN];\r
468     ap_hard_timeout("[mod_shib] getRequestBody", m_req);\r
469     while ((len=ap_get_client_block(m_req, buff, sizeof(buff))) > 0) {\r
470       ap_reset_timeout(m_req);\r
471       m_body.append(buff, len);\r
472     }\r
473     ap_kill_timeout(m_req);\r
474 #else\r
475     const char *data;\r
476     apr_size_t len;\r
477     int seen_eos = 0;\r
478     apr_bucket_brigade* bb = apr_brigade_create(m_req->pool, m_req->connection->bucket_alloc);\r
479     do {\r
480         apr_bucket *bucket;\r
481         apr_status_t rv = ap_get_brigade(m_req->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);\r
482         if (rv != APR_SUCCESS) {\r
483             log(SPError, "Apache function (ap_get_brigade) failed while reading request body.");\r
484             break;\r
485         }\r
486 \r
487         for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket)) {\r
488             if (APR_BUCKET_IS_EOS(bucket)) {\r
489                 seen_eos = 1;\r
490                 break;\r
491             }\r
492 \r
493             /* We can't do much with this. */\r
494             if (APR_BUCKET_IS_FLUSH(bucket))\r
495                 continue;\r
496 \r
497             /* read */\r
498             apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);\r
499             if (len > 0)\r
500                 m_body.append(data, len);\r
501         }\r
502         apr_brigade_cleanup(bb);\r
503     } while (!seen_eos);\r
504     apr_brigade_destroy(bb);\r
505     m_gotBody=true;\r
506 #endif\r
507     return m_body.c_str();\r
508   }\r
509   const char* getParameter(const char* name) const {\r
510       return AbstractSPRequest::getParameter(name);\r
511   }\r
512   vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const {\r
513       return AbstractSPRequest::getParameters(name, values);\r
514   }\r
515   void clearHeader(const char* rawname, const char* cginame) {\r
516     if (m_dc->bUseHeaders == 1) {\r
517        // ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_clear_header: hdr\n");\r
518         if (g_checkSpoofing && m_firsttime) {\r
519             if (m_allhttp.empty()) {\r
520                 // First time, so populate set with "CGI" versions of client-supplied headers.\r
521 #ifdef SHIB_APACHE_13\r
522                 array_header *hdrs_arr = ap_table_elts(m_req->headers_in);\r
523                 table_entry *hdrs = (table_entry *) hdrs_arr->elts;\r
524 #else\r
525                 const apr_array_header_t *hdrs_arr = apr_table_elts(m_req->headers_in);\r
526                 const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;\r
527 #endif\r
528                 for (int i = 0; i < hdrs_arr->nelts; ++i) {\r
529                     if (!hdrs[i].key)\r
530                         continue;\r
531                     string cgiversion("HTTP_");\r
532                     const char* pch = hdrs[i].key;\r
533                     while (*pch) {\r
534                         cgiversion += (isalnum(*pch) ? toupper(*pch) : '_');\r
535                         pch++;\r
536                     }\r
537                     m_allhttp.insert(cgiversion);\r
538                 }\r
539             }\r
540 \r
541             if (m_allhttp.count(cginame) > 0)\r
542                 throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, rawname));\r
543         }\r
544         ap_table_unset(m_req->headers_in, rawname);\r
545         ap_table_set(m_req->headers_in, rawname, g_unsetHeaderValue.c_str());\r
546     }\r
547   }\r
548   void setHeader(const char* name, const char* value) {\r
549     if (m_dc->bUseEnvVars != 0) {\r
550        if (!m_rc) {\r
551           // this happens on subrequests\r
552           // ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_setheader: no_m_rc\n");\r
553           m_rc = get_request_config(m_req);\r
554        }\r
555        if (!m_rc->env)\r
556            m_rc->env = ap_make_table(m_req->pool, 10);\r
557        // ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_set_env: %s=%s\n", name, value?value:"Null");\r
558        ap_table_set(m_rc->env, name, value ? value : "");\r
559     }\r
560     if (m_dc->bUseHeaders == 1)\r
561        ap_table_set(m_req->headers_in, name, value);\r
562   }\r
563   string getHeader(const char* name) const {\r
564     const char* hdr = ap_table_get(m_req->headers_in, name);\r
565     return string(hdr ? hdr : "");\r
566   }\r
567   string getSecureHeader(const char* name) const {\r
568     if (m_dc->bUseEnvVars != 0) {\r
569        const char *hdr;\r
570        if (m_rc && m_rc->env)\r
571            hdr = ap_table_get(m_rc->env, name);\r
572        else\r
573            hdr = nullptr;\r
574        return string(hdr ? hdr : "");\r
575     }\r
576     return getHeader(name);\r
577   }\r
578   void setRemoteUser(const char* user) {\r
579       SH_AP_USER(m_req) = user ? ap_pstrdup(m_req->pool, user) : nullptr;\r
580       if (m_dc->bUseHeaders == 1) {\r
581           if (user) {\r
582               ap_table_set(m_req->headers_in, "REMOTE_USER", user);\r
583           }\r
584           else {\r
585               ap_table_unset(m_req->headers_in, "REMOTE_USER");\r
586               ap_table_set(m_req->headers_in, "REMOTE_USER", g_unsetHeaderValue.c_str());\r
587           }\r
588       }\r
589   }\r
590   string getRemoteUser() const {\r
591     return string(SH_AP_USER(m_req) ? SH_AP_USER(m_req) : "");\r
592   }\r
593   void setAuthType(const char* authtype) {\r
594       if (authtype && m_dc->bBasicHijack == 1)\r
595           authtype = "Basic";\r
596       SH_AP_AUTH_TYPE(m_req) = authtype ? ap_pstrdup(m_req->pool, authtype) : nullptr;\r
597   }\r
598   string getAuthType() const {\r
599     return string(SH_AP_AUTH_TYPE(m_req) ? SH_AP_AUTH_TYPE(m_req) : "");\r
600   }\r
601   void setContentType(const char* type) {\r
602       m_req->content_type = ap_psprintf(m_req->pool, "%s", type);\r
603   }\r
604   void setResponseHeader(const char* name, const char* value) {\r
605     HTTPResponse::setResponseHeader(name, value);\r
606     if (name) {\r
607 #ifdef SHIB_DEFERRED_HEADERS\r
608         if (!m_rc) {\r
609             // this happens on subrequests\r
610             m_rc = get_request_config(m_req);\r
611         }\r
612         if (m_handler) {\r
613             if (!m_rc->hdr_out) {\r
614                 m_rc->hdr_out = ap_make_table(m_req->pool, 5);\r
615             }\r
616             ap_table_add(m_rc->hdr_out, name, value);\r
617         }\r
618         else\r
619 #endif\r
620             ap_table_add(m_req->err_headers_out, name, value);\r
621     }\r
622   }\r
623   long sendResponse(istream& in, long status) {\r
624     if (status != XMLTOOLING_HTTP_STATUS_OK)\r
625         m_req->status = status;\r
626     ap_send_http_header(m_req);\r
627     char buf[1024];\r
628     while (in) {\r
629         in.read(buf,1024);\r
630         ap_rwrite(buf,in.gcount(),m_req);\r
631     }\r
632 #if (defined(SHIB_APACHE_20) || defined(SHIB_APACHE_22) || defined(SHIB_APACHE_24))\r
633     if (status != XMLTOOLING_HTTP_STATUS_OK && status != XMLTOOLING_HTTP_STATUS_ERROR)\r
634         return status;\r
635 #endif\r
636     return DONE;\r
637   }\r
638   long sendRedirect(const char* url) {\r
639     HTTPResponse::sendRedirect(url);\r
640     ap_table_set(m_req->headers_out, "Location", url);\r
641     if (m_dc->bExpireRedirects != 0) {\r
642         ap_table_set(m_req->err_headers_out, "Expires", "Wed, 01 Jan 1997 12:00:00 GMT");\r
643         ap_table_set(m_req->err_headers_out, "Cache-Control", "private,no-store,no-cache,max-age=0");\r
644     }\r
645     return REDIRECT;\r
646   }\r
647   const vector<string>& getClientCertificates() const {\r
648       if (m_certs.empty()) {\r
649           const char* cert = ap_table_get(m_req->subprocess_env, "SSL_CLIENT_CERT");\r
650           if (cert)\r
651               m_certs.push_back(cert);\r
652           int i = 0;\r
653           do {\r
654               cert = ap_table_get(m_req->subprocess_env, ap_psprintf(m_req->pool, "SSL_CLIENT_CERT_CHAIN_%d", i++));\r
655               if (cert)\r
656                   m_certs.push_back(cert);\r
657           } while (cert);\r
658       }\r
659       return m_certs;\r
660   }\r
661   long returnDecline(void) { return DECLINED; }\r
662   long returnOK(void) { return OK; }\r
663 #if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)\r
664   gss_ctx_id_t getGSSContext() const {\r
665     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;\r
666     apr_pool_userdata_get((void**)&ctx, g_szGSSContextKey, m_req->pool);\r
667     return ctx;\r
668   }\r
669   gss_name_t getGSSName() const {\r
670       if (m_gssname == GSS_C_NO_NAME) {\r
671           gss_ctx_id_t ctx = getGSSContext();\r
672           if (ctx != GSS_C_NO_CONTEXT) {\r
673               OM_uint32 minor;\r
674               OM_uint32 major = gss_inquire_context(&minor, ctx, &m_gssname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);\r
675               if (major != GSS_S_COMPLETE)\r
676                   m_gssname = GSS_C_NO_NAME;\r
677           }\r
678       }\r
679       return m_gssname;\r
680   }\r
681   #endif\r
682 };\r
683 \r
684 /********************************************************************************/\r
685 // Apache hooks\r
686 \r
687 #ifndef SHIB_APACHE_13\r
688 extern "C" apr_status_t shib_request_cleanup(void* rc)\r
689 {\r
690     if (rc && reinterpret_cast<shib_request_config*>(rc)->sta) {\r
691         delete reinterpret_cast<ShibTargetApache*>(reinterpret_cast<shib_request_config*>(rc)->sta);\r
692         reinterpret_cast<shib_request_config*>(rc)->sta = nullptr;\r
693     }\r
694     return APR_SUCCESS;\r
695 }\r
696 #endif\r
697 \r
698 // Initial look at a request - create the per-request structure if need be\r
699 static int shib_post_read(request_rec *r)\r
700 {\r
701     shib_request_config* rc = get_request_config(r);\r
702 #ifdef SHIB_APACHE_24\r
703     if (!rc->sta) {\r
704         rc->sta = new ShibTargetApache(r);\r
705         apr_pool_cleanup_register(r->pool, rc, shib_request_cleanup, apr_pool_cleanup_null);\r
706     }\r
707 #endif\r
708     return DECLINED;\r
709 }\r
710 \r
711 // Performs authentication and enforce session requirements.\r
712 // Also does header/env export from session, and will dispatch\r
713 // SP handler requests if it detects a handler URL.\r
714 extern "C" int shib_check_user(request_rec* r)\r
715 {\r
716     // Short-circuit entirely?\r
717     if (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bOff == 1)\r
718         return DECLINED;\r
719 \r
720     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user entered in pid (%d)", (int)getpid());\r
721 \r
722     string threadid("[");\r
723     threadid += lexical_cast<string>(getpid()) + "] shib_check_user";\r
724     xmltooling::NDC ndc(threadid.c_str());\r
725 \r
726     try {\r
727 #ifndef SHIB_APACHE_24\r
728         ShibTargetApache sta(r);\r
729         ShibTargetApache* psta = &sta;\r
730 #else\r
731         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
732         if (!rc || !rc->sta) {\r
733             ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user found no per-request structure");\r
734             shib_post_read(r);  // ensures objects are created if post_read hook didn't run\r
735             rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
736         }\r
737         ShibTargetApache* psta = rc->sta;\r
738 #endif\r
739         if (!psta->init(false, true)) {\r
740             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user unable to initialize SP request object");\r
741             return SERVER_ERROR;\r
742         }\r
743 \r
744         // Check user authentication and export information, then set the handler bypass\r
745         pair<bool,long> res = psta->getServiceProvider().doAuthentication(*psta, true);\r
746         apr_pool_userdata_setn((const void*)42,g_UserDataKey,nullptr,r->pool);\r
747         // If directed, install a spoof key to recognize when we've already cleared headers.\r
748         if (!g_spoofKey.empty() && (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bUseHeaders == 1))\r
749             ap_table_set(r->headers_in, "Shib-Spoof-Check", g_spoofKey.c_str());\r
750         if (res.first) {\r
751 #ifdef SHIB_APACHE_24\r
752             // This is insane, but Apache's internal request.c logic insists that an auth module\r
753             // returning OK MUST set r->user to avoid a failure. But they check for NULL and not\r
754             // for an empty string. If this turns out to cause trouble, there's no solution except\r
755             // to set a dummy ID any time it's not set.\r
756             if (res.second == OK && !r->user)\r
757                 r->user = "";\r
758 #endif\r
759             return res.second;\r
760         }\r
761 \r
762         // user auth was okay -- export the session data now\r
763         res = psta->getServiceProvider().doExport(*psta);\r
764         if (res.first) {\r
765 #ifdef SHIB_APACHE_24\r
766             // See above for explanation of this hack.\r
767             if (res.second == OK && !r->user)\r
768                 r->user = "";\r
769 #endif\r
770             return res.second;\r
771         }\r
772 \r
773 #ifdef SHIB_APACHE_24\r
774         // See above for explanation of this hack.\r
775         if (!r->user)\r
776             r->user = "";\r
777 #endif\r
778         return OK;\r
779     }\r
780     catch (std::exception& e) {\r
781         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user threw an exception: %s", e.what());\r
782         return SERVER_ERROR;\r
783     }\r
784     catch (...) {\r
785         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_check_user threw an unknown exception!");\r
786         if (g_catchAll)\r
787             return SERVER_ERROR;\r
788         throw;\r
789     }\r
790 }\r
791 \r
792 // Runs SP handler requests when invoked directly.\r
793 extern "C" int shib_handler(request_rec* r)\r
794 {\r
795     // Short-circuit entirely?\r
796     if (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bOff == 1)\r
797         return DECLINED;\r
798 \r
799     string threadid("[");\r
800     threadid += lexical_cast<string>(getpid()) + "] shib_handler";\r
801     xmltooling::NDC ndc(threadid.c_str());\r
802 \r
803 #ifndef SHIB_APACHE_13\r
804     // With 2.x, this handler always runs, though last.\r
805     // We check if shib_check_user ran, because it will detect a handler request\r
806     // and dispatch it directly.\r
807     void* data;\r
808     apr_pool_userdata_get(&data,g_UserDataKey,r->pool);\r
809     if (data==(const void*)42) {\r
810         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler skipped since check_user ran");\r
811         return DECLINED;\r
812     }\r
813 #endif\r
814 \r
815     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler entered in pid (%d): %s", (int)getpid(), r->handler);\r
816 \r
817     try {\r
818 #ifndef SHIB_APACHE_24\r
819         ShibTargetApache sta(r);\r
820         ShibTargetApache* psta = &sta;\r
821 #else\r
822         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
823         if (!rc || !rc->sta) {\r
824             ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_handler found no per-request structure");\r
825             shib_post_read(r);  // ensures objects are created if post_read hook didn't run\r
826             rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
827         }\r
828         ShibTargetApache* psta = rc->sta;\r
829 #endif\r
830         if (!psta->init(true, false)) {\r
831             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_handler unable to initialize SP request object");\r
832             return SERVER_ERROR;\r
833         }\r
834 \r
835         pair<bool,long> res = psta->getServiceProvider().doHandler(*psta);\r
836         if (res.first) return res.second;\r
837 \r
838         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "doHandler() did not handle the request");\r
839         return SERVER_ERROR;\r
840     }\r
841     catch (std::exception& e) {\r
842         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_handler threw an exception: %s", e.what());\r
843         return SERVER_ERROR;\r
844     }\r
845     catch (...) {\r
846         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_handler threw an unknown exception!");\r
847         if (g_catchAll)\r
848           return SERVER_ERROR;\r
849         throw;\r
850     }\r
851 }\r
852 \r
853 // This performs authorization functions to limit access.\r
854 // On all versions, this runs any RequestMap-attached plugins.\r
855 // For pre-2.4 versions, the RequestMap will always find an htAccess plugin\r
856 // that runs code to parse and enforce Apache Require rules.\r
857 // On 2.4, we have to short-circuit that and let Apache run callbacks\r
858 // for each Require rule we handle.\r
859 extern "C" int shib_auth_checker(request_rec* r)\r
860 {\r
861     // Short-circuit entirely?\r
862     shib_dir_config* dc = (shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib);\r
863     if (dc->bOff == 1\r
864 #ifdef SHIB_APACHE_24\r
865         || dc->bRequestMapperAuthz == 0     // this allows for bypass of the full auth_checker hook if only htaccess is used\r
866 #endif\r
867         ) {\r
868         return DECLINED;\r
869     }\r
870 \r
871     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker entered in pid (%d)", (int)getpid());\r
872 \r
873     string threadid("[");\r
874     threadid += lexical_cast<string>(getpid()) + "] shib_auth_checker";\r
875     xmltooling::NDC ndc(threadid.c_str());\r
876 \r
877     try {\r
878 #ifndef SHIB_APACHE_24\r
879         ShibTargetApache sta(r);\r
880         ShibTargetApache* psta = &sta;\r
881 #else\r
882         shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
883         if (!rc || !rc->sta) {\r
884             ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker found no per-request structure");\r
885             shib_post_read(r);  // ensures objects are created if post_read hook didn't run\r
886             rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
887         }\r
888         ShibTargetApache* psta = rc->sta;\r
889 #endif\r
890         if (!psta->init(false, false)) {\r
891             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker unable to initialize SP request object");\r
892             return SERVER_ERROR;\r
893         }\r
894 \r
895         pair<bool,long> res = psta->getServiceProvider().doAuthorization(*psta);\r
896         if (res.first) return res.second;\r
897 \r
898         // The SP method should always return true, so if we get this far, something unusual happened.\r
899         // Just let Apache (or some other module) decide what to do.\r
900         return DECLINED;\r
901     }\r
902     catch (std::exception& e) {\r
903         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker threw an exception: %s", e.what());\r
904         return SERVER_ERROR;\r
905     }\r
906     catch (...) {\r
907         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(r), "shib_auth_checker threw an unknown exception!");\r
908         if (g_catchAll)\r
909           return SERVER_ERROR;\r
910         throw;\r
911     }\r
912 }\r
913 \r
914 // Overlays environment variables on top of subprocess table.\r
915 extern "C" int shib_fixups(request_rec* r)\r
916 {\r
917     shib_dir_config *dc = (shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib);\r
918     if (dc->bOff==1 || dc->bUseEnvVars==0)\r
919         return DECLINED;\r
920 \r
921     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_fixups entered in pid (%d)", (int)getpid());\r
922 \r
923     shib_request_config *rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
924     if (rc==nullptr || rc->env==nullptr || ap_is_empty_table(rc->env))\r
925         return DECLINED;\r
926 \r
927     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_fixups adding %d vars", ap_table_elts(rc->env)->nelts);\r
928     r->subprocess_env = ap_overlay_tables(r->pool, r->subprocess_env, rc->env);\r
929 \r
930     return OK;\r
931 }\r
932 \r
933 \r
934 // Access control plugin that enforces pre-2.4 htaccess rules.\r
935 // Post-2.4, we have to register individual methods to respond\r
936 // to each require rule we want to handle, and have those call\r
937 // into these methods directly.\r
938 class htAccessControl : virtual public AccessControl\r
939 {\r
940 public:\r
941     htAccessControl() {}\r
942     ~htAccessControl() {}\r
943     Lockable* lock() {return this;}\r
944     void unlock() {}\r
945     aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
946 \r
947     aclresult_t doAccessControl(const ShibTargetApache& sta, const Session* session, const char* plugin) const;\r
948     aclresult_t doUser(const ShibTargetApache& sta, const char* params) const;\r
949 #ifndef SHIB_APACHE_24\r
950     aclresult_t doGroup(const ShibTargetApache& sta, const char* params) const;\r
951 #endif\r
952     aclresult_t doAuthnContext(const ShibTargetApache& sta, const char* acRef, const char* params) const;\r
953     aclresult_t doShibAttr(const ShibTargetApache& sta, const Session* session, const char* rule, const char* params) const;\r
954 \r
955 private:\r
956     bool checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const;\r
957 };\r
958 \r
959 AccessControl* htAccessFactory(const xercesc::DOMElement* const & e)\r
960 {\r
961     return new htAccessControl();\r
962 }\r
963 \r
964 AccessControl::aclresult_t htAccessControl::doAccessControl(const ShibTargetApache& sta, const Session* session, const char* plugin) const\r
965 {\r
966         aclresult_t result = shib_acl_false;\r
967         try {\r
968         ifstream aclfile(plugin);\r
969         if (!aclfile)\r
970             throw ConfigurationException("Unable to open access control file ($1).", params(1, plugin));\r
971         xercesc::DOMDocument* acldoc = XMLToolingConfig::getConfig().getParser().parse(aclfile);\r
972                 XercesJanitor<xercesc::DOMDocument> docjanitor(acldoc);\r
973                 static XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);\r
974         string t(XMLHelper::getAttrString(acldoc ? acldoc->getDocumentElement() : nullptr, nullptr, _type));\r
975         if (t.empty())\r
976             throw ConfigurationException("Missing type attribute in AccessControl plugin configuration.");\r
977         scoped_ptr<AccessControl> aclplugin(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acldoc->getDocumentElement()));\r
978                 Locker acllock(aclplugin.get());\r
979                 result = aclplugin->authorized(sta, session);\r
980         }\r
981         catch (std::exception& ex) {\r
982                 sta.log(SPRequest::SPError, ex.what());\r
983         }\r
984     return result;\r
985 }\r
986 \r
987 AccessControl::aclresult_t htAccessControl::doUser(const ShibTargetApache& sta, const char* params) const\r
988 {\r
989     bool regexp = false;\r
990     bool negated = false;\r
991     while (*params) {\r
992         const char* w = ap_getword_conf(sta.m_req->pool, &params);\r
993         if (*w == '~') {\r
994             regexp = true;\r
995             continue;\r
996         }\r
997         else if (*w == '!') {\r
998             // A negated rule presumes success unless a match is found.\r
999             negated = true;\r
1000             if (*(w+1) == '~')\r
1001                 regexp = true;\r
1002             continue;\r
1003         }\r
1004 \r
1005         // Figure out if there's a match.\r
1006         bool match = false;\r
1007         if (regexp) {\r
1008             try {\r
1009                 // To do regex matching, we have to convert from UTF-8.\r
1010                 auto_arrayptr<XMLCh> trans(fromUTF8(w));\r
1011                 RegularExpression re(trans.get());\r
1012                 auto_arrayptr<XMLCh> trans2(fromUTF8(sta.getRemoteUser().c_str()));\r
1013                 match = re.matches(trans2.get());\r
1014             }\r
1015             catch (XMLException& ex) {\r
1016                 auto_ptr_char tmp(ex.getMessage());\r
1017                 sta.log(SPRequest::SPError,\r
1018                     string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());\r
1019             }\r
1020         }\r
1021         else if (sta.getRemoteUser() == w) {\r
1022             match = true;\r
1023         }\r
1024 \r
1025         if (match) {\r
1026             if (sta.isPriorityEnabled(SPRequest::SPDebug))\r
1027                 sta.log(SPRequest::SPDebug,\r
1028                     string("htaccess: require user ") + (negated ? "rejecting (" : "accepting (") + sta.getRemoteUser() + ")");\r
1029             return (negated ? shib_acl_false : shib_acl_true);\r
1030         }\r
1031     }\r
1032     return (negated ? shib_acl_true : shib_acl_false);\r
1033 }\r
1034 \r
1035 #ifndef SHIB_APACHE_24\r
1036 static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpfile)\r
1037 {\r
1038     SH_AP_CONFIGFILE* f;\r
1039     SH_AP_TABLE* grps=ap_make_table(r->pool,15);\r
1040     char l[MAX_STRING_LEN];\r
1041     const char *group_name, *ll, *w;\r
1042 \r
1043 #ifdef SHIB_APACHE_13\r
1044     if (!(f=ap_pcfg_openfile(r->pool, grpfile))) {\r
1045 #else\r
1046     if (ap_pcfg_openfile(&f,r->pool,grpfile) != APR_SUCCESS) {\r
1047 #endif\r
1048         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, SH_AP_R(r), "groups_for_user: could not open group file: %s\n", grpfile);\r
1049         return nullptr;\r
1050     }\r
1051 \r
1052     SH_AP_POOL* sp;\r
1053 #ifdef SHIB_APACHE_13\r
1054     sp=ap_make_sub_pool(r->pool);\r
1055 #else\r
1056     if (apr_pool_create(&sp,r->pool) != APR_SUCCESS) {\r
1057         ap_log_rerror(APLOG_MARK,APLOG_ERR,0,r,\r
1058             "groups_for_user: could not create a subpool");\r
1059         return nullptr;\r
1060     }\r
1061 #endif\r
1062 \r
1063     while (!(ap_cfg_getline(l,MAX_STRING_LEN,f))) {\r
1064         if ((*l=='#') || (!*l))\r
1065             continue;\r
1066         ll = l;\r
1067         ap_clear_pool(sp);\r
1068         group_name = ap_getword(sp,&ll,':');\r
1069         while (*ll) {\r
1070             w=ap_getword_conf(sp,&ll);\r
1071             if (!strcmp(w,user)) {\r
1072                 ap_table_setn(grps,ap_pstrdup(r->pool,group_name),"in");\r
1073                 break;\r
1074             }\r
1075         }\r
1076     }\r
1077     ap_cfg_closefile(f);\r
1078     ap_destroy_pool(sp);\r
1079     return grps;\r
1080 }\r
1081 \r
1082 AccessControl::aclresult_t htAccessControl::doGroup(const ShibTargetApache& sta, const char* params) const\r
1083 {\r
1084     SH_AP_TABLE* grpstatus = nullptr;\r
1085     if (sta.m_dc->szAuthGrpFile) {\r
1086         if (sta.isPriorityEnabled(SPRequest::SPDebug))\r
1087             sta.log(SPRequest::SPDebug, string("htaccess plugin using groups file: ") + sta.m_dc->szAuthGrpFile);\r
1088         grpstatus = groups_for_user(sta.m_req, sta.getRemoteUser().c_str(), sta.m_dc->szAuthGrpFile);\r
1089     }\r
1090 \r
1091     bool negated = false;\r
1092     while (*params) {\r
1093         const char* w = ap_getword_conf(sta.m_req->pool, &params);\r
1094         if (*w == '!') {\r
1095             // A negated rule presumes success unless a match is found.\r
1096             negated = true;\r
1097             continue;\r
1098         }\r
1099 \r
1100         if (grpstatus && ap_table_get(grpstatus, w)) {\r
1101             // If we matched, then we're done with this rule either way and we flip status to reflect the outcome.\r
1102             sta.log(SPRequest::SPDebug, string("htaccess: require group ") + (negated ? "rejecting (" : "accepting (") + w + ")");\r
1103             return (negated ? shib_acl_false : shib_acl_true);\r
1104         }\r
1105     }\r
1106 \r
1107     return (negated ? shib_acl_true : shib_acl_false);\r
1108 }\r
1109 #endif\r
1110 \r
1111 AccessControl::aclresult_t htAccessControl::doAuthnContext(const ShibTargetApache& sta, const char* ref, const char* params) const\r
1112 {\r
1113     if (ref && *ref) {\r
1114         bool regexp = false;\r
1115         bool negated = false;\r
1116         while (ref && *params) {\r
1117             const char* w = ap_getword_conf(sta.m_req->pool, &params);\r
1118             if (*w == '~') {\r
1119                 regexp = true;\r
1120                 continue;\r
1121             }\r
1122             else if (*w == '!') {\r
1123                 // A negated rule presumes success unless a match is found.\r
1124                 negated = true;\r
1125                 if (*(w+1) == '~')\r
1126                     regexp = true;\r
1127                 continue;\r
1128             }\r
1129 \r
1130             // Figure out if there's a match.\r
1131             bool match = false;\r
1132             if (regexp) {\r
1133                 try {\r
1134                     RegularExpression re(w);\r
1135                     match = re.matches(ref);\r
1136                 }\r
1137                 catch (XMLException& ex) {\r
1138                     auto_ptr_char tmp(ex.getMessage());\r
1139                     sta.log(SPRequest::SPError,\r
1140                         string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());\r
1141                 }\r
1142             }\r
1143             else if (!strcmp(w, ref)) {\r
1144                 match = true;\r
1145             }\r
1146 \r
1147             if (match) {\r
1148                 if (sta.isPriorityEnabled(SPRequest::SPDebug))\r
1149                     sta.log(SPRequest::SPDebug,\r
1150                         string("htaccess: require authnContext ") + (negated ? "rejecting (" : "accepting (") + ref + ")");\r
1151                 return (negated ? shib_acl_false : shib_acl_true);\r
1152             }\r
1153         }\r
1154         return (negated ? shib_acl_true : shib_acl_false);\r
1155     }\r
1156 \r
1157     if (sta.isPriorityEnabled(SPRequest::SPDebug))\r
1158         sta.log(SPRequest::SPDebug, "htaccess: require authnContext rejecting session with no context associated");\r
1159     return shib_acl_false;\r
1160 }\r
1161 \r
1162 bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const\r
1163 {\r
1164     bool caseSensitive = attr->isCaseSensitive();\r
1165     const vector<string>& vals = attr->getSerializedValues();\r
1166     for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {\r
1167         if (re) {\r
1168             auto_arrayptr<XMLCh> trans(fromUTF8(v->c_str()));\r
1169             if (re->matches(trans.get())) {\r
1170                 if (request.isPriorityEnabled(SPRequest::SPDebug))\r
1171                     request.log(SPRequest::SPDebug, string("htaccess: expecting regexp ") + toMatch + ", got " + *v + ": accepted");\r
1172                 return true;\r
1173             }\r
1174         }\r
1175         else if ((caseSensitive && *v == toMatch) || (!caseSensitive && !strcasecmp(v->c_str(), toMatch))) {\r
1176             if (request.isPriorityEnabled(SPRequest::SPDebug))\r
1177                 request.log(SPRequest::SPDebug, string("htaccess: expecting ") + toMatch + ", got " + *v + ": accepted");\r
1178             return true;\r
1179         }\r
1180         else if (request.isPriorityEnabled(SPRequest::SPDebug)) {\r
1181             request.log(SPRequest::SPDebug, string("htaccess: expecting ") + toMatch + ", got " + *v + ": rejected");\r
1182         }\r
1183     }\r
1184     return false;\r
1185 }\r
1186 \r
1187 AccessControl::aclresult_t htAccessControl::doShibAttr(const ShibTargetApache& sta, const Session* session, const char* rule, const char* params) const\r
1188 {\r
1189 #ifndef SHIB_APACHE_24\r
1190     // Look for the new shib-attr placeholder and move past it.\r
1191     if (sta.m_dc->bCompatWith24 == 1 && rule && !strcmp(rule, "shib-attr")) {\r
1192         if (*params)\r
1193             rule = ap_getword_conf(sta.m_req->pool, &params);\r
1194     }\r
1195 #endif\r
1196 \r
1197     // Find the attribute(s) matching the require rule.\r
1198     pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs =\r
1199         session->getIndexedAttributes().equal_range(rule ? rule : "");\r
1200 \r
1201     bool regexp = false;\r
1202     while (attrs.first != attrs.second && *params) {\r
1203         const char* w = ap_getword_conf(sta.m_req->pool, &params);\r
1204         if (*w == '~') {\r
1205             regexp = true;\r
1206             continue;\r
1207         }\r
1208 \r
1209         try {\r
1210             scoped_ptr<RegularExpression> re;\r
1211             if (regexp) {\r
1212                 auto_arrayptr<XMLCh> trans(fromUTF8(w));\r
1213                 re.reset(new xercesc::RegularExpression(trans.get()));\r
1214             }\r
1215                     \r
1216             pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs2(attrs);\r
1217             for (; attrs2.first != attrs2.second; ++attrs2.first) {\r
1218                 if (checkAttribute(sta, attrs2.first->second, w, regexp ? re.get() : nullptr)) {\r
1219                     return shib_acl_true;\r
1220                 }\r
1221             }\r
1222         }\r
1223         catch (XMLException& ex) {\r
1224             auto_ptr_char tmp(ex.getMessage());\r
1225             sta.log(SPRequest::SPError, string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());\r
1226         }\r
1227     }\r
1228     return shib_acl_false;\r
1229 }\r
1230 \r
1231 AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, const Session* session) const\r
1232 {\r
1233 #ifdef SHIB_APACHE_24\r
1234     // We should never be invoked in 2.4 as an SP plugin.\r
1235     throw ConfigurationException("Save my walrus!");\r
1236 #else\r
1237     // Make sure the object is our type.\r
1238     const ShibTargetApache* sta=dynamic_cast<const ShibTargetApache*>(&request);\r
1239     if (!sta)\r
1240         throw ConfigurationException("Request wrapper object was not of correct type.");\r
1241 \r
1242     int m = sta->m_req->method_number;\r
1243     bool method_restricted = false;\r
1244     const char *t, *w;\r
1245 \r
1246     const array_header* reqs_arr = ap_requires(sta->m_req);\r
1247     if (!reqs_arr)\r
1248         return shib_acl_indeterminate;  // should never happen\r
1249 \r
1250         // Check for an "embedded" AccessControl plugin.\r
1251         if (sta->m_dc->szAccessControl) {\r
1252         aclresult_t result = doAccessControl(*sta, session, sta->m_dc->szAccessControl);\r
1253         if (result == shib_acl_true && sta->m_dc->bRequireAll != 1) {\r
1254             // If we're not insisting that all rules be met, then we're done.\r
1255             request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was successful, granting access");\r
1256             return shib_acl_true;\r
1257         }\r
1258         else if (result != shib_acl_true && sta->m_dc->bRequireAll == 1) {\r
1259             // If we're insisting that all rules be met, which is not something Apache really handles well,\r
1260             // then we either return false or indeterminate based on the authoritative option, which defaults on.\r
1261             if (sta->m_dc->bAuthoritative != 0) {\r
1262                 request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was unsuccessful, denying access");\r
1263                 return shib_acl_false;\r
1264             }\r
1265 \r
1266             request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was unsuccessful but not authoritative, leaving it up to Apache");\r
1267             return shib_acl_indeterminate;\r
1268         }\r
1269     }\r
1270 \r
1271     require_line* reqs = (require_line*)reqs_arr->elts;\r
1272 \r
1273     for (int x = 0; x < reqs_arr->nelts; ++x) {\r
1274         // This rule should be completely ignored, the method doesn't fit.\r
1275         // The rule just doesn't exist for our purposes.\r
1276         if (!(reqs[x].method_mask & (1 << m)))\r
1277             continue;\r
1278 \r
1279         method_restricted = true; // this lets us know at the end that at least one rule was potentially enforcable.\r
1280 \r
1281         // Tracks status of this rule's evaluation.\r
1282         bool status = false;\r
1283 \r
1284         string remote_user = request.getRemoteUser();\r
1285 \r
1286         t = reqs[x].requirement;\r
1287         w = ap_getword_white(sta->m_req->pool, &t);\r
1288 \r
1289         if (!strcasecmp(w,"shibboleth")) {\r
1290             // This is a dummy rule needed because Apache conflates authn and authz.\r
1291             // Without some require rule, AuthType is ignored and no check_user hooks run.\r
1292 \r
1293             // We evaluate to false if ShibAccessControl is used and ShibRequireAll is off.\r
1294             // This allows actual rules to dictate the result, since ShibAccessControl returned\r
1295             // non-true, and if nothing else is used, access will be denied.\r
1296             if (!sta->m_dc->szAccessControl || sta->m_dc->bRequireAll == 1) {\r
1297                 // We evaluate to true, because ShibRequireAll is enabled (so a true is just a no-op)\r
1298                 // or because there was no other AccessControl rule in place, so this may be the only\r
1299                 // rule in effect.\r
1300                 status = true;\r
1301             }\r
1302         }\r
1303         else if (!strcmp(w,"valid-user") && session) {\r
1304             request.log(SPRequest::SPDebug, "htaccess: accepting valid-user based on active session");\r
1305             status = true;\r
1306         }\r
1307         else if (sta->m_dc->bCompatWith24 == 1 && !strcmp(w,"shib-session") && session) {\r
1308             request.log(SPRequest::SPDebug, "htaccess: accepting shib-session based on active session");\r
1309             status = true;\r
1310         }\r
1311         else if (!strcmp(w,"user") && !remote_user.empty()) {\r
1312             status = (doUser(*sta, t) == shib_acl_true);\r
1313         }\r
1314         else if (sta->m_dc->bCompatWith24 == 1 && !strcmp(w,"shib-user") && !remote_user.empty()) {\r
1315             status = (doUser(*sta, t) == shib_acl_true);\r
1316         }\r
1317         else if (!strcmp(w,"group")  && !remote_user.empty()) {\r
1318             status = (doGroup(*sta, t) == shib_acl_true);\r
1319         }\r
1320         else if (!strcmp(w,"authnContextClassRef") || !strcmp(w,"authnContextDeclRef")) {\r
1321             const char* ref = !strcmp(w, "authnContextClassRef") ? session->getAuthnContextClassRef() : session->getAuthnContextDeclRef();\r
1322             status = (doAuthnContext(*sta, ref, t) == shib_acl_true);\r
1323         }\r
1324         else if (!session) {\r
1325             request.log(SPRequest::SPError, string("htaccess: require ") + w + " not given a valid session, are you using lazy sessions?");\r
1326         }\r
1327         else if (sta->m_dc->bCompatWith24 == 1 && !strcmp(w,"shib-plugin")) {\r
1328             w = ap_getword_conf(sta->m_req->pool, &t);\r
1329             if (w) {\r
1330                 status = (doAccessControl(*sta, session, w) == shib_acl_true);\r
1331             }\r
1332         }\r
1333         else {\r
1334             status = (doShibAttr(*sta, session, w, t) == shib_acl_true);\r
1335         }\r
1336 \r
1337         // If status is false, we found a rule we couldn't satisfy.\r
1338         // Could be an unknown rule to us, or it just didn't match.\r
1339 \r
1340         if (status && sta->m_dc->bRequireAll != 1) {\r
1341             // If we're not insisting that all rules be met, then we're done.\r
1342             request.log(SPRequest::SPDebug, "htaccess: a rule was successful, granting access");\r
1343             return shib_acl_true;\r
1344         }\r
1345         else if (!status && sta->m_dc->bRequireAll == 1) {\r
1346             // If we're insisting that all rules be met, which is not something Apache really handles well,\r
1347             // then we either return false or indeterminate based on the authoritative option, which defaults on.\r
1348             if (sta->m_dc->bAuthoritative != 0) {\r
1349                 request.log(SPRequest::SPDebug, "htaccess: a rule was unsuccessful, denying access");\r
1350                 return shib_acl_false;\r
1351             }\r
1352 \r
1353             request.log(SPRequest::SPDebug, "htaccess: a rule was unsuccessful but not authoritative, leaving it up to Apache");\r
1354             return shib_acl_indeterminate;\r
1355         }\r
1356 \r
1357         // Otherwise, we keep going. If we're requring all, then we have to check every rule.\r
1358         // If not we just didn't find a successful rule yet, so we keep going anyway.\r
1359     }\r
1360 \r
1361     // If we get here, we either "failed" or we're in require all mode (but not both).\r
1362     // If no rules possibly apply or we insisted that all rules check out, then we're good.\r
1363     if (!method_restricted) {\r
1364         request.log(SPRequest::SPDebug, "htaccess: no rules applied to this request method, granting access");\r
1365         return shib_acl_true;\r
1366     }\r
1367     else if (sta->m_dc->bRequireAll == 1) {\r
1368         request.log(SPRequest::SPDebug, "htaccess: all rules successful, granting access");\r
1369         return shib_acl_true;\r
1370     }\r
1371     else if (sta->m_dc->bAuthoritative != 0) {\r
1372         request.log(SPRequest::SPDebug, "htaccess: no rules were successful, denying access");\r
1373         return shib_acl_false;\r
1374     }\r
1375 \r
1376     request.log(SPRequest::SPDebug, "htaccess: no rules were successful but not authoritative, leaving it up to Apache");\r
1377     return shib_acl_indeterminate;\r
1378 #endif\r
1379 }\r
1380 \r
1381 class ApacheRequestMapper : public virtual RequestMapper, public virtual PropertySet\r
1382 {\r
1383 public:\r
1384     ApacheRequestMapper(const xercesc::DOMElement* e);\r
1385     ~ApacheRequestMapper() {}\r
1386     Lockable* lock() { return m_mapper->lock(); }\r
1387     void unlock() { m_staKey->setData(nullptr); m_propsKey->setData(nullptr); m_mapper->unlock(); }\r
1388     Settings getSettings(const HTTPRequest& request) const;\r
1389 \r
1390     const PropertySet* getParent() const { return nullptr; }\r
1391     void setParent(const PropertySet*) {}\r
1392     pair<bool,bool> getBool(const char* name, const char* ns=nullptr) const;\r
1393     pair<bool,const char*> getString(const char* name, const char* ns=nullptr) const;\r
1394     pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=nullptr) const;\r
1395     pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=nullptr) const;\r
1396     pair<bool,int> getInt(const char* name, const char* ns=nullptr) const;\r
1397     void getAll(map<string,const char*>& properties) const;\r
1398     const PropertySet* getPropertySet(const char* name, const char* ns=shibspconstants::ASCII_SHIB2SPCONFIG_NS) const;\r
1399     const xercesc::DOMElement* getElement() const;\r
1400 \r
1401     const htAccessControl& getHTAccessControl() const { return m_htaccess; }\r
1402 \r
1403 private:\r
1404     scoped_ptr<RequestMapper> m_mapper;\r
1405     scoped_ptr<ThreadKey> m_staKey,m_propsKey;\r
1406     mutable htAccessControl m_htaccess;\r
1407 };\r
1408 \r
1409 RequestMapper* ApacheRequestMapFactory(const xercesc::DOMElement* const & e)\r
1410 {\r
1411     return new ApacheRequestMapper(e);\r
1412 }\r
1413 \r
1414 ApacheRequestMapper::ApacheRequestMapper(const xercesc::DOMElement* e)\r
1415     : m_mapper(SPConfig::getConfig().RequestMapperManager.newPlugin(XML_REQUEST_MAPPER,e)),\r
1416         m_staKey(ThreadKey::create(nullptr)), m_propsKey(ThreadKey::create(nullptr))\r
1417 {\r
1418 }\r
1419 \r
1420 RequestMapper::Settings ApacheRequestMapper::getSettings(const HTTPRequest& request) const\r
1421 {\r
1422     Settings s = m_mapper->getSettings(request);\r
1423     m_staKey->setData((void*)dynamic_cast<const ShibTargetApache*>(&request));\r
1424     m_propsKey->setData((void*)s.first);\r
1425     // Only return the htAccess plugin for pre-2.4 servers.\r
1426 #ifdef SHIB_APACHE_24\r
1427     return pair<const PropertySet*,AccessControl*>(this, s.second);\r
1428 #else\r
1429     return pair<const PropertySet*,AccessControl*>(this, s.second ? s.second : &m_htaccess);\r
1430 #endif\r
1431 }\r
1432 \r
1433 pair<bool,bool> ApacheRequestMapper::getBool(const char* name, const char* ns) const\r
1434 {\r
1435     const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());\r
1436     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1437     if (sta && !ns) {\r
1438         // Override Apache-settable boolean properties.\r
1439         if (name && !strcmp(name,"requireSession") && sta->m_dc->bRequireSession != -1)\r
1440             return make_pair(true, sta->m_dc->bRequireSession==1);\r
1441         else if (name && !strcmp(name,"exportAssertion") && sta->m_dc->bExportAssertion != -1)\r
1442             return make_pair(true, sta->m_dc->bExportAssertion==1);\r
1443         else if (sta->m_dc->tSettings) {\r
1444             const char* prop = ap_table_get(sta->m_dc->tSettings, name);\r
1445             if (prop)\r
1446                 return make_pair(true, !strcmp(prop, "true") || !strcmp(prop, "1") || !strcmp(prop, "On"));\r
1447         }\r
1448     }\r
1449     return s ? s->getBool(name,ns) : make_pair(false,false);\r
1450 }\r
1451 \r
1452 pair<bool,const char*> ApacheRequestMapper::getString(const char* name, const char* ns) const\r
1453 {\r
1454     const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());\r
1455     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1456     if (sta && !ns) {\r
1457         // Override Apache-settable string properties.\r
1458         if (name && !strcmp(name,"authType")) {\r
1459             const char* auth_type = ap_auth_type(sta->m_req);\r
1460             if (auth_type) {\r
1461                 // Check for Basic Hijack\r
1462                 if (!strcasecmp(auth_type, "basic") && sta->m_dc->bBasicHijack == 1)\r
1463                     auth_type = "shibboleth";\r
1464                 return make_pair(true, auth_type);\r
1465             }\r
1466         }\r
1467         else if (name && !strcmp(name,"applicationId") && sta->m_dc->szApplicationId)\r
1468             return pair<bool,const char*>(true,sta->m_dc->szApplicationId);\r
1469         else if (name && !strcmp(name,"requireSessionWith") && sta->m_dc->szRequireWith)\r
1470             return pair<bool,const char*>(true,sta->m_dc->szRequireWith);\r
1471         else if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)\r
1472             return pair<bool,const char*>(true,sta->m_dc->szRedirectToSSL);\r
1473         else if (sta->m_dc->tSettings) {\r
1474             const char* prop = ap_table_get(sta->m_dc->tSettings, name);\r
1475             if (prop)\r
1476                 return make_pair(true, prop);\r
1477         }\r
1478     }\r
1479     return s ? s->getString(name,ns) : pair<bool,const char*>(false,nullptr);\r
1480 }\r
1481 \r
1482 pair<bool,const XMLCh*> ApacheRequestMapper::getXMLString(const char* name, const char* ns) const\r
1483 {\r
1484     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1485     return s ? s->getXMLString(name,ns) : pair<bool,const XMLCh*>(false,nullptr);\r
1486 }\r
1487 \r
1488 pair<bool,unsigned int> ApacheRequestMapper::getUnsignedInt(const char* name, const char* ns) const\r
1489 {\r
1490     const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());\r
1491     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1492     if (sta && !ns) {\r
1493         // Override Apache-settable int properties.\r
1494         if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)\r
1495             return pair<bool,unsigned int>(true, strtol(sta->m_dc->szRedirectToSSL, nullptr, 10));\r
1496         else if (sta->m_dc->tSettings) {\r
1497             const char* prop = ap_table_get(sta->m_dc->tSettings, name);\r
1498             if (prop)\r
1499                 return pair<bool,unsigned int>(true, atoi(prop));\r
1500         }\r
1501     }\r
1502     return s ? s->getUnsignedInt(name,ns) : pair<bool,unsigned int>(false,0);\r
1503 }\r
1504 \r
1505 pair<bool,int> ApacheRequestMapper::getInt(const char* name, const char* ns) const\r
1506 {\r
1507     const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());\r
1508     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1509     if (sta && !ns) {\r
1510         // Override Apache-settable int properties.\r
1511         if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)\r
1512             return pair<bool,int>(true,atoi(sta->m_dc->szRedirectToSSL));\r
1513         else if (sta->m_dc->tSettings) {\r
1514             const char* prop = ap_table_get(sta->m_dc->tSettings, name);\r
1515             if (prop)\r
1516                 return make_pair(true, atoi(prop));\r
1517         }\r
1518     }\r
1519     return s ? s->getInt(name,ns) : pair<bool,int>(false,0);\r
1520 }\r
1521 \r
1522 static int _rm_get_all_table_walk(void *v, const char *key, const char *value)\r
1523 {\r
1524     reinterpret_cast<map<string,const char*>*>(v)->insert(pair<string,const char*>(key, value));\r
1525     return 1;\r
1526 }\r
1527 \r
1528 void ApacheRequestMapper::getAll(map<string,const char*>& properties) const\r
1529 {\r
1530     const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());\r
1531     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1532 \r
1533     if (s)\r
1534         s->getAll(properties);\r
1535     if (!sta)\r
1536         return;\r
1537 \r
1538     const char* auth_type=ap_auth_type(sta->m_req);\r
1539     if (auth_type) {\r
1540         // Check for Basic Hijack\r
1541         if (!strcasecmp(auth_type, "basic") && sta->m_dc->bBasicHijack == 1)\r
1542             auth_type = "shibboleth";\r
1543         properties["authType"] = auth_type;\r
1544     }\r
1545 \r
1546     if (sta->m_dc->szApplicationId)\r
1547         properties["applicationId"] = sta->m_dc->szApplicationId;\r
1548     if (sta->m_dc->szRequireWith)\r
1549         properties["requireSessionWith"] = sta->m_dc->szRequireWith;\r
1550     if (sta->m_dc->szRedirectToSSL)\r
1551         properties["redirectToSSL"] = sta->m_dc->szRedirectToSSL;\r
1552     if (sta->m_dc->bRequireSession != 0)\r
1553         properties["requireSession"] = (sta->m_dc->bRequireSession==1) ? "true" : "false";\r
1554     if (sta->m_dc->bExportAssertion != 0)\r
1555         properties["exportAssertion"] = (sta->m_dc->bExportAssertion==1) ? "true" : "false";\r
1556 \r
1557     if (sta->m_dc->tSettings)\r
1558         ap_table_do(_rm_get_all_table_walk, &properties, sta->m_dc->tSettings, NULL);\r
1559 }\r
1560 \r
1561 const PropertySet* ApacheRequestMapper::getPropertySet(const char* name, const char* ns) const\r
1562 {\r
1563     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1564     return s ? s->getPropertySet(name,ns) : nullptr;\r
1565 }\r
1566 \r
1567 const xercesc::DOMElement* ApacheRequestMapper::getElement() const\r
1568 {\r
1569     const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());\r
1570     return s ? s->getElement() : nullptr;\r
1571 }\r
1572 \r
1573 // Authz callbacks for Apache 2.4\r
1574 // For some reason, these get run twice for each request, once before hooks like check_user, etc.\r
1575 // and once after. The first time through, the request object exists, but isn't initialized.\r
1576 // The other case is subrequests of some kinds: then post_read doesn't run, and the objects\r
1577 // themselves don't exist. We do deferred creation of the objects in check_user to fix that case.\r
1578 // In each screwed up case, we return "denied" so that nothing bad happens.\r
1579 #ifdef SHIB_APACHE_24\r
1580 pair<ShibTargetApache*,authz_status> shib_base_check_authz(request_rec* r)\r
1581 {\r
1582     shib_request_config* rc = (shib_request_config*)ap_get_module_config(r->request_config, &mod_shib);\r
1583     if (!rc || !rc->sta) {\r
1584         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_base_check_authz found no per-request structure");\r
1585         return make_pair((ShibTargetApache*)nullptr, AUTHZ_DENIED_NO_USER);\r
1586     }\r
1587     else if (!rc->sta->isInitialized()) {\r
1588         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "shib_base_check_authz found uninitialized request object");\r
1589         return make_pair((ShibTargetApache*)nullptr, AUTHZ_DENIED_NO_USER);\r
1590     }\r
1591     return make_pair(rc->sta, AUTHZ_GRANTED);\r
1592 }\r
1593 \r
1594 extern "C" authz_status shib_shibboleth_check_authz(request_rec* r, const char* require_line, const void*)\r
1595 {\r
1596     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1597     if (!sta.first)\r
1598         return sta.second;\r
1599     return AUTHZ_GRANTED;\r
1600 }\r
1601 \r
1602 extern "C" authz_status shib_session_check_authz(request_rec* r, const char* require_line, const void*)\r
1603 {\r
1604     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1605     if (!sta.first)\r
1606         return sta.second;\r
1607 \r
1608     try {\r
1609         Session* session = sta.first->getSession(false, true, false);\r
1610         Locker slocker(session, false);\r
1611         if (session) {\r
1612             sta.first->log(SPRequest::SPDebug, "htaccess: accepting shib-session/valid-user based on active session");\r
1613             return AUTHZ_GRANTED;\r
1614         }\r
1615     }\r
1616     catch (std::exception& e) {\r
1617         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());\r
1618     }\r
1619 \r
1620     sta.first->log(SPRequest::SPDebug, "htaccess: denying shib-access/valid-user rule, no active session");\r
1621     return AUTHZ_DENIED_NO_USER;\r
1622 }\r
1623 \r
1624 extern "C" authz_status shib_validuser_check_authz(request_rec* r, const char* require_line, const void*)\r
1625 {\r
1626     // Shouldn't have actually ever hooked this, and now we're in conflict with mod_authz_user over the meaning.\r
1627     // For now, added a command to restore "normal" semantics for valid-user so that combined deployments can\r
1628     // use valid-user for non-Shibboleth cases and shib-session for the Shibboleth semantic.\r
1629 \r
1630     // In future, we may want to expose the AuthType set to honor down at this level so we can differentiate\r
1631     // based on AuthType. Unfortunately we allow overriding the AuthType to honor and we don't have access to\r
1632     // that setting from the ServiceProvider class..\r
1633 \r
1634     shib_server_config* sc = (shib_server_config*)ap_get_module_config(r->server->module_config, &mod_shib);\r
1635     if (sc->bCompatValidUser != 1) {\r
1636         return shib_session_check_authz(r, require_line, nullptr);\r
1637     }\r
1638 \r
1639     // Reproduce mod_authz_user version...\r
1640 \r
1641     if (!r->user) {\r
1642         return AUTHZ_DENIED_NO_USER;\r
1643     }\r
1644 \r
1645     return AUTHZ_GRANTED;\r
1646 }\r
1647 \r
1648 extern "C" authz_status shib_ext_user_check_authz(request_rec* r, const char* require_line, const void*)\r
1649 {\r
1650     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1651     if (!sta.first)\r
1652         return sta.second;\r
1653 \r
1654     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();\r
1655     if (hta.doUser(*sta.first, require_line) == AccessControl::shib_acl_true)\r
1656         return AUTHZ_GRANTED;\r
1657     return AUTHZ_DENIED;\r
1658 }\r
1659 \r
1660 extern "C" authz_status shib_user_check_authz(request_rec* r, const char* require_line, const void*)\r
1661 {\r
1662     // Shouldn't have actually ever hooked this, and now we're in conflict with mod_authz_user over the meaning.\r
1663     // For now, added a command to restore "normal" semantics for user rules so that combined deployments can\r
1664     // use user for non-Shibboleth cases and shib-user for the Shibboleth semantic.\r
1665 \r
1666     // In future, we may want to expose the AuthType set to honor down at this level so we can differentiate\r
1667     // based on AuthType. Unfortunately we allow overriding the AuthType to honor and we don't have access to\r
1668     // that setting from the ServiceProvider class..\r
1669 \r
1670     shib_server_config* sc = (shib_server_config*)ap_get_module_config(r->server->module_config, &mod_shib);\r
1671     if (sc->bCompatValidUser != 1) {\r
1672         return shib_ext_user_check_authz(r, require_line, nullptr);\r
1673     }\r
1674 \r
1675     // Reproduce mod_authz_user version...\r
1676 \r
1677     if (!r->user) {\r
1678         return AUTHZ_DENIED_NO_USER;\r
1679     }\r
1680         \r
1681     const char* t = require_line;\r
1682     const char *w;\r
1683     while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {\r
1684         if (!strcmp(r->user, w)) {\r
1685             return AUTHZ_GRANTED;\r
1686         }\r
1687     }\r
1688         \r
1689     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01663)\r
1690         "access to %s failed, reason: user '%s' does not meet "\r
1691         "'require'ments for user to be allowed access",\r
1692         r->uri, r->user);\r
1693         \r
1694     return AUTHZ_DENIED;\r
1695 }\r
1696 \r
1697 extern "C" authz_status shib_acclass_check_authz(request_rec* r, const char* require_line, const void*)\r
1698 {\r
1699     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1700     if (!sta.first)\r
1701         return sta.second;\r
1702 \r
1703     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();\r
1704 \r
1705     try {\r
1706         Session* session = sta.first->getSession(false, true, false);\r
1707         Locker slocker(session, false);\r
1708         if (session && hta.doAuthnContext(*sta.first, session->getAuthnContextClassRef(), require_line) == AccessControl::shib_acl_true)\r
1709             return AUTHZ_GRANTED;\r
1710         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;\r
1711     }\r
1712     catch (std::exception& e) {\r
1713         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());\r
1714     }\r
1715 \r
1716     return AUTHZ_GENERAL_ERROR;\r
1717 }\r
1718 \r
1719 extern "C" authz_status shib_acdecl_check_authz(request_rec* r, const char* require_line, const void*)\r
1720 {\r
1721     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1722     if (!sta.first)\r
1723         return sta.second;\r
1724 \r
1725     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();\r
1726 \r
1727     try {\r
1728         Session* session = sta.first->getSession(false, true, false);\r
1729         Locker slocker(session, false);\r
1730         if (session && hta.doAuthnContext(*sta.first, session->getAuthnContextDeclRef(), require_line) == AccessControl::shib_acl_true)\r
1731             return AUTHZ_GRANTED;\r
1732         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;\r
1733     }\r
1734     catch (std::exception& e) {\r
1735         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());\r
1736     }\r
1737 \r
1738     return AUTHZ_GENERAL_ERROR;\r
1739 }\r
1740 \r
1741 extern "C" authz_status shib_attr_check_authz(request_rec* r, const char* require_line, const void*)\r
1742 {\r
1743     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1744     if (!sta.first)\r
1745         return sta.second;\r
1746 \r
1747     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();\r
1748 \r
1749     try {\r
1750         Session* session = sta.first->getSession(false, true, false);\r
1751         Locker slocker(session, false);\r
1752         if (session) {\r
1753             const char* rule = ap_getword_conf(r->pool, &require_line);\r
1754             if (rule && hta.doShibAttr(*sta.first, session, rule, require_line) == AccessControl::shib_acl_true)\r
1755                 return AUTHZ_GRANTED;\r
1756         }\r
1757         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;\r
1758     }\r
1759     catch (std::exception& e) {\r
1760         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());\r
1761     }\r
1762 \r
1763     return AUTHZ_GENERAL_ERROR;\r
1764 }\r
1765 \r
1766 extern "C" authz_status shib_plugin_check_authz(request_rec* r, const char* require_line, const void*)\r
1767 {\r
1768     pair<ShibTargetApache*,authz_status> sta = shib_base_check_authz(r);\r
1769     if (!sta.first)\r
1770         return sta.second;\r
1771 \r
1772     const htAccessControl& hta = dynamic_cast<const ApacheRequestMapper*>(sta.first->getRequestSettings().first)->getHTAccessControl();\r
1773 \r
1774     try {\r
1775         Session* session = sta.first->getSession(false, true, false);\r
1776         Locker slocker(session, false);\r
1777         if (session) {\r
1778             const char* config = ap_getword_conf(r->pool, &require_line);\r
1779             if (config && hta.doAccessControl(*sta.first, session, config) == AccessControl::shib_acl_true)\r
1780                 return AUTHZ_GRANTED;\r
1781         }\r
1782         return session ? AUTHZ_DENIED : AUTHZ_DENIED_NO_USER;\r
1783     }\r
1784     catch (std::exception& e) {\r
1785         sta.first->log(SPRequest::SPWarn, string("htaccess: unable to obtain session for access control check: ") +  e.what());\r
1786     }\r
1787 \r
1788     return AUTHZ_GENERAL_ERROR;\r
1789 }\r
1790 #endif\r
1791 \r
1792 // Command manipulation functions\r
1793 \r
1794 extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*, const char* arg)\r
1795 {\r
1796     *((char**)(parms->info))=ap_pstrdup(parms->pool,arg);\r
1797     return nullptr;\r
1798 }\r
1799 \r
1800 extern "C" const char* shib_set_server_string_slot(cmd_parms* parms, void*, const char* arg)\r
1801 {\r
1802     char* base=(char*)ap_get_module_config(parms->server->module_config,&mod_shib);\r
1803     size_t offset=(size_t)parms->info;\r
1804     *((char**)(base + offset))=ap_pstrdup(parms->pool,arg);\r
1805     return nullptr;\r
1806 }\r
1807 \r
1808 extern "C" const char* shib_set_server_flag_slot(cmd_parms* parms, void*, int arg)\r
1809 {\r
1810     char* base=(char*)ap_get_module_config(parms->server->module_config,&mod_shib);\r
1811     size_t offset=(size_t)parms->info;\r
1812     *((int*)(base + offset)) = arg;\r
1813     return nullptr;\r
1814 }\r
1815 \r
1816 extern "C" const char* shib_ap_set_file_slot(cmd_parms* parms,\r
1817 #ifdef SHIB_APACHE_13\r
1818                                              char* arg1, char* arg2\r
1819 #else\r
1820                                              void* arg1, const char* arg2\r
1821 #endif\r
1822                                              )\r
1823 {\r
1824   ap_set_file_slot(parms, arg1, arg2);\r
1825   return DECLINE_CMD;\r
1826 }\r
1827 \r
1828 extern "C" const char* shib_table_set(cmd_parms* parms, shib_dir_config* dc, const char* arg1, const char* arg2)\r
1829 {\r
1830     if (!dc->tSettings)\r
1831         dc->tSettings = ap_make_table(parms->pool, 4);\r
1832     ap_table_set(dc->tSettings, arg1, arg2);\r
1833     return nullptr;\r
1834 }\r
1835 \r
1836 #ifndef SHIB_APACHE_24\r
1837 extern "C" const char* shib_set_acl_slot(cmd_parms* params, shib_dir_config* dc, char* arg)\r
1838 {\r
1839     bool absolute;\r
1840     switch (*arg) {\r
1841         case 0:\r
1842             absolute = false;\r
1843             break;\r
1844         case '/':\r
1845         case '\\':\r
1846             absolute = true;\r
1847             break;\r
1848         case '.':\r
1849             absolute = (*(arg+1) == '.' || *(arg+1) == '/' || *(arg+1) == '\\');\r
1850             break;\r
1851         default:\r
1852             absolute = *(arg+1) == ':';\r
1853     }\r
1854 \r
1855     if (absolute || !params->path)\r
1856         dc->szAccessControl = ap_pstrdup(params->pool, arg);\r
1857     else\r
1858         dc->szAccessControl = ap_pstrcat(params->pool, params->path, arg, NULL);\r
1859     return nullptr;\r
1860 }\r
1861 #endif\r
1862 \r
1863 \r
1864 #ifdef SHIB_APACHE_13\r
1865 /*\r
1866  * shib_child_exit()\r
1867  *  Cleanup the (per-process) pool info.\r
1868  */\r
1869 extern "C" void shib_child_exit(server_rec* s, SH_AP_POOL* p)\r
1870 {\r
1871     if (g_Config) {\r
1872         g_Config->term();\r
1873         g_Config = nullptr;\r
1874     }\r
1875     ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s), "child_exit: mod_shib shutdown in pid (%d)", (int)getpid());\r
1876 }\r
1877 #else\r
1878 /*\r
1879  * shib_exit()\r
1880  *  Apache 2.x doesn't allow for per-child cleanup, causes CGI forks to hang.\r
1881  */\r
1882 extern "C" apr_status_t shib_exit(void* data)\r
1883 {\r
1884     if (g_Config) {\r
1885         g_Config->term();\r
1886         g_Config = nullptr;\r
1887     }\r
1888     server_rec* s = reinterpret_cast<server_rec*>(data);\r
1889     ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s), "shib_exit: mod_shib shutdown in pid (%d)", (int)getpid());\r
1890     return OK;\r
1891 }\r
1892 \r
1893 /*\r
1894  * shib_post_config()\r
1895  *  We do the library init/term work here for 2.x to reduce overhead and\r
1896  *  get default logging established before the fork happens.\r
1897  */\r
1898 apr_status_t shib_post_config(apr_pool_t* p, apr_pool_t*, apr_pool_t*, server_rec* s)\r
1899 {\r
1900     // Initialize runtime components.\r
1901     ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s),"post_config: mod_shib initializing in pid (%d)", (int)getpid());\r
1902 \r
1903     if (g_Config) {\r
1904         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(s), "post_config: mod_shib already initialized");\r
1905         return !OK;\r
1906     }\r
1907 \r
1908     g_Config = &SPConfig::getConfig();\r
1909     g_Config->setFeatures(\r
1910         SPConfig::Listener |\r
1911         SPConfig::Caching |\r
1912         SPConfig::RequestMapping |\r
1913         SPConfig::InProcess |\r
1914         SPConfig::Logging |\r
1915         SPConfig::Handlers\r
1916         );\r
1917     if (!g_Config->init(g_szSchemaDir, g_szPrefix)) {\r
1918         ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "post_config: mod_shib failed to initialize libraries");\r
1919         return !OK;\r
1920     }\r
1921 #ifndef SHIB_APACHE_24\r
1922     g_Config->AccessControlManager.registerFactory(HT_ACCESS_CONTROL, &htAccessFactory);\r
1923 #endif\r
1924     g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);\r
1925 \r
1926     // Set the cleanup handler, passing in the server_rec for logging.\r
1927     apr_pool_cleanup_register(p, s, &shib_exit, apr_pool_cleanup_null);\r
1928 \r
1929     return OK;\r
1930 }\r
1931 \r
1932 #endif\r
1933 \r
1934 /*\r
1935  * shib_child_init()\r
1936  *  Things to do when the child process is initialized.\r
1937  *  We can't use post-config for all of it on 2.x because only the forking thread shows\r
1938  *  up in the child, losing the internal threads spun up by plugins in the SP.\r
1939  */\r
1940 #ifdef SHIB_APACHE_13\r
1941 extern "C" void shib_child_init(server_rec* s, SH_AP_POOL* p)\r
1942 #else\r
1943 extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)\r
1944 #endif\r
1945 {\r
1946     // Initialize runtime components.\r
1947 \r
1948     ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, SH_AP_R(s),"child_init: mod_shib initializing in pid (%d)", (int)getpid());\r
1949 \r
1950     // 2.x versions have already initialized the libraries.\r
1951 #ifdef SHIB_APACHE_13\r
1952     if (g_Config) {\r
1953         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib already initialized, exiting");\r
1954         exit(1);\r
1955     }\r
1956 \r
1957     g_Config = &SPConfig::getConfig();\r
1958     g_Config->setFeatures(\r
1959         SPConfig::Listener |\r
1960         SPConfig::Caching |\r
1961         SPConfig::RequestMapping |\r
1962         SPConfig::InProcess |\r
1963         SPConfig::Logging |\r
1964         SPConfig::Handlers\r
1965         );\r
1966     if (!g_Config->init(g_szSchemaDir, g_szPrefix)) {\r
1967         ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib failed to initialize libraries");\r
1968         exit(1);\r
1969     }\r
1970     g_Config->AccessControlManager.registerFactory(HT_ACCESS_CONTROL, &htAccessFactory);\r
1971     g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);\r
1972 #endif\r
1973 \r
1974     // The config gets installed for all versions here due to the background thread/fork issues.\r
1975     try {\r
1976         if (!g_Config->instantiate(g_szSHIBConfig, true))\r
1977             throw runtime_error("unknown error");\r
1978     }\r
1979     catch (std::exception& ex) {\r
1980         ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib failed to load configuration: %s", ex.what());\r
1981         g_Config->term();\r
1982         exit(1);\r
1983     }\r
1984 \r
1985     ServiceProvider* sp = g_Config->getServiceProvider();\r
1986     xmltooling::Locker locker(sp);\r
1987     const PropertySet* props = sp->getPropertySet("InProcess");\r
1988     if (props) {\r
1989         pair<bool,const char*> unsetValue = props->getString("unsetHeaderValue");\r
1990         if (unsetValue.first)\r
1991             g_unsetHeaderValue = unsetValue.second;\r
1992         pair<bool,bool> flag=props->getBool("checkSpoofing");\r
1993         g_checkSpoofing = !flag.first || flag.second;\r
1994         if (g_checkSpoofing) {\r
1995             unsetValue=props->getString("spoofKey");\r
1996             if (unsetValue.first)\r
1997                 g_spoofKey = unsetValue.second;\r
1998         }\r
1999         flag=props->getBool("catchAll");\r
2000         g_catchAll = flag.first && flag.second;\r
2001     }\r
2002 \r
2003     // Set the cleanup handler, passing in the server_rec for logging.\r
2004     apr_pool_cleanup_register(p, s, &shib_exit, apr_pool_cleanup_null);\r
2005 \r
2006     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(s), "child_init: mod_shib config initialized");\r
2007 }\r
2008 \r
2009 // Output filters\r
2010 #ifdef SHIB_DEFERRED_HEADERS\r
2011 static void set_output_filter(request_rec *r)\r
2012 {\r
2013    ap_add_output_filter("SHIB_HEADERS_OUT", nullptr, r, r->connection);\r
2014 }\r
2015 \r
2016 static void set_error_filter(request_rec *r)\r
2017 {\r
2018    ap_add_output_filter("SHIB_HEADERS_ERR", nullptr, r, r->connection);\r
2019 }\r
2020 \r
2021 static int _table_add(void *v, const char *key, const char *value)\r
2022 {\r
2023     apr_table_addn((apr_table_t*)v, key, value);\r
2024     return 1;\r
2025 }\r
2026 \r
2027 static apr_status_t do_output_filter(ap_filter_t *f, apr_bucket_brigade *in)\r
2028 {\r
2029     request_rec *r = f->r;\r
2030     shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib);\r
2031 \r
2032     if (rc && rc->hdr_out) {\r
2033         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "output_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);\r
2034         // can't use overlap call because it will collapse Set-Cookie headers\r
2035         //apr_table_overlap(r->headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);\r
2036         apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL);\r
2037     }\r
2038 \r
2039     /* remove ourselves from the filter chain */\r
2040     ap_remove_output_filter(f);\r
2041 \r
2042     /* send the data up the stack */\r
2043     return ap_pass_brigade(f->next,in);\r
2044 }\r
2045 \r
2046 static apr_status_t do_error_filter(ap_filter_t *f, apr_bucket_brigade *in)\r
2047 {\r
2048     request_rec *r = f->r;\r
2049     shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib);\r
2050 \r
2051     if (rc && rc->hdr_out) {\r
2052         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, SH_AP_R(r), "error_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);\r
2053         // can't use overlap call because it will collapse Set-Cookie headers\r
2054         //apr_table_overlap(r->err_headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);\r
2055         apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL);\r
2056     }\r
2057 \r
2058     /* remove ourselves from the filter chain */\r
2059     ap_remove_output_filter(f);\r
2060 \r
2061     /* send the data up the stack */\r
2062     return ap_pass_brigade(f->next,in);\r
2063 }\r
2064 #endif // SHIB_DEFERRED_HEADERS\r
2065 \r
2066 typedef const char* (*config_fn_t)(void);\r
2067 \r
2068 #ifdef SHIB_APACHE_13\r
2069 \r
2070 // SHIB Module commands\r
2071 \r
2072 static command_rec shire_cmds[] = {\r
2073   {"ShibPrefix", (config_fn_t)ap_set_global_string_slot, &g_szPrefix,\r
2074    RSRC_CONF, TAKE1, "Shibboleth installation directory"},\r
2075   {"ShibConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIBConfig,\r
2076    RSRC_CONF, TAKE1, "Path to shibboleth2.xml config file"},\r
2077   {"ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,\r
2078    RSRC_CONF, TAKE1, "Paths of XML schema catalogs"},\r
2079 \r
2080   {"ShibURLScheme", (config_fn_t)shib_set_server_string_slot,\r
2081    (void *) XtOffsetOf (shib_server_config, szScheme),\r
2082    RSRC_CONF, TAKE1, "URL scheme to force into generated URLs for a vhost"},\r
2083 \r
2084   {"ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,\r
2085    OR_AUTHCFG, TAKE2, "Set arbitrary Shibboleth request property for content"},\r
2086 \r
2087   {"ShibAccessControl", (config_fn_t)shib_set_acl_slot, nullptr,\r
2088    OR_AUTHCFG, TAKE1, "Set arbitrary Shibboleth access control plugin for content"},\r
2089 \r
2090   {"ShibDisable", (config_fn_t)ap_set_flag_slot,\r
2091    (void *) XtOffsetOf (shib_dir_config, bOff),\r
2092    OR_AUTHCFG, FLAG, "Disable all Shib module activity here to save processing effort"},\r
2093   {"ShibApplicationId", (config_fn_t)ap_set_string_slot,\r
2094    (void *) XtOffsetOf (shib_dir_config, szApplicationId),\r
2095    OR_AUTHCFG, TAKE1, "Set Shibboleth applicationId property for content"},\r
2096   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,\r
2097    (void *) XtOffsetOf (shib_dir_config, bBasicHijack),\r
2098    OR_AUTHCFG, FLAG, "(DEPRECATED) Respond to AuthType Basic and convert to shibboleth"},\r
2099   {"ShibRequireSession", (config_fn_t)ap_set_flag_slot,\r
2100    (void *) XtOffsetOf (shib_dir_config, bRequireSession),\r
2101    OR_AUTHCFG, FLAG, "Initiates a new session if one does not exist"},\r
2102   {"ShibRequireSessionWith", (config_fn_t)ap_set_string_slot,\r
2103    (void *) XtOffsetOf (shib_dir_config, szRequireWith),\r
2104    OR_AUTHCFG, TAKE1, "Initiates a new session if one does not exist using a specific SessionInitiator"},\r
2105   {"ShibExportAssertion", (config_fn_t)ap_set_flag_slot,\r
2106    (void *) XtOffsetOf (shib_dir_config, bExportAssertion),\r
2107    OR_AUTHCFG, FLAG, "Export SAML attribute assertion(s) to Shib-Attributes header"},\r
2108   {"ShibRedirectToSSL", (config_fn_t)ap_set_string_slot,\r
2109    (void *) XtOffsetOf (shib_dir_config, szRedirectToSSL),\r
2110    OR_AUTHCFG, TAKE1, "Redirect non-SSL requests to designated port" },\r
2111   {"AuthGroupFile", (config_fn_t)shib_ap_set_file_slot,\r
2112    (void *) XtOffsetOf (shib_dir_config, szAuthGrpFile),\r
2113    OR_AUTHCFG, TAKE1, "text file containing group names and member user IDs"},\r
2114   {"ShibRequireAll", (config_fn_t)ap_set_flag_slot,\r
2115    (void *) XtOffsetOf (shib_dir_config, bRequireAll),\r
2116    OR_AUTHCFG, FLAG, "All require directives must match"},\r
2117   {"AuthzShibAuthoritative", (config_fn_t)ap_set_flag_slot,\r
2118    (void *) XtOffsetOf (shib_dir_config, bAuthoritative),\r
2119    OR_AUTHCFG, FLAG, "Allow failed mod_shib htaccess authorization to fall through to other modules"},\r
2120   {"ShibCompatWith24", (config_fn_t)ap_set_flag_slot,\r
2121    (void *) XtOffsetOf (shib_dir_config, bCompatWith24),\r
2122    OR_AUTHCFG, FLAG, "Support Apache 2.4-style require rules"},\r
2123   {"ShibUseEnvironment", (config_fn_t)ap_set_flag_slot,\r
2124    (void *) XtOffsetOf (shib_dir_config, bUseEnvVars),\r
2125    OR_AUTHCFG, FLAG, "Export attributes using environment variables (default)"},\r
2126   {"ShibUseHeaders", (config_fn_t)ap_set_flag_slot,\r
2127    (void *) XtOffsetOf (shib_dir_config, bUseHeaders),\r
2128    OR_AUTHCFG, FLAG, "Export attributes using custom HTTP headers"},\r
2129   {"ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,\r
2130    (void *) XtOffsetOf (shib_dir_config, bExpireRedirects),\r
2131    OR_AUTHCFG, FLAG, "Expire SP-generated redirects"},\r
2132 \r
2133   {nullptr}\r
2134 };\r
2135 \r
2136 extern "C"{\r
2137 handler_rec shib_handlers[] = {\r
2138   { "shib-handler", shib_handler },\r
2139   { nullptr }\r
2140 };\r
2141 \r
2142 module MODULE_VAR_EXPORT mod_shib = {\r
2143     STANDARD_MODULE_STUFF,\r
2144     nullptr,                        /* initializer */\r
2145     create_shib_dir_config,     /* dir config creater */\r
2146     merge_shib_dir_config,      /* dir merger --- default is to override */\r
2147     create_shib_server_config, /* server config */\r
2148     merge_shib_server_config,   /* merge server config */\r
2149     shire_cmds,                 /* command table */\r
2150     shib_handlers,              /* handlers */\r
2151     nullptr,                    /* filename translation */\r
2152     shib_check_user,            /* check_user_id */\r
2153     shib_auth_checker,          /* check auth */\r
2154     nullptr,                    /* check access */\r
2155     nullptr,                    /* type_checker */\r
2156     shib_fixups,                /* fixups */\r
2157     nullptr,                    /* logger */\r
2158     nullptr,                    /* header parser */\r
2159     shib_child_init,            /* child_init */\r
2160     shib_child_exit,            /* child_exit */\r
2161     shib_post_read              /* post read-request */\r
2162 };\r
2163 \r
2164 #else\r
2165 \r
2166 #ifdef SHIB_APACHE_24\r
2167 extern "C" const authz_provider shib_authz_shibboleth_provider = { &shib_shibboleth_check_authz, nullptr };\r
2168 extern "C" const authz_provider shib_authz_validuser_provider = { &shib_validuser_check_authz, nullptr };\r
2169 extern "C" const authz_provider shib_authz_session_provider = { &shib_session_check_authz, nullptr };\r
2170 extern "C" const authz_provider shib_authz_user_provider = { &shib_user_check_authz, nullptr };\r
2171 extern "C" const authz_provider shib_authz_ext_user_provider = { &shib_ext_user_check_authz, nullptr };\r
2172 extern "C" const authz_provider shib_authz_acclass_provider = { &shib_acclass_check_authz, nullptr };\r
2173 extern "C" const authz_provider shib_authz_acdecl_provider = { &shib_acdecl_check_authz, nullptr };\r
2174 extern "C" const authz_provider shib_authz_attr_provider = { &shib_attr_check_authz, nullptr };\r
2175 extern "C" const authz_provider shib_authz_plugin_provider = { &shib_plugin_check_authz, nullptr };\r
2176 #endif\r
2177 \r
2178 extern "C" void shib_register_hooks (apr_pool_t *p)\r
2179 {\r
2180 #ifdef SHIB_DEFERRED_HEADERS\r
2181     ap_register_output_filter("SHIB_HEADERS_OUT", do_output_filter, nullptr, AP_FTYPE_CONTENT_SET);\r
2182     ap_hook_insert_filter(set_output_filter, nullptr, nullptr, APR_HOOK_LAST);\r
2183     ap_register_output_filter("SHIB_HEADERS_ERR", do_error_filter, nullptr, AP_FTYPE_CONTENT_SET);\r
2184     ap_hook_insert_error_filter(set_error_filter, nullptr, nullptr, APR_HOOK_LAST);\r
2185     ap_hook_post_read_request(shib_post_read, nullptr, nullptr, APR_HOOK_MIDDLE);\r
2186 #endif\r
2187     ap_hook_post_config(shib_post_config, nullptr, nullptr, APR_HOOK_MIDDLE);\r
2188     ap_hook_child_init(shib_child_init, nullptr, nullptr, APR_HOOK_MIDDLE);\r
2189     const char* prereq = getenv("SHIBSP_APACHE_PREREQ");\r
2190 #ifdef SHIB_APACHE_24\r
2191     if (prereq && *prereq) {\r
2192         const char* const authnPre[] = { prereq, nullptr };\r
2193         ap_hook_check_authn(shib_check_user, authnPre, nullptr, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_URI);\r
2194     }\r
2195     else {\r
2196         ap_hook_check_authn(shib_check_user, nullptr, nullptr, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_URI);\r
2197     }\r
2198     ap_hook_check_authz(shib_auth_checker, nullptr, nullptr, APR_HOOK_FIRST, AP_AUTH_INTERNAL_PER_URI);\r
2199 #else\r
2200     if (prereq && *prereq) {\r
2201         const char* const authnPre[] = { prereq, nullptr };\r
2202         ap_hook_check_user_id(shib_check_user, authnPre, nullptr, APR_HOOK_MIDDLE);\r
2203     }\r
2204     else {\r
2205         ap_hook_check_user_id(shib_check_user, nullptr, nullptr, APR_HOOK_MIDDLE);\r
2206     }\r
2207     ap_hook_auth_checker(shib_auth_checker, nullptr, nullptr, APR_HOOK_FIRST);\r
2208 #endif\r
2209     ap_hook_handler(shib_handler, nullptr, nullptr, APR_HOOK_LAST);\r
2210     ap_hook_fixups(shib_fixups, nullptr, nullptr, APR_HOOK_MIDDLE);\r
2211 \r
2212 #ifdef SHIB_APACHE_24\r
2213     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shibboleth", AUTHZ_PROVIDER_VERSION, &shib_authz_shibboleth_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2214     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "valid-user", AUTHZ_PROVIDER_VERSION, &shib_authz_validuser_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2215     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-session", AUTHZ_PROVIDER_VERSION, &shib_authz_session_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2216     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "user", AUTHZ_PROVIDER_VERSION, &shib_authz_user_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2217     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-user", AUTHZ_PROVIDER_VERSION, &shib_authz_ext_user_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2218     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "authnContextClassRef", AUTHZ_PROVIDER_VERSION, &shib_authz_acclass_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2219     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "authnContextDeclRef", AUTHZ_PROVIDER_VERSION, &shib_authz_acdecl_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2220     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-attr", AUTHZ_PROVIDER_VERSION, &shib_authz_attr_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2221     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "shib-plugin", AUTHZ_PROVIDER_VERSION, &shib_authz_plugin_provider, AP_AUTH_INTERNAL_PER_CONF);\r
2222 #endif\r
2223 }\r
2224 \r
2225 // SHIB Module commands\r
2226 \r
2227 extern "C" {\r
2228 static command_rec shib_cmds[] = {\r
2229     AP_INIT_TAKE1("ShibPrefix", (config_fn_t)ap_set_global_string_slot, &g_szPrefix,\r
2230         RSRC_CONF, "Shibboleth installation directory"),\r
2231     AP_INIT_TAKE1("ShibConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIBConfig,\r
2232         RSRC_CONF, "Path to shibboleth2.xml config file"),\r
2233     AP_INIT_TAKE1("ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,\r
2234         RSRC_CONF, "Paths of XML schema catalogs"),\r
2235     AP_INIT_TAKE1("ShibGSSKey", (config_fn_t)ap_set_global_string_slot, &g_szGSSContextKey,\r
2236         RSRC_CONF, "Name of user data key containing GSS context established by GSS module"),\r
2237 \r
2238     AP_INIT_TAKE1("ShibURLScheme", (config_fn_t)shib_set_server_string_slot,\r
2239         (void *) offsetof (shib_server_config, szScheme),\r
2240         RSRC_CONF, "URL scheme to force into generated URLs for a vhost"),\r
2241 \r
2242     AP_INIT_TAKE2("ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,\r
2243         OR_AUTHCFG, "Set arbitrary Shibboleth request property for content"),\r
2244 \r
2245     AP_INIT_FLAG("ShibDisable", (config_fn_t)ap_set_flag_slot,\r
2246         (void *) offsetof (shib_dir_config, bOff),\r
2247         OR_AUTHCFG, "Disable all Shib module activity here to save processing effort"),\r
2248     AP_INIT_TAKE1("ShibApplicationId", (config_fn_t)ap_set_string_slot,\r
2249         (void *) offsetof (shib_dir_config, szApplicationId),\r
2250         OR_AUTHCFG, "Set Shibboleth applicationId property for content"),\r
2251     AP_INIT_FLAG("ShibBasicHijack", (config_fn_t)ap_set_flag_slot,\r
2252         (void *) offsetof (shib_dir_config, bBasicHijack),\r
2253         OR_AUTHCFG, "(DEPRECATED) Respond to AuthType Basic and convert to shibboleth"),\r
2254     AP_INIT_FLAG("ShibRequireSession", (config_fn_t)ap_set_flag_slot,\r
2255         (void *) offsetof (shib_dir_config, bRequireSession),\r
2256         OR_AUTHCFG, "Initiates a new session if one does not exist"),\r
2257     AP_INIT_TAKE1("ShibRequireSessionWith", (config_fn_t)ap_set_string_slot,\r
2258         (void *) offsetof (shib_dir_config, szRequireWith),\r
2259         OR_AUTHCFG, "Initiates a new session if one does not exist using a specific SessionInitiator"),\r
2260     AP_INIT_FLAG("ShibExportAssertion", (config_fn_t)ap_set_flag_slot,\r
2261         (void *) offsetof (shib_dir_config, bExportAssertion),\r
2262         OR_AUTHCFG, "Export SAML attribute assertion(s) to Shib-Attributes header"),\r
2263     AP_INIT_TAKE1("ShibRedirectToSSL", (config_fn_t)ap_set_string_slot,\r
2264         (void *) offsetof (shib_dir_config, szRedirectToSSL),\r
2265         OR_AUTHCFG, "Redirect non-SSL requests to designated port"),\r
2266 #ifdef SHIB_APACHE_24\r
2267     AP_INIT_FLAG("ShibRequestMapperAuthz", (config_fn_t)ap_set_flag_slot,\r
2268         (void *) offsetof (shib_dir_config, bRequestMapperAuthz),\r
2269         OR_AUTHCFG, "Support access control via shibboleth2.xml / RequestMapper"),\r
2270     AP_INIT_FLAG("ShibCompatValidUser", (config_fn_t)shib_set_server_flag_slot,\r
2271         (void *) offsetof (shib_server_config, bCompatValidUser),\r
2272         RSRC_CONF, "Handle 'require valid-user' in mod_authz_user-compatible fashion (requiring username)"),\r
2273 #else\r
2274     AP_INIT_TAKE1("AuthGroupFile", (config_fn_t)shib_ap_set_file_slot,\r
2275         (void *) offsetof (shib_dir_config, szAuthGrpFile),\r
2276         OR_AUTHCFG, "Text file containing group names and member user IDs"),\r
2277     AP_INIT_TAKE1("ShibAccessControl", (config_fn_t)shib_set_acl_slot, nullptr,\r
2278         OR_AUTHCFG, "Set arbitrary Shibboleth access control plugin for content"),\r
2279     AP_INIT_FLAG("ShibRequireAll", (config_fn_t)ap_set_flag_slot,\r
2280         (void *) offsetof (shib_dir_config, bRequireAll),\r
2281         OR_AUTHCFG, "All require directives must match"),\r
2282     AP_INIT_FLAG("AuthzShibAuthoritative", (config_fn_t)ap_set_flag_slot,\r
2283         (void *) offsetof (shib_dir_config, bAuthoritative),\r
2284         OR_AUTHCFG, "Allow failed mod_shib htaccess authorization to fall through to other modules"),\r
2285     AP_INIT_FLAG("ShibCompatWith24", (config_fn_t)ap_set_flag_slot,\r
2286         (void *) offsetof (shib_dir_config, bCompatWith24),\r
2287         OR_AUTHCFG, "Support Apache 2.4-style require rules"),\r
2288 #endif\r
2289     AP_INIT_FLAG("ShibUseEnvironment", (config_fn_t)ap_set_flag_slot,\r
2290         (void *) offsetof (shib_dir_config, bUseEnvVars),\r
2291         OR_AUTHCFG, "Export attributes using environment variables (default)"),\r
2292     AP_INIT_FLAG("ShibUseHeaders", (config_fn_t)ap_set_flag_slot,\r
2293         (void *) offsetof (shib_dir_config, bUseHeaders),\r
2294         OR_AUTHCFG, "Export attributes using custom HTTP headers"),\r
2295     AP_INIT_FLAG("ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,\r
2296         (void *) offsetof (shib_dir_config, bExpireRedirects),\r
2297         OR_AUTHCFG, "Expire SP-generated redirects"),\r
2298 \r
2299     {nullptr}\r
2300 };\r
2301 \r
2302 module AP_MODULE_DECLARE_DATA mod_shib = {\r
2303     STANDARD20_MODULE_STUFF,\r
2304     create_shib_dir_config,     /* create dir config */\r
2305     merge_shib_dir_config,      /* merge dir config --- default is to override */\r
2306     create_shib_server_config,  /* create server config */\r
2307     merge_shib_server_config,   /* merge server config */\r
2308     shib_cmds,                  /* command table */\r
2309     shib_register_hooks         /* register hooks */\r
2310 };\r
2311 \r
2312 #endif\r
2313 \r
2314 }\r