Compare commits

...

No commits in common. "master" and "do-not-verify-logout-signature" have entirely different histories.

36 changed files with 1344 additions and 19708 deletions

View File

@ -1,2 +0,0 @@
( (nil . ((indent-tabs-mode . nil)))
(c-mode . ((c-basic-offset . 4))) )

10
.gitignore vendored
View File

@ -1,10 +0,0 @@
*.lo
*.la
*.o
*.slo
aclocal.m4
config.*
configure
Makefile
.libs/
.vscode/

View File

@ -1,15 +0,0 @@
dist: bionic
language: c
addons:
apt:
packages:
- apache2-dev
- libglib2.0-dev
- liblasso3-dev
- libssl-dev
script:
- ./autogen.sh
- ./configure CFLAGS=-Werror
- make
- make distfile

149
COPYING
View File

@ -1,21 +1,3 @@
mod_auth_mellon is distributed under the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
In addition, as a special exception, permission is granted to link the code
of this release of mod_mellon with the OpenSSL project's "OpenSSL" library
(or with modified versions of it that use the same licence as the "OpenSSL"
library), and distribute the linked executables. You must obey the GNU
General Public License version 2 in all respects for all of the code used
other than "OpenSSL". If you modify the code, you may extend this exception
to your version of the code, but you are not obligated to do so. If you do
not wish to do so, delete this exception statement from your version.
The full text of the GNU General Public License:
===============================================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
@ -355,134 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
The full text of the OpenSSL License:
===============================================================================
LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts. Actually both licenses are BSD-style
Open Source licenses. In case of any license issues related to OpenSSL
please contact openssl-core@openssl.org.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/

286
ECP.rst
View File

@ -1,286 +0,0 @@
Guide to using ECP
==================
Introduction
------------
The **Enhanced Client or Proxy** (ECP) profile of SAML2
The Enhanced Client or Proxy (ECP) Profile supports several SSO use
cases, in particular:
* Clients with capabilities beyond those of a browser, allowing them
to more actively participate in IdP discovery and message flow.
* Using a proxy server, for example a WAP gateway in front of a mobile
device which has limited functionality.
* When other bindings are precluded (e.g. where the client does not
support redirects, or when auto form post is not possible without
Javascript, or when the artifact binding is ruled out because the
identity provider and service provider cannot directly communicate.
An enhanced client or proxy (ECP) is a system entity that knows how to
contact an appropriate identity provider, possibly in a
context-dependent fashion, and also supports the Reverse SOAP (PAOS)
binding.
An example scenario enabled by ECP profile is as follows: A principal,
wielding an ECP, uses it to either access a resource at a service
provider, or access an identity provider such that the service
provider and desired resource are understood or implicit. The
principal authenticates (or has already authenticated) with the
identity provider [1]_, which then produces an authentication assertion
(possibly with input from the service provider). The service provider
then consumes the assertion and subsequently establishes a security
context for the principal. During this process, a name identifier
might also be established between the providers for the principal,
subject to the parameters of the interaction and the consent of the
principal.
SAML2 Profile for ECP (Section 4.2) defines these steps for an ECP
transaction:
1. ECP issues HTTP Request to SP
2. SP issues <AuthnRequest> to ECP using PAOS
3. ECP determines IdP
4. ECP conveys <AuthnRequest> to IdP using SOAP
5. IdP identifies principal
6. IdP issues <Response> to ECP, targeted at SP using SOAP
7. ECP conveys <Response> to SP using PAOS
8. SP grants or denies access to principal
mod_auth_mellon and ECP
-----------------------
mod_auth_mellon plays the role of the SP in an ECP transaction.
mod_auth_mellon utilizes the Lasso library to provide it's SAML2
functionality. Fully functioning SAML2 ECP support in Lasso is
relatively new. When mod_auth_mellon is built it detects the presence
of SAML2 ECP in Lasso and only compiles in the ECP code in
mod_auth_mellon if it's present in Lasso.
How does mod_auth_mellon recognize a request is from an ECP client?
```````````````````````````````````````````````````````````````````
In Step 1. when the ECP client issues the HTTP Request to the SP it
**MUST** include `application/vnd.paos+xml` as a mime type in the HTTP
`Accept` header field and include an HTTP `PAOS` header specifying a
PAOS version of `urn:liberty:paos:2003-08` and an ECP service
declaration of `urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp` [2]_,
for example::
Accept: text/html, application/vnd.paos+xml
PAOS: ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"
If mod_auth_mellon sees this in the incoming request it knows the
client is ECP aware and capable. If authentication is required
mod_auth_mellon will initiate an ECP flow.
The role of IdP's in ECP
````````````````````````
The SAML2 ECP profile states it is the ECP client which determines the
IdP that will be used for authentication. This is in contrast to the
Web SSO flow where the SP determines the IdP. However, the ECP
protocol permits an SP to send the ECP client a list of IdP's it
trusts. It is optional if the SP sends an IDPList, if it does the ECP
client should select the IdP from the SP provided IDPList otherwise
the ECP client is free to select any IdP it wishes.
If the mellon configuration option `MellonECPSendIDPList` is true then
mod_auth_mellon will include an IDPList when it returns a PAOS
<AuthnRequest> to the ECP client.
To build the IDPList mod_auth_mellon scans it's list of loaded IdP's
selecting those which are ECP capable. To support ECP an IdP must
advertise the SingleSignOn service utilizing the SOAP binding.
ECP specific mod_auth_mellon configuration directives
`````````````````````````````````````````````````````
These configuration directives are specific to ECP:
MellonECPSendIDPList
If `On` mod_auth_mellon will send an IdP list to the ECP client
containing only those IdP's capable of ECP flow. The ECP client
should select an IdP only from this list. If this option is `Off`
no IdP list will be sent and the ECP client is free to select any
IdP.
Example ECP client
``````````````````
To illustrate a simple ECP client based on Lasso we'll use the Lasso
Python binding (as opposed to pseudo code, Python is quite
readable). All error checking and another necessary ancillary code has
been eliminated in order to clearly illustrate only the ECP
operations.
.. code-block:: python
import lasso
import requests
ecp = lasso.Ecp(server)
session = requests.Session()
MEDIA_TYPE_PAOS = 'application/vnd.paos+xml'
PAOS_HEADER = 'ver="%s";"%s"' % (lasso.PAOS_HREF,lasso.ECP_HREF)
# Step 1: Request protected resource, indicate ECP capable
response = session.get(protected, headers={'Accept': MEDIA_TYPE_PAOS,
'PAOS': PAOS_HEADER})
# Process returned PAOS wrapped <AuthnRequest>
ecp.processAuthnRequestMsg(response.text)
# Post SOAP wrapped <AuthnRequest> to IdP, use Digest Auth to authenticate
response = session.post(ecp.msgUrl,
data=ecp.msgBody,
auth=requests.auth.HTTPDigestAuth(user, password)
headers={'Content-Type': 'text/xml'})
# Process returned SOAP wrapped <Assertion> from IdP
ecp.processResponseMsg(response.text)
# Post PASO wrapped <Assertion> to SP, response is protected resource
response = session.post(ecp.msgUrl,
data=ecp.msgBody,
headers={'Content-Type': 'application/vnd.paos+xml'})
mod_auth_mellon internal ECP implementation notes
-------------------------------------------------
Notes on ECP vs. Web SSO flow
`````````````````````````````
Web SSO (Single Sign-On) flow is by far the most common and what
most people are familiar with when they think of SAML. The Web SSO
profile is designed so that browsers ignorant of SAML can perform
SAML authentication without modification. This is accomplished with
existing HTTP paradigms such as redirects, form posts, etc. which a
browser will process normally yielding the desired result.
ECP (Enhanced Client or Proxy) is a different SAML profile that
also accomplishes SSO (Single Sign-On). The distinction is an ECP
client is fully SAML aware and actively participates in the SAML
conversation.
Web SSO and ECP have very different flows, mod_auth_mellon must
support both flows. mod_auth_mellon is a SP (Service Provider).
IdP Selection Differences
`````````````````````````
With Web SSO the SP determines the IdP and redirects there.
With ECP the ECP client determines the IdP, the SP has no a prori
knowledge of the target IdP, although the SP may provide a
suggested list of IdP's when responding to the ECP client.
Since with ECP it is the ECP client which selects the IdP the set of
IdP's loaded into mod_auth_mellon are not relevant **except** if
`MellonECPSendIDPList` is enabled. In this case mod_auth_mellon will
filter the set of loaded IdP's and forward those IdP's supporting
SingleSignOn with the SOAP binding.
Apache request processing pipeline
``````````````````````````````````
Apache implements a request processing pipeline composed of
stages. An Apache extension module can participate in the pipeline
by asking to be called at specific stages (steps) by registering a
hook function for that stage. Final content returned to the HTTP
client in the HTTP response is generated in the "handler", one of
the final stages in the request processing pipeline.
One of the stages in the request pipeline is determining
authentication and authorization for protected resources. If a
resource is protected and the authentication and authorization
pipeline stages deny access or fail the request processing pipeline
is aborted early, a non-success HTTP response is returned, the
content handler is never reached.
With Web SSO if authentication needs to be performed a redirect will
be returned that redirects to a SAML endpoint (login) on our SP. This
in turn generates the SAML <AuthnRequest> with a redirect to the
IdP. All of this is very vanilla standard HTTP easily accommodated by
Apache's request processing pipeline which is designed to handle these
types of flows.
ECP requires special handling
`````````````````````````````
However ECP has a very different flow. When an ECP client sends a
request to the SP it includes a special HTTP headers indicating it is
ECP capable. If the SP determines the resource is protected and
authentication is needed and the client has signaled it is ECP capable
then the SP responds successfully (200) with a SAML <AuthnRequest>
wrapped in PAOS. *This is very different than conventional HTTP
request processing.* Here we have a case where there is a protected
resource that has **not** been authenticated yet the web server will
responds with an HTTP 200 success and content! One might normally
expect a HTTP 401 or redirect response for a protected resource when
there is no authenticated user. *This is clearly contrary to the
expectations of Apache's request processing pipeline.*
Reaching the Apache content handler
```````````````````````````````````
In order to be able to return a successful (HTTP 200) PAOS response
when doing the ECP we have to reach the part of Apache's request
processing pipeline that generates the response. In Apache terminology
this is called a (content) handler.
At an early stage we detect if authentication is required. For the
normal Web SSO profile we would redirect the client back to our login
endpoint which will be handled by our handler in a different
request. But for ECP the current request must proceed. We set a flag
on the request indicating ECP authentication is required. The pipeline
continues. When the pipeline reaches the authentication and
authorization stages we check the ECP flag on the request, if ECP
authentication is indicated we lie and tell the pipeline the user is
authenticated and authorized. We do this only so we can reach the
handler stage (otherwise because the request is for a protected
resource the pipeline would terminate with an error). Despite our
having forced authentication and authorization to be valid for the
protected resource the request processing pipeline *will not return the
protected resource* because we will subsequently intercept the request
in our handler before the pipeline reaches the point of returning the
protected resource.
At the handler stage
````````````````````
Once our handler is invoked it has 3 possible actions to perform:
1. The request is for one of our SAML endpoints (e.g. login,
logout, metadata, etc.) We dispatch to the handler for the specific
action. We detect this case by matching the request URI to our SAML
endpoints. We signal to the pipeline that our hook handled the request.
2. The request is for a protected resource and needs ECP
authentication performed. We detect this case by examining the ECP flag
set on the request by an earlier hook function. The request URI is
for the protected resource and has nothing to do with our SAML
endpoints. We generate the PAOS <AuthnRequest> and respond with
success (200) and signal to the pipeline that our hook handled the
request. Note, we have not returned the protected resource, instead
we've returned the PAOS request.
3. The request has nothing to do with us, we decline to handle
it. The pipeline proceeds to the next handler.
.. [1] The means by which a principal authenticates with an identity
provider is outside of the scope of SAML. Typically an ECP
client will utilize an HTTP authentication method when posting
the <AuthnRequest> SOAP message to the IdP.
.. [2] Contrary to most HTTP headers the values in the PAOS header must
be enclosed in double quotes. A semicolon is used to separate
the values.

View File

@ -1,52 +1,39 @@
# Source files. mod_auth_mellon.c must be the first file.
SRC=mod_auth_mellon.c \
auth_mellon_cache.c \
auth_mellon_config.c \
auth_mellon_cookie.c \
auth_mellon_diagnostics.c \
auth_mellon_handler.c \
auth_mellon_cache.c auth_mellon_config.c \
auth_mellon_cookie.c auth_mellon_handler.c \
auth_mellon_util.c \
auth_mellon_session.c \
auth_mellon_httpclient.c
# Documentation files
USER_GUIDE_FILES=\
doc/user_guide/mellon_user_guide.adoc \
doc/user_guide/Guardfile \
doc/user_guide/README \
doc/user_guide/images/chrome_SAML_Chrome_Panel.png \
doc/user_guide/images/chrome_SAML_Chrome_Panel.svg \
doc/user_guide/images/saml-tracer.png \
doc/user_guide/images/saml-tracer.svg \
doc/user_guide/images/saml-web-sso.svg
# Files to include when making a .tar.gz-file for distribution
DISTFILES=$(SRC) \
auth_mellon.h \
auth_mellon_compat.h \
lasso_compat.h \
config.h.in \
configure \
configure.ac \
Makefile.in \
autogen.sh \
README.md \
ECP.rst \
TODO \
README \
COPYING \
NEWS \
mellon_create_metadata.sh \
$(USER_GUIDE_FILES)
.tarball-version \
tools/git-version-gen
debian/auth_mellon.conf \
debian/auth_mellon.load \
debian/changelog \
debian/compat \
debian/control \
debian/copyright \
debian/dirs \
debian/docs \
debian/install \
debian/rules
.tarball-version:
echo @PACKAGE_VERSION@ > $@-t && mv $@-t $@
all: mod_auth_mellon.la
mod_auth_mellon.la: $(SRC) auth_mellon.h auth_mellon_compat.h
@APXS2@ -Wc,"-std=c99 @MELLON_CFLAGS@ @OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@ @CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC)
mod_auth_mellon.la: $(SRC) auth_mellon.h
@APXS2@ -Wc,"@OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC)
# Building configure (for distribution)
@ -56,8 +43,6 @@ configure: configure.ac
@NAMEVER@.tar.gz: $(DISTFILES)
tar -c --transform="s#^#@NAMEVER@/#" -vzf $@ $(DISTFILES)
@NAMEVER@.tar.bz2: $(DISTFILES)
tar -c --transform="s#^#@NAMEVER@/#" -vjf $@ $(DISTFILES)
.PHONY: install
install: mod_auth_mellon.la
@ -69,7 +54,6 @@ distfile: @NAMEVER@.tar.gz
.PHONY: clean
clean:
rm -f mod_auth_mellon.la
rm -f $(SRC:%.c=%.o)
rm -f $(SRC:%.c=%.lo)
rm -f $(SRC:%.c=%.slo)
rm -rf .libs/
@ -84,6 +68,3 @@ distclean: clean
.PHONY: fullclean
fullclean: distclean
rm -f configure aclocal.m4
.PHONY: dist-bzip2
dist-bzip2: @NAMEVER@.tar.bz2

419
NEWS
View File

@ -1,410 +1,3 @@
Version 0.14.2
---------------------------------------------------------------------------
Security fixes:
* [CVE-2019-3878] Authentication bypass when Apache is used as a
reverse proxy
If Apache is configured as a reverse proxy with mod_auth_mellon for
authentication, the authentication can be bypassed by adding SAML
2.0 ECP headers to the request.
This vulnerability affects mod_auth_mellon 0.11.0 and newer.
This vulnerability is due to both mod_auth_mellon and mod_proxy
registering as handlers for the requests, with the same
priority. When mod_auth_mellon handles the request first, it will
trigger a ECP authentication request. If mod_proxy handles it first,
it will forward it to the backend server.
Which module handles it first depends on the order modules are
loaded by Apache.
This vulnerability is fixes by specifically registering that the
mod_auth_mellon handler should run before mod_proxy.
Thanks to Jakub Hrozek and John Dennis at RedHat for fixing this
vulnerability.
* [CVE-2019-3877] Redirect URL validation bypass
Version 0.14.1 and older of mod_auth_mellon allows the redirect URL
validation to be bypassed by specifying an URL with backslashes
instead of forward slashes. Browsers silently convert backslashes to
forward slashes, which allows an attacker to bypass the redirect URL
validation by using `%5c` in the ReturnTo-parameter. E.g.:
https://sp.example.org/mellon/logout?ReturnTo=https:%5c%5cmalicious.example.org/
This version fixes that issue by rejecting all URLs with
backslashes.
Thanks to Eric Chamberland for discovering this vulnerability.
Version 0.14.1
---------------------------------------------------------------------------
Bug fixes:
* Fix environment variables in MellonCond
* Fix detection of AJAX requests
* Fix trailing semi-colon in Set-Cookie header
Version 0.14.0
---------------------------------------------------------------------------
Backwards incompatible changes:
This version switches the default signature algorithm used when
signing messages from rsa-sha1 to rsa-sha256. If your IdP does not
allow messages to be signed with that algorithm, you need to add a
setting switching back to the old algorithm:
MellonSignatureMethod rsa-sha1
Note that this only affects messages sent from mod_auth_mellon to your
IdP. It does not affect authentication responses or other messages
sent from your IdP to mod_auth_mellon.
New features:
* Many improvements in what is logged during various errors.
* Diagnostics logging, which creates a detailed log during request
processing.
* Add support for selecting which signature algorithm is used when
signing messages, and switch to rsa-sha256 by default.
Bug fixes:
* Fix segmentation fault in POST replay functionality on empty value.
* Fix incorrect error check for many `lasso_*`-functions.
* Fix case sensitive match on MellonUser attribute name.
Version 0.13.1
---------------------------------------------------------------------------
Security fix:
Fix a cross-site session transfer vulnerability. mod_auth_mellon
version 0.13.0 and older failed to validate that the session specified
in the user's session cookie was created for the web site the user
actually accesses.
If two different web sites are hosted on the same web server, and both
web sites use mod_auth_mellon for authentication, this vulnerability
makes it possible for an attacker with access to one of the web sites
to copy their session cookie to the other web site, and then use the
same session to get access to the other web site.
Thanks to François Kooman for reporting this vulnerability.
This vulnerability has been assigned CVE-2017-6807.
Note: The fix for this vunlerability makes mod_auth_mellon validate
that the cookie parameters used when creating the session match the
cookie parameters that should be used when accessing the current
page. If you currently use mod_auth_mellon across multiple subdomains,
you must make sure that you set the `MellonCookie`-option to the same
value on all domains.
Bug fixes:
* Fix segmentation fault if a (trusted) identity provider returns a
SAML 2.0 attribute without a Name.
* Fix segmentation fault if MellonPostReplay is enabled but
MellonPostDirectory is not set.
Version 0.13.0
---------------------------------------------------------------------------
Security fix:
Fix a denial of service attack in the logout handler, which allows a
remote attacker to crash the Apache worker process with a segmentation
fault. This is caused by a null-pointer dereference when processing a
malformed logout message.
New features:
* Allow MellonSecureCookie to be configured to enable just one of the
"httponly" of "secure" flags, instead of always enabling both flags.
* Support per-module log level with Apache 2.4.
* Allow disabling the Cache-Control HTTP response header.
* Add support for SameSite cookie parameter.
Bug fixes:
* Fix MellonProbeDiscoveryIdP redirecting to the wrong IdP if no IdPs
respond to the probe request.
* Fix mod_auth_mellon interfering with other Apache authentication
modules even when it is disabled for a path.
* Fix wrong HTTP status code being returned in some cases during user
permission checks.
* Fix default POST size limit to actually be 1 MB.
* Fix error if authentication response is missing the optional
Conditions-element.
* Fix AJAX requests being redirected to the IdP.
* Fix wrong content type for ECP authentication request responses.
In addition there are various fixes for errors in the documentation,
as well as internal code changes that do not have any user visible
effects.
Version 0.12.0
---------------------------------------------------------------------------
Security fixes:
* [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to
incorrect error handling when reading POST data from client.
* [CVE-2016-2146] Fix DOS attack (Apache worker process crash /
resource exhaustion) due to missing size checks when reading
POST data.
In addition this release contains the following new features and fixes:
* Add MellonRedirectDomains option to limit the sites that
mod_auth_mellon can redirect to. This option is enabled by default.
* Add support for ECP service options in PAOS requests.
* Fix AssertionConsumerService lookup for PAOS requests.
Version 0.11.1
---------------------------------------------------------------------------
Security fixes:
* [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to
incorrect error handling when reading POST data from client.
* [CVE-2016-2146] Fix DOS attack (Apache worker process crash /
resource exhaustion) due to missing size checks when reading
POST data.
Version 0.11.0
---------------------------------------------------------------------------
* Add SAML 2.0 ECP support.
* The MellonDecode option has been disabled. It was used to decode
attributes in a Feide-specific encoding that is no longer used.
* Set max-age=0 in Cache-Control header, to ensure that all browsers
verifies the data on each request.
* MellonMergeEnvVars On now accepts second optional parameter, the
separator to be used instead of the default ';'.
* Add option MellonEnvVarsSetCount to specify if the number of values
for any attribute should also be stored in environment variable
suffixed _N.
* Add option MellonEnvVarsIndexStart to specify if environment variables
for multi-valued attributes should start indexing with 0 (default) or
with 1.
* Bugfixes:
* Fix error about missing authentication with DirectoryIndex in
Apache 2.4.
Version 0.10.0
---------------------------------------------------------------------------
* Make sure that we fail in the unlikely case where OpenSSL is not able
to provide us with a secure session id.
* Increase the number of key-value pairs in the session to 2048.
* Add MellonMergeEnvVars-option to store multi-valued attributes in
a single environment variable, separated with ';'.
* Bugfixes:
* Fix the [MAP] option for MellonCond.
* Fix cookie deletion for the session cookie. (Logout is not dependent
on the cookie being deleted, so this only fixes the cookie showing
up after the session is deleted.)
Version 0.9.1
---------------------------------------------------------------------------
* Bugfixes:
* Fix session offset calculation that prevented us from having
active sessions at once.
* Run mod_auth_mellon request handler before most other handlers,
so that other handlers cannot block it by accident.
Version 0.9.0
---------------------------------------------------------------------------
* Set the AssertionConsumerServiceURL attribute in authentication
requests.
* Bugfixes:
* Fix use of uninitialized data during logout.
* Fix session entry overflow leading to segmentation faults.
* Fix looking up sessions by NameID, which is used during logout.
Version 0.8.1
---------------------------------------------------------------------------
This is a security release with fixes backported from version 0.9.1.
It turned out that session overflow bugs fixes in version 0.9.0 and
0.9.1 can lead to information disclosure, where data from one session
is leaked to another session. Depending on how this data is used by the
web application, this may lead to data from one session being disclosed
to an user in a different session. (CVE-2014-8566)
In addition to the information disclosure, this release contains some
fixes for logout processing, where logout requests would crash the
Apache web server. (CVE-2014-8567)
Version 0.8.0
---------------------------------------------------------------------------
* Add support for receiving HTTP-Artifact identifiers as POST data.
* Simplify caching headers.
* Map login errors into more appropriate HTTP error codes than
400 Bad Request.
* Add MellonNoSuccessErrorPage option to redirect to a error page on login
failure.
* Turn session storage into a dynamic pool of memory, which means that
attribute values (and other items) can have arbitrary sizes as long as
they fit in the session as a whole.
* Various bugfixes:
* Fix for compatibility with recent versions of CURL.
* Fix broken option MellonDoNotVerifyLogoutSignature.
* Fix deadlock that could occur during logout processing.
* Fix some compile warnings.
* Fix some NULL derefernce bugs that may lead to segmentation faults.
* Fix a minor memory leak during IdP metadata loading.
Version 0.7.0
---------------------------------------------------------------------------
* Add MellonSPentityId to control entityId in autogenerated metadata
* Fix compatibility with Apache 2.4.
* Handle empty RelayState the same as missing RelayState.
* Add MellonSetEvnNoPrefix directive to set environment variables
without "MELLON_"-prefix.
Version 0.6.1
---------------------------------------------------------------------------
* Fix the POST replay functionality when multiple users logging in
at once.
* Add a fallback for the case where the POST replay data has expired
before the user logs in.
Version 0.6.0
---------------------------------------------------------------------------
Backwards-incompatible changes:
* The POST replay functionality has been disabled by default, and the
automatic creation of the MellonPostDirectory target directory has been
removed. If you want to use the POST replay functionality, take a
look at the README file for instructions for how to enable this.
* Start discovery service when accessing the login endpoint. We used
to bypass the discovery service in this case, and just pick the first
IdP. This has been changed to send a request to the discovery service
instead, if one is configured.
* The MellonLockFile default path has been changed to:
/var/run/mod_auth_mellon.lock
This only affects platforms where a lock file is required and
where Apache doesn't have write access to that directory during
startup. (Apache can normally create files in that directory
during startup.)
Other changes:
* Fix support for SOAP logout.
* Local logout when IdP does not support SAML 2.0 Single Logout.
* MellonDoNotVerifyLogoutSignature option to disable logout signature
validation.
* Support for relative file paths in configuration.
* The debian build-directory has been removed from the repository.
* Various cleanups and bugfixes:
* Fix cookie parsing header parsing for some HTTP libraries.
* Fix inheritance of MellonAuthnContextClassRef option.
* Use ap_set_content_type() instead of accessing request->content_type.
* README indentation cleanups.
* Support for even older versions of GLib.
* Fixes for error handling during session initialization.
* Directly link with GLib rather than relying on the Lasso library
linking to it for us.
* Some code cleanups.
Version 0.5.0
---------------------------------------------------------------------------
@ -429,7 +22,6 @@ Version 0.5.0
* Several cleanups in the code that starts authentication.
Version 0.4.0
---------------------------------------------------------------------------
@ -453,7 +45,6 @@ Version 0.4.0
* Several bugfixes.
Version 0.3.0
---------------------------------------------------------------------------
@ -465,7 +56,6 @@ Version 0.3.0
* Various bugfixes.
Version 0.2.7
---------------------------------------------------------------------------
@ -473,13 +63,11 @@ Version 0.2.7
* Shibboleth 2 interoperability
Version 0.2.6
---------------------------------------------------------------------------
* Fix XSS/DOS vulnerability in repost handler.
Version 0.2.5
---------------------------------------------------------------------------
@ -487,7 +75,6 @@ Version 0.2.5
* Fix HTTP response splitting vulnerability.
Version 0.2.4
---------------------------------------------------------------------------
@ -496,27 +83,23 @@ Version 0.2.4
* Mark session as disabled as soon as logout starts, in case the IdP
doesn't respond.
Version 0.2.3
---------------------------------------------------------------------------
* Bugfix for session lifetime. Take the session lifetime from the
SessionNotOnOrAfter attribute if it is present.
Version 0.2.2
---------------------------------------------------------------------------
* Improve metadata autogeneration: cleanup certificate, allow Organizarion
element data to be supplied from Apache configuration
Version 0.2.1
---------------------------------------------------------------------------
* Make SAML authentication assertion and Lasso session available in the
environment.
environement.
Version 0.2.0
---------------------------------------------------------------------------

View File

@ -1,11 +1,15 @@
# mod_auth_mellon
===========================================================================
README file for mod_auth_mellon
===========================================================================
mod_auth_mellon is an authentication module for Apache. It authenticates
the user against a SAML 2.0 IdP, and grants access to directories
mod_auth_mellon is a authentication module for apache. It authenticates
the user against a SAML 2.0 IdP, and and grants access to directories
depending on attributes received from the IdP.
## Dependencies
===========================================================================
Dependencies
===========================================================================
mod_auth_mellon has four dependencies:
* pkg-config
@ -13,71 +17,74 @@ mod_auth_mellon has four dependencies:
* OpenSSL
* lasso (>=2.1)
You will also require development headers and tools for all of the
You will also require developement headers and tools for all of the
dependencies.
If OpenSSL or lasso are installed in a "strange" directory, then you may
have to specify the directory containing "lasso.pc" and/or "openssl.pc" in
the PKG_CONFIG_PATH environment variable. For example, if OpenSSL is
the PKG_CONFIG_PATH environment variable. For example, if openssl is
installed in /usr/local/openssl (with openssl.pc in
/usr/local/openssl/lib/pkgconfig/) and lasso is installed in /opt/lasso
(lasso.pc in /opt/lasso/lib/pkgconfig/), then you can set PKG_CONFIG_PATH
before running configure like this:
```
PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig:/opt/lasso/lib/pkgconfig
export PKG_CONFIG_PATH
```
If Apache is installed in a "strange" directory, then you may have to
specify the path to apxs2 using the `--with-apxs2=/full/path/to/apxs2`
specify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2
option to configure. If, for example, Apache is installed in /opt/apache,
with apxs2 in /opt/apache/bin, then you run
```
./configure --with-apxs2=/opt/apache2/bin/apxs2
```
Note that, depending on your distribution, apxs2 may be named apxs.
Note that, depending on your distribution, apxs2 may be named apxs.
## Installing mod_auth_mellon
===========================================================================
Installing mod_auth_mellon
===========================================================================
mod_auth_mellon uses autoconf, and can be installed by running the
following commands:
```
./configure
make
make install
```
## Configuring mod_auth_mellon
===========================================================================
Configuring mod_auth_mellon
===========================================================================
Here we are going to assume that your web server's hostname is
Here we are going to assume that your web servers hostname is
'example.com', and that the directory you are going to protect is
'https://example.com/secret/'. We are also going to assume that you have
'http://example.com/secret/'. We are also going to assume that you have
configured your web site to use SSL.
You need to edit the configuration file for your web server. Depending on
your distribution, it may be named '/etc/apache/httpd.conf' or something
different.
You need to add a LoadModule directive for mod_auth_mellon. This will
You need to add a LoadModule directove for mod_auth_mellon. This will
look similar to this:
```
LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so
```
To find the full path to mod_auth_mellon.so, you may run:
```
apxs2 -q LIBEXECDIR
```
This will print the path where Apache stores modules. mod_auth_mellon.so
will be stored in that directory.
You will also need to make sure that Apache's authn_core module is also
enabled. Most likely you also want authz_user to be enabled.
After you have added the LoadModule directive, you must add configuration
for mod_auth_mellon. The following is an example configuration:
```ApacheConf
###########################################################################
# Global configuration for mod_auth_mellon. This configuration is shared by
# every virtual server and location in this instance of apache.
@ -90,25 +97,19 @@ for mod_auth_mellon. The following is an example configuration:
# Default: MellonCacheSize 100
MellonCacheSize 100
# MellonCacheEntrySize sets the maximum size for a single session entry in
# bytes. When mod_auth_mellon reaches this limit, it cannot store any more
# data in the session and will return an error. The minimum entry size is
# 65536 bytes, values lower than that will be ignored and the minimum will
# be used.
# Default: MellonCacheEntrySize 196608
# MellonLockFile is the full path to a file used for synchronizing access
# to the session data. The path should only be used by one instance of
# apache at a time. The server must be restarted before any changes to this
# option takes effect.
# Default: MellonLockFile "/var/run/mod_auth_mellon.lock"
MellonLockFile "/var/run/mod_auth_mellon.lock"
# Default: MellonLockFile "/tmp/mellonLock"
MellonLockFile "/tmp/mellonLock"
# MellonPostDirectory is the full path of a directory where POST requests
# are saved during authentication. This directory must writable by the
# Apache user. It should not be writable (or readable) by other users.
# Default: None
# Example: MellonPostDirectory "/var/cache/mod_auth_mellon_postdata"
# are saved during authentication. This directory must be owned by the
# Apache user and be mode 700. We will attempt to create it if it does not
# exist.
# Default: MellonPostDirectory "/var/tmp/mellonpost"
MellonPostDirectory "/var/tmp/mellonpost"
# MellonPostTTL is the delay in seconds before a saved POST request can
# be flushed.
@ -116,31 +117,13 @@ MellonLockFile "/var/run/mod_auth_mellon.lock"
MellonPostTTL 900
# MellonPostSize is the maximum size for saved POST requests
# Default: MellonPostSize 1048576 (1 MB)
MellonPostSize 1048576
# Default: MellonPostSize 1073741824 (1 MB)
MellonPostSize 1073741824
# MellonPostCount is the maximum amount of saved POST requests
# MellonPostCount is the maxmimum amount of saved POST requests
# Default: MellonPostCount 100
MellonPostCount 100
# MellonDiagnosticsFile If Mellon was built with diagnostic capability
# then diagnostic is written here, it may be either a filename or a pipe.
# If it's a filename then the resulting path is relative to the ServerRoot.
# If the value is preceeded by the pipe character "|" it should be followed
# by a path to a program to receive the log information on its standard input.
# This is a server context directive, hence it may be specified in the
# main server config area or within a <VirtualHost> directive.
# Default: logs/mellon_diagnostics
MellonDiagnosticsFile logs/mellon_diagnostics
# MellonDiagnosticsEnable If Mellon was built with diagnostic capability
# then this is a list of words controlling diagnostic output.
# Currently only On and Off are supported.
# This is a server context directive, hence it may be specified in the
# main server config area or within a <VirtualHost> directive.
# Default: Off
MellonDiagnosticsEnable Off
###########################################################################
# End of global configuration for mod_auth_mellon.
###########################################################################
@ -150,9 +133,7 @@ MellonDiagnosticsEnable Off
<Location /secret>
# These are standard Apache apache configuration directives.
# You must have enabled Apache's authn_core and authz_user modules.
# See http://httpd.apache.org/docs/2.4/mod/mod_authn_core.html and
# http://httpd.apache.org/docs/2.4/mod/mod_authz_user.html
# See http://httpd.apache.org/docs/2.2/mod/core.html for information
# about them.
Require valid-user
AuthType "Mellon"
@ -168,10 +149,6 @@ MellonDiagnosticsEnable Off
# the user. If the user isn't authorized, then we won't
# populate the environment, but we won't deny the user
# access either.
#
# You can also use this to set up the Mellon SSO paramaters
# transparently at the top level of your site, and then use
# "auth" to protect individual paths elsewhere in the site.
# "auth": We will populate the environment with information about
# the user if he is authorized. If he is authenticated
# (logged in), but not authorized (according to the
@ -179,17 +156,21 @@ MellonDiagnosticsEnable Off
# return a 403 Forbidden error. If he isn't authenticated
# then we will redirect him to the login page of the IdP.
#
# There is a special handling of AJAX requests, that are
# identified by the "X-Requested-With: XMLHttpRequest" HTTP
# header. Since no user interaction can happen there,
# we always fail unauthenticated (not logged in) requests
# with a 403 Forbidden error without redirecting to the IdP.
#
# Default: MellonEnable "off"
MellonEnable "auth"
# MellonDecoder is an obsolete option which is a no-op but is
# still accepted for backwards compatibility.
# MellonDecoder is used to select which decoder mod_auth_mellon
# will use when decoding attribute values.
# There are two possible values: "none" and "feide". "none" is the
# default.
# They have the following meanings:
# "none": mod_auth_mellon will store the attribute as it is
# received from the IdP. This is the default behaviour.
# "feide": *DEPRECATED* Feide used to transmit attributes with a
# special encoding. That is no longer necessary, and
# this decoder should therefore no longer be used.
# Default: MellonDecoder "none"
MellonDecoder "none"
# MellonVariable is used to select the name of the cookie which
# mod_auth_mellon should use to remember the session id. If you
@ -197,11 +178,10 @@ MellonDiagnosticsEnable Off
# you will have to choose a different name for the cookie for each
# site.
# Default: "cookie"
MellonVariable "cookie"
MellonVariable "cookie"
# Whether the cookie set by auth_mellon should have HttpOnly and
# secure flags set. Once "On" - both flags will be set. Values
# "httponly" or "secure" will respectively set only one flag.
# MellonSecureCookie enforces the HttpOnly and secure flags
# for the mod_mellon cookie
# Default: Off
MellonSecureCookie On
@ -216,73 +196,25 @@ MellonDiagnosticsEnable Off
# Default: /
MellonCookiePath /
# MellonCookieSameSite allows control over the SameSite value used
# for the authentication cookie.
# The setting accepts values of "Strict" or "Lax"
# If not set, the SameSite attribute is not set on the cookie.
# Default: not set
# MellonCookieSameSite lax
# MellonUser selects which attribute we should use for the username.
# The username is passed on to other apache modules and to the web
# page the user visits. NAME_ID is an attribute which we set to
# the id we get from the IdP.
# Note: If MellonUser refers to a multi-valued attribute, any single
# value from that attribute may be used. Do not rely on it selecting a
# specific value.
# Default: MellonUser "NAME_ID"
MellonUser "NAME_ID"
MellonUser "NAME_ID"
# MellonIdP selects in which attribute we should dump the remote
# IdP entityId. This is passed to other apache modules and to
# IdP providerId. This is passed to other apache modules and to
# the web pages the user visits.
# Default: none
# MellonIdP "IDP"
# MellonIdP "IDP"
# MellonSetEnv configuration directives allows you to map
# attribute names received from the IdP to names you choose
# yourself. The syntax is 'MellonSetEnv <local name> <IdP name>'.
# You can list multiple MellonSetEnv directives.
# Default. None set.
MellonSetEnv "e-mail" "mail"
# MellonSetEnvNoPrefix is identical to MellonSetEnv, except this
# does not prepend 'MELLON_' to the constructed environment variable.
# The syntax is 'MellonSetEnvNoPrefix <local name> <IdP name>'.
# You can list multiple MellonSetEnvNoPrefix directives.
# Default. None set.
MellonSetEnvNoPrefix "DISPLAY_NAME" "displayName"
# MellonEnvPrefix changes the string the variables passed from the
# IdP are prefixed with.
# Default: MELLON_
MellonEnvPrefix "NOLLEM_"
# MellonMergeEnvVars merges multiple values of environment variables
# set using MellonSetEnv into single variable:
# ie: MYENV_VAR => val1;val2;val3 instead of default behaviour of:
# MYENV_VAR_0 => val1, MYENV_VAR_1 => val2 ... etc.
# Second optional parameter specifies the separator, to override the
# default semicolon.
# Default: MellonMergeEnvVars Off
MellonMergeEnvVars On
MellonMergeEnvVars On ":"
# MellonEnvVarsIndexStart specifies if environment variables for
# multi-valued attributes should start indexing from 0 or 1
# The syntax is 'MellonEnvVarsIndexStart <0|1>'.
# Default: MellonEnvVarsIndexStart 0
MellonEnvVarsIndexStart 1
# MellonEnvVarsSetCount specifies if number of values for any given
# attribute should also be stored in variable suffixed _N.
# ie: if user is member of two groups, the following will be set:
# MELLON__groups=group1
# MELLON__groups_0=group1
# MELLON__groups_1=group2
# MELLON__groups_N=2
# Default: MellonEnvVarsSetCount Off
MellonEnvVarsSetCount On
MellonSetEnv "e-mail" "mail"
# If MellonSessionDump is set, then the SAML session will be
# available in the MELLON_SESSION environment variable
@ -307,10 +239,10 @@ MellonDiagnosticsEnable Off
# attribute, the last overrides the previous ones.
#
# Default: None set.
MellonRequire "eduPersonAffiliation" "student" "employee"
MellonRequire "eduPersonAffiliation" "student" "employee"
# MellonCond provides the same function as MellonRequire, with
# extra functionality (MellonRequire is retained for backward
# extra functionnality (MellonRequire is retained for backward
# compatibility). The syntax is
# 'MellonCond <attribute name> <value> [<options>]'
#
@ -329,14 +261,14 @@ MellonDiagnosticsEnable Off
# are substituted.
# %{num} Same as %n, with num being a number that may be
# greater than 9.
# %{ENV:x} Substitute Apache environment variable x.
# %% Escape substitution to get a literal %.
# %{ENV:x} Substitute Apache environement variable x.
# %% Escape substitution to get a litteral %.
#
# <options> is an optional, comma-separated list of option
# enclosed with brackets. Here is an example: [NOT,NC]
# encloseed with brackets. Here is an example: [NOT,NC]
# The valid options are:
# OR If this MellonCond evaluated to false, then the
# next one will be checked. If it evaluates to true,
# OR If this MellonCond evaluted to false, then the
# next one will be checked. If it evalutes to true,
# then the overall check succeeds.
# NOT This MellonCond evaluates to true if the attribute
# does not match the value.
@ -357,9 +289,9 @@ MellonDiagnosticsEnable Off
# option has effect on a following MellonRequire directive.
#
# Default: none set
# MellonCond "mail" "@example\.net$" [OR,REG]
# MellonCond "mail" "@example\.com$" [OR,REG]
# MellonCond "uid" "superuser"
# MellonCond "mail" "@example\.net$" [OR,REG]
# MellonCond "mail" "@example\.com$" [OR,REG]
# MellonCond "uid" "superuser"
# MellonEndpointPath selects which directory mod_auth_mellon
# should assume contains the SAML 2.0 endpoints. Any request to
@ -369,7 +301,7 @@ MellonDiagnosticsEnable Off
# the directory. The directory must be a sub-directory of this
# <Location ...>.
# Default: MellonEndpointPath "/mellon"
MellonEndpointPath "/secret/endpoint"
MellonEndpointPath "/secret/endpoint"
# MellonDefaultLoginPath is the location where one should be
# redirected after an IdP-initiated login. Default is "/"
@ -387,7 +319,7 @@ MellonDiagnosticsEnable Off
# mod_auth_mellon will redirect the user to if he returns from the
# IdP without a cookie with a session id.
# Note that the user may also get this error if he for some reason
# loses the cookie between being redirected to the IdP's login page
# loses the cookie between being redirected to the IdPs login page
# and returning from it.
# If this option is unset, then mod_auth_mellon will return a
# 400 Bad Request error if the cookie is missing.
@ -395,17 +327,11 @@ MellonDiagnosticsEnable Off
MellonNoCookieErrorPage "https://example.com/no_cookie.html"
# MellonSPMetadataFile is the full path to the file containing
# the metadata for this service provider.
# If mod_auth_mellon was compiled against Lasso version 2.2.2
# or higher, this option is optional. Otherwise, it is mandatory.
# Default: None set.
# the metadata for this service provider.
# Default: if not set, metadata will be autogenerated
MellonSPMetadataFile /etc/apache2/mellon/sp-metadata.xml
# If you choose to autogenerate metadata, this option
# can be used to control the SP entityId
# MellonSPentityId "https://www.example.net/foo"
#
# If you choose to autogenerate metadata, these options
# If you choose to autogenerate metadata, these option
# can be used to fill the <Organization> element. They
# all follow the syntax "option [lang] value":
# MellonOrganizationName "random-service"
@ -417,9 +343,9 @@ MellonDiagnosticsEnable Off
# key of the service provider. The .pem-file cannot be encrypted
# with a password. If built with lasso-2.2.2 or higher, the
# private key only needs to be readable by root, otherwise it has
# to be readable by the Apache pseudo user.
# to be reasbable by the Apache pseudo user.
# Default: None set.
MellonSPPrivateKeyFile /etc/apache2/mellon/sp-private-key.pem
MellonSPPrivateKeyFile /etc/apache2/mellon/sp-private-key.pem
# MellonSPCertFile is a .pem file with the certificate for the
# service provider. This directive is optional.
@ -428,31 +354,31 @@ MellonDiagnosticsEnable Off
# MellonIdPMetadataFile is the full path to the file which contains
# metadata for the IdP you are authenticating against. This
# directive is required. Multiple IdP metadata can be configured
# directive is required. Mutliple IdP metadata can be configured
# by using multiple MellonIdPMetadataFile directives.
#
# If your lasso library is recent enough (higher than 2.3.5),
# then MellonIdPMetadataFile will accept an XML file containing
# descriptors for multiple IdP. An optional validating chain can
# be supplied as a second argument to MellonIdPMetadataFile. If
# omitted, no metadata validation will take place.
# ommitted, no metadata validation will take place.
#
# Default: None set.
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
# MellonIdPMetadataGlob is a glob(3) pattern enabled alternative
# to MellonIdPMetadataFile. Like MellonIdPMetadataFile it will
# accept an optional validating chain if lasso is recent enough.
#
# Default: None set.
#MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml
#MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml
# MellonIdpPublicKeyFile is the full path of the public key of the
# IdP. This parameter is optional if the public key is embedded
# in the IdP's metadata file, or if a certificate authority is
# used. This parameter cannot be used if multiple IdP are configured.
# Default: None set.
MellonIdPPublicKeyFile /etc/apache2/mellon/idp-public-key.pem
MellonIdPPublicKeyFile /etc/apache2/mellon/idp-public-key.pem
# MellonIdPCAFile is the full path to the certificate of the
# certificate authority. This can be used instead of an
@ -461,7 +387,7 @@ MellonDiagnosticsEnable Off
MellonIdPCAFile /etc/apache2/mellon/ca.pem
# MellonIdPIgnore lists IdP entityId that should not loaded
# from XML federation metadata files. This is useful if an
# from XML federation metadata files. This is usefull if an
# IdP cause bugs. Multiple entityId may be specified through
# single MellonIdPIgnore, and multiple MellonIdPIgnore are allowed.
# Default: None set.
@ -469,16 +395,16 @@ MellonDiagnosticsEnable Off
# MellonDiscoveryURL is the URL for IdP discovery service.
# This is used for selecting among multiple configured IdP.
# On initial user authentication, it is redirected to the
# On initiali user authentication, it is redirected to the
# IdP discovery URL, with the following arguments set:
#
# entityID SP entityId URL, where our metadata
# are published.
# returnIDParam Argument that IdP discovery must send back.
# entityID SP providerID URL, where our metadata
# are pubblished.
# retueniDParam Argument that IdP discovery must send back.
# return Return URL the IdP discovery should return to.
#
# The IdP discovery must redirect the user to the return URL,
# with returnIDParam set to the selected IdP entityId.
# with retueniDParam set to the selected IdP providerID.
#
# The builtin:get-metadata discovery URL is not supported anymore
# starting with 0.3.1. See MellonProbeDiscoveryTimeout for
@ -503,9 +429,6 @@ MellonDiagnosticsEnable Off
# MellonProbeDiscoveryIdP can be used to restrict the
# list of IdP queried by the IdP probe discovery service.
# If probe discovery fails and this is provided, an
# HTTP error 500 is returned, instead of proceeding
# with first available IdP.
#
# Default unset, which means that all configured IdP are
# queried.
@ -513,28 +436,24 @@ MellonDiagnosticsEnable Off
# MellonProbeDiscoveryIdP http://idp2.example.net/saml/metadata
# This option will make the SAML authentication assertion
# available in the MELLON_SAML_RESPONSE environment
# available in the MELLON_SAML_RESPONSE environement
# variable. This assertion holds a verifiable signature
# that can be checked again. Default is Off.
MellonSamlResponseDump Off
MellonSamlResponseDump Off
# This option will make the Lasso session available in
# the MELLON_SESSION environment variable. Default is Off.
MellonSessionDump Off
# the MELLON_SESSION environement variable. Default is Off.
MellonSessionDump Off
# This option will request specific authentication security-level
# through the AuthnContextClassRef element of the AuthnRequest It will
# also request enforcement of this level when receiving an
# authenticating Assertion.
# If the assertion does not have the required security level, an HTTP
# Forbidden status code is returned to the browser.
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos"
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:SoftwarePKI"
# This option will set the "Comparsion" attribute within the AuthnRequest
# It could be set to "exact", "minimum", "maximum" or "better"
# MellonAuthnContextComparisonType "minimum"
# This option will request specific authentication security-level
# through the AuthnContextClassRef element of the AuthnRequest It will
# also request enforcement of this level when receiving an
# authenticating Assertion.
# If the assertion does not have the required security level, an HTTP
# Forbidden status code is returned to the browser.
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos"
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:SoftwarePKI"
# MellonSubjectConfirmationDataAddressCheck is used to control
# the checking of client IP address against the address returned by the
@ -542,72 +461,12 @@ MellonDiagnosticsEnable Off
# behind a reverse proxy or any kind of strange network topology making IP address of client
# different for the IdP and the SP. Default is on.
# MellonSubjectConfirmationDataAddressCheck On
# Does not check signature on logout messages exchanges with idp1
# MellonDoNotVerifyLogoutSignature http://idp1.example.com/saml/metadata
# Whether to enable replay of POST requests after authentication. When this option is
# enabled, POST requests that trigger authentication will be saved until the
# authentication is completed, and then replayed. If this option isn't enabled,
# the requests will be turned into normal GET requests after authentication.
#
# Note that if this option is enabled, you must also
# set the MellonPostDirectory option in the server configuration.
#
# The default is that it is "Off".
# MellonPostReplay Off
# Page to redirect to if the IdP sends an error in response to
# the authentication request.
#
# Example:
# MellonNoSuccessErrorPage https://sp.example.org/login_failed.html
#
# The default is to not redirect, but rather send a
# 401 Unautorized error.
# This option controls whether to include a list of IDP's when
# sending an ECP PAOS <AuthnRequest> message to an ECP client.
MellonECPSendIDPList Off
# This option controls whether the Cache-control header is sent
# back in responses.
# Default: On
# MellonSendCacheControlHeader Off
# List of domains that we allow redirects to.
# The special name "[self]" means the domain of the current request.
# The domain names can also use wildcards.
#
# Example:
# * Allow redirects to example.com and all subdomains:
# MellonRedirectDomains example.com *.example.com
# * Allow redirects to the host running mod_auth_mellon, as well as the
# web page at www.example.com:
# MellonRedirectDomains [self] www.example.com
# * Allow redirects to all domains:
# MellonRedirectDomains *
#
# Default:
# MellonRedirectDomains [self]
MellonRedirectDomains [self]
# This option controls the signature method used to sign SAML
# messages generated by Mellon, it may be one of the following
# (depending if feature was supported when Mellon was built):
#
# rsa-sha1
# rsa-sha256
# rsa-sha384
# rsa-sha512
#
# Default: rsa-sha256
# MellonSignatureMethod
</Location>
```
## Service provider metadata
===========================================================================
Service provider metadata
===========================================================================
The contents of the metadata will depend on your hostname and on what path
you selected with the MellonEndpointPath configuration directive. You can
@ -616,10 +475,9 @@ can just let it be autogenerated.
The following is an example of metadata for the example configuration:
```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<EntityDescriptor
entityID="https://example.com"
entityID="examlpe.com"
xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
<SPSSODescriptor
AuthnRequestsSigned="false"
@ -638,27 +496,26 @@ The following is an example of metadata for the example configuration:
Location="https://example.com/secret/endpoint/postResponse" />
</SPSSODescriptor>
</EntityDescriptor>
```
You should update `entityID="https://example.com"` and the two Location
attributes. Note that '/secret/endpoint' in the two Location attributes matches
the path set in MellonEndpointPath.
To use the HTTP-Artifact binding instead of the HTTP-POST binding, change
You should update entityID="example.com" and the two Location attributes.
Note that '/secret/endpoint' in the two Location attributes matches the
path set in MellonEndpointPath.
To use HTTP-Artifact binding instead of the HTTP-POST binding, change
the AssertionConsumerService-element to something like this:
```xml
<AssertionConsumerService
index="0"
isDefault="true"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="https://example.com/secret/endpoint/artifactResponse" />
```
The metadata are published at the 'endpoint/metadata' URL.
## Using mod_auth_mellon
===========================================================================
Using mod_auth_mellon
===========================================================================
After you have set up mod_auth_mellon, you should be able to visit (in our
example) https://example.com/secret/, and be redirected to the IdP's login
@ -667,35 +524,29 @@ https://example.com/secret/, and get the contents of that page.
When authenticating a user, mod_auth_mellon will set some environment
variables to the attributes it received from the IdP. The name of the
variables will be `MELLON_<attribute name>`. If you have specified a
different name with the MellonSetEnv or MellonSetEnvNoPrefix configuration
directive, then that name will be used instead. In the case of MellonSetEnv,
the name will still be prefixed by `MELLON_`.
variables will be MELLON_<attribute name>. If you have specified a
different name with the MellonSetEnv configuration directive, then that
name will be used instead. The name will still be prefixed by 'MELLON_'.
The value of the attribute will be base64 decoded.
mod_auth_mellon supports multivalued attributes with the following format:
<base64 encoded value>_<base64 encoded value>_<base 64 encoded value>...
If an attribute has multiple values, then they will be stored as
`MELLON_<name>_0`, `MELLON_<name>_1`, `MELLON_<name>_2`, ...
MELLON_<name>_0, MELLON_<name>_1, MELLON_<name>_2, ...
Since mod_auth_mellon doesn't know which attributes may have multiple
values, it will store every attribute at least twice: once named
`MELLON_<name>`, and once named `<MELLON_<name>_0`.
values, it will store every attribute at least twice. Once named
MELLON_<name>, and once named <MELLON_<name>_0.
In the case of multivalued attributes, `MELLON_<name>` will contain the first
In the case of multivalued attributes MELLON_<name> will contain the first
value.
NOTE:
If MellonMergeEnvVars is set to On, multiple values of attributes
will be stored in single environment variable, separated by semicolons:
```
MELLON_<name>="value1;value2;value3[;valueX]"
```
and variables `MELLON_<name>_0`, `MELLON_<name>_1`, `MELLON_<name>_2`, ...
will not be created.
The following code is a simple PHP script which prints out all the
The following code is a simple php-script which prints out all the
variables:
```php
<?php
header('Content-Type: text/plain');
@ -705,25 +556,26 @@ foreach($_SERVER as $key=>$value) {
}
}
?>
```
## Manual login
===========================================================================
Manual login
===========================================================================
It is possible to manually trigger login operations. This can be done by
accessing "<endpoint path>/login". That endpoint accepts three parameters:
- `ReturnTo`: A mandatory parameter which contains the URL we should return
- ReturnTo: A mandatory parameter which contains the URL we should return
to after login.
- `IdP`: The entity ID of the IdP we should send a login request to. This
- IdP: The entity ID of the IdP we should send a login request to. This
parameter is optional.
- `IsPassive`: This parameter can be set to "true" to send a passive
- IsPassive: This parameter can be set to "true" to send a passive
authentication request to the IdP.
===========================================================================
Logging out
===========================================================================
## Logging out
mod_auth_mellon supports both IdP-initiated and SP-initiated logout
mod_auth_mellon supports both IdP initiated and SP initiated logout
through the same endpoint. The endpoint is located at
"<endpoint path>/logout". "<endpoint path>/logoutRequest" is an alias for
this endpoint, provided for compatibility with version 0.0.6 and earlier of
@ -736,27 +588,28 @@ located at "https://www.example.com/secret", and the mellon endpoints are
located under "https://www.example.com/secret/endpoint", then the web site
could contain a link element like the following:
```html
<a href="/secret/endpoint/logout?ReturnTo=https://www.example.org/logged_out.html">Log out</a>
```
<a href=
"/secret/endpoint/logout?ReturnTo=https://www.example.org/logged_out.html"
>Log out</a>
This will return the user to "https://www.example.org/logged_out.html"
after the logout operation has completed.
## Probe IdP discovery
===========================================================================
Probe IdP discovery
===========================================================================
mod_auth_mellon has an IdP probe discovery service that sends HTTP GET
to IdP and picks the first that answers. This can be used as a poor
man's failover setup that redirects to your organisation internal IdP.
Here is a sample configuration:
```ApacheConf
MellonEndpointPath "/saml"
(...)
MellonDiscoveryUrl "/saml/probeDisco"
MellonProbeDiscoveryTimeout 1
```
The SP will send an HTTP GET to each configured IdP entityId URL until
MellonEndpointPath "/saml"
(...)
MellonDiscoveryUrl "/saml/probeDisco"
MellonProbeDiscoveryTimeout 1
The SP will sends HTTP GET to each configured IdP providerId URL until
it gets an HTTP 200 response within the 1 second timeout. It will then
proceed with that IdP.
@ -768,151 +621,21 @@ you will want to configure external IdP in mod_auth_mellon, but not
use them for IdP probe discovery. The MellonProbeDiscoveryIdP
directive can be used to limit the usable IdP for probe discovery:
```ApacheConf
MellonEndpointPath "/saml"
(...)
MellonDiscoveryUrl "/saml/probeDisco"
MellonProbeDiscoveryTimeout 1
MellonProbeDiscoveryIdP "https://idp1.example.net/saml/metadata"
MellonProbeDiscoveryIdP "https://idp2.example.net/saml/metadata"
```
MellonEndpointPath "/saml"
(...)
MellonDiscoveryUrl "/saml/probeDisco"
MellonProbeDiscoveryTimeout 1
MellonProbeDiscoveryIdP "https://idp1.example.net/saml/metadata"
MellonProbeDiscoveryIdP "https://idp2.example.net/saml/metadata"
===========================================================================
Contributors
===========================================================================
## Replaying POST requests
By default, POST requests received when the user isn't logged in are turned
into GET requests after authentication. mod_auth_mellon can instead save
the received POST request and replay/repost it after authentication. To
enable this:
1. Create a data directory where mod_auth_mellon can store the saved data:
mkdir /var/cache/mod_auth_mellon_postdata
2. Set the appropriate permissions on this directory. It needs to be
accessible for the web server, but nobody else.
chown www-data /var/cache/mod_auth_mellon_postdata
chgrp www-data /var/cache/mod_auth_mellon_postdata
chmod 0700 /var/cache/mod_auth_mellon_postdata
3. Set the MellonPostDirectory option in your server configuration:
MellonPostDirectory "/var/cache/mod_auth_mellon_postdata"
4. Enable POST replay functionality for the locations you want:
<Location /secret>
MellonEnable auth
[...]
MellonPostReplay On
</Location>
After you restart Apache to activate the new configuration, any POST
requests that trigger authentication should now be stored while the
user logs in.
## Example to support both SAML and Basic Auth
The below snippet will allow for preemptive basic auth (such as from a REST client)
for the "/auth" path, but if accessed interactively will trigger SAML auth with
mod_auth_mellon.
```ApacheConf
<Location />
MellonEnable "info"
MellonVariable "cookie"
MellonEndpointPath "/sso"
Mellon... # Other parameters as needed
</Location>
<Location /auth>
<If "-n req('Authorization')">
AuthName "My Auth"
AuthBasicProvider ldap
AuthType basic
AuthLDAP* # Other basic auth config parms as needed
require ldap-group ....
</If>
<Else>
Require valid-user
AuthType "Mellon"
MellonEnable "auth"
</Else>
</Location>
```
## Mellon & User Agent Caching behavior
For each content within an Apache Location enabled with "info" or "auth",
mod_auth_mellon sends by default the HTTP/1.1 Cache-Control header with value
`private, must-revalidate`:
- `private` protects content against caching by any proxy servers.
- `must-revalidate` obligates the user agent to revalidate maybe locally
cached or stored content each time on accessing location.
This default behavior ensures that the user agent never shows cached static
HTML pages after logout without revalidating. So the user can't be
misled about a malfunction of the logout procedure. Revalidating content after
logout leads to a new authentication procedure via mellon.
But mod_auth_mellon will never prohibit specifically any user agent from
caching or storing content locally, that has to be revalidated. So that during
the session, a user agent only revalidates data by the server `304 Not Modified`
response, and does not have to download content again.
For special content types like images it could make sense to disable
revalidation completely, so that user agents can provide cached and stored
content directly to the user. This can be achieved by using other Apache
modules mod_headers and mod_setenvif. E.g. for PNG images:
* Using Apache 2.2 configuration options:
SetEnvIf Request_URI "\.png$" DISABLE_REVALIDATION
Header always unset Cache-Control env=DISABLE_REVALIDATION
* In Apache 2.4, there's a shorter notation:
Header always unset Cache-Control expr=%{CONTENT_TYPE}==image/png
Editing, appending, and overwriting headers is possible in other cases.
## Support
There's a mailing list for discussion and support.
* To subscribe: https://sympa.uninett.no/lists/uninett.no/subscribe/modmellon
* List archives: https://sympa.uninett.no/lists/uninett.no/arc/modmellon
## Reporting security vulnerabilities
For reporting security vulnerabilities in mod_auth_mellon, please contact
the maintainer directly at the following email address:
olav.morken@uninett.no
This allows us to coordinate the disclosure of the vulnerability with the
fixes for the vulnerability.
## Contributors
Thanks to [Emmanuel Dreyfus](mailto:manu@netbsd.org) for many new features,
Thanks to Emmanuel Dreyfus <manu@netbsd.org> for many new features,
including:
- Metadata autogeneration support.
- Support for multiple IdPs.
- IdP discovery service support.
- SOAP logout support.
[Benjamin Dauvergne](mailto:bdauvergne@entrouvert.com) has contributed many
patches, both with bugfixes and new features:
- Cookie settings, for specifying domain and path of cookie.
- Support for SAML 2.0 authentication contexts.
- Support for running behind a reverse proxy.
- Logout improvements, including support for local logout.

4
TODO Normal file
View File

@ -0,0 +1,4 @@
TODO for auth_mellon:
* Change session storage to use less than 64KiB/session.
* Optimize session lookup.

View File

@ -1,7 +1,7 @@
/*
*
* auth_mellon.h: an authentication apache module
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,11 +22,6 @@
#ifndef MOD_AUTH_MELLON_H
#define MOD_AUTH_MELLON_H
#include "config.h"
#include <stdbool.h>
#include <strings.h>
#include <lasso/lasso.h>
#include <lasso/xml/saml-2.0/samlp2_authn_request.h>
#include <lasso/xml/saml-2.0/samlp2_logout_request.h>
@ -38,7 +33,6 @@
#include <lasso/xml/saml-2.0/saml2_authn_statement.h>
#include <lasso/xml/saml-2.0/saml2_audience_restriction.h>
#include <lasso/xml/misc_text_node.h>
#include "lasso_compat.h"
/* The following are redefined in ap_config_auto.h */
#undef PACKAGE_BUGREPORT
@ -70,38 +64,22 @@
#include "mod_ssl.h"
/* Backwards-compatibility helpers. */
#include "auth_mellon_compat.h"
/* Size definitions for the session cache.
*/
#define AM_CACHE_KEYSIZE 120
#define AM_CACHE_ENVSIZE 2048
#define AM_CACHE_VARSIZE 128
#define AM_CACHE_VALSIZE 512-AM_CACHE_VARSIZE
#define AM_CACHE_ENVSIZE 128
#define AM_CACHE_USERSIZE 512
#define AM_CACHE_DEFAULT_ENTRY_SIZE 196608
#define AM_CACHE_MIN_ENTRY_SIZE 65536
/* Internal error codes */
#define AM_ERROR_INVALID_PAOS_HEADER 1
#define AM_ERROR_MISSING_PAOS_HEADER 2
#define AM_ERROR_MISSING_PAOS_MEDIA_TYPE 3
#define AM_CACHE_MAX_LASSO_IDENTITY_SIZE 1024
#define AM_CACHE_MAX_LASSO_SESSION_SIZE 32768
#define AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE 65536
#ifdef ENABLE_DIAGNOSTICS
typedef enum {
AM_DIAG_FLAG_ENABLED = (1 << 0),
AM_DIAG_FLAG_DISABLE = 0,
AM_DIAG_FLAG_ENABLE_ALL = ~0,
} am_diag_flags_t;
#endif
/* This is the length of the id we use (for session IDs and
* replaying POST data).
/* This is the length of the session id we use.
*/
#define AM_ID_LENGTH 32
#define AM_SESSION_ID_LENGTH 32
#define MEDIA_TYPE_PAOS "application/vnd.paos+xml"
#define am_get_srv_cfg(s) (am_srv_cfg_rec *)ap_get_module_config((s)->module_config, &auth_mellon_module)
@ -109,11 +87,6 @@ typedef enum {
#define am_get_dir_cfg(r) (am_dir_cfg_rec *)ap_get_module_config((r)->per_dir_config, &auth_mellon_module)
#define am_get_req_cfg(r) (am_req_cfg_rec *)ap_get_module_config((r)->request_config, &auth_mellon_module)
#ifdef ENABLE_DIAGNOSTICS
#define am_get_diag_cfg(s) (&(am_get_srv_cfg((s)))->diag_cfg)
#endif
typedef struct am_mod_cfg_rec {
int cache_size;
@ -123,35 +96,20 @@ typedef struct am_mod_cfg_rec {
int post_count;
apr_size_t post_size;
int entry_size;
/* These variables can't be allowed to change after the session store
* has been initialized. Therefore we copy them before initializing
* the session store.
*/
int init_cache_size;
const char *init_lock_file;
apr_size_t init_entry_size;
apr_shm_t *cache;
apr_global_mutex_t *lock;
} am_mod_cfg_rec;
#ifdef ENABLE_DIAGNOSTICS
typedef struct am_diag_cfg_rec {
const char *filename;
apr_file_t *fd;
am_diag_flags_t flags;
apr_table_t *dir_cfg_emitted;
} am_diag_cfg_rec;
#endif
typedef struct am_srv_cfg_rec {
am_mod_cfg_rec *mc;
#ifdef ENABLE_DIAGNOSTICS
am_diag_cfg_rec diag_cfg;
#endif
} am_srv_cfg_rec;
typedef enum {
@ -162,10 +120,10 @@ typedef enum {
} am_enable_t;
typedef enum {
am_samesite_default,
am_samesite_lax,
am_samesite_strict
} am_samesite_t;
am_decoder_default,
am_decoder_none,
am_decoder_feide,
} am_decoder_t;
typedef enum {
AM_COND_FLAG_NULL = 0x000, /* No flags */
@ -185,30 +143,6 @@ typedef enum {
extern const char *am_cond_options[];
/*
* am_file_data_t is used to maintain information about a file:
*
* * The filesystem pathname
* * Stat information about the file (e.g. type, size, times, etc.)
* * If and when the file was stat'ed or read
* * Error code of failed operation and error string description
* * Contents of the file
* * Flag indicating if contents were generated instead of being read
* from a file.
*/
typedef struct am_file_data_t {
apr_pool_t *pool; /* allocation pool */
const char *path; /* filesystem pathname, NULL for generated file */
apr_time_t stat_time; /* when stat was performed, zero indicates never */
apr_finfo_t finfo; /* stat data */
char *contents; /* file contents */
apr_time_t read_time; /* when contents was read, zero indicates never */
apr_status_t rv; /* most recent result value */
const char *strerror; /* if rv is error then this is error description */
bool generated; /* true if contents generated instead of being
read from path */
} am_file_data_t;
typedef struct {
const char *varname;
int flags;
@ -218,8 +152,8 @@ typedef struct {
} am_cond_t;
typedef struct am_metadata {
am_file_data_t *metadata; /* Metadata file with one or many IdP */
am_file_data_t *chain; /* Validating chain */
const char *file; /* Metadata file with one or many IdP */
const char *chain; /* Validating chain */
} am_metadata_t;
typedef struct am_dir_cfg_rec {
@ -227,21 +161,19 @@ typedef struct am_dir_cfg_rec {
*/
am_enable_t enable_mellon;
/* The decoder attribute is used to specify which decoder we should use
* when parsing attributes.
*/
am_decoder_t decoder;
const char *varname;
int secure;
int http_only;
const char *merge_env_vars;
int env_vars_index_start;
int env_vars_count_in_n;
const char *cookie_domain;
const char *cookie_path;
am_samesite_t cookie_samesite;
apr_array_header_t *cond;
apr_hash_t *envattr;
const char *env_prefix;
const char *userattr;
const char *idpattr;
LassoSignatureMethod signature_method;
int dump_session;
int dump_saml_response;
@ -253,16 +185,15 @@ typedef struct am_dir_cfg_rec {
const char *endpoint_path;
/* Lasso configuration variables. */
am_file_data_t *sp_metadata_file;
am_file_data_t *sp_private_key_file;
am_file_data_t *sp_cert_file;
const char *sp_metadata_file;
const char *sp_private_key_file;
const char *sp_cert_file;
apr_array_header_t *idp_metadata;
am_file_data_t *idp_public_key_file;
am_file_data_t *idp_ca_file;
const char *idp_public_key_file;
const char *idp_ca_file;
GList *idp_ignore;
/* metadata autogeneration helper */
char *sp_entity_id;
apr_hash_t *sp_org_name;
apr_hash_t *sp_org_display_name;
apr_hash_t *sp_org_url;
@ -273,9 +204,6 @@ typedef struct am_dir_cfg_rec {
/* No cookie error page. */
const char *no_cookie_error_page;
/* Authorization error page. */
const char *no_success_error_page;
/* Login path for IdP initiated logins */
const char *login_path;
@ -291,81 +219,37 @@ typedef struct am_dir_cfg_rec {
/* AuthnContextClassRef list */
apr_array_header_t *authn_context_class_ref;
/* AuthnContextComparisonType */
const char *authn_context_comparison_type;
/* Controls the checking of SubjectConfirmationData.Address attribute */
int subject_confirmation_data_address_check;
/* MellonDoNotVerifyLogoutSignature idp set */
/* AuthnContextClassRef list */
apr_hash_t *do_not_verify_logout_signature;
/* Controls whether the cache control header is set */
int send_cache_control_header;
/* Whether we should replay POST data after authentication. */
int post_replay;
/* Cached lasso server object. */
LassoServer *server;
/* Whether to send an ECP client a list of IdP's */
int ecp_send_idplist;
/* List of domains we can redirect to. */
const char * const *redirect_domains;
} am_dir_cfg_rec;
/* Bitmask for PAOS service options */
typedef enum {
ECP_SERVICE_OPTION_CHANNEL_BINDING = 1,
ECP_SERVICE_OPTION_HOLDER_OF_KEY = 2,
ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED = 4,
ECP_SERVICE_OPTION_DELEGATION = 8,
} ECPServiceOptions;
typedef struct am_req_cfg_rec {
char *cookie_value;
#ifdef HAVE_ECP
bool ecp_authn_req;
ECPServiceOptions ecp_service_options;
#endif /* HAVE_ECP */
#ifdef ENABLE_DIAGNOSTICS
bool diag_emitted;
#endif
} am_req_cfg_rec;
typedef struct am_cache_storage_t {
apr_size_t ptr;
} am_cache_storage_t;
typedef struct am_cache_env_t {
am_cache_storage_t varname;
am_cache_storage_t value;
char varname[AM_CACHE_VARSIZE];
char value[AM_CACHE_VALSIZE];
} am_cache_env_t;
typedef struct am_cache_entry_t {
char key[AM_CACHE_KEYSIZE];
am_cache_storage_t cookie_token;
apr_time_t access;
apr_time_t expires;
int logged_in;
unsigned short size;
am_cache_storage_t user;
char user[AM_CACHE_USERSIZE];
/* Variables used to store lasso state between login requests
*and logout requests.
*/
am_cache_storage_t lasso_identity;
am_cache_storage_t lasso_session;
am_cache_storage_t lasso_saml_response;
char lasso_identity[AM_CACHE_MAX_LASSO_IDENTITY_SIZE];
char lasso_session[AM_CACHE_MAX_LASSO_SESSION_SIZE];
char lasso_saml_response[AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE];
am_cache_env_t env[AM_CACHE_ENVSIZE];
apr_size_t pool_size;
apr_size_t pool_used;
char pool[];
} am_cache_entry_t;
typedef enum {
@ -373,23 +257,8 @@ typedef enum {
AM_CACHE_NAMEID
} am_cache_key_t;
/* Type for configuring environment variable names */
typedef struct am_envattr_conf_t {
// Name of the variable
const char *name;
// Should a prefix be added
int prefixed;
} am_envattr_conf_t;
extern const command_rec auth_mellon_commands[];
typedef struct am_error_map_t {
int lasso_error;
int http_error;
} am_error_map_t;
extern const am_error_map_t auth_mellon_errormap[];
/* When using a value from a directory configuration structure, a special value is used
* to state "inherit" from parent, when reading a value and the value is still inherit from, it
* means that no value has ever been set for this directive, in this case, we use the default
@ -409,58 +278,30 @@ extern const am_error_map_t auth_mellon_errormap[];
static const int default_subject_confirmation_data_address_check = 1;
static const int inherit_subject_confirmation_data_address_check = -1;
/** Default values for seting the cache-control header
*/
static const int default_send_cache_control_header = 1;
static const int inherit_send_cache_control_header = -1;
/* Default and inherit values for MellonPostReplay option. */
static const int default_post_replay = 0;
static const int inherit_post_replay = -1;
/* Whether to send an ECP client a list of IdP's */
static const int default_ecp_send_idplist = 0;
static const int inherit_ecp_send_idplist = -1;
/* Algorithm to use when signing Mellon SAML messages */
static const LassoSignatureMethod default_signature_method =
#if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256
LASSO_SIGNATURE_METHOD_RSA_SHA256;
#else
LASSO_SIGNATURE_METHOD_RSA_SHA1;
#endif
static const int inherit_signature_method = -1;
void *auth_mellon_dir_config(apr_pool_t *p, char *d);
void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add);
void *auth_mellon_server_config(apr_pool_t *p, server_rec *s);
void *auth_mellon_srv_merge(apr_pool_t *p, void *base, void *add);
const char *am_cookie_get(request_rec *r);
void am_cookie_set(request_rec *r, const char *id);
void am_cookie_delete(request_rec *r);
const char *am_cookie_token(request_rec *r);
void am_cache_init(am_mod_cfg_rec *mod_cfg);
am_cache_entry_t *am_cache_lock(request_rec *r,
am_cache_entry_t *am_cache_lock(server_rec *s,
am_cache_key_t type, const char *key);
const char *am_cache_entry_get_string(am_cache_entry_t *e,
am_cache_storage_t *slot);
am_cache_entry_t *am_cache_new(request_rec *r,
const char *key,
const char *cookie_token);
void am_cache_unlock(request_rec *r, am_cache_entry_t *entry);
am_cache_entry_t *am_cache_new(server_rec *s, const char *key);
void am_cache_unlock(server_rec *s, am_cache_entry_t *entry);
void am_cache_update_expires(request_rec *r, am_cache_entry_t *t, apr_time_t expires);
void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires);
void am_cache_env_populate(request_rec *r, am_cache_entry_t *session);
int am_cache_env_append(am_cache_entry_t *session,
const char *var, const char *val);
const char *am_cache_env_fetch_first(am_cache_entry_t *t,
const char *var);
void am_cache_delete(request_rec *r, am_cache_entry_t *session);
void am_cache_delete(server_rec *s, am_cache_entry_t *session);
int am_cache_set_lasso_state(am_cache_entry_t *session,
const char *lasso_identity,
@ -479,9 +320,8 @@ void am_delete_request_session(request_rec *r, am_cache_entry_t *session);
char *am_reconstruct_url(request_rec *r);
int am_validate_redirect_url(request_rec *r, const char *url);
int am_check_permissions(request_rec *r, am_cache_entry_t *session);
void am_set_cache_control_headers(request_rec *r);
void am_set_nocache(request_rec *r);
int am_read_post_data(request_rec *r, char **data, apr_size_t *length);
char *am_extract_query_parameter(apr_pool_t *pool,
const char *query_string,
@ -489,12 +329,8 @@ char *am_extract_query_parameter(apr_pool_t *pool,
char *am_urlencode(apr_pool_t *pool, const char *str);
int am_urldecode(char *data);
int am_check_url(request_rec *r, const char *url);
char *am_generate_id(request_rec *r);
am_file_data_t *am_file_data_new(apr_pool_t *pool, const char *path);
am_file_data_t *am_file_data_copy(apr_pool_t *pool,
am_file_data_t *src_file_data);
apr_status_t am_file_read(am_file_data_t *file_data);
apr_status_t am_file_stat(am_file_data_t *file_data);
char *am_generate_session_id(request_rec *r);
char *am_getfile(apr_pool_t *conf, server_rec *s, const char *file);
char *am_get_endpoint_url(request_rec *r);
int am_postdir_cleanup(request_rec *s);
char *am_htmlencode(request_rec *r, const char *str);
@ -512,21 +348,7 @@ const char *am_get_mime_header(request_rec *r, const char *m, const char *h);
const char *am_get_mime_body(request_rec *r, const char *mime);
char *am_get_service_url(request_rec *r,
LassoProfile *profile, char *service_name);
bool am_parse_paos_header(request_rec *r, const char *header, ECPServiceOptions *options_return);
bool am_header_has_media_type(request_rec *r, const char *header,
const char *media_type);
const char *am_get_config_langstring(apr_hash_t *h, const char *lang);
int am_get_boolean_query_parameter(request_rec *r, const char *name,
int *return_value, int default_value);
char *am_get_assertion_consumer_service_by_binding(LassoProvider *provider, const char *binding);
#ifdef HAVE_ECP
char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options);
bool am_is_paos_request(request_rec *r, int *error_code);
#endif /* HAVE_ECP */
char *
am_saml_response_status_str(request_rec *r, LassoNode *node);
int am_auth_mellon_user(request_rec *r);
int am_check_uid(request_rec *r);
@ -535,7 +357,7 @@ int am_handler(request_rec *r);
int am_httpclient_get(request_rec *r, const char *uri,
void **buffer, apr_size_t *size,
int timeout, long *status);
apr_time_t timeout, long *status);
int am_httpclient_post(request_rec *r, const char *uri,
const void *post_data, apr_size_t post_length,
const char *content_type,
@ -548,95 +370,4 @@ int am_httpclient_post_str(request_rec *r, const char *uri,
extern module AP_MODULE_DECLARE_DATA auth_mellon_module;
#ifdef ENABLE_DIAGNOSTICS
#if AP_SERVER_MAJORVERSION_NUMBER < 2 || \
(AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 4)
#error "Diagnostics requires Apache version 2.4 or newer."
#endif
/* Initializing an apr_time_t to 0x7fffffffffffffffLL yields an
* iso 8601 time with 1 second precision of "294247-01-10T04:00:54Z"
* this is 22 characters, +1 for null terminator. */
#define ISO_8601_BUF_SIZE 23
typedef struct {
bool req_headers_written;
} am_diag_request_data;
const char *
am_diag_cache_key_type_str(am_cache_key_t key_type);
const char *
am_diag_cond_str(request_rec *r, const am_cond_t *cond);
int
am_diag_finalize_request(request_rec *r);
const char *
am_diag_lasso_http_method_str(LassoHttpMethod http_method);
void
am_diag_log_cache_entry(request_rec *r, int level, am_cache_entry_t *entry,
const char *fmt, ...)
__attribute__((format(printf,4,5)));
void
am_diag_log_file_data(request_rec *r, int level, am_file_data_t *file_data,
const char *fmt, ...)
__attribute__((format(printf,4,5)));
int
am_diag_log_init(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s);
void
am_diag_log_lasso_node(request_rec *r, int level, LassoNode *node,
const char *fmt, ...)
__attribute__((format(printf,4,5)));
void
am_diag_log_saml_status_response(request_rec *r, int level, LassoNode *node,
const char *fmt, ...)
__attribute__((format(printf,4,5)));
void
am_diag_log_profile(request_rec *r, int level, LassoProfile *profile,
const char *fmt, ...)
__attribute__((format(printf,4,5)));
void
am_diag_printf(request_rec *r, const char *fmt, ...)
__attribute__((format(printf,2,3)));
void
am_diag_rerror(const char *file, int line, int module_index,
int level, apr_status_t status,
request_rec *r, const char *fmt, ...);
char *
am_diag_time_t_to_8601(request_rec *r, apr_time_t t);
/* Define AM_LOG_RERROR log to both the Apache log and diagnostics log */
#define AM_LOG_RERROR(...) AM_LOG_RERROR__(__VA_ARGS__)
/* need additional step to expand macros */
#define AM_LOG_RERROR__(file, line, mi, level, status, r, ...) \
{ \
ap_log_rerror(file, line, mi, level, status, r, __VA_ARGS__); \
am_diag_rerror(file, line, mi, level, status, r, __VA_ARGS__); \
}
#else /* ENABLE_DIAGNOSTICS */
#define am_diag_log_cache_entry(...) do {} while(0)
#define am_diag_log_file_data(...) do {} while(0)
#define am_diag_log_lasso_node(...) do {} while(0)
#define am_diag_log_saml_status_response(...) do {} while(0)
#define am_diag_log_profile(...) do {} while(0)
#define am_diag_printf(...) do {} while(0)
/* Define AM_LOG_RERROR log only to the Apache log */
#define AM_LOG_RERROR(...) ap_log_rerror(__VA_ARGS__)
#endif /* ENABLE_DIAGNOSTICS */
#endif /* MOD_AUTH_MELLON_H */

View File

@ -21,69 +21,26 @@
#include "auth_mellon.h"
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#endif
/* Calculate the pointer to a cache entry.
*
* Parameters:
* am_mod_cfg_rec *mod_cfg The module configuration.
* void *table The base pointer for the table.
* apr_size_t index The index we are looking for.
*
* Returns:
* The session entry with the given index.
*/
static inline am_cache_entry_t *am_cache_entry_ptr(am_mod_cfg_rec *mod_cfg,
void *table, apr_size_t index)
{
uint8_t *table_calc;
table_calc = table;
return (am_cache_entry_t *)&table_calc[mod_cfg->init_entry_size * index];
}
/* Initialize the session table.
*
* Parameters:
* am_mod_cfg_rec *mod_cfg The module configuration.
*
* Returns:
* Nothing.
*/
void am_cache_init(am_mod_cfg_rec *mod_cfg)
{
void *table;
apr_size_t i;
/* Initialize the session table. */
table = apr_shm_baseaddr_get(mod_cfg->cache);
for (i = 0; i < mod_cfg->init_cache_size; i++) {
am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i);
e->key[0] = '\0';
e->access = 0;
}
}
/* This function locks the session table and locates a session entry.
* Unlocks the table and returns NULL if the entry wasn't found.
* If a entry was found, then you _must_ unlock it with am_cache_unlock
* after you are done with it.
*
* Parameters:
* request_rec *r The request we are processing.
* am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID
* server_rec *s The current server.
* const char *key The session key or user
* am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID
*
* Returns:
* The session entry on success or NULL on failure.
*/
am_cache_entry_t *am_cache_lock(request_rec *r,
am_cache_entry_t *am_cache_lock(server_rec *s,
am_cache_key_t type,
const char *key)
{
am_mod_cfg_rec *mod_cfg;
void *table;
apr_size_t i;
am_cache_entry_t *table;
int i;
int rv;
char buffer[512];
@ -94,24 +51,26 @@ am_cache_entry_t *am_cache_lock(request_rec *r,
switch (type) {
case AM_CACHE_SESSION:
if (strlen(key) != AM_ID_LENGTH)
if (strlen(key) != AM_SESSION_ID_LENGTH)
return NULL;
break;
case AM_CACHE_NAMEID:
if (strlen(key) > AM_CACHE_MAX_LASSO_IDENTITY_SIZE)
return NULL;
break;
default:
return NULL;
break;
}
mod_cfg = am_get_mod_cfg(r->server);
mod_cfg = am_get_mod_cfg(s);
/* Lock the table. */
if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
"apr_global_mutex_lock() failed [%d]: %s",
rv, apr_strerror(rv, buffer, sizeof(buffer)));
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"apr_global_mutex_lock() failed [%d]: %s",
rv, apr_strerror(rv, buffer, sizeof(buffer)));
return NULL;
}
@ -119,21 +78,15 @@ am_cache_entry_t *am_cache_lock(request_rec *r,
for(i = 0; i < mod_cfg->init_cache_size; i++) {
am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i);
const char *tablekey;
if (e->key[0] == '\0') {
/* This entry is empty. Skip it. */
continue;
}
switch (type) {
case AM_CACHE_SESSION:
tablekey = e->key;
tablekey = table[i].key;
break;
case AM_CACHE_NAMEID:
/* tablekey may be NULL */
tablekey = am_cache_env_fetch_first(e, "NAME_ID");
tablekey = am_cache_env_fetch_first(&table[i], "NAME_ID");
break;
default:
tablekey = NULL;
@ -144,16 +97,10 @@ am_cache_entry_t *am_cache_lock(request_rec *r,
continue;
if(strcmp(tablekey, key) == 0) {
apr_time_t now = apr_time_now();
/* We found the entry. */
if(e->expires > now) {
if(table[i].expires > apr_time_now()) {
/* And it hasn't expired. */
return e;
}
else {
am_diag_log_cache_entry(r, 0, e,
"found expired session, now %s\n",
am_diag_time_t_to_8601(r, now));
return &table[i];
}
}
}
@ -166,109 +113,6 @@ am_cache_entry_t *am_cache_lock(request_rec *r,
return NULL;
}
static inline bool am_cache_entry_slot_is_empty(am_cache_storage_t *slot)
{
return (slot->ptr == 0);
}
static inline void am_cache_storage_null(am_cache_storage_t *slot)
{
slot->ptr = 0;
}
static inline void am_cache_entry_env_null(am_cache_entry_t *e)
{
for (int i = 0; i < AM_CACHE_ENVSIZE; i++) {
am_cache_storage_null(&e->env[i].varname);
am_cache_storage_null(&e->env[i].value);
}
}
static inline apr_size_t am_cache_entry_pool_left(am_cache_entry_t *e)
{
return e->pool_size - e->pool_used;
}
static inline apr_size_t am_cache_entry_pool_size(am_mod_cfg_rec *cfg)
{
return cfg->init_entry_size - sizeof(am_cache_entry_t);
}
/* This function sets a string into the specified storage on the entry.
*
* NOTE: The string pointer may be NULL, in that case storage is freed
* and set to NULL.
*
* Parametrs:
* am_cache_entry_t *entry Pointer to an entry
* am_cache_storage_t *slot Pointer to storage
* const char *string Pointer to a replacement string
*
* Returns:
* 0 on success, HTTP_INTERNAL_SERVER_ERROR on error.
*/
static int am_cache_entry_store_string(am_cache_entry_t *entry,
am_cache_storage_t *slot,
const char *string)
{
char *datastr = NULL;
apr_size_t datalen = 0;
apr_size_t str_len = 0;
if (string == NULL) return 0;
if (slot->ptr != 0) {
datastr = &entry->pool[slot->ptr];
datalen = strlen(datastr) + 1;
}
str_len = strlen(string) + 1;
if (str_len - datalen <= 0) {
memcpy(datastr, string, str_len);
return 0;
}
/* recover space if slot happens to point to the last allocated space */
if (slot->ptr + datalen == entry->pool_used) {
entry->pool_used -= datalen;
slot->ptr = 0;
}
if (am_cache_entry_pool_left(entry) < str_len) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"apr_cache_entry_store_string() asked %zd available: %zd. "
"It may be a good idea to increase MellonCacheEntrySize.",
str_len, am_cache_entry_pool_left(entry));
return HTTP_INTERNAL_SERVER_ERROR;
}
slot->ptr = entry->pool_used;
datastr = &entry->pool[slot->ptr];
memcpy(datastr, string, str_len);
entry->pool_used += str_len;
return 0;
}
/* Returns a pointer to the string in the storage slot specified
*
*
* Parametrs:
* am_cache_entry_t *entry Pointer to an entry
* am_cache_storage_t *slot Pointer to storage slot
*
* Returns:
* A string or NULL if the slot is empty.
*/
const char *am_cache_entry_get_string(am_cache_entry_t *e,
am_cache_storage_t *slot)
{
char *ret = NULL;
if (slot->ptr != 0) {
ret = &e->pool[slot->ptr];
}
return ret;
}
/* This function locks the session table and creates a new session entry.
* It will first attempt to locate a free session. If it doesn't find a
@ -277,21 +121,18 @@ const char *am_cache_entry_get_string(am_cache_entry_t *e,
* Remember to unlock the table with am_cache_unlock(...) afterwards.
*
* Parameters:
* request_rec *r The request we are processing.
* server_rec *s The current server.
* const char *key The key of the session to allocate.
* const char *cookie_token The cookie token to tie the session to.
*
* Returns:
* The new session entry on success. NULL if key is a invalid session
* key.
*/
am_cache_entry_t *am_cache_new(request_rec *r,
const char *key,
const char *cookie_token)
am_cache_entry_t *am_cache_new(server_rec *s, const char *key)
{
am_cache_entry_t *t;
am_mod_cfg_rec *mod_cfg;
void *table;
am_cache_entry_t *table;
apr_time_t current_time;
int i;
apr_time_t age;
@ -299,19 +140,19 @@ am_cache_entry_t *am_cache_new(request_rec *r,
char buffer[512];
/* Check if we have a valid session key. We abort if we don't. */
if(key == NULL || strlen(key) != AM_ID_LENGTH) {
if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) {
return NULL;
}
mod_cfg = am_get_mod_cfg(r->server);
mod_cfg = am_get_mod_cfg(s);
/* Lock the table. */
if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
"apr_global_mutex_lock() failed [%d]: %s",
rv, apr_strerror(rv, buffer, sizeof(buffer)));
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"apr_global_mutex_lock() failed [%d]: %s",
rv, apr_strerror(rv, buffer, sizeof(buffer)));
return NULL;
}
@ -326,7 +167,7 @@ am_cache_entry_t *am_cache_new(request_rec *r,
* initalize it to the first entry in the table to simplify the
* following code (saves test for t == NULL).
*/
t = am_cache_entry_ptr(mod_cfg, table, 0);
t = &table[0];
/* Iterate over the session table. Update 't' to match the "best"
* entry (the least recently used). 't' will point a free entry
@ -334,30 +175,25 @@ am_cache_entry_t *am_cache_new(request_rec *r,
* used entry.
*/
for(i = 0; i < mod_cfg->init_cache_size; i++) {
am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i);
if (e->key[0] == '\0') {
if(table[i].key[0] == '\0') {
/* This entry is free. Update 't' to this entry
* and exit loop.
*/
t = e;
t = &table[i];
break;
}
if (e->expires <= current_time) {
if(table[i].expires <= current_time) {
/* This entry is expired, and is therefore free.
* Update 't' and exit loop.
*/
t = e;
am_diag_log_cache_entry(r, 0, e,
"%s ejecting expired sessions, now %s\n",
__func__,
am_diag_time_t_to_8601(r, current_time));
t = &table[i];
break;
}
if (e->access < t->access) {
if(table[i].access < t->access) {
/* This entry is older than 't' - update 't'. */
t = e;
t = &table[i];
}
}
@ -367,11 +203,11 @@ am_cache_entry_t *am_cache_new(request_rec *r,
age = (current_time - t->access) / 1000000;
if(age < 3600) {
AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r,
"Dropping LRU entry entry with age = %" APR_TIME_T_FMT
"s, which is less than one hour. It may be a good"
" idea to increase MellonCacheSize.",
age);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
"Dropping LRU entry entry with age = %" APR_TIME_T_FMT
"s, which is less than one hour. It may be a good"
" idea to increase MellonCacheSize.",
age);
}
}
@ -386,34 +222,10 @@ am_cache_entry_t *am_cache_new(request_rec *r,
t->logged_in = 0;
t->size = 0;
t->user[0] = '\0';
am_cache_storage_null(&t->cookie_token);
am_cache_storage_null(&t->user);
am_cache_storage_null(&t->lasso_identity);
am_cache_storage_null(&t->lasso_session);
am_cache_storage_null(&t->lasso_saml_response);
am_cache_entry_env_null(t);
t->pool_size = am_cache_entry_pool_size(mod_cfg);
t->pool[0] = '\0';
t->pool_used = 1;
rv = am_cache_entry_store_string(t, &t->cookie_token, cookie_token);
if (rv != 0) {
/* For some strange reason our cookie token is too big to fit in the
* session. This should never happen outside of absurd configurations.
*/
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
"Unable to store cookie token in new session.");
t->key[0] = '\0'; /* Mark the entry as free. */
apr_global_mutex_unlock(mod_cfg->lock);
return NULL;
}
am_diag_printf(r, "%s created new session, id=%s at %s"
" cookie_token=\"%s\"\n",
__func__, t->key, am_diag_time_t_to_8601(r, current_time),
cookie_token);
t->lasso_identity[0] = '\0';
t->lasso_session[0] = '\0';
return t;
}
@ -422,20 +234,20 @@ am_cache_entry_t *am_cache_new(request_rec *r,
/* This function unlocks a session entry.
*
* Parameters:
* request_rec *r The request we are processing.
* server_rec *s The current server.
* am_cache_entry_t *entry The session entry.
*
* Returns:
* Nothing.
*/
void am_cache_unlock(request_rec *r, am_cache_entry_t *entry)
void am_cache_unlock(server_rec *s, am_cache_entry_t *entry)
{
am_mod_cfg_rec *mod_cfg;
/* Update access time. */
entry->access = apr_time_now();
mod_cfg = am_get_mod_cfg(r->server);
mod_cfg = am_get_mod_cfg(s);
apr_global_mutex_unlock(mod_cfg->lock);
}
@ -444,14 +256,13 @@ void am_cache_unlock(request_rec *r, am_cache_entry_t *entry)
* timestamp is earlier than the previous.
*
* Parameters:
* request_rec *r The request we are processing.
* am_cache_entry_t *t The current session.
* apr_time_t expires The new timestamp.
*
* Returns:
* Nothing.
*/
void am_cache_update_expires(request_rec *r, am_cache_entry_t *t, apr_time_t expires)
void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires)
{
/* Check if we should update the expires timestamp. */
if(t->expires == 0 || t->expires > expires) {
@ -475,36 +286,27 @@ void am_cache_update_expires(request_rec *r, am_cache_entry_t *t, apr_time_t exp
int am_cache_env_append(am_cache_entry_t *t,
const char *var, const char *val)
{
int status;
/* Make sure that the name and value will fit inside the
* fixed size buffer.
*/
if(strlen(val) >= AM_CACHE_VALSIZE ||
strlen(var) >= AM_CACHE_VARSIZE) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Unable to store session data because it is to big. "
"Name = \"%s\"; Value = \"%s\".", var, val);
return HTTP_INTERNAL_SERVER_ERROR;
}
if(t->size >= AM_CACHE_ENVSIZE) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Unable to store attribute value because we have"
" reached the maximum number of name-value pairs for"
" this session. The maximum number is %d.",
AM_CACHE_ENVSIZE);
return HTTP_INTERNAL_SERVER_ERROR;
}
status = am_cache_entry_store_string(t, &t->env[t->size].varname, var);
if (status != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Unable to store session data because there is no more "
"space in the session. Attribute Name = \"%s\".", var);
return HTTP_INTERNAL_SERVER_ERROR;
}
status = am_cache_entry_store_string(t, &t->env[t->size].value, val);
if (status != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Unable to store session data because there is no more "
"space in the session. Attribute Value = \"%s\".", val);
" this session.");
return HTTP_INTERNAL_SERVER_ERROR;
}
strcpy(t->env[t->size].varname, var);
strcpy(t->env[t->size].value, val);
t->size++;
return OK;
@ -523,15 +325,11 @@ int am_cache_env_append(am_cache_entry_t *t,
const char *am_cache_env_fetch_first(am_cache_entry_t *t,
const char *var)
{
const char *str;
int i;
for (i = 0; i < t->size; i++) {
str = am_cache_entry_get_string(t, &t->env[i].varname);
if (str == NULL)
break;
if (strcmp(str, var) == 0)
return am_cache_entry_get_string(t, &t->env[i].value);
for (i = 0; t->size; i++) {
if (strcmp(t->env[i].varname, var) == 0)
return t->env[i].value;
}
return NULL;
@ -553,30 +351,19 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t)
am_dir_cfg_rec *d;
int i;
apr_hash_t *counters;
am_envattr_conf_t *env_varname_conf;
const char *varname;
const char *varname_prefix;
const char *env_varname;
const char *value;
const char *prefixed_varname;
int *count;
int status;
d = am_get_dir_cfg(r);
/* Check if the user attribute has been set, and set it if it
* hasn't been set. */
if (am_cache_entry_slot_is_empty(&t->user)) {
if(t->user[0] == '\0') {
for(i = 0; i < t->size; ++i) {
varname = am_cache_entry_get_string(t, &t->env[i].varname);
if (strcasecmp(varname, d->userattr) == 0) {
value = am_cache_entry_get_string(t, &t->env[i].value);
status = am_cache_entry_store_string(t, &t->user, value);
if (status != 0) {
AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r,
"Unable to store the user name because there"
" is no more space in the session. "
"Username = \"%s\".", value);
}
if(strcmp(t->env[i].varname, d->userattr) == 0) {
strcpy(t->user, t->env[i].value);
}
}
}
@ -588,37 +375,22 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t)
* received from the IdP.
*/
for(i = 0; i < t->size; ++i) {
varname = am_cache_entry_get_string(t, &t->env[i].varname);
varname_prefix = d->env_prefix;
varname = t->env[i].varname;
/* Check if we should map this name into another name. */
env_varname_conf = (am_envattr_conf_t *)apr_hash_get(
env_varname = (const char*)apr_hash_get(
d->envattr, varname, APR_HASH_KEY_STRING);
if(env_varname_conf != NULL) {
varname = env_varname_conf->name;
if (!env_varname_conf->prefixed) {
varname_prefix = "";
}
if(env_varname != NULL) {
varname = env_varname;
}
value = am_cache_entry_get_string(t, &t->env[i].value);
value = t->env[i].value;
/*
* If we find a variable remapping to MellonUser, use it.
*/
if (am_cache_entry_slot_is_empty(&t->user) &&
(strcasecmp(varname, d->userattr) == 0)) {
status = am_cache_entry_store_string(t, &t->user, value);
if (status != 0) {
AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r,
"Unable to store the user name because there"
" is no more space in the session. "
"Username = \"%s\".", value);
}
}
prefixed_varname = apr_pstrcat(r->pool, varname_prefix, varname, NULL);
if ((t->user[0] == '\0') && (strcmp(varname, d->userattr) == 0))
strcpy(t->user, value);
/* Find the number of times this variable has been set. */
count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING);
@ -630,56 +402,29 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t)
apr_hash_set(counters, varname, APR_HASH_KEY_STRING, count);
/* Add the variable without a suffix. */
apr_table_set(r->subprocess_env,prefixed_varname,value);
}
/* Check if merging of environment variables is disabled.
* This is either if it is NULL (default value if not configured
* by user) or an empty string (if specifically disabled by the user).
*/
if (d->merge_env_vars == NULL || *d->merge_env_vars == '\0') {
/* Add the variable with a suffix indicating how many times it has
* been added before.
*/
apr_table_set(r->subprocess_env,
apr_psprintf(r->pool, "%s_%d", prefixed_varname,
(d->env_vars_index_start > -1
? *count + d->env_vars_index_start
: *count)),
apr_pstrcat(r->pool, "MELLON_", varname, NULL),
value);
} else if (*count > 0) {
/*
* Merge multiple values, separating by default with ";"
* this makes auth_mellon work same way mod_shib is:
* https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAttributeAccess
*/
apr_table_set(r->subprocess_env,
prefixed_varname,
apr_pstrcat(r->pool,
apr_table_get(r->subprocess_env,prefixed_varname),
d->merge_env_vars, value, NULL));
}
/* Add the variable with a suffix indicating how many times it has
* been added before.
*/
apr_table_set(r->subprocess_env,
apr_psprintf(r->pool, "MELLON_%s_%d", varname, *count),
value);
/* Increase the count. */
++(*count);
if (d->env_vars_count_in_n > 0) {
apr_table_set(r->subprocess_env,
apr_pstrcat(r->pool, prefixed_varname, "_N", NULL),
apr_itoa(r->pool, *count));
}
}
if (!am_cache_entry_slot_is_empty(&t->user)) {
if(t->user[0] != '\0') {
/* We have a user-"name". Set r->user and r->ap_auth_type. */
r->user = apr_pstrdup(r->pool, am_cache_entry_get_string(t, &t->user));
r->user = apr_pstrdup(r->pool, t->user);
r->ap_auth_type = apr_pstrdup(r->pool, "Mellon");
} else {
/* We don't have a user-"name". Log error. */
AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
"Didn't find the attribute \"%s\" in the attributes"
" which were received from the IdP. Cannot set a user"
" for this request without a valid user attribute.",
@ -690,37 +435,33 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t)
/* Populate with the session? */
if (d->dump_session) {
char *session;
const char *srcstr;
int srclen, dstlen;
srcstr = am_cache_entry_get_string(t, &t->lasso_session);
srclen = strlen(srcstr);
srclen = strlen(t->lasso_session);
dstlen = apr_base64_encode_len(srclen);
session = apr_palloc(r->pool, dstlen);
(void)apr_base64_encode(session, srcstr, srclen);
(void)apr_base64_encode(session, t->lasso_session, srclen);
apr_table_set(r->subprocess_env, "MELLON_SESSION", session);
}
if (d->dump_saml_response) {
const char *sr = am_cache_entry_get_string(t, &t->lasso_saml_response);
if (sr) {
apr_table_set(r->subprocess_env, "MELLON_SAML_RESPONSE", sr);
}
}
if (d->dump_saml_response)
apr_table_set(r->subprocess_env,
"MELLON_SAML_RESPONSE",
t->lasso_saml_response);
}
/* This function deletes a given key from the session store.
*
* Parameters:
* request_rec *r The request we are processing.
* server_rec *s The current server.
* am_cache_entry_t *cache The entry we are deleting.
*
* Returns:
* Nothing.
*/
void am_cache_delete(request_rec *r, am_cache_entry_t *cache)
void am_cache_delete(server_rec *s, am_cache_entry_t *cache)
{
/* We write a null-byte at the beginning of the key to
* mark this slot as unused.
@ -728,7 +469,7 @@ void am_cache_delete(request_rec *r, am_cache_entry_t *cache)
cache->key[0] = '\0';
/* Unlock the entry. */
am_cache_unlock(r, cache);
am_cache_unlock(s, cache);
}
@ -749,39 +490,56 @@ int am_cache_set_lasso_state(am_cache_entry_t *session,
const char *lasso_session,
const char *lasso_saml_response)
{
int status;
status = am_cache_entry_store_string(session,
&session->lasso_identity,
lasso_identity);
if (status != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso identity is too big for storage. Size of lasso"
" identity is %" APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_identity));
return HTTP_INTERNAL_SERVER_ERROR;
if(lasso_identity != NULL) {
if(strlen(lasso_identity) < AM_CACHE_MAX_LASSO_IDENTITY_SIZE) {
strcpy(session->lasso_identity, lasso_identity);
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso identity is to big for storage. Size of lasso"
" identity is %" APR_SIZE_T_FMT ", max size is %"
APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_identity),
(apr_size_t)AM_CACHE_MAX_LASSO_IDENTITY_SIZE - 1);
return HTTP_INTERNAL_SERVER_ERROR;
}
} else {
/* No identity dump to save. */
strcpy(session->lasso_identity, "");
}
status = am_cache_entry_store_string(session,
&session->lasso_session,
lasso_session);
if (status != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso session is too big for storage. Size of lasso"
" session is %" APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_session));
return HTTP_INTERNAL_SERVER_ERROR;
if(lasso_session != NULL) {
if(strlen(lasso_session) < AM_CACHE_MAX_LASSO_SESSION_SIZE) {
strcpy(session->lasso_session, lasso_session);
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso session is to big for storage. Size of lasso"
" session is %" APR_SIZE_T_FMT ", max size is %"
APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_session),
(apr_size_t)AM_CACHE_MAX_LASSO_SESSION_SIZE - 1);
return HTTP_INTERNAL_SERVER_ERROR;
}
} else {
/* No session dump to save. */
strcpy(session->lasso_session, "");
}
status = am_cache_entry_store_string(session,
&session->lasso_saml_response,
lasso_saml_response);
if (status != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso SAML response is too big for storage. Size of "
"lasso SAML Response is %" APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_saml_response));
return HTTP_INTERNAL_SERVER_ERROR;
if(lasso_saml_response != NULL) {
if(strlen(lasso_saml_response) < AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE) {
strcpy(session->lasso_saml_response, lasso_saml_response);
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"Lasso SAML response is to big for storage. "
"Size of lasso session is %" APR_SIZE_T_FMT
", max size is %" APR_SIZE_T_FMT ".",
(apr_size_t)strlen(lasso_saml_response),
(apr_size_t)AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE - 1);
return HTTP_INTERNAL_SERVER_ERROR;
}
} else {
/* No session dump to save. */
strcpy(session->lasso_saml_response, "");
}
return OK;
@ -798,7 +556,11 @@ int am_cache_set_lasso_state(am_cache_entry_t *session,
*/
const char *am_cache_get_lasso_identity(am_cache_entry_t *session)
{
return am_cache_entry_get_string(session, &session->lasso_identity);
if(strlen(session->lasso_identity) == 0) {
return NULL;
}
return session->lasso_identity;
}
@ -812,5 +574,9 @@ const char *am_cache_get_lasso_identity(am_cache_entry_t *session)
*/
const char *am_cache_get_lasso_session(am_cache_entry_t *session)
{
return am_cache_entry_get_string(session, &session->lasso_session);
if(strlen(session->lasso_session) == 0) {
return NULL;
}
return session->lasso_session;
}

View File

@ -1,57 +0,0 @@
#ifndef AUTH_MELLON_COMPAT_H
#define AUTH_MELLON_COMPAT_H
#include <glib.h>
#include "ap_config.h"
#include "ap_release.h"
#ifdef AP_NEED_SET_MUTEX_PERMS
#include "unixd.h"
#endif
/* Old glib compatibility */
#if (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 14)
static void g_hash_table_get_keys_helper(gpointer key, gpointer value,
gpointer user_data)
{
GList **out = user_data;
*out = g_list_prepend(*out, key);
}
static GList *g_hash_table_get_keys(GHashTable *ht)
{
GList *ret = NULL;
g_hash_table_foreach(ht, g_hash_table_get_keys_helper, &ret);
return g_list_reverse(ret);
}
#endif
/* "remote_ip" in struct conn_rec changed name to "client_ip" in Apache 2.4.
* This function retrieves the corrent member depending on the Apache version.
*/
static inline const char *am_compat_request_ip(request_rec *r) {
#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
return r->connection->remote_ip;
#else
return r->connection->client_ip;
#endif
}
/* unixd_set_global_mutex_perms changed name to ap_unixd_set_global_mutex_perms
* in Apache 2.4. This function provides a wrapper with the new name for old
* versions.
*/
#ifdef AP_NEED_SET_MUTEX_PERMS
#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
static inline apr_status_t ap_unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) {
return unixd_set_global_mutex_perms(gmutex);
}
#endif
#endif /* AP_NEED_SET_MUTEX_PERMS */
#endif /* AUTH_MELLON_COMPAT_H */

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,6 @@
#include "auth_mellon.h"
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#endif
/* This function retrieves the name of our cookie.
*
@ -44,49 +41,6 @@ static const char *am_cookie_name(request_rec *r)
}
/* Calculate the cookie parameters.
*
* Parameters:
* request_rec *r The request we should set the cookie in.
*
* Returns:
* The cookie parameters as a string.
*/
static const char *am_cookie_params(request_rec *r)
{
int secure_cookie;
int http_only_cookie;
const char *cookie_domain = ap_get_server_name(r);
const char *cookie_path = "/";
const char *cookie_samesite = "";
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
if (cfg->cookie_domain) {
cookie_domain = cfg->cookie_domain;
}
if (cfg->cookie_path) {
cookie_path = cfg->cookie_path;
}
if (cfg->cookie_samesite == am_samesite_lax) {
cookie_samesite = "; SameSite=Lax";
} else if (cfg->cookie_samesite == am_samesite_strict) {
cookie_samesite = "; SameSite=Strict";
}
secure_cookie = cfg->secure;
http_only_cookie = cfg->http_only;
return apr_psprintf(r->pool,
"Version=1; Path=%s; Domain=%s%s%s%s",
cookie_path, cookie_domain,
http_only_cookie ? "; HttpOnly" : "",
secure_cookie ? "; secure" : "",
cookie_samesite);
}
/* This functions finds the value of our cookie.
*
* Parameters:
@ -97,7 +51,6 @@ static const char *am_cookie_params(request_rec *r)
*/
const char *am_cookie_get(request_rec *r)
{
am_req_cfg_rec *req_cfg;
const char *name;
const char *value;
const char *cookie;
@ -111,8 +64,8 @@ const char *am_cookie_get(request_rec *r)
}
/* Check if we have added a note on the current request. */
req_cfg = am_get_req_cfg(r);
value = req_cfg->cookie_value;
value = (const char *)ap_get_module_config(r->request_config,
&auth_mellon_module);
if(value != NULL) {
return value;
}
@ -195,18 +148,31 @@ const char *am_cookie_get(request_rec *r)
*/
void am_cookie_set(request_rec *r, const char *id)
{
am_req_cfg_rec *req_cfg;
const char *name;
const char *cookie_params;
char *cookie;
int secure_cookie;
const char *cookie_domain = ap_get_server_name(r);
const char *cookie_path = "/";
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
if (id == NULL)
return;
name = am_cookie_name(r);
cookie_params = am_cookie_params(r);
if (cfg->cookie_domain) {
cookie_domain = cfg->cookie_domain;
}
cookie = apr_psprintf(r->pool, "%s=%s; %s", name, id, cookie_params);
if (cfg->cookie_path) {
cookie_path = cfg->cookie_path;
}
secure_cookie = cfg->secure;
name = am_cookie_name(r);
cookie = apr_psprintf(r->pool,
"%s=%s; Version=1; Path=%s; Domain=%s%s;",
name, id, cookie_path, cookie_domain,
secure_cookie ? "; HttpOnly; secure" : "");
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"cookie_set: %s", cookie);
@ -218,8 +184,8 @@ void am_cookie_set(request_rec *r, const char *id)
/* Add a note on the current request, to allow us to retrieve this
* cookie in the current request.
*/
req_cfg = am_get_req_cfg(r);
req_cfg->cookie_value = apr_pstrdup(r->pool, id);
ap_set_module_config(r->request_config, &auth_mellon_module,
apr_pstrdup(r->pool, id));
}
@ -235,48 +201,19 @@ void am_cookie_set(request_rec *r, const char *id)
void am_cookie_delete(request_rec *r)
{
const char *name;
const char *cookie_params;
char *cookie;
name = am_cookie_name(r);
cookie_params = am_cookie_params(r);
/* Format a cookie. To delete a cookie we set the expires-timestamp
* to the past.
*/
cookie = apr_psprintf(r->pool, "%s=NULL;"
" version=1;"
" expires=Thu, 01-Jan-1970 00:00:00 GMT;"
" %s",
name, cookie_params);
" path=/",
name);
apr_table_addn(r->err_headers_out, "Set-Cookie", cookie);
}
/* Get string that is used to tie a session to a specific cookie.
*
* request_rec *r The current request.
* Returns:
* The cookie token, as a fixed length byte buffer.
*/
const char *am_cookie_token(request_rec *r)
{
const char *cookie_name = am_cookie_name(r);
const char *cookie_domain = ap_get_server_name(r);
const char *cookie_path = "/";
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
if (cfg->cookie_domain) {
cookie_domain = cfg->cookie_domain;
}
if (cfg->cookie_path) {
cookie_path = cfg->cookie_path;
}
return apr_psprintf(r->pool, "Name='%s' Domain='%s' Path='%s'",
cookie_name,
cookie_domain,
cookie_path
);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -19,16 +19,15 @@
*
*/
#include "auth_mellon.h"
#include <curl/curl.h>
/* The size of the blocks we will allocate. */
#define AM_HC_BLOCK_SIZE 1000
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#endif
/* This structure describes a single-linked list of downloaded blocks. */
typedef struct am_hc_block_s {
@ -98,25 +97,24 @@ static am_hc_block_t *am_hc_block_write(
{
apr_size_t num_cpy;
while(size > 0) {
/* Find the number of bytes we should write to this block. */
num_cpy = AM_HC_BLOCK_SIZE - block->used;
if(num_cpy == 0) {
/* This block is full -- allocate a new block. */
block->next = am_hc_block_alloc(pool);
block = block->next;
num_cpy = AM_HC_BLOCK_SIZE;
}
if(num_cpy > size) {
num_cpy = size;
}
/* Find the number of bytes we should write to this block. */
num_cpy = AM_HC_BLOCK_SIZE - block->used;
if(num_cpy > size) {
num_cpy = size;
}
/* Copy data to this block. */
memcpy(&block->data[block->used], data, num_cpy);
block->used += num_cpy;
/* Copy data to this block. */
memcpy(&block->data[block->used], data, num_cpy);
block->used += num_cpy;
size -= num_cpy;
data += num_cpy;
if(block->used == AM_HC_BLOCK_SIZE) {
/* This block is full. Allocate a new block, and continue
* filling it.
*/
block->next = am_hc_block_alloc(pool);
return am_hc_block_write(block->next, pool, &data[num_cpy],
size - num_cpy);
}
/* The next write should be to this block. */
@ -255,7 +253,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Initialize the curl object. */
curl = curl_easy_init();
if(curl == NULL) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to initialize a curl object.");
return NULL;
}
@ -264,7 +262,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Set up error reporting. */
res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set curl error buffer: [%u]\n", res);
goto cleanup_fail;
}
@ -272,7 +270,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Disable progress reporting. */
res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to disable curl progress reporting: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -281,7 +279,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Disable use of signals. */
res = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to disable signals in curl: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -290,19 +288,37 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Set the timeout of the transfer. It is currently set to two minutes. */
res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set the timeout of the curl download:"
" [%u] %s", res, curl_error);
goto cleanup_fail;
}
/* Enable SSL peer certificate verification. */
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
if(res != CURLE_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to enable SSL peer certificate verification:"
" [%u] %s", res, curl_error);
goto cleanup_fail;
}
/* Enable SSL peer hostname verification. */
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
if(res != CURLE_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to enable SSL peer hostname verification:"
" [%u] %s", res, curl_error);
goto cleanup_fail;
}
/* If we have a CA configured, try to use it */
if (cfg->idp_ca_file != NULL) {
res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file->path);
res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set SSL CA info %s:"
" [%u] %s", cfg->idp_ca_file->path, res, curl_error);
" [%u] %s", cfg->idp_ca_file, res, curl_error);
goto cleanup_fail;
}
}
@ -310,7 +326,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Enable fail on http error. */
res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to enable failure on http error: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -319,7 +335,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Select which uri we should download. */
res = curl_easy_setopt(curl, CURLOPT_URL, uri);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set curl download uri to \"%s\": [%u] %s",
uri, res, curl_error);
goto cleanup_fail;
@ -331,7 +347,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Set curl write function. */
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, am_hc_data_write);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set the curl write function: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -340,7 +356,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
/* Set the curl write function parameter. */
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, bh);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set the curl write function data: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -367,7 +383,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
* apr_size_t *size This is a pointer to where we will store the length
* of the downloaded data, not including the
* null-terminator we add. This parameter can be NULL.
* int timeout Timeout in seconds, 0 for no timeout.
* apr_time_t timeout Timeout in seconds, 0 for no timeout.
* long *status Pointer to HTTP status code.
*
* Returns:
@ -376,7 +392,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
*/
int am_httpclient_get(request_rec *r, const char *uri,
void **buffer, apr_size_t *size,
int timeout, long *status)
apr_time_t timeout, long *status)
{
am_hc_block_header_t bh;
CURL *curl;
@ -392,18 +408,18 @@ int am_httpclient_get(request_rec *r, const char *uri,
return HTTP_INTERNAL_SERVER_ERROR;
}
res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to download data from the uri \"%s\", "
"cannot set timeout to %ld: [%u] %s",
uri, (long)timeout, res, curl_error);
goto cleanup_fail;
}
res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, (long)timeout);
res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to download data from the uri \"%s\", "
"cannot set connect timeout to %ld: [%u] %s",
uri, (long)timeout, res, curl_error);
@ -413,7 +429,7 @@ int am_httpclient_get(request_rec *r, const char *uri,
/* Do the download. */
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to download data from the uri \"%s\", "
"transaction aborted: [%u] %s",
uri, res, curl_error);
@ -423,7 +439,7 @@ int am_httpclient_get(request_rec *r, const char *uri,
if (status != NULL) {
res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to download data from the uri \"%s\", "
"no status report: [%u] %s",
uri, res, curl_error);
@ -496,7 +512,7 @@ int am_httpclient_post(request_rec *r, const char *uri,
/* Enable POST request. */
res = curl_easy_setopt(curl, CURLOPT_POST, 1L);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to enable POST request: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -505,7 +521,7 @@ int am_httpclient_post(request_rec *r, const char *uri,
/* Set POST data size. */
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_length);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set the POST data length: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -514,7 +530,7 @@ int am_httpclient_post(request_rec *r, const char *uri,
/* Set POST data. */
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set the POST data: [%u] %s",
res, curl_error);
goto cleanup_fail;
@ -540,7 +556,7 @@ int am_httpclient_post(request_rec *r, const char *uri,
/* Set headers. */
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, ctheader);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to set content-type header to \"%s\": [%u] %s",
content_type, res, curl_error);
goto cleanup_fail;
@ -550,7 +566,7 @@ int am_httpclient_post(request_rec *r, const char *uri,
/* Do the download. */
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Failed to download data from the uri \"%s\": [%u] %s",
uri, res, curl_error);
goto cleanup_fail;

View File

@ -21,54 +21,6 @@
#include "auth_mellon.h"
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#endif
/* Retrieve a session from the cache and validate its cookie settings
*
* Parameters:
* request_rec *r The request we received from the user.
* am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID
* const char *key The session key or user
*
* Returns:
* The session associated, or NULL if unable to retrieve the given session.
*/
am_cache_entry_t *am_lock_and_validate(request_rec *r,
am_cache_key_t type,
const char *key)
{
am_cache_entry_t *session = NULL;
am_diag_printf(r, "searching for session with key %s (%s) ... ",
key, am_diag_cache_key_type_str(type));
session = am_cache_lock(r, type, key);
if (session == NULL) {
am_diag_printf(r, "not found\n");
return NULL;
} else {
am_diag_printf(r, "found.\n");
am_diag_log_cache_entry(r, 0, session, "Session Cache Entry");
}
const char *cookie_token_session = am_cache_entry_get_string(
session, &session->cookie_token);
const char *cookie_token_target = am_cookie_token(r);
if (strcmp(cookie_token_session, cookie_token_target)) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
"Session cookie parameter mismatch. "
"Session created with {%s}, but current "
"request has {%s}.",
cookie_token_session,
cookie_token_target);
am_cache_unlock(r, session);
return NULL;
}
return session;
}
/* This function gets the session associated with a user, using a cookie
*
@ -90,7 +42,7 @@ am_cache_entry_t *am_get_request_session(request_rec *r)
return NULL;
}
return am_lock_and_validate(r, AM_CACHE_SESSION, session_id);
return am_cache_lock(r->server, AM_CACHE_SESSION, session_id);
}
/* This function gets the session associated with a user, using a NameID
@ -105,7 +57,7 @@ am_cache_entry_t *am_get_request_session(request_rec *r)
*/
am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r, char *nameid)
{
return am_lock_and_validate(r, AM_CACHE_NAMEID, nameid);
return am_cache_lock(r->server, AM_CACHE_NAMEID, nameid);
}
/* This function creates a new session.
@ -121,22 +73,18 @@ am_cache_entry_t *am_new_request_session(request_rec *r)
const char *session_id;
/* Generate session id. */
session_id = am_generate_id(r);
session_id = am_generate_session_id(r);
if(session_id == NULL) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Error creating session id.");
return NULL;
}
/* Set session id. */
am_cookie_set(r, session_id);
const char *cookie_token = am_cookie_token(r);
am_diag_printf(r, "%s id=%s cookie_token=\"%s\"\n",
__func__, session_id, cookie_token);
return am_cache_new(r, session_id, cookie_token);
return am_cache_new(r->server, session_id);
}
@ -152,7 +100,7 @@ am_cache_entry_t *am_new_request_session(request_rec *r)
*/
void am_release_request_session(request_rec *r, am_cache_entry_t *session)
{
am_cache_unlock(r, session);
am_cache_unlock(r->server, session);
}
@ -168,8 +116,6 @@ void am_release_request_session(request_rec *r, am_cache_entry_t *session)
*/
void am_delete_request_session(request_rec *r, am_cache_entry_t *session)
{
am_diag_log_cache_entry(r, 0, session, "delete session");
/* Delete the cookie. */
am_cookie_delete(r);
@ -178,5 +124,5 @@ void am_delete_request_session(request_rec *r, am_cache_entry_t *session)
}
/* Delete session from the session store. */
am_cache_delete(r, session);
am_cache_delete(r->server, session);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
#!/bin/sh
autoreconf --force --install
rm -rf autom4te.cache/
./configure "$@"

View File

@ -1,8 +1,4 @@
AC_INIT([mod_auth_mellon],m4_esyscmd([tools/git-version-gen .tarball-version]),[olav.morken@uninett.no])
AC_CONFIG_HEADERS([config.h])
# We require support for C99.
AC_PROG_CC_C99
AC_INIT([mod_auth_mellon],[0.5.0],[olav.morken@uninett.no])
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
@ -42,16 +38,6 @@ The executable may also be named 'apxs'.
])
fi
AC_ARG_ENABLE(
[diagnostics],
[AS_HELP_STRING([--enable-diagnostics],
[Build with diagnostic support])],
[],
[enable_diagnostics=no])
AS_IF([test "x$enable_diagnostics" != xno],
[AC_DEFINE([ENABLE_DIAGNOSTICS],[],[build with diagnostics])])
# Replace any occurances of @APXS2@ with the value of $APXS2 in the Makefile.
AC_SUBST(APXS2)
@ -59,17 +45,11 @@ AC_SUBST(APXS2)
PKG_CHECK_MODULES(LASSO, lasso)
saved_LIBS=$LIBS; LIBS="$LIBS $LASSO_LIBS";
AC_CHECK_LIB(lasso, lasso_server_new_from_buffers,
[AC_DEFINE([HAVE_lasso_server_new_from_buffers],[],
[lasso library exports lasso_server_new_from_buffers])])
LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_new_from_buffers")
AC_CHECK_LIB(lasso, lasso_server_load_metadata,
[AC_DEFINE([HAVE_lasso_server_load_metadata],[],
[lasso library exports lasso_server_load_metadata])])
LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_load_metadata")
AC_CHECK_LIB(lasso, lasso_profile_set_signature_verify_hint,
[AC_DEFINE([HAVE_lasso_profile_set_signature_verify_hint],[],
[lasso library exports lasso_profile_set_signature_verify_hint])])
AC_CHECK_LIB(lasso, lasso_ecp_request_new,
[AC_DEFINE([HAVE_ECP],[],
[lasso library supports ECP profile])])
LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_profile_set_signature_verify_hint")
LIBS=$saved_LIBS;
AC_SUBST(LASSO_CFLAGS)
AC_SUBST(LASSO_LIBS)
@ -84,32 +64,9 @@ PKG_CHECK_MODULES(OPENSSL, openssl)
AC_SUBST(OPENSSL_CFLAGS)
AC_SUBST(OPENSSL_LIBS)
# We need at least version 2.12 of GLib.
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12])
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
# We need at least version 2.14 of GLib.
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.14])
AC_SUBST(MELLON_CFLAGS)
# Test to see if we can include lasso/utils.h
# AC_CHECK_HEADER won't work correctly unless we specifiy the include directories
# found in the LASSO_CFLAGS. Save and restore CFLAGS and CPPFLAGS.
saved_CFLAGS=$CFLAGS
saved_CPPFLAGS=$CPPFLAGS
CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS"
CPPFLAGS="$CPPFLAGS $pkg_cv_LASSO_CFLAGS"
AC_CHECK_HEADER([lasso/utils.h], LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_LASSO_UTILS_H")
CFLAGS=$saved_CFLAGS
CPPFLAGS=$saved_CPPFLAGS
# Determine what definitions exist in Lasso
saved_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS"
AC_CHECK_DECLS([LASSO_SIGNATURE_METHOD_RSA_SHA256,
LASSO_SIGNATURE_METHOD_RSA_SHA384,
LASSO_SIGNATURE_METHOD_RSA_SHA512,
], [], [], [#include <lasso/xml/xml.h>])
CFLAGS=$saved_CFLAGS
# Create Makefile from Makefile.in
AC_CONFIG_FILES([Makefile])

4
debian/control vendored
View File

@ -2,7 +2,7 @@ Source: libapache2-mod-auth-mellon
Section: web
Priority: extra
Maintainer: Olav Morken <olavmrk@gmail.com>
Build-Depends: debhelper (>= 5), autotools-dev, automake, autoconf, apache2-dev, libcurl3-dev, liblasso3-dev (>= 2.1.0)
Build-Depends: debhelper (>= 5), autotools-dev, apache2-prefork-dev (>= 2.0.55), libcurl3-dev, liblasso3-dev (>= 2.1.0)
Standards-Version: 3.7.2
Package: libapache2-mod-auth-mellon
@ -17,5 +17,5 @@ Description: A SAML 2.0 authentication module for Apache
Package: libapache2-mod-auth-mellon-dbg
Architecture: any
Depends: libapache2-mod-auth-mellon
Depends: libapache2-mod-auth-mellon (=${Source-Version})
Description: Debug symbols for libapache2-mod-auth-mellon.

1
debian/docs vendored
View File

@ -1,2 +1,3 @@
NEWS
README
TODO

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Convert urn:mace:shibboleth:2.0:attribute-map to MellonSetEnv statements
Author: Pat Riehecky <riehecky@fnal.gov>
Copyright (2019). Fermi Research Alliance, LLC
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="urn:mace:shibboleth:2.0:attribute-map"
>
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/map:Attributes">
<xsl:apply-templates select="map:Attribute">
<xsl:sort select="@id" data-type="text" />
<xsl:sort select="@name" data-type="text" order="descending"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match='map:Attribute'>
<xsl:value-of select="concat('MellonSetEnvNoPrefix ', @id, ' ' , @name)"/><xsl:text>&#xa;</xsl:text>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,11 +0,0 @@
Bundler.require :default
guard 'shell' do
watch(/^mellon_user_guide\.adoc$/) {|m|
Asciidoctor.convert_file m[0]
}
end
guard 'livereload' do
watch(%r{^.+\.(css|js|html)$})
end

View File

@ -1,21 +0,0 @@
This is the mod_auth_mellon User Guide. It is written in AsciiDoc
which is a popular plaintext markup language much like markdown or
reStructuredText. You can find extensive documentation on AsciiDoc and
AsciiDoctor on the web. AsciiDoc can be rendered into a variety of
formats, but one of it's great advantages is the ability to produece
DocBook.
There are many ways to render AsciiDoc, but the simplest to produce
html is this:
% asciidoctor -a data-uri mellon_user_guide.adoc
Note: the "-a data-uri" causes the images to be inlined in the HTML
output so that everything is contained in one HTML file.
If you want to edit the source it can be very useful to be able to see
the rendered version as you work. This link is a guide on how to do
that. Included in this directory is a Guardfile which can be used in
conjuction with the live preview discussed in the link.
http://asciidoctor.org/docs/editing-asciidoc-with-live-preview/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 164 KiB

View File

@ -1,812 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="6.776546in"
height="5.968811in"
viewBox="0 0 172.12427 151.6078"
version="1.1"
id="svg8"
inkscape:version="0.92+devel unknown"
sodipodi:docname="saml-web-sso.svg">
<defs
id="defs2">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker13542"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path13540"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker12194"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path12192"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker12008"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path12006"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker11121"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path11119"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker10561"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
inkscape:connector-curvature="0"
id="path10559"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker10367"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path10365"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker10183"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path10181"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker10047"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path10045"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker9929"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path9927"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker9811"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path9809"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker9639"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path9637"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4364"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker9453"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path9451"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker9359"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path9357"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker9176"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path9174"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker8948"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path8946"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4373"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker8521"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path8519"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker8133"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path8131"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4385"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
inkscape:connector-curvature="0"
id="path4385-2"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-9"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4385-0"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker9176-2"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
inkscape:connector-curvature="0"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path9174-1" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-1"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4373-6"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="289.16938"
inkscape:cy="283.98371"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1103"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-text-baseline="true"
units="in">
<inkscape:grid
type="xygrid"
id="grid4267"
originx="-123.04236"
originy="-320.37079" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-14.490125,-16.188666)">
<g
id="g8094">
<g
id="g4349">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.70599997;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4269"
width="36.459583"
height="15.875"
x="14.843125"
y="16.541666" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.96875px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="32.880096"
y="21.833294"
id="text4273"><tspan
sodipodi:role="line"
id="tspan4271"
x="32.880096"
y="21.833294"
style="stroke-width:0.26458332px">Browser</tspan></text>
</g>
<path
inkscape:connector-curvature="0"
id="path4362"
d="M 33.068256,32.416668 V 167.35417"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
sodipodi:nodetypes="cc" />
</g>
<g
id="g8114">
<g
id="g4355">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.70599997;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4269-2"
width="36.459583"
height="15.875"
x="82.311874"
y="16.541666" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.96875px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="100.34885"
y="21.833294"
id="text4273-5"><tspan
sodipodi:role="line"
id="tspan4271-8"
x="100.34885"
y="21.833294"
style="stroke-width:0.26458332px">Service Provider</tspan><tspan
sodipodi:role="line"
x="100.34885"
y="26.794231"
style="stroke-width:0.26458332px"
id="tspan4325">(e.g. Mellon)</tspan></text>
</g>
<path
inkscape:connector-curvature="0"
id="path4362-9"
d="M 100.53701,32.416668 V 167.35417"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-6)"
sodipodi:nodetypes="cc" />
</g>
<g
id="g8127">
<g
id="g4360">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.70599997;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4269-6"
width="36.501942"
height="15.875"
x="149.75945"
y="16.541666" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.96875px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="167.8176"
y="21.833294"
id="text4273-2"><tspan
sodipodi:role="line"
id="tspan4271-84"
x="167.8176"
y="21.833294"
style="stroke-width:0.26458332px">Identity Provider</tspan></text>
</g>
<path
inkscape:connector-curvature="0"
id="path4362-8"
d="M 168.00576,32.416668 V 167.35417"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9)"
sodipodi:nodetypes="cc" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8133)"
d="m 32.980878,46.524449 67.243102,-0.07039"
id="path8129"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="41.003376"
y="39.734055"
id="text8173"><tspan
sodipodi:role="line"
id="tspan8171"
x="41.003376"
y="39.734055"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start"
id="tspan8930">1.</tspan> User attempts to access</tspan><tspan
sodipodi:role="line"
x="41.003376"
y="43.702805"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8175">resource at SP</tspan></text>
<g
id="g14165">
<rect
y="37.235371"
x="106.86239"
height="9.3031454"
width="52.736366"
id="rect8177"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.17638889;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
id="text8181"
y="40.92421"
x="108.40961"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan8183"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
y="40.92421"
x="108.40961"
sodipodi:role="line">Is there a session for this user?</tspan><tspan
id="tspan8187"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
y="44.89296"
x="108.40961"
sodipodi:role="line">No, then create one.</tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.96875px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="130.88298"
y="43.221416"
id="text8191"><tspan
sodipodi:role="line"
id="tspan8189"
x="130.88298"
y="46.73283"
style="stroke-width:0.26458332px"></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8521)"
d="M 100.54167,56.229167 H 29.104167 v 7.9375 l 137.873523,0.475306"
id="path8511"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="108.40961"
y="54.901424"
id="text8173-5"><tspan
sodipodi:role="line"
x="108.40961"
y="54.901424"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8922"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start"
id="tspan8928">2.</tspan> SP determines IdP. Creates</tspan><tspan
sodipodi:role="line"
x="108.40961"
y="58.870174"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8926">&lt;AuthnRequest&gt; message and</tspan><tspan
sodipodi:role="line"
x="108.40961"
y="62.838924"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8920">embeds it in URL redirect to IdP.</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10367)"
d="M 168.01456,78.093696 H 33.390649"
id="path9166"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker9929)"
d="M 33.235937,92.461912 H 167.55173"
id="path10513"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10561)"
d="M 167.9244,107.65285 H 28.971263 l 0.132904,9.43048 h 71.437503"
id="path10551"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="41.003376"
y="75.538948"
id="text8173-5-1"><tspan
sodipodi:role="line"
x="41.003376"
y="75.538948"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8920-7">Login form sent to user</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="41.003376"
y="89.566666"
id="text8173-5-1-7"><tspan
sodipodi:role="line"
x="41.003376"
y="89.566666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8920-7-7">User replies with userid &amp; password</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="108.40961"
y="86.209251"
id="text8173-5-0"><tspan
sodipodi:role="line"
x="108.40961"
y="86.209251"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8920-1"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8928-5">3.</tspan> IdP authenticates principal.</tspan></text>
<g
id="g11956"
transform="translate(-1.0583333)">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path11111"
d="M 108.47917,85.333333 103.1875,80.041667"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend)" />
<path
inkscape:connector-curvature="0"
id="path11111-8"
d="M 108.47917,85.333333 103.1875,90.625"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="108.40961"
y="97.234795"
id="text8173-5-04"><tspan
sodipodi:role="line"
x="108.40961"
y="97.234795"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8920-2"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8928-4">4.</tspan> IdP responds with &lt;Assertion&gt;</tspan><tspan
sodipodi:role="line"
x="108.40961"
y="101.20354"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan11994">embedded in form which</tspan><tspan
sodipodi:role="line"
x="108.40961"
y="105.17229"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan11996">automatically posts to SP.</tspan></text>
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker12008)"
d="M 100.54167,130.3125 H 34.395833"
id="path11998"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="41.003376"
y="123.87157"
id="text8173-4"><tspan
sodipodi:role="line"
x="41.003376"
y="123.87157"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8175-4"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan8930-5">6.</tspan> SP replies with redirect for</tspan><tspan
sodipodi:role="line"
x="41.003376"
y="127.84032"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan12182">original resource (RelayState)</tspan></text>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path12190"
d="m 32.980878,143.36225 67.243102,-0.0704"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker12194)" />
<text
id="text13526"
y="137.62979"
x="41.003376"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan13524"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
y="137.62979"
x="41.003376"
sodipodi:role="line"><tspan
id="tspan13520"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start">7.</tspan> SP accesses resource again</tspan></text>
<path
inkscape:connector-curvature="0"
id="path13538"
d="M 100.54167,156.771 H 34.395833"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.40569443;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker13542)" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="41.003376"
y="152.97542"
id="text14118"><tspan
sodipodi:role="line"
x="41.003376"
y="152.97542"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan14116"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start"
id="tspan14114">8.</tspan> SP responds with resource</tspan></text>
<text
id="text14128"
y="116.28479"
x="108.40961"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan14122"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
y="116.28479"
x="108.40961"
sodipodi:role="line"><tspan
id="tspan14120"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px">5.</tspan> SP validates &lt;Assertion&gt;</tspan><tspan
id="tspan14126"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
y="120.25354"
x="108.40961"
sodipodi:role="line">and creates user session.</tspan></text>
<g
id="g14171">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.17638889;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect14132"
width="52.736366"
height="9.3031454"
x="106.81046"
y="133.54376" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="108.40961"
y="137.2326"
id="text14138"><tspan
sodipodi:role="line"
x="108.40961"
y="137.2326"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan14134">Is there a session for this user?</tspan><tspan
sodipodi:role="line"
x="108.40961"
y="141.20135"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.17499995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px"
id="tspan14136">Yes, validate &amp; return resource.</tspan></text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 46 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,62 +0,0 @@
#ifdef HAVE_LASSO_UTILS_H
#include <lasso/utils.h>
#else
#define lasso_assign_string(dest,src) \
{ \
char *__tmp = g_strdup(src); \
lasso_release_string(dest); \
dest = __tmp; \
}
#define lasso_release_string(dest) \
lasso_release_full(dest, g_free)
#define lasso_release_full(dest, free_function) \
{ \
if (dest) { \
free_function(dest); dest = NULL; \
} \
}
#define lasso_check_type_equality(a,b)
#define lasso_release_full2(dest, free_function, type) \
{ \
lasso_check_type_equality(dest, type); \
if (dest) { \
free_function(dest); dest = NULL; \
} \
}
#define lasso_release_list(dest) \
lasso_release_full2(dest, g_list_free, GList*)
#define lasso_release_list_of_full(dest, free_function) \
{ \
GList **__tmp = &(dest); \
if (*__tmp) { \
g_list_foreach(*__tmp, (GFunc)free_function, NULL); \
lasso_release_list(*__tmp); \
} \
}
#define lasso_release_list_of_strings(dest) \
lasso_release_list_of_full(dest, g_free)
#endif
#ifndef LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED
#define LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned"
#endif
#ifndef LASSO_SAML2_CONDITIONS_DELEGATION
#define LASSO_SAML2_CONDITIONS_DELEGATION "urn:oasis:names:tc:SAML:2.0:conditions:delegation"
#endif
#ifndef LASSO_SAML_EXT_CHANNEL_BINDING
#define LASSO_SAML_EXT_CHANNEL_BINDING "urn:oasis:names:tc:SAML:protocol:ext:channel-binding"
#endif

View File

@ -33,10 +33,10 @@ if ! echo "$BASEURL" | grep -q '^https\?://'; then
exit 1
fi
HOST="$(echo "$BASEURL" | sed 's#^[a-z]*://\([^:/]*\).*#\1#')"
HOST="$(echo "$BASEURL" | sed 's#^[a-z]*://\([^/]*\).*#\1#')"
BASEURL="$(echo "$BASEURL" | sed 's#/$##')"
OUTFILE="$(echo "$ENTITYID" | sed 's/[^0-9A-Za-z.]/_/g' | sed 's/__*/_/g')"
OUTFILE="$(echo "$ENTITYID" | sed 's/[^A-Za-z.]/_/g' | sed 's/__*/_/g')"
echo "Output files:"
echo "Private key: $OUTFILE.key"
echo "Certificate: $OUTFILE.cert"

View File

@ -19,14 +19,16 @@
*
*/
#include "auth_mellon.h"
#include <curl/curl.h>
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#ifdef AP_NEED_SET_MUTEX_PERMS
#include "unixd.h"
#endif
/* This function is called after the configuration of the server is parsed
* (it's a post-config hook).
*
@ -48,9 +50,10 @@ APLOG_USE_MODULE(auth_mellon);
static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
apr_pool_t *tmp, server_rec *s)
{
am_cache_entry_t *table;
apr_size_t mem_size;
am_mod_cfg_rec *mod;
int rv;
int rv, i;
const char userdata_key[] = "auth_mellon_init";
char buffer[512];
void *data;
@ -90,13 +93,9 @@ static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
*/
mod->init_cache_size = mod->cache_size;
mod->init_lock_file = apr_pstrdup(conf, mod->lock_file);
mod->init_entry_size = mod->entry_size;
if (mod->init_entry_size < AM_CACHE_MIN_ENTRY_SIZE) {
mod->init_entry_size = AM_CACHE_MIN_ENTRY_SIZE;
}
/* find out the memory size of the cache */
mem_size = mod->init_entry_size * mod->init_cache_size;
mem_size = sizeof(am_cache_entry_t) * mod->init_cache_size;
/* Create the shared memory, exit if it fails. */
@ -110,7 +109,11 @@ static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
}
/* Initialize the session table. */
am_cache_init(mod);
table = apr_shm_baseaddr_get(mod->cache);
for (i = 0; i < mod->cache_size; i++) {
table[i].key[0] = '\0';
table[i].access = 0;
}
/* Now create the mutex that we need for locking the shared memory, then
* test for success. we really need this, so we exit on failure. */
@ -130,7 +133,7 @@ static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
/* On some platforms the mutex is implemented as a file. To allow child
* processes running as a different user to open it, it is necessary to
* change the permissions on it. */
rv = ap_unixd_set_global_mutex_perms(mod->lock);
rv = unixd_set_global_mutex_perms(mod->lock);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"Failed to set permissions on session table lock;"
@ -185,55 +188,14 @@ static void am_child_init(apr_pool_t *p, server_rec *s)
}
static int am_create_request(request_rec *r)
{
am_req_cfg_rec *req_cfg;
req_cfg = apr_pcalloc(r->pool, sizeof(am_req_cfg_rec));
req_cfg->cookie_value = NULL;
#ifdef HAVE_ECP
req_cfg->ecp_authn_req = false;
#endif /* HAVE_ECP */
#ifdef ENABLE_DIAGNOSTICS
req_cfg->diag_emitted = false;
#endif
ap_set_module_config(r->request_config, &auth_mellon_module, req_cfg);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
/* Our handler needs to run before mod_proxy so that it can properly
* return ECP AuthnRequest messages when running as a reverse proxy.
* See: https://github.com/Uninett/mod_auth_mellon/pull/196
*/
static const char * const run_handler_before[]={ "mod_proxy.c", NULL };
ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_create_request(am_create_request, NULL, NULL, APR_HOOK_MIDDLE);
/* Add the hook to handle requests to the mod_auth_mellon endpoint.
*
* This is APR_HOOK_FIRST because we do not expect nor require users
* to add a SetHandler option for the endpoint. Instead, simply
* setting MellonEndpointPath should be enough.
*
* Therefore this hook must run before any handler that may check
* r->handler and decide that it is the only handler for this URL.
*/
ap_hook_handler(am_handler, NULL, run_handler_before, APR_HOOK_FIRST);
#ifdef ENABLE_DIAGNOSTICS
ap_hook_open_logs(am_diag_log_init,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_log_transaction(am_diag_finalize_request,NULL,NULL,APR_HOOK_REALLY_LAST);
#endif
ap_hook_handler(am_handler, NULL, NULL, APR_HOOK_MIDDLE);
return;
}
@ -243,7 +205,7 @@ module AP_MODULE_DECLARE_DATA auth_mellon_module =
auth_mellon_dir_config,
auth_mellon_dir_merge,
auth_mellon_server_config,
auth_mellon_srv_merge,
NULL,
auth_mellon_commands,
register_hooks
};

View File

@ -1,226 +0,0 @@
#!/bin/sh
# Print a version string.
scriptversion=2012-12-31.23; # UTC
# Copyright (C) 2007-2013 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
# It may be run two ways:
# - from a git repository in which the "git describe" command below
# produces useful output (thus requiring at least one signed tag)
# - from a non-git-repo directory containing a .tarball-version file, which
# presumes this script is invoked like "./git-version-gen .tarball-version".
# In order to use intra-version strings in your project, you will need two
# separate generated version string files:
#
# .tarball-version - present only in a distribution tarball, and not in
# a checked-out repository. Created with contents that were learned at
# the last time autoconf was run, and used by git-version-gen. Must not
# be present in either $(srcdir) or $(builddir) for git-version-gen to
# give accurate answers during normal development with a checked out tree,
# but must be present in a tarball when there is no version control system.
# Therefore, it cannot be used in any dependencies. GNUmakefile has
# hooks to force a reconfigure at distribution time to get the value
# correct, without penalizing normal development with extra reconfigures.
#
# .version - present in a checked-out repository and in a distribution
# tarball. Usable in dependencies, particularly for files that don't
# want to depend on config.h but do want to track version changes.
# Delete this file prior to any autoconf run where you want to rebuild
# files to pick up a version string change; and leave it stale to
# minimize rebuild time after unrelated changes to configure sources.
#
# As with any generated file in a VC'd directory, you should add
# /.version to .gitignore, so that you don't accidentally commit it.
# .tarball-version is never generated in a VC'd directory, so needn't
# be listed there.
#
# Use the following line in your configure.ac, so that $(VERSION) will
# automatically be up-to-date each time configure is run (and note that
# since configure.ac no longer includes a version string, Makefile rules
# should not depend on configure.ac for version updates).
#
# AC_INIT([GNU project],
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
# [bug-project@example])
#
# Then use the following lines in your Makefile.am, so that .version
# will be present for dependencies, and so that .version and
# .tarball-version will exist in distribution tarballs.
#
# EXTRA_DIST = $(top_srcdir)/.version
# BUILT_SOURCES = $(top_srcdir)/.version
# $(top_srcdir)/.version:
# echo $(VERSION) > $@-t && mv $@-t $@
# dist-hook:
# echo $(VERSION) > $(distdir)/.tarball-version
me=$0
version="git-version-gen $scriptversion
Copyright 2011 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
usage="\
Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT]
Print a version string.
Options:
--prefix prefix of git tags (default 'v')
--fallback fallback version to use if \"git --version\" fails
--help display this help and exit
--version output version information and exit
Running without arguments will suffice in most cases."
prefix=v
fallback=
while test $# -gt 0; do
case $1 in
--help) echo "$usage"; exit 0;;
--version) echo "$version"; exit 0;;
--prefix) shift; prefix="$1";;
--fallback) shift; fallback="$1";;
-*)
echo "$0: Unknown option '$1'." >&2
echo "$0: Try '--help' for more information." >&2
exit 1;;
*)
if test "x$tarball_version_file" = x; then
tarball_version_file="$1"
elif test "x$tag_sed_script" = x; then
tag_sed_script="$1"
else
echo "$0: extra non-option argument '$1'." >&2
exit 1
fi;;
esac
shift
done
if test "x$tarball_version_file" = x; then
echo "$usage"
exit 1
fi
tag_sed_script="${tag_sed_script:-s/x/x/}"
nl='
'
# Avoid meddling by environment variable of the same name.
v=
v_from_git=
# First see if there is a tarball-only version file.
# then try "git describe", then default.
if test -f $tarball_version_file
then
v=`cat $tarball_version_file` || v=
case $v in
*$nl*) v= ;; # reject multi-line output
[0-9]*) ;;
*) v= ;;
esac
test "x$v" = x \
&& echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
fi
if test "x$v" != x
then
: # use $v
# Otherwise, if there is at least one git commit involving the working
# directory, and "git describe" output looks sensible, use that to
# derive a version string.
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
&& v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
&& case $v in
$prefix[0-9]*) ;;
*) (exit 1) ;;
esac
then
# Is this a new git that lists number of commits since the last
# tag or the previous older version that did not?
# Newer: v6.10-77-g0f8faeb
# Older: v6.10-g0f8faeb
case $v in
*-*-*) : git describe is okay three part flavor ;;
*-*)
: git describe is older two part flavor
# Recreate the number of commits and rewrite such that the
# result is the same as if we were using the newer version
# of git describe.
vtag=`echo "$v" | sed 's/-.*//'`
commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
|| { commit_list=failed;
echo "$0: WARNING: git rev-list failed" 1>&2; }
numcommits=`echo "$commit_list" | wc -l`
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
test "$commit_list" = failed && v=UNKNOWN
;;
esac
# Change the first '-' to a '.', so version-comparing tools work properly.
# Remove the "g" in git describe's output string, to save a byte.
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1.g/'`;
v_from_git=1
elif test "x$fallback" = x || git --version >/dev/null 2>&1; then
v=UNKNOWN
else
v=$fallback
fi
v=`echo "$v" |sed "s/^$prefix//"`
# Test whether to append the "-dirty" suffix only if the version
# string we're using came from git. I.e., skip the test if it's "UNKNOWN"
# or if it came from .tarball-version.
if test "x$v_from_git" != x; then
# Don't declare a version "dirty" merely because a time stamp has changed.
git update-index --refresh > /dev/null 2>&1
dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
case "$dirty" in
'') ;;
*) # Append the suffix only if there isn't one already.
case $v in
*-dirty) ;;
*) v="$v-dirty" ;;
esac ;;
esac
fi
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
echo "$v" | tr -d "$nl"
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End: