Fix for secadv 20060615
[shibboleth/cpp-sp.git] / apache / mod_apache.cpp
index 39ecdf5..5c1f692 100644 (file)
@@ -6,6 +6,10 @@
  * $Id$
  */
 
+#ifdef SOLARIS2
+#undef _XOPEN_SOURCE    // causes gethostname conflict in unistd.h
+#endif
+
 // SAML Runtime
 #include <saml/saml.h>
 #include <shib/shib.h>
@@ -52,6 +56,37 @@ namespace {
     static const char* g_UserDataKey = "_shib_check_user_";
 }
 
+// per-server module configuration structure
+struct shib_server_config
+{
+    char* szScheme;
+};
+
+// creates the per-server configuration
+extern "C" void* create_shib_server_config(SH_AP_POOL* p, server_rec* s)
+{
+    shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));
+    sc->szScheme = NULL;
+    return sc;
+}
+
+// overrides server configuration in virtual servers
+extern "C" void* merge_shib_server_config (SH_AP_POOL* p, void* base, void* sub)
+{
+    shib_server_config* sc=(shib_server_config*)ap_pcalloc(p,sizeof(shib_server_config));
+    shib_server_config* parent=(shib_server_config*)base;
+    shib_server_config* child=(shib_server_config*)sub;
+
+    if (child->szScheme)
+        sc->szScheme=ap_pstrdup(p,child->szScheme);
+    else if (parent->szScheme)
+        sc->szScheme=ap_pstrdup(p,parent->szScheme);
+    else
+        sc->szScheme=NULL;
+
+    return sc;
+}
+
 // per-dir module configuration structure
 struct shib_dir_config
 {
@@ -105,6 +140,14 @@ extern "C" const char* ap_set_global_string_slot(cmd_parms* parms, void*, const
     return NULL;
 }
 
+extern "C" const char* shib_set_server_string_slot(cmd_parms* parms, void*, const char* arg)
+{
+    char* base=(char*)ap_get_module_config(parms->server->module_config,&mod_shib);
+    int offset=(int)parms->info;
+    *((char**)(base + offset))=ap_pstrdup(parms->pool,arg);
+    return NULL;
+}
+
 typedef const char* (*config_fn_t)(void);
 
 static int shib_error_page(request_rec* r, const IApplication* app, const char* page, ShibMLP& mlp)
@@ -131,17 +174,31 @@ static int shib_error_page(request_rec* r, const IApplication* app, const char*
     return SERVER_ERROR;
 }
 
+static char* shib_get_targeturl(request_rec* r, const char* scheme=NULL)
+{
+    // On 1.3, this is always canonical, but on 2.0, UseCanonicalName comes into play.
+    // However, we also have a setting to forcibly replace the scheme for esoteric cases.
+    if (scheme) {
+        unsigned port = ap_get_server_port(r);
+        if ((!strcmp(scheme,"http") && port==80) || (!strcmp(scheme,"https") && port==443)) {
+            return ap_pstrcat(r->pool, scheme, "://", ap_get_server_name(r), r->unparsed_uri, NULL);
+        }
+        return ap_psprintf(r->pool, "%s://%s:%u%s", scheme, ap_get_server_name(r), port, r->unparsed_uri);
+    }
+    return ap_construct_url(r->pool,r->unparsed_uri,r);
+}
+
 extern "C" int shib_check_user(request_rec* r)
 {
     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_check_user: ENTER");
     shib_dir_config* dc=(shib_dir_config*)ap_get_module_config(r->per_dir_config,&mod_shib);
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
 
     ostringstream threadid;
     threadid << "[" << getpid() << "] shib_check_user" << '\0';
     saml::NDC ndc(threadid.str().c_str());
 
-    // This will always be normalized, because Apache uses ap_get_server_name in this API call.
-    const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r);
+    const char* targeturl=shib_get_targeturl(r,sc->szScheme);
 
     // We lock the configuration system for the duration.
     IConfig* conf=g_Config->getINI();
@@ -151,7 +208,7 @@ extern "C" int shib_check_user(request_rec* r)
     IRequestMapper* mapper=conf->getRequestMapper();
     Locker locker2(mapper);
     IRequestMapper::Settings settings=mapper->getSettingsFromParsedURL(
-        ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
+        (sc-> szScheme ? sc-> szScheme : ap_http_method(r)), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
         );
     pair<bool,const char*> application_id=settings.first->getString("applicationId");
     const IApplication* application=conf->getApplication(application_id.second);
@@ -164,8 +221,15 @@ extern "C" int shib_check_user(request_rec* r)
     // Declare SHIRE object for this request.
     SHIRE shire(application);
     
+    const char* shireURL=shire.getShireURL(targeturl);
+    if (!shireURL) {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(r),
+           "shib_check_user: unable to map request to proper shireURL setting, check configuration");
+        return SERVER_ERROR;
+    }
+    
     // Get location of this application's assertion consumer service and see if this is it.
-    if (strstr(targeturl,shire.getShireURL(targeturl))) {
+    if (strstr(targeturl,shireURL)) {
         return shib_handler(r,application,shire);
     }
 
@@ -325,7 +389,7 @@ extern "C" int shib_check_user(request_rec* r)
     // Do we have an access control plugin?
     if (settings.second) {
         Locker acllock(settings.second);
-        if (!settings.second->authorized(assertions)) {
+        if (!settings.second->authorized(*sso_statement,assertions)) {
             for (int k = 0; k < assertions.size(); k++)
                 delete assertions[k];
             delete sso_statement;
@@ -345,8 +409,10 @@ extern "C" int shib_check_user(request_rec* r)
             Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
             while (rules.hasNext()) {
                 const char* header=rules.next()->getHeader();
-                if (header)
+                if (header) {
                     ap_table_unset(r->headers_in,header);
+                    ap_table_set(r->headers_in,header,"");
+                }
             }
         }
         catch(...) {
@@ -381,23 +447,21 @@ extern "C" int shib_check_user(request_rec* r)
     ap_table_unset(r->headers_in,"Shib-Origin-Site");
     ap_table_unset(r->headers_in,"Shib-Authentication-Method");
     ap_table_unset(r->headers_in,"Shib-NameIdentifier-Format");
-    if (sso_statement) {
-        auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
-        auto_ptr_char am(sso_statement->getAuthMethod());
-        ap_table_set(r->headers_in,"Shib-Origin-Site", os.get());
-        ap_table_set(r->headers_in,"Shib-Authentication-Method", am.get());
-        
-        // Export NameID?
-        AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
-        if (!wrapper.fail() && wrapper->getHeader()) {
-            auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
-            auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
-            ap_table_set(r->headers_in,"Shib-NameIdentifier-Format",form.get());
-            if (!strcmp(wrapper->getHeader(),"REMOTE_USER"))
-                SH_AP_USER(r)=ap_pstrdup(r->pool,nameid.get());
-            else
-                ap_table_set(r->headers_in,wrapper->getHeader(),nameid.get());
-        }
+    auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
+    auto_ptr_char am(sso_statement->getAuthMethod());
+    ap_table_set(r->headers_in,"Shib-Origin-Site", os.get());
+    ap_table_set(r->headers_in,"Shib-Authentication-Method", am.get());
+    
+    // Export NameID?
+    AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+    if (!wrapper.fail() && wrapper->getHeader()) {
+        auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
+        auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
+        ap_table_set(r->headers_in,"Shib-NameIdentifier-Format",form.get());
+        if (!strcmp(wrapper->getHeader(),"REMOTE_USER"))
+            SH_AP_USER(r)=ap_pstrdup(r->pool,nameid.get());
+        else
+            ap_table_set(r->headers_in,wrapper->getHeader(),nameid.get());
     }
     
     ap_table_unset(r->headers_in,"Shib-Application-ID");
@@ -459,8 +523,9 @@ extern "C" int shib_check_user(request_rec* r)
 
 extern "C" int shib_post_handler(request_rec* r)
 {
-  ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
+    ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),
                "shib_post_handler(%d): ENTER", (int)getpid());
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
 
 #ifndef SHIB_APACHE_13
     // With 2.x, this handler always runs, though last.
@@ -486,7 +551,7 @@ extern "C" int shib_post_handler(request_rec* r)
     IRequestMapper* mapper=conf->getRequestMapper();
     Locker locker2(mapper);
     IRequestMapper::Settings settings=mapper->getSettingsFromParsedURL(
-        ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
+        (sc->szScheme ? sc->szScheme : ap_http_method(r)), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri
         );
     pair<bool,const char*> application_id=settings.first->getString("applicationId");
     const IApplication* application=conf->getApplication(application_id.second);
@@ -504,11 +569,19 @@ extern "C" int shib_post_handler(request_rec* r)
 
 int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire)
 {
-    // Prime the pump...
-    const char* targeturl = ap_construct_url(r->pool,r->unparsed_uri,r);
+    shib_server_config* sc=(shib_server_config*)ap_get_module_config(r->server->module_config,&mod_shib);
+
+    const char* targeturl=shib_get_targeturl(r,sc->szScheme);
+
+    const char* shireURL=shire.getShireURL(targeturl);
+    if (!shireURL) {
+        ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(r),
+           "shib_post_handler: unable to map request to proper shireURL setting, check configuration");
+        return SERVER_ERROR;
+    }
 
     // Make sure we only process the SHIRE requests.
-    if (!strstr(targeturl,shire.getShireURL(targeturl)))
+    if (!strstr(targeturl,shireURL))
         return DECLINED;
 
     ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_handler() running");
@@ -566,6 +639,7 @@ int shib_handler(request_rec* r, const IApplication* application, SHIRE& shire)
         ap_hard_timeout("[mod_shib] CGI Parser", r);
         memset(buff, 0, sizeof(buff));
         while (ap_get_client_block(r, buff, sizeof(buff)-1) > 0) {
+            ap_reset_timeout(r);
             cgistr += buff;
             memset(buff, 0, sizeof(buff));
         }
@@ -930,41 +1004,36 @@ extern "C" int shib_auth_checker(request_rec* r)
     return shib_error_page(r, application, "access", markupProcessor);
 }
 
+#ifndef SHIB_APACHE_13
 /*
  * shib_exit()
- *  Cleanup the (per-process) pool info.
+ *  Empty cleanup hook, Apache 2.x doesn't check NULL very well...
  */
-#ifdef SHIB_APACHE_13
-extern "C" void shib_exit(server_rec* s, SH_AP_POOL* p)
-{
-#else
 extern "C" apr_status_t shib_exit(void* data)
 {
-    server_rec* s = NULL;
-#endif
-
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_exit(%d) dealing with g_Config..", (int)getpid());
-
-    g_Config->shutdown();
-    g_Config = NULL;
-
-    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_exit() done\n");
-#ifndef SHIB_APACHE_13
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,0,NULL,"shib_exit() done\n");
     return OK;
-#endif
 }
+#endif
 
 
+/*
+ * shib_child_exit()
+ *  Cleanup the (per-process) pool info.
+ */
 #ifdef SHIB_APACHE_13
 extern "C" void shib_child_exit(server_rec* s, SH_AP_POOL* p)
+{
 #else
 extern "C" apr_status_t shib_child_exit(void* data)
-#endif
 {
   server_rec* s = NULL;
+#endif
 
-  ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit(%d)",
-              (int)getpid());
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit(%d) dealing with g_Config..", (int)getpid());
+    g_Config->shutdown();
+    g_Config = NULL;
+    ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_exit() done\n");
 
 #ifndef SHIB_APACHE_13
     return OK;
@@ -979,8 +1048,7 @@ extern "C" apr_status_t shib_child_exit(void* data)
 #ifdef SHIB_APACHE_13
 extern "C" void shib_child_init(server_rec* s, SH_AP_POOL* p)
 #else
-extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
-                               apr_pool_t* ptemp, server_rec* s)
+extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
 #endif
 {
     // Initialize runtime components.
@@ -989,11 +1057,7 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
 
     if (g_Config) {
         ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() already initialized!");
-#ifdef SHIB_APACHE_13
         exit(1);
-#else
-       return OK;
-#endif
     }
 
     try {
@@ -1003,7 +1067,8 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
             ShibTargetConfig::Metadata |
             ShibTargetConfig::AAP |
             ShibTargetConfig::RequestMapper |
-            ShibTargetConfig::SHIREExtensions
+            ShibTargetConfig::SHIREExtensions |
+            ShibTargetConfig::Logging
             );
         if (!g_Config->init(g_szSchemaDir,g_szSHIBConfig)) {
             ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to initialize SHIB Target");
@@ -1016,13 +1081,9 @@ extern "C" int shib_post_config(apr_pool_t* pconf, apr_pool_t* plog,
     }
 
     // Set the cleanup handler
-    apr_pool_cleanup_register(pconf, NULL, &shib_exit, &shib_child_exit);
+    apr_pool_cleanup_register(p, NULL, &shib_exit, &shib_child_exit);
 
     ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() done");
-
-#ifndef SHIB_APACHE_13
-    return OK;
-#endif
 }
 
 #ifdef SHIB_APACHE_13
@@ -1037,6 +1098,10 @@ static command_rec shire_cmds[] = {
   {"ShibSchemaDir", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,
    RSRC_CONF, TAKE1, "Path to Shibboleth XML schema directory."},
 
+  {"ShibURLScheme", (config_fn_t)shib_set_server_string_slot,
+   (void *) XtOffsetOf (shib_server_config, szScheme),
+   RSRC_CONF, TAKE1, "URL scheme to force into generated URLs for a vhost."},
+   
   {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
    (void *) XtOffsetOf (shib_dir_config, bBasicHijack),
    OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shib?"},
@@ -1067,8 +1132,8 @@ module MODULE_VAR_EXPORT mod_shib = {
     NULL,                        /* initializer */
     create_shib_dir_config,    /* dir config creater */
     merge_shib_dir_config,     /* dir merger --- default is to override */
-    NULL,                      /* server config */
-    NULL,                      /* merge server config */
+    create_shib_server_config, /* server config */
+    merge_shib_server_config,   /* merge server config */
     shire_cmds,                        /* command table */
     shib_handlers,             /* handlers */
     NULL,                      /* filename translation */
@@ -1080,7 +1145,7 @@ module MODULE_VAR_EXPORT mod_shib = {
     NULL,                      /* logger */
     NULL,                      /* header parser */
     shib_child_init,           /* child_init */
-    shib_exit,                 /* child_exit */
+    shib_child_exit,           /* child_exit */
     NULL                       /* post read-request */
 };
 
@@ -1088,7 +1153,7 @@ module MODULE_VAR_EXPORT mod_shib = {
 
 extern "C" void shib_register_hooks (apr_pool_t *p)
 {
-  ap_hook_post_config(shib_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+  ap_hook_child_init(shib_child_init, NULL, NULL, APR_HOOK_MIDDLE);
   ap_hook_check_user_id(shib_check_user, NULL, NULL, APR_HOOK_MIDDLE);
   ap_hook_auth_checker(shib_auth_checker, NULL, NULL, APR_HOOK_FIRST);
   ap_hook_handler(shib_post_handler, NULL, NULL, APR_HOOK_LAST);
@@ -1105,6 +1170,11 @@ static command_rec shib_cmds[] = {
      (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,
       RSRC_CONF, "Path to Shibboleth XML schema directory."),
 
+  AP_INIT_TAKE1("ShibURLScheme",
+     (config_fn_t)shib_set_server_string_slot,
+     (void *) offsetof (shib_server_config, szScheme),
+      RSRC_CONF, "URL scheme to force into generated URLs for a vhost."),
+
   AP_INIT_FLAG("ShibBasicHijack", (config_fn_t)ap_set_flag_slot,
               (void *) offsetof (shib_dir_config, bBasicHijack),
               OR_AUTHCFG, "Respond to AuthType Basic and convert to shib?"),
@@ -1126,12 +1196,12 @@ static command_rec shib_cmds[] = {
 
 module AP_MODULE_DECLARE_DATA mod_shib = {
     STANDARD20_MODULE_STUFF,
-    create_shib_dir_config,    /* create dir config */
-    merge_shib_dir_config,     /* merge dir config --- default is to override */
-    NULL,                      /* create server config */
-    NULL,                      /* merge server config */
-    shib_cmds,                 /* command table */
-    shib_register_hooks                /* register hooks */
+    create_shib_dir_config,     /* create dir config */
+    merge_shib_dir_config,      /* merge dir config --- default is to override */
+    create_shib_server_config,  /* create server config */
+    merge_shib_server_config,   /* merge server config */
+    shib_cmds,                  /* command table */
+    shib_register_hooks         /* register hooks */
 };
 
 #else