-/*
- * Copyright 2001-2010 Internet2
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
*/
/**
# define _CRT_SECURE_NO_DEPRECATE 1
#endif
+#include <shibsp/exceptions.h>
#include <shibsp/AbstractSPRequest.h>
#include <shibsp/AccessControl.h>
-#include <shibsp/exceptions.h>
+#include <shibsp/GSSRequest.h>
#include <shibsp/RequestMapper.h>
#include <shibsp/SPConfig.h>
#include <shibsp/ServiceProvider.h>
#include <shibsp/SessionCache.h>
#include <shibsp/attribute/Attribute.h>
+
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/regx/RegularExpression.hpp>
#include <xmltooling/XMLToolingConfig.h>
#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ParserPool.h>
#include <xmltooling/util/Threads.h>
#include <xmltooling/util/XMLConstants.h>
#include <xmltooling/util/XMLHelper.h>
#include <sstream>
#include <stdexcept>
+#include <cstddef>
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for getpid()
#endif
string g_unsetHeaderValue,g_spoofKey;
bool g_checkSpoofing = true;
bool g_catchAll = false;
+#ifndef SHIB_APACHE_13
+ char* g_szGSSContextKey = "mod_auth_gssapi:gss_ctx";
+#endif
static const char* g_UserDataKey = "urn:mace:shibboleth:Apache:shib_check_user";
}
char* szApplicationId; // Shib applicationId value
char* szRequireWith; // require a session using a specific initiator?
char* szRedirectToSSL; // redirect non-SSL requests to SSL port
+ char* szAccessControl; // path to "external" AccessControl plugin file
int bOff; // flat-out disable all Shib processing
int bBasicHijack; // activate for AuthType Basic?
int bRequireSession; // require a session?
int bExportAssertion; // export SAML assertion to the environment?
int bUseEnvVars; // use environment?
int bUseHeaders; // use headers?
+ int bExpireRedirects; // expire redirects?
};
// creates per-directory config structure
dc->szApplicationId = nullptr;
dc->szRequireWith = nullptr;
dc->szRedirectToSSL = nullptr;
+ dc->szAccessControl = nullptr;
dc->bOff = -1;
dc->bBasicHijack = -1;
dc->bRequireSession = -1;
dc->bExportAssertion = -1;
dc->bUseEnvVars = -1;
dc->bUseHeaders = -1;
+ dc->bExpireRedirects = -1;
return dc;
}
else
dc->szRedirectToSSL=nullptr;
+ if (child->szAccessControl)
+ dc->szAccessControl=ap_pstrdup(p,child->szAccessControl);
+ else if (parent->szAccessControl)
+ dc->szAccessControl=ap_pstrdup(p,parent->szAccessControl);
+ else
+ dc->szAccessControl=nullptr;
+
dc->bOff=((child->bOff==-1) ? parent->bOff : child->bOff);
dc->bBasicHijack=((child->bBasicHijack==-1) ? parent->bBasicHijack : child->bBasicHijack);
dc->bRequireSession=((child->bRequireSession==-1) ? parent->bRequireSession : child->bRequireSession);
dc->bAuthoritative=((child->bAuthoritative==-1) ? parent->bAuthoritative : child->bAuthoritative);
dc->bUseEnvVars=((child->bUseEnvVars==-1) ? parent->bUseEnvVars : child->bUseEnvVars);
dc->bUseHeaders=((child->bUseHeaders==-1) ? parent->bUseHeaders : child->bUseHeaders);
+ dc->bExpireRedirects=((child->bExpireRedirects==-1) ? parent->bExpireRedirects : child->bExpireRedirects);
return dc;
}
return nullptr;
}
-/********************************************************************************/
-// Apache ShibTarget subclass(es) here.
class ShibTargetApache : public AbstractSPRequest
+#if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)
+ , public GSSRequest
+#endif
{
bool m_handler;
mutable string m_body;
const char* getScheme() const {
return m_sc->szScheme ? m_sc->szScheme : ap_http_method(m_req);
}
+ bool isSecure() const {
+ return HTTPRequest::isSecure();
+ }
const char* getHostname() const {
return ap_get_server_name(m_req);
}
(level == SPWarn ? APLOG_WARNING :
(level == SPError ? APLOG_ERR : APLOG_CRIT))))|APLOG_NOERRNO,
SH_AP_R(m_req),
+ "%s",
msg.c_str()
);
}
#endif
return m_body.c_str();
}
+ const char* getParameter(const char* name) const {
+ return AbstractSPRequest::getParameter(name);
+ }
+ vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const {
+ return AbstractSPRequest::getParameters(name, values);
+ }
void clearHeader(const char* rawname, const char* cginame) {
if (m_dc->bUseHeaders == 1) {
// ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_clear_header: hdr\n");
return string(SH_AP_AUTH_TYPE(m_req) ? SH_AP_AUTH_TYPE(m_req) : "");
}
void setContentType(const char* type) {
- m_req->content_type = ap_psprintf(m_req->pool, type);
+ m_req->content_type = ap_psprintf(m_req->pool, "%s", type);
}
void setResponseHeader(const char* name, const char* value) {
HTTPResponse::setResponseHeader(name, value);
long sendRedirect(const char* url) {
HTTPResponse::sendRedirect(url);
ap_table_set(m_req->headers_out, "Location", url);
+ if (m_dc->bExpireRedirects == 1) {
+ ap_table_set(m_req->err_headers_out, "Expires", "Wed, 01 Jan 1997 12:00:00 GMT");
+ ap_table_set(m_req->err_headers_out, "Cache-Control", "private,no-store,no-cache,max-age=0");
+ }
return REDIRECT;
}
const vector<string>& getClientCertificates() const {
}
long returnDecline(void) { return DECLINED; }
long returnOK(void) { return OK; }
+#if defined(SHIBSP_HAVE_GSSAPI) && !defined(SHIB_APACHE_13)
+ gss_ctx_id_t getGSSContext() const {
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ apr_pool_userdata_get((void**)&ctx, g_szGSSContextKey, m_req->pool);
+ return ctx;
+ }
+#endif
};
/********************************************************************************/
properties["exportAssertion"] = (sta->m_dc->bExportAssertion==1) ? "true" : "false";
if (sta->m_dc->tSettings)
- ap_table_do(_rm_get_all_table_walk, &properties, sta->m_dc->tSettings, nullptr);
+ ap_table_do(_rm_get_all_table_walk, &properties, sta->m_dc->tSettings, NULL);
}
const PropertySet* ApacheRequestMapper::getPropertySet(const char* name, const char* ns) const
if (!sta)
throw ConfigurationException("Request wrapper object was not of correct type.");
- // mod_auth clone
-
int m=sta->m_req->method_number;
bool method_restricted=false;
const char *t, *w;
if (!reqs_arr)
return shib_acl_indeterminate; // should never happen
+ // Check for an "embedded" AccessControl plugin.
+ if (sta->m_dc->szAccessControl) {
+ aclresult_t result = shib_acl_false;
+ try {
+ ifstream aclfile(sta->m_dc->szAccessControl);
+ xercesc::DOMDocument* acldoc = XMLToolingConfig::getConfig().getParser().parse(aclfile);
+ XercesJanitor<xercesc::DOMDocument> docjanitor(acldoc);
+ static XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
+ string t(XMLHelper::getAttrString(acldoc ? acldoc->getDocumentElement() : nullptr, nullptr, _type));
+ if (t.empty())
+ throw ConfigurationException("Missing type attribute in AccessControl plugin configuration.");
+ auto_ptr<AccessControl> aclplugin(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acldoc->getDocumentElement()));
+ Locker acllock(aclplugin.get());
+ result = aclplugin->authorized(request, session);
+ }
+ catch (exception& ex) {
+ request.log(SPRequest::SPError, ex.what());
+ }
+
+ if (result == shib_acl_true && sta->m_dc->bRequireAll != 1) {
+ // If we're not insisting that all rules be met, then we're done.
+ request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was successful, granting access");
+ return shib_acl_true;
+ }
+ else if (result != shib_acl_true && sta->m_dc->bRequireAll == 1) {
+ // If we're insisting that all rules be met, which is not something Apache really handles well,
+ // then we either return false or indeterminate based on the authoritative option, which defaults on.
+ if (sta->m_dc->bAuthoritative != 0) {
+ request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was unsuccessful, denying access");
+ return shib_acl_false;
+ }
+
+ request.log(SPRequest::SPDebug, "htaccess: embedded AccessControl plugin was unsuccessful but not authoritative, leaving it up to Apache");
+ return shib_acl_indeterminate;
+ }
+ }
+
+
require_line* reqs=(require_line*)reqs_arr->elts;
for (int x=0; x<reqs_arr->nelts; x++) {
if (!strcasecmp(w,"shibboleth")) {
// This is a dummy rule needed because Apache conflates authn and authz.
// Without some require rule, AuthType is ignored and no check_user hooks run.
- status = true; // treat it as an "accepted" rule
+
+ // We evaluate to false if ShibAccessControl is used and ShibRequireAll is off.
+ // This allows actual rules to dictate the result, since ShibAccessControl returned
+ // non-true, and if nothing else is used, access will be denied.
+ if (!sta->m_dc->szAccessControl || sta->m_dc->bRequireAll == 1) {
+ // We evaluate to true, because ShibRequireAll is enabled (so a true is just a no-op)
+ // or because there was no other AccessControl rule in place, so this may be the only
+ // rule in effect.
+ status = true;
+ }
}
else if (!strcmp(w,"valid-user") && session) {
request.log(SPRequest::SPDebug, "htaccess: accepting valid-user based on active session");
status = true;
}
else if (!strcmp(w,"user") && !remote_user.empty()) {
- bool regexp=false,negate=false;
+ bool regexp = false;
while (*t) {
- w=ap_getword_conf(sta->m_req->pool,&t);
- if (*w=='~') {
- regexp=true;
+ w = ap_getword_conf(sta->m_req->pool,&t);
+ if (*w == '~') {
+ regexp = true;
continue;
}
- else if (*w=='!') {
- negate=true;
- if (*(w+1)=='~')
- regexp=true;
+ else if (*w == '!') {
+ // A negated rule presumes success unless a match is found.
+ status = true;
+ if (*(w+1) == '~')
+ regexp = true;
continue;
}
string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
}
}
- else if (remote_user==w) {
+ else if (remote_user == w) {
match = true;
}
if (match) {
- // If we matched, then we're done with this rule either way and status is set to reflect the outcome.
- status = !negate;
+ // If we matched, then we're done with this rule either way and we flip status to reflect the outcome.
+ status = !status;
if (request.isPriorityEnabled(SPRequest::SPDebug))
request.log(SPRequest::SPDebug,
- string("htaccess: require user ") + (negate ? "rejecting (" : "accepting (") + remote_user + ")");
+ string("htaccess: require user ") + (!status ? "rejecting (" : "accepting (") + remote_user + ")");
break;
}
}
}
else if (!strcmp(w,"group") && !remote_user.empty()) {
- SH_AP_TABLE* grpstatus=nullptr;
+ SH_AP_TABLE* grpstatus = nullptr;
if (sta->m_dc->szAuthGrpFile) {
if (request.isPriorityEnabled(SPRequest::SPDebug))
request.log(SPRequest::SPDebug,string("htaccess plugin using groups file: ") + sta->m_dc->szAuthGrpFile);
- grpstatus=groups_for_user(sta->m_req,remote_user.c_str(),sta->m_dc->szAuthGrpFile);
+ grpstatus = groups_for_user(sta->m_req,remote_user.c_str(),sta->m_dc->szAuthGrpFile);
}
- bool negate=false;
while (*t) {
- w=ap_getword_conf(sta->m_req->pool,&t);
- if (*w=='!') {
- negate=true;
+ w = ap_getword_conf(sta->m_req->pool,&t);
+ if (*w == '!') {
+ // A negated rule presumes success unless a match is found.
+ status = true;
continue;
}
if (grpstatus && ap_table_get(grpstatus,w)) {
- // If we matched, then we're done with this rule either way and status is set to reflect the outcome.
- status = !negate;
- request.log(SPRequest::SPDebug, string("htaccess: require group ") + (negate ? "rejecting (" : "accepting (") + w + ")");
+ // If we matched, then we're done with this rule either way and we flip status to reflect the outcome.
+ status = !status;
+ request.log(SPRequest::SPDebug, string("htaccess: require group ") + (!status ? "rejecting (" : "accepting (") + w + ")");
break;
}
}
}
else if (!strcmp(w,"authnContextClassRef") || !strcmp(w,"authnContextDeclRef")) {
const char* ref = !strcmp(w,"authnContextClassRef") ? session->getAuthnContextClassRef() : session->getAuthnContextDeclRef();
- bool regexp=false,negate=false;
- while (ref && *t) {
- w=ap_getword_conf(sta->m_req->pool,&t);
- if (*w=='~') {
- regexp=true;
- continue;
- }
- else if (*w=='!') {
- negate=true;
- if (*(w+1)=='~')
+ if (ref && *ref) {
+ bool regexp = false;
+ while (ref && *t) {
+ w = ap_getword_conf(sta->m_req->pool,&t);
+ if (*w == '~') {
regexp=true;
- continue;
- }
+ continue;
+ }
+ else if (*w == '!') {
+ // A negated rule presumes success unless a match is found.
+ status = true;
+ if (*(w+1)=='~')
+ regexp = true;
+ continue;
+ }
- // Figure out if there's a match.
- bool match = false;
- if (regexp) {
- try {
- // To do regex matching, we have to convert from UTF-8.
- RegularExpression re(w);
- match = re.matches(ref);
+ // Figure out if there's a match.
+ bool match = false;
+ if (regexp) {
+ try {
+ // To do regex matching, we have to convert from UTF-8.
+ RegularExpression re(w);
+ match = re.matches(ref);
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ request.log(SPRequest::SPError,
+ string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
+ }
}
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- request.log(SPRequest::SPError,
- string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
+ else if (!strcmp(w,ref)) {
+ match = true;
}
- }
- else if (!strcmp(w,ref)) {
- match = true;
- }
- if (match) {
- // If we matched, then we're done with this rule either way and status is set to reflect the outcome.
- status = !negate;
- if (request.isPriorityEnabled(SPRequest::SPDebug))
- request.log(SPRequest::SPDebug,
- string("htaccess: require authnContext ") + (negate ? "rejecting (" : "accepting (") + ref + ")");
- break;
+ if (match) {
+ // If we matched, then we're done with this rule either way and we flip status to reflect the outcome.
+ status = !status;
+ if (request.isPriorityEnabled(SPRequest::SPDebug))
+ request.log(SPRequest::SPDebug,
+ string("htaccess: require authnContext ") + (!status ? "rejecting (" : "accepting (") + ref + ")");
+ break;
+ }
}
}
+ else if (request.isPriorityEnabled(SPRequest::SPDebug)) {
+ request.log(SPRequest::SPDebug, "htaccess: require authnContext rejecting session with no context associated");
+ }
}
else if (!session) {
request.log(SPRequest::SPError, string("htaccess: require ") + w + " not given a valid session, are you using lazy sessions?");
// Initial look at a request - create the per-request structure
static int shib_post_read(request_rec *r)
{
- shib_request_config* rc = init_request_config(r);
+ init_request_config(r);
//ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r), "shib_post_read");
return DECLINED;
}
throw runtime_error("unknown error");
}
catch (exception& ex) {
- ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),ex.what());
+ ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"%s",ex.what());
ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,SH_AP_R(s),"shib_child_init() failed to load configuration");
exit(1);
}
ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_out_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
// can't use overlap call because it will collapse Set-Cookie headers
//apr_table_overlap(r->headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);
- apr_table_do(_table_add,r->headers_out, rc->hdr_out,nullptr);
+ apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL);
}
/* remove ourselves from the filter chain */
ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_err_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts);
// can't use overlap call because it will collapse Set-Cookie headers
//apr_table_overlap(r->err_headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE);
- apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,nullptr);
+ apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL);
}
/* remove ourselves from the filter chain */
{"ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,
OR_AUTHCFG, TAKE2, "Set arbitrary Shibboleth request property for content"},
+ {"ShibAccessControl", (config_fn_t)ap_set_string_slot,
+ (void *) XtOffsetOf (shib_dir_config, szAccessControl),
+ OR_AUTHCFG, TAKE1, "Set arbitrary Shibboleth access control plugin for content"},
+
{"ShibDisable", (config_fn_t)ap_set_flag_slot,
(void *) XtOffsetOf (shib_dir_config, bOff),
OR_AUTHCFG, FLAG, "Disable all Shib module activity here to save processing effort"},
{"ShibUseHeaders", (config_fn_t)ap_set_flag_slot,
(void *) XtOffsetOf (shib_dir_config, bUseHeaders),
OR_AUTHCFG, FLAG, "Export attributes using custom HTTP headers"},
+ {"ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,
+ (void *) XtOffsetOf (shib_dir_config, bExpireRedirects),
+ OR_AUTHCFG, FLAG, "Expire SP-generated redirects"},
{nullptr}
};
RSRC_CONF, "Path to shibboleth2.xml config file"),
AP_INIT_TAKE1("ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir,
RSRC_CONF, "Paths of XML schema catalogs"),
+ AP_INIT_TAKE1("ShibGSSKey", (config_fn_t)ap_set_global_string_slot, &g_szGSSContextKey,
+ RSRC_CONF, "Name of user data key containing GSS context established by GSS module"),
AP_INIT_TAKE1("ShibURLScheme", (config_fn_t)shib_set_server_string_slot,
(void *) offsetof (shib_server_config, szScheme),
AP_INIT_TAKE2("ShibRequestSetting", (config_fn_t)shib_table_set, nullptr,
OR_AUTHCFG, "Set arbitrary Shibboleth request property for content"),
+ AP_INIT_TAKE1("ShibAccessControl", (config_fn_t)ap_set_string_slot,
+ (void *) offsetof (shib_dir_config, szAccessControl),
+ OR_AUTHCFG, "Set arbitrary Shibboleth access control plugin for content"),
+
AP_INIT_FLAG("ShibDisable", (config_fn_t)ap_set_flag_slot,
(void *) offsetof (shib_dir_config, bOff),
OR_AUTHCFG, "Disable all Shib module activity here to save processing effort"),
AP_INIT_FLAG("ShibUseHeaders", (config_fn_t)ap_set_flag_slot,
(void *) offsetof (shib_dir_config, bUseHeaders),
OR_AUTHCFG, "Export attributes using custom HTTP headers"),
+ AP_INIT_FLAG("ShibExpireRedirects", (config_fn_t)ap_set_flag_slot,
+ (void *) offsetof (shib_dir_config, bExpireRedirects),
+ OR_AUTHCFG, "Expire SP-generated redirects"),
{nullptr}
};