diff -Nru libapache2-mod-auth-openidc-2.3.1/auth_openidc.conf libapache2-mod-auth-openidc-2.3.2/auth_openidc.conf --- libapache2-mod-auth-openidc-2.3.1/auth_openidc.conf 2017-07-13 12:23:30.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/auth_openidc.conf 2017-09-05 08:59:38.000000000 +0000 @@ -365,6 +365,11 @@ # An optional regular expression can be added as a 2nd parameter that will be applied to the # claim value from the 1st parameter and the first match returned from that expression will # be set as the REMOTE_USER. E.g. to strip a domain from an e-mail style address you'd use ^(.*)@ +# +# An optional 3rd parameter can be added that would contain string with number backrefrences. +# Backrefrences must be in the form $1, $2.. etc. +# E.g. to extract username in the form DOMAIN\userid from e-mail style address you may use +# ^(.*)@([^.]+)\..+$ $2\\$1 #OIDCOAuthRemoteUserClaim [] # Define the way(s) in which bearer OAuth 2.0 access tokens can be passed to this Resource Server. @@ -616,6 +621,11 @@ # An optional regular expression can be added as a 2nd parameter that will be applied to the # resulting value from the 1st parameter and the first match returned from that expression will # be set as the REMOTE_USER. E.g. to strip a domain from an e-mail style address you'd use ^(.*)@ +# +# An optional 3rd parameter can be added that would contain string with number backrefrences. +# Backrefrences must be in the form $1, $2.. etc. +# E.g. to extract username in the form DOMAIN\userid from e-mail style address you may use +# ^(.*)@([^.]+)\..+$ $2\\$1 #OIDCRemoteUserClaim [@] [] # Define the way(s) in which the id_token contents are passed to the application according to OIDCPassClaimsAs. @@ -691,20 +701,21 @@ # Can be configured on a per Directory/Location basis. The default is "Off". #OIDCPassRefreshToken [On|Off] -# Request Object/URI settings. For example: +# Request Object/URI settings expressed as a string that is a "double-quote-escaped" JSON object. For example: # "{ \"copy_from_request\": [ \"claims\", \"response_type\", \"response_mode\", \"login_hint\", \"id_token_hint\", \"nonce\", \"state\", \"redirect_uri\", \"scope\", \"client_id\" ], \"static\": { \"some\": \"value\", \"some_nested\": { \"some_array\": [ 1,2,3] } }, \"crypto\": { \"sign_alg\": \"HS256\", \"crypt_alg\": \"A256KW\", \"crypt_enc\": \"A256CBC-HS512\" }, \"url\": \"https://www.pingidentity.nl/protected/\", \"request_object_type\" : \"request\" }" # Parameters: -# copy_from_request (array) : array of parameters copied from request -# static (object) : parameter value is merged to the request object -# crypto (object) : defines cryptography used to create request object -# sign_alg (string) : algorithm used to sign request object (JWS alg parameter) -# crypt_alg (string) : algorithm used to encrypt CEK of request object (JWE alg parameter) -# crypt_enc (string) : algorithm used to encrypt request object (JWE enc parameter) -# url (string) : use this url instead of redirect_uri for request_uri -# request_object_type (string) : parameter used for sending authorization request object -# "request_uri" (default) or "request" +# copy_from_request (array) : array of query parameter names copied from request +# copy_and_remove_from_request (array) : array of parameter names copied from request and removed as query parameter +# static (object) : parameter value is merged to the request object +# crypto (object) : defines cryptography used to create request object +# sign_alg (string) : algorithm used to sign request object (JWS alg parameter) +# crypt_alg (string) : algorithm used to encrypt CEK of request object (JWE alg parameter) +# crypt_enc (string) : algorithm used to encrypt request object (JWE enc parameter) +# url (string) : use this url instead of redirect_uri for request_uri +# request_object_type (string) : parameter used for sending authorization request object +# "request_uri" (default) or "request" # NB: this can be overridden on a per-OP basis in the .conf file using the key: request_object -#OIDCRequestObject +#OIDCRequestObject # Provider metadata refresh interval for the metadata in a multi-provider setup (with OIDCMetadataDir). # When not defined the default is 0 seconds, i.e. it is never refreshed. diff -Nru libapache2-mod-auth-openidc-2.3.1/AUTHORS libapache2-mod-auth-openidc-2.3.2/AUTHORS --- libapache2-mod-auth-openidc-2.3.1/AUTHORS 2017-05-30 23:07:09.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/AUTHORS 2017-08-29 11:31:12.000000000 +0000 @@ -36,3 +36,5 @@ Wouter Hund Hans Keeler Moritz Schlarb + remi-cc + hihellobolke diff -Nru libapache2-mod-auth-openidc-2.3.1/ChangeLog libapache2-mod-auth-openidc-2.3.2/ChangeLog --- libapache2-mod-auth-openidc-2.3.1/ChangeLog 2017-07-19 18:00:22.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/ChangeLog 2017-09-18 14:09:39.000000000 +0000 @@ -1,3 +1,49 @@ +09/18/2017 +- release 2.3.2 + +09/11/2017 +- fix "graceful" restart for shm/redis cache backends; see #296 +- bump to 2.3.2rc8 + +09/05/2017 +- optionally remove request object parameters from the authorization request URL with "copy_and_remove_from_request"; see #294 +- bump to 2.3.2rc7 + +08/29/2017 +- properly support JSON boolean values in metadata .conf files +- add regex substitution for *RemoteUserClaim; thanks @hihellobolke +- bump to 2.3.2rc6 + +08/27/2017 +- add issuer specific redirect URI option ("issuer_specific_redirect_uri") for multi-provider setups to mitigate IDP mixup +- bump to 2.3.2rc5 + +08/20/2017 +- fix public clients; add endpoint authentication method "none" +- bump to 2.3.2rc4 + +08/02/2017 +- update experimental token binding support to https://tools.ietf.org/html/draft-ietf-tokbind-ttrp-01 + and use header names prefixed with "Sec-"; depends on mod_token_binding >= 0.3.4 now +- bump to 2.3.2rc3 + +08/01/2017 +- don't abort when mutex operations fail +- printout textual descriptions of errors returned by mutex operations +- bump to 2.3.2rc2 + +07/28/2017 +- fix issue with the combination of shared memory (shm) cache and using encryption (OIDCCacheEncrypt On) + where the cache value would be corrupted after the first (successful) retrieval +- bump to 2.3.2rc1 + +07/27/2017 +- support paths that are relative to the Apache root dir for: + OIDCHTMLErrorTemplate, OIDCPublicKeyFiles, OIDCPrivateKeyFiles, + OIDCOAuthVerifyCertFiles, OIDCClientTokenEndpointCert, OIDCClientTokenEndpointKey, + OIDCOAuthIntrospectionEndpointCert and OIDCOAuthIntrospectionEndpointKey +- bump to 2.3.2rc0 + 07/19/2017 - handle multiple values in X-Forwarded-* headers as to better support chains of reverse proxies in front of mod_auth_openidc - log request headers in oidc_util_hdr_in_get diff -Nru libapache2-mod-auth-openidc-2.3.1/configure libapache2-mod-auth-openidc-2.3.2/configure --- libapache2-mod-auth-openidc-2.3.1/configure 2017-07-19 18:01:43.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/configure 2017-09-18 16:01:23.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for mod_auth_openidc 2.3.1. +# Generated by GNU Autoconf 2.69 for mod_auth_openidc 2.3.2. # # Report bugs to . # @@ -580,8 +580,8 @@ # Identity of this package. PACKAGE_NAME='mod_auth_openidc' PACKAGE_TARNAME='mod_auth_openidc' -PACKAGE_VERSION='2.3.1' -PACKAGE_STRING='mod_auth_openidc 2.3.1' +PACKAGE_VERSION='2.3.2' +PACKAGE_STRING='mod_auth_openidc 2.3.2' PACKAGE_BUGREPORT='hans.zandbelt@zmartzone.eu' PACKAGE_URL='' @@ -1269,7 +1269,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures mod_auth_openidc 2.3.1 to adapt to many kinds of systems. +\`configure' configures mod_auth_openidc 2.3.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1331,7 +1331,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of mod_auth_openidc 2.3.1:";; + short | recursive ) echo "Configuration of mod_auth_openidc 2.3.2:";; esac cat <<\_ACEOF @@ -1445,7 +1445,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -mod_auth_openidc configure 2.3.1 +mod_auth_openidc configure 2.3.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1747,7 +1747,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by mod_auth_openidc $as_me 2.3.1, which was +It was created by mod_auth_openidc $as_me 2.3.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2096,7 +2096,7 @@ -NAMEVER=mod_auth_openidc-2.3.1 +NAMEVER=mod_auth_openidc-2.3.2 # This section defines the --with-apxs2 option. @@ -4886,7 +4886,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by mod_auth_openidc $as_me 2.3.1, which was +This file was extended by mod_auth_openidc $as_me 2.3.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4939,7 +4939,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -mod_auth_openidc config.status 2.3.1 +mod_auth_openidc config.status 2.3.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru libapache2-mod-auth-openidc-2.3.1/configure.ac libapache2-mod-auth-openidc-2.3.2/configure.ac --- libapache2-mod-auth-openidc-2.3.1/configure.ac 2017-07-19 15:38:32.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/configure.ac 2017-09-18 14:09:51.000000000 +0000 @@ -1,4 +1,4 @@ -AC_INIT([mod_auth_openidc],[2.3.1],[hans.zandbelt@zmartzone.eu]) +AC_INIT([mod_auth_openidc],[2.3.2],[hans.zandbelt@zmartzone.eu]) AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION()) diff -Nru libapache2-mod-auth-openidc-2.3.1/debian/changelog libapache2-mod-auth-openidc-2.3.2/debian/changelog --- libapache2-mod-auth-openidc-2.3.1/debian/changelog 2017-08-08 07:31:43.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/debian/changelog 2017-11-14 11:14:22.000000000 +0000 @@ -1,3 +1,10 @@ +libapache2-mod-auth-openidc (2.3.2-1) unstable; urgency=medium + + * New upstream version 2.3.2 + * link against openssl 1.1 (closes: #858993) + + -- Christoph Martin Tue, 14 Nov 2017 12:14:22 +0100 + libapache2-mod-auth-openidc (2.3.1-2) unstable; urgency=medium * Fix maintainer script generation to enable/disable the module on diff -Nru libapache2-mod-auth-openidc-2.3.1/debian/control libapache2-mod-auth-openidc-2.3.2/debian/control --- libapache2-mod-auth-openidc-2.3.1/debian/control 2017-08-08 07:31:43.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/debian/control 2017-11-14 11:14:22.000000000 +0000 @@ -2,7 +2,7 @@ Priority: extra Maintainer: Hans Zandbelt Uploaders: Christoph Martin , Moritz Schlarb -Build-Depends: debhelper (>= 8.0.0), autotools-dev, apache2-dev, libssl1.0-dev | libssl-dev (<< 1.1), libcurl4-openssl-dev, libjansson-dev, libhiredis-dev, libpcre3-dev, libcjose-dev, pkg-config +Build-Depends: debhelper (>= 8.0.0), autotools-dev, apache2-dev, libssl-dev, libcurl4-openssl-dev, libjansson-dev, libhiredis-dev, libpcre3-dev, libcjose-dev, pkg-config Standards-Version: 3.9.8 Section: web Homepage: https://github.com/pingidentity/mod_auth_openidc diff -Nru libapache2-mod-auth-openidc-2.3.1/INSTALL libapache2-mod-auth-openidc-2.3.2/INSTALL --- libapache2-mod-auth-openidc-2.3.1/INSTALL 2017-05-12 20:34:59.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/INSTALL 2017-09-13 06:22:35.000000000 +0000 @@ -32,6 +32,10 @@ Note that, depending on your distribution, apxs2 may be named apxs. +FreeBSD users can use one of the following two options to install mod_auth_openidc: +- To install the port: cd /usr/ports/www/mod_auth_openidc/ && make install clean +- To add the package: pkg install ap24-mod_auth_openidc + Configuration ============= diff -Nru libapache2-mod-auth-openidc-2.3.1/Makefile.in libapache2-mod-auth-openidc-2.3.2/Makefile.in --- libapache2-mod-auth-openidc-2.3.1/Makefile.in 2017-05-12 20:34:59.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/Makefile.in 2017-08-29 11:31:12.000000000 +0000 @@ -12,7 +12,8 @@ src/session.c \ src/metadata.c \ src/jose.c \ - src/parse.c + src/parse.c \ + src/pcre_subst.c \ ifeq (@HAVE_LIBHIREDIS@, 1) SRC += \ @@ -31,7 +32,8 @@ src/mod_auth_openidc.h \ src/jose.h \ src/parse.h \ - src/cache/cache.h + src/cache/cache.h \ + src/pcre_subst.h \ # Files to include when making a .tar.gz-file for distribution DISTFILES=$(SRC) \ diff -Nru libapache2-mod-auth-openidc-2.3.1/README.md libapache2-mod-auth-openidc-2.3.2/README.md --- libapache2-mod-auth-openidc-2.3.1/README.md 2017-07-17 08:19:36.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/README.md 2017-08-29 11:31:12.000000000 +0000 @@ -16,9 +16,8 @@ receives user identity information from the OP in a so called ID Token and passes the identity information (a.k.a. claims) in the ID Token to applications hosted and protected by the Apache web server. -It can also be configured as an OAuth 2.0 *Resource Server* (RS), consuming bearer access tokens and introspecting/validating -them against a token introspection endpoint of an OAuth 2.0 Authorization Server and authorizing the Clients based on the -introspection results. +It can also be configured as an OAuth 2.0 *Resource Server* (RS), consuming bearer access tokens and validating +them against an OAuth 2.0 Authorization Server, authorizing the Clients based on the validation results. The protected content and/or applications can be served by the Apache server itself or it can be served from elsewhere when Apache is configured as a Reverse Proxy in front of the origin server(s). @@ -30,12 +29,8 @@ It allows for authorization rules (based on standard Apache `Require` primitives) that can be matched against the set of claims provided in the `id_token`/ `userinfo` claims. -This module supports all defined OpenID Connect flows, including *Basic Client Profile*, *Implicit Client Profile*, -*Hybrid Flows* and the *Refresh Flow*. It supports connecting to multiple OpenID Connect Providers through reading/writing -provider metadata files in a specified metadata directory. - *mod_auth_openidc* supports the following specifications: -- [OpenID Connect](http://openid.net/specs/openid-connect-core-1_0.html) +- [OpenID Connect](http://openid.net/specs/openid-connect-core-1_0.html) Basic, Implicit, Hybrid and Refresh flows. - [OpenID Connect Dynamic Client Registration](http://openid.net/specs/openid-connect-registration-1_0.html) - [OpenID Provider Discovery](http://openid.net/specs/openid-connect-discovery-1_0.html) - [OAuth 2.0 Form Post Response Mode](http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) @@ -44,7 +39,8 @@ on how to configure it. Alternatively the module can operate as an OAuth 2.0 Resource Server to an OAuth 2.0 Authorization Server, -introspecting/validating bearer Access Tokens conforming to [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662) or similar. +introspecting/validating bearer Access Tokens conforming to [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662) (or similar), +or verifiying them locally if they are JWTs. The `REMOTE_USER` variable setting, passing claims in HTTP headers and authorization based on `Require` primitives works in the same way as described for OpenID Connect above. See the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server) for information on how to configure it. @@ -89,127 +85,42 @@ The above is an authorization example of an exact match of a provided claim against a string value. For more authorization options see the [Wiki page on Authorization](https://github.com/pingidentity/mod_auth_openidc/wiki/Authorization). -### OpenID Connect SSO with multiple OpenID Connect Providers - -Sample configuration for multiple OpenID Connect providers, which triggers OpenID -Connect Discovery first to find the user's OP. - -`OIDCMetadataDir` points to a directory that contains files that contain per-provider -configuration data. For each provider, there are 3 types of files in the directory: +### Quickstart with a generic OpenID Connect Provider -1. `.provider` -contains (standardized) OpenID Connect Discovery OP JSON metadata where each -name of the file is the url-encoded issuer name of the OP that is described -by the metadata in that file. - -2. `.client` -contains statically configured or dynamically registered Dynamic Client Registration -specific JSON metadata (based on the OpenID Connect Client Registration specification) -and the filename is the url-encoded issuer name of the OP that this client is registered -with. Sample client metadata for issuer `https://localhost:9031`, so the client metadata -filename is `localhost%3A9031.client`: - - { - "client_id" : "ac_oic_client", - "client_secret" : "abc123DEFghijklmnop4567rstuvwxyzZYXWUT8910SRQPOnmlijhoauthplaygroundapplication" - } - -3. `.conf` -contains *mod_auth_openidc* specific custom JSON metadata that can be used to overrule -some of the settings defined in `auth_openidc.conf` on a per-client basis. The filename -is the URL-encoded issuer name of the OP that this client is registered with. - -Entries that can be included in the .conf file are: - - "ssl_validate_server" overrides OIDCSSLValidateServer (value 0 or 1...) - "scope" overrides OIDCScope - "response_type" overrides OIDCResponseType - "response_mode" overrides OIDCResponseMode - "pkce_method" overrides OIDCPKCEMethod - "client_name" overrides OIDCClientName - "client_contact" overrides OIDCClientContact - "idtoken_iat_slack" overrides OIDCIDTokenIatSlack - "session_max_duration" overrides OIDCSessionMaxDuration - "jwks_refresh_interval" overrides OIDCJWKSRefreshInterval - "client_jwks_uri" overrides OIDCClientJwksUri - "id_token_signed_response_alg" overrides OIDCIDTokenSignedResponseAlg - "id_token_encrypted_response_alg" overrides OIDCIDTokenEncryptedResponseAlg - "id_token_encrypted_response_enc" overrides OIDCIDTokenEncryptedResponseEnc - "userinfo_signed_response_alg" overrides OIDCUserInfoSignedResponseAlg - "userinfo_encrypted_response_alg" overrides OIDCUserInfoEncryptedResponseAlg - "userinfo_encrypted_response_enc" overrides OIDCUserInfoEncryptedResponseEnc - "auth_request_params" overrides OIDCAuthRequestParams - "token_endpoint_params" overrides OIDCProviderTokenEndpointParams - "token_endpoint_auth" overrides OIDCProviderTokenEndpointAuth - "registration_endpoint_json" overrides OIDCProviderRegistrationEndpointJson - "userinfo_refresh_interval" overrides OIDCUserInfoRefreshInterval - "userinfo_token_method" overrides OIDCUserInfoTokenMethod - "request_object" overrides OIDCRequestObject - "auth_request_method" overrides OIDCProviderAuthRequestMethod - "registration_token" an access_token that will be used on client registration calls for the associated OP - -Sample client metadata for issuer `https://localhost:9031`, so the *mod_auth_openidc* -configuration filename is `localhost%3A9031.conf`: - - { - "ssl_validate_server" : 0, - "scope" : "openid email profile" - } - -And the related *mod_auth_openidc* Apache config section: +1. install and load `mod_auth_openidc.so` in your Apache server +1. configure your protected content/locations with `AuthType openid-connect` +1. set `OIDCRedirectURI` to a "vanity" URL within a location that is protected by mod_auth_openidc +1. register/generate a Client identifier and a secret with the OpenID Connect Provider and configure those in `OIDCClientID` and `OIDCClientSecret` respectively +1. and register the `OIDCRedirectURI` as the Redirect or Callback URI with your client at the Provider +1. configure `OIDCProviderMetadataURL` so it points to the Discovery metadata of your OpenID Connect Provider served on the `.well-known/openid-configuration` endpoint +1. configure a random password in `OIDCCryptoPassphrase` for session/state encryption purposes ```apache -OIDCMetadataDir /metadata +LoadModule auth_openidc_module modules/mod_auth_openidc.so -OIDCRedirectURI https://www.example.com/example/redirect_uri/ +OIDCProviderMetadataURL /.well-known/openid-configuration +OIDCClientID +OIDCClientSecret + +OIDCRedirectURI https:///secure/redirect_uri OIDCCryptoPassphrase - + AuthType openid-connect Require valid-user ``` +For details on configuring multiple providers see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/Multiple-Providers). -If you do not want to use the internal discovery page (you really shouldn't...), you -can have the user being redirected to an external discovery page by setting -`OIDCDiscoverURL`. That URL will be accessed with a number parameters: `oidc_callback`, `target_link_uri`, -`method` and `x_csrf`. All parameters (except `oidc_callback`) need to be returned to the `oidc_callback` URL -together with an `iss` parameter that contains the URL-encoded issuer value of the selected Provider, or a -URL-encoded account name for OpenID Connect Discovery purposes (aka. e-mail style identifier), or a domain name. - -Sample callback: - - ?target_link_uri=&iss=[||][&login_hint=][&scopes=][&auth_request_params=] - -This is also the OpenID Connect specified way of triggering 3rd party initiated SSO -to a specific provider when multiple OPs have been configured. In that case the callback -may also contain a "login_hint" parameter with the login identifier the user might use to log in. - -An additional *mod_auth_openidc* specific parameter named `auth_request_params` may also be passed -in, see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki#13-how-can-i-add-custom-parameters-to-the-authorization-request) -for its usage. - -### OpenID Connect SSO & OAuth 2.0 Access Control with PingFederate - -Another example config for using PingFederate as your OpenID Connect OP and/or -OAuth 2.0 Authorization server, based on the OAuth 2.0 PlayGround 3.x default -configuration and doing claims-based authorization. (running on `localhost` and -`https://localhost/example/redirect_uri/` registered as *redirect_uri* for the -client `ac_oic_client`) +### PingFederate OAuth 2.0 Resource Server -```apache -OIDCProviderMetadataURL https://macbook:9031/.well-known/openid-configuration +Example config for using PingFederate as your OAuth 2.0 Authorization server, +based on the OAuth 2.0 PlayGround configuration and doing claims-based authorization, using +RFC 7662 compliant Token Introspection. -OIDCSSLValidateServer Off -OIDCClientID ac_oic_client -OIDCClientSecret abc123DEFghijklmnop4567rstuvwxyzZYXWUT8910SRQPOnmlijhoauthplaygroundapplication - -OIDCRedirectURI https://localhost/example/redirect_uri/ -OIDCCryptoPassphrase -OIDCScope "openid email profile" - -OIDCOAuthIntrospectionEndpoint https://macbook:9031/as/token.oauth2 -OIDCOAuthIntrospectionEndpointParams grant_type=urn%3Apingidentity.com%3Aoauth2%3Agrant_type%3Avalidate_bearer +```apache +# remote validation +OIDCOAuthIntrospectionEndpoint https://localhost:9031/as/introspect.oauth2 OIDCOAuthIntrospectionEndpointAuth client_secret_basic OIDCOAuthRemoteUserClaim Username @@ -217,17 +128,29 @@ OIDCOAuthClientID rs_client OIDCOAuthClientSecret 2Federate - - AuthType openid-connect - #Require valid-user - Require claim sub:joe + + AuthType oauth20 + Require claim client_id:ro_client + #Require claim scope~\bprofile\b +``` +For details and additional options on the OAuth 2.0 Resource Server setup see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server). + +### Quickstart with a generic OAuth 2.0 Resource Server - +Using "local" validation of JWT bearer tokens: + +1. install and load `mod_auth_openidc.so` in your Apache server +1. configure your protected APIs/locations with `AuthType oauth20` and `Require claim` directives to restrict access to specific clients/scopes/claims/resource-owners +1. configure local or remote bearer token validation following the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server) + +```apache +# local validation +OIDCOAuthVerifySharedKeys plain## + + AuthType oauth20 - #Require valid-user - Require claim Username:joe - #Require claim scope~\bprofile\b + Require claim sub: ``` diff -Nru libapache2-mod-auth-openidc-2.3.1/src/cache/cache.h libapache2-mod-auth-openidc-2.3.2/src/cache/cache.h --- libapache2-mod-auth-openidc-2.3.1/src/cache/cache.h 2017-06-07 17:47:25.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/cache/cache.h 2017-09-11 13:22:52.000000000 +0000 @@ -54,6 +54,7 @@ #define _MOD_AUTH_OPENIDC_CACHE_H_ #include "apr_global_mutex.h" +#include "apr_shm.h" typedef void * (*oidc_cache_cfg_create)(apr_pool_t *pool); typedef int (*oidc_cache_post_config_function)(server_rec *s); @@ -78,6 +79,8 @@ typedef struct oidc_cache_mutex_t { apr_global_mutex_t *mutex; char *mutex_filename; + apr_shm_t *shm; + int *sema; } oidc_cache_mutex_t; oidc_cache_mutex_t *oidc_cache_mutex_create(apr_pool_t *pool); diff -Nru libapache2-mod-auth-openidc-2.3.1/src/cache/common.c libapache2-mod-auth-openidc-2.3.2/src/cache/common.c --- libapache2-mod-auth-openidc-2.3.1/src/cache/common.c 2017-06-09 07:16:32.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/cache/common.c 2017-09-11 13:56:31.000000000 +0000 @@ -79,9 +79,21 @@ oidc_cache_mutex_t *ctx = apr_pcalloc(pool, sizeof(oidc_cache_mutex_t)); ctx->mutex = NULL; ctx->mutex_filename = NULL; + ctx->shm = NULL; + ctx->sema = NULL; return ctx; } +#define OIDC_CACHE_ERROR_STR_MAX 255 + +/* + * convert a apr status code to a string + */ +char *oidc_cache_status2str(apr_status_t statcode) { + char buf[OIDC_CACHE_ERROR_STR_MAX]; + return apr_strerror(statcode, buf, OIDC_CACHE_ERROR_STR_MAX); +} + apr_byte_t oidc_cache_mutex_post_config(server_rec *s, oidc_cache_mutex_t *m, const char *type) { @@ -99,8 +111,8 @@ APR_LOCK_DEFAULT, s->process->pool); if (rv != APR_SUCCESS) { oidc_serror(s, - "apr_global_mutex_create failed to create mutex on file %s", - m->mutex_filename); + "apr_global_mutex_create failed to create mutex on file %s: %s (%d)", + m->mutex_filename, oidc_cache_status2str(rv), rv); return FALSE; } @@ -113,11 +125,21 @@ #endif if (rv != APR_SUCCESS) { oidc_serror(s, - "unixd_set_global_mutex_perms failed; could not set permissions "); + "unixd_set_global_mutex_perms failed; could not set permissions: %s (%d)", + oidc_cache_status2str(rv), rv); return FALSE; } #endif + rv = apr_shm_create(&m->shm, sizeof(int), NULL, s->process->pool); + if (rv != APR_SUCCESS) { + oidc_serror(s, "apr_shm_create failed to create shared memory segment"); + return FALSE; + } + + m->sema = apr_shm_baseaddr_get(m->shm); + *m->sema = 1; + return TRUE; } @@ -133,10 +155,17 @@ if (rv != APR_SUCCESS) { oidc_serror(s, - "apr_global_mutex_child_init failed to reopen mutex on file %s", - m->mutex_filename); + "apr_global_mutex_child_init failed to reopen mutex on file %s: %s (%d)", + m->mutex_filename, oidc_cache_status2str(rv), rv); + } else { + apr_global_mutex_lock(m->mutex); + m->sema = apr_shm_baseaddr_get(m->shm); + (*m->sema)++; + apr_global_mutex_unlock(m->mutex); } + //oidc_sdebug(s, "semaphore: %d (m=%pp,s=%pp)", *m->sema, m, s); + return rv; } @@ -147,10 +176,9 @@ apr_status_t rv = apr_global_mutex_lock(m->mutex); - if (rv != APR_SUCCESS) { - oidc_error(r, "apr_global_mutex_lock() failed [%d]", rv); - return FALSE; - } + if (rv != APR_SUCCESS) + oidc_error(r, "apr_global_mutex_lock() failed: %s (%d)", + oidc_cache_status2str(rv), rv); return TRUE; } @@ -162,10 +190,9 @@ apr_status_t rv = apr_global_mutex_unlock(m->mutex); - if (rv != APR_SUCCESS) { - oidc_error(r, "apr_global_mutex_unlock() failed [%d]", rv); - return FALSE; - } + if (rv != APR_SUCCESS) + oidc_error(r, "apr_global_mutex_unlock() failed: %s (%d)", + oidc_cache_status2str(rv), rv); return TRUE; } @@ -178,11 +205,24 @@ apr_status_t rv = APR_SUCCESS; if (m->mutex != NULL) { - rv = apr_global_mutex_destroy(m->mutex); - if (rv != APR_SUCCESS) { - oidc_swarn(s, "apr_global_mutex_destroy failed: [%d]", rv); + + apr_global_mutex_lock(m->mutex); + (*m->sema)--; + //oidc_sdebug(s, "semaphore: %d (m=%pp,s=%pp)", *m->sema, m->mutex, s); + apr_global_mutex_unlock(m->mutex); + + if ((m->shm != NULL) && (*m->sema == 0)) { + + rv = apr_global_mutex_destroy(m->mutex); + oidc_sdebug(s, "apr_global_mutex_destroy returned :%d", rv); + m->mutex = NULL; + + rv = apr_shm_destroy(m->shm); + oidc_sdebug(s, "apr_shm_destroy for semaphore returned: %d", rv); + m->shm = NULL; + + rv = APR_SUCCESS; } - m->mutex = NULL; } return rv; @@ -429,9 +469,15 @@ /* grab the base64url-encoded tag after the "." */ char *encoded_tag = strstr(cache_value, "."); - if (encoded_tag == NULL) + if (encoded_tag == NULL) { + oidc_error(r, + "corrupted cache value: no tag separator found in encrypted value"); return FALSE; - *encoded_tag = '\0'; + } + + /* make sure we don't modify the original string since it may be just a pointer into the cache (shm) */ + cache_value = apr_pstrmemdup(r->pool, cache_value, + strlen(cache_value) - strlen(encoded_tag)); encoded_tag++; /* base64url decode the ciphertext */ @@ -496,8 +542,8 @@ const char *key) { char *input = apr_psprintf(r->pool, "%s:%s", passphrase, key); char *output = NULL; - if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, input, - &output) == FALSE) { + if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, + input, &output) == FALSE) { oidc_error(r, "oidc_util_hash_string_and_base64url_encode returned an error"); return NULL; diff -Nru libapache2-mod-auth-openidc-2.3.1/src/cache/shm.c libapache2-mod-auth-openidc-2.3.2/src/cache/shm.c --- libapache2-mod-auth-openidc-2.3.1/src/cache/shm.c 2017-07-12 12:55:14.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/cache/shm.c 2017-09-11 13:48:40.000000000 +0000 @@ -331,9 +331,13 @@ apr_status_t rv = APR_SUCCESS; if (context->shm) { - rv = apr_shm_destroy(context->shm); - oidc_sdebug(s, "apr_shm_destroy returned: %d", rv); + apr_global_mutex_lock(context->mutex->mutex); + if (*context->mutex->sema == 1) { + rv = apr_shm_destroy(context->shm); + oidc_sdebug(s, "apr_shm_destroy returned: %d", rv); + } context->shm = NULL; + apr_global_mutex_unlock(context->mutex->mutex); } oidc_cache_mutex_destroy(s, context->mutex); diff -Nru libapache2-mod-auth-openidc-2.3.1/src/config.c libapache2-mod-auth-openidc-2.3.2/src/config.c --- libapache2-mod-auth-openidc-2.3.1/src/config.c 2017-07-18 08:23:49.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/config.c 2017-09-11 13:05:24.000000000 +0000 @@ -159,6 +159,8 @@ #define OIDC_DEFAULT_PROVIDER_TOKEN_BINDING_POLICY OIDC_TOKEN_BINDING_POLICY_OPTIONAL /* define the default HTTP method used to send the authentication request to the provider */ #define OIDC_DEFAULT_AUTH_REQUEST_METHOD OIDC_AUTH_REQUEST_METHOD_GET +/* define whether the issuer will be added to the redirect uri by default to mitigate the IDP mixup attack */ +#define OIDC_DEFAULT_PROVIDER_ISSUER_SPECIFIC_REDIRECT_URI 0 #define OIDCProviderMetadataURL "OIDCProviderMetadataURL" #define OIDCProviderIssuer "OIDCProviderIssuer" @@ -621,6 +623,8 @@ if (rv != NULL) return rv; + fname = oidc_util_get_full_path(cmd->pool, fname); + if (oidc_jwk_parse_rsa_public_key(cmd->pool, kid, fname, &jwk, &err) == FALSE) { return apr_psprintf(cmd->pool, @@ -688,6 +692,8 @@ if (rv != NULL) return rv; + fname = oidc_util_get_full_path(cmd->pool, fname); + if (oidc_jwk_parse_rsa_private_key(cmd->pool, kid, fname, &jwk, &err) == FALSE) { return apr_psprintf(cmd->pool, @@ -799,7 +805,7 @@ * set the remote user name claims, optionally plus the regular expression applied to it */ static const char *oidc_set_remote_user_claim(cmd_parms *cmd, void *struct_ptr, - const char *v1, const char *v2) { + const char *v1, const char *v2, const char *v3) { oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config( cmd->server->module_config, &auth_openidc_module); @@ -810,6 +816,8 @@ remote_user_claim->claim_name = v1; if (v2) remote_user_claim->reg_exp = v2; + if (v3) + remote_user_claim->replace = v3; return NULL; } @@ -1033,6 +1041,7 @@ c->oauth.remote_user_claim.claim_name = OIDC_DEFAULT_OAUTH_CLAIM_REMOTE_USER; c->oauth.remote_user_claim.reg_exp = NULL; + c->oauth.remote_user_claim.replace = NULL; c->oauth.verify_jwks_uri = NULL; c->oauth.verify_public_keys = NULL; @@ -1069,6 +1078,7 @@ c->claim_prefix = NULL; c->remote_user_claim.claim_name = OIDC_DEFAULT_CLAIM_REMOTE_USER; c->remote_user_claim.reg_exp = NULL; + c->remote_user_claim.replace = NULL; c->pass_idtoken_as = OIDC_PASS_IDTOKEN_AS_CLAIMS; c->cookie_http_only = OIDC_DEFAULT_COOKIE_HTTPONLY; c->cookie_same_site = OIDC_DEFAULT_COOKIE_SAME_SITE; @@ -1093,6 +1103,9 @@ c->black_listed_claims = NULL; c->white_listed_claims = NULL; + c->provider.issuer_specific_redirect_uri = + OIDC_DEFAULT_PROVIDER_ISSUER_SPECIFIC_REDIRECT_URI; + return c; } @@ -1338,6 +1351,10 @@ add->oauth.remote_user_claim.reg_exp != NULL ? add->oauth.remote_user_claim.reg_exp : base->oauth.remote_user_claim.reg_exp; + c->oauth.remote_user_claim.replace = + add->oauth.remote_user_claim.replace != NULL ? + add->oauth.remote_user_claim.replace : + base->oauth.remote_user_claim.replace; c->oauth.verify_jwks_uri = add->oauth.verify_jwks_uri != NULL ? @@ -1444,6 +1461,10 @@ add->remote_user_claim.reg_exp != NULL ? add->remote_user_claim.reg_exp : base->remote_user_claim.reg_exp; + c->remote_user_claim.replace = + add->remote_user_claim.replace != NULL ? + add->remote_user_claim.replace : + base->remote_user_claim.replace; c->pass_idtoken_as = add->pass_idtoken_as != OIDC_PASS_IDTOKEN_AS_CLAIMS ? add->pass_idtoken_as : base->pass_idtoken_as; @@ -1502,6 +1523,12 @@ add->white_listed_claims != NULL ? add->white_listed_claims : base->white_listed_claims; + c->provider.issuer_specific_redirect_uri = + add->provider.issuer_specific_redirect_uri + != OIDC_DEFAULT_PROVIDER_ISSUER_SPECIFIC_REDIRECT_URI ? + add->provider.issuer_specific_redirect_uri : + base->provider.issuer_specific_redirect_uri; + return c; } @@ -1945,8 +1972,7 @@ #endif /* defined(OPENSSL_THREADS) && APR_HAS_THREADS */ -static apr_status_t oidc_cleanup(void *data) { - +static apr_status_t oidc_cleanup_child(void *data) { server_rec *sp = (server_rec *) data; while (sp != NULL) { oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(sp->module_config, @@ -1967,6 +1993,13 @@ sp = sp->next; } + return APR_SUCCESS; +} + +static apr_status_t oidc_cleanup_parent(void *data) { + + oidc_cleanup_child(data); + #if (defined (OPENSSL_THREADS) && APR_HAS_THREADS) if (CRYPTO_get_locking_callback() == oidc_ssl_locking_callback) CRYPTO_set_locking_callback(NULL); @@ -2054,7 +2087,7 @@ } #endif /* OPENSSL_NO_THREADID */ #endif /* defined(OPENSSL_THREADS) && APR_HAS_THREADS */ - apr_pool_cleanup_register(pool, s, oidc_cleanup, apr_pool_cleanup_null); + apr_pool_cleanup_register(pool, s, oidc_cleanup_parent, apr_pool_cleanup_null); server_rec *sp = s; while (sp != NULL) { @@ -2105,16 +2138,18 @@ * initialize cache context in child process if required */ static void oidc_child_init(apr_pool_t *p, server_rec *s) { - while (s != NULL) { - oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(s->module_config, + server_rec *sp = s; + while (sp != NULL) { + oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(sp->module_config, &auth_openidc_module); if (cfg->cache->child_init != NULL) { - if (cfg->cache->child_init(p, s) != APR_SUCCESS) { - oidc_serror(s, "cfg->cache->child_init failed"); + if (cfg->cache->child_init(p, sp) != APR_SUCCESS) { + oidc_serror(sp, "cfg->cache->child_init failed"); } } - s = s->next; + sp = sp->next; } + apr_pool_cleanup_register(p, s, oidc_cleanup_child, apr_pool_cleanup_null); } /* @@ -2416,7 +2451,7 @@ (void*)APR_OFFSETOF(oidc_cfg, claim_prefix), RSRC_CONF, "The prefix to use when setting claims in the HTTP headers."), - AP_INIT_TAKE12(OIDCRemoteUserClaim, + AP_INIT_TAKE123(OIDCRemoteUserClaim, oidc_set_remote_user_claim, (void*)APR_OFFSETOF(oidc_cfg, remote_user_claim), RSRC_CONF, @@ -2485,7 +2520,7 @@ (void*)APR_OFFSETOF(oidc_cfg, oauth.ssl_validate_server), RSRC_CONF, "Require validation of the OAuth 2.0 AS Validation Endpoint SSL server certificate for successful authentication (On or Off)"), - AP_INIT_TAKE12(OIDCOAuthRemoteUserClaim, + AP_INIT_TAKE123(OIDCOAuthRemoteUserClaim, oidc_set_remote_user_claim, (void*)APR_OFFSETOF(oidc_cfg, oauth.remote_user_claim), RSRC_CONF, diff -Nru libapache2-mod-auth-openidc-2.3.1/src/jose.c libapache2-mod-auth-openidc-2.3.2/src/jose.c --- libapache2-mod-auth-openidc-2.3.1/src/jose.c 2017-07-13 12:09:37.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/jose.c 2017-08-09 07:40:18.000000000 +0000 @@ -142,7 +142,7 @@ if (cjose_base64url_encode((const uint8_t *) s_payload, strlen(s_payload), &out, &out_len, &cjose_err) == FALSE) return FALSE; - cser = apr_pstrndup(pool, out, out_len); + cser = apr_pstrmemdup(pool, out, out_len); cjose_get_dealloc()(out); free(s_payload); @@ -385,7 +385,7 @@ if (cjose_base64url_encode(hashed, hashed_len, &out, &out_len, &cjose_err) == FALSE) return FALSE; - *output = apr_pstrndup(pool, out, out_len); + *output = apr_pstrmemdup(pool, out, out_len); cjose_get_dealloc()(out); return TRUE; } @@ -1240,7 +1240,7 @@ int i = 0; char *s = apr_psprintf(pool, "%s\n", OIDC_JOSE_CERT_BEGIN); while (i < strlen(s_x5c)) { - s = apr_psprintf(pool, "%s%s\n", s, apr_pstrndup(pool, s_x5c + i, len)); + s = apr_psprintf(pool, "%s%s\n", s, apr_pstrmemdup(pool, s_x5c + i, len)); i += len; } s = apr_psprintf(pool, "%s%s\n", s, OIDC_JOSE_CERT_END); diff -Nru libapache2-mod-auth-openidc-2.3.1/src/metadata.c libapache2-mod-auth-openidc-2.3.2/src/metadata.c --- libapache2-mod-auth-openidc-2.3.1/src/metadata.c 2017-07-18 08:23:49.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/metadata.c 2017-08-29 11:31:12.000000000 +0000 @@ -123,6 +123,7 @@ #define OIDC_METADATA_USERINFO_TOKEN_METHOD "userinfo_token_method" #define OIDC_METADATA_TOKEN_BINDING_POLICY "token_binding_policy" #define OIDC_METADATA_AUTH_REQUEST_METHOD "auth_request_method" +#define OIDC_METADATA_ISSUER_SPECIFIC_REDIRECT_URI "issuer_specific_redirect_uri" /* * get the metadata filename for a specified issuer (cq. urlencode it) @@ -487,7 +488,7 @@ json_object_set_new(data, OIDC_METADATA_CLIENT_NAME, json_string(provider->client_name)); json_object_set_new(data, OIDC_METADATA_REDIRECT_URIS, - json_pack("[s]", oidc_get_redirect_uri(r, cfg))); + json_pack("[s]", oidc_get_redirect_uri_iss(r, cfg, provider))); json_t *response_types = json_array(); apr_array_header_t *flows = oidc_proto_supported_flows(r->pool); @@ -907,16 +908,20 @@ const char *key, int *value, int default_value) { int int_value = 0; char *s_value = NULL; - oidc_json_object_get_string(r->pool, json, key, &s_value, - NULL); - if (s_value != NULL) { - const char *rv = oidc_parse_boolean(r->pool, s_value, &int_value); - if (rv != NULL) { - oidc_warn(r, "%s: %s", key, rv); - int_value = default_value; + if (oidc_json_object_get_bool(r->pool, json, key, &int_value, + default_value) == FALSE) { + oidc_json_object_get_string(r->pool, json, key, &s_value, + NULL); + if (s_value != NULL) { + const char *rv = oidc_parse_boolean(r->pool, s_value, &int_value); + if (rv != NULL) { + oidc_warn(r, "%s: %s", key, rv); + int_value = default_value; + } + } else { + oidc_json_object_get_int(r->pool, json, key, &int_value, + default_value); } - } else { - oidc_json_object_get_int(r->pool, json, key, &int_value, default_value); } *value = (int_value != 0) ? TRUE : FALSE; } @@ -1234,6 +1239,12 @@ else provider->auth_request_method = cfg->provider.auth_request_method; + /* get the issuer specific redirect URI option */ + oidc_metadata_parse_boolean(r, j_conf, + OIDC_METADATA_ISSUER_SPECIFIC_REDIRECT_URI, + &provider->issuer_specific_redirect_uri, + cfg->provider.issuer_specific_redirect_uri); + return TRUE; } diff -Nru libapache2-mod-auth-openidc-2.3.1/src/mod_auth_openidc.c libapache2-mod-auth-openidc-2.3.2/src/mod_auth_openidc.c --- libapache2-mod-auth-openidc-2.3.1/src/mod_auth_openidc.c 2017-07-19 13:24:49.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/mod_auth_openidc.c 2017-08-29 11:31:12.000000000 +0000 @@ -208,11 +208,9 @@ } if (i == strip->nelts) { - result = - result ? - apr_psprintf(r->pool, "%s%s%s", result, - OIDC_STR_SEMI_COLON, cookie) : - cookie; + result = result ? apr_psprintf(r->pool, "%s%s%s", result, + OIDC_STR_SEMI_COLON, cookie) : + cookie; } cookie = apr_strtok(NULL, OIDC_STR_SEMI_COLON, &ctx); @@ -284,7 +282,7 @@ * return the name for the state cookie */ static char *oidc_get_state_cookie_name(request_rec *r, const char *state) { - return apr_psprintf(r->pool, "%s%s", OIDCStateCookiePrefix, state); + return apr_psprintf(r->pool, "%s%s", OIDC_STATE_COOKIE_PREFIX, state); } /* @@ -681,7 +679,7 @@ while (cookie != NULL) { while (*cookie == OIDC_CHAR_SPACE) cookie++; - if (strstr(cookie, OIDCStateCookiePrefix) == cookie) { + if (strstr(cookie, OIDC_STATE_COOKIE_PREFIX) == cookie) { char *cookieName = cookie; while (cookie != NULL && *cookie != OIDC_CHAR_EQUAL) cookie++; @@ -1492,7 +1490,8 @@ * get the r->user for this request based on the configuration for OIDC/OAuth */ apr_byte_t oidc_get_remote_user(request_rec *r, const char *claim_name, - const char *reg_exp, json_t *json, char **request_user) { + const char *reg_exp, const char *replace, json_t *json, + char **request_user) { /* get the claim value from the JSON object */ json_t *username = json_object_get(json, claim_name); @@ -1506,12 +1505,25 @@ if (reg_exp != NULL) { char *error_str = NULL; - if (oidc_util_regexp_first_match(r->pool, *request_user, reg_exp, - request_user, &error_str) == FALSE) { - oidc_error(r, "oidc_util_regexp_first_match failed: %s", error_str); + + if (replace == NULL) { + + if (oidc_util_regexp_first_match(r->pool, *request_user, reg_exp, + request_user, &error_str) == FALSE) { + oidc_error(r, "oidc_util_regexp_first_match failed: %s", + error_str); + *request_user = NULL; + return FALSE; + } + + } else if (oidc_util_regexp_substitute(r->pool, *request_user, reg_exp, + replace, request_user, &error_str) == FALSE) { + + oidc_error(r, "oidc_util_regexp_substitute failed: %s", error_str); *request_user = NULL; return FALSE; } + } return TRUE; @@ -1542,11 +1554,12 @@ oidc_util_decode_json_object(r, s_claims, &claims); if (claims == NULL) { rc = oidc_get_remote_user(r, claim_name, c->remote_user_claim.reg_exp, - jwt->payload.value.json, &remote_user); + c->remote_user_claim.replace, jwt->payload.value.json, + &remote_user); } else { oidc_util_json_merge(r, jwt->payload.value.json, claims); rc = oidc_get_remote_user(r, claim_name, c->remote_user_claim.reg_exp, - claims, &remote_user); + c->remote_user_claim.replace, claims, &remote_user); json_decref(claims); } @@ -1566,8 +1579,10 @@ oidc_debug(r, "set remote_user to \"%s\" based on claim: \"%s\"%s", r->user, c->remote_user_claim.claim_name, c->remote_user_claim.reg_exp ? - apr_psprintf(r->pool, " and expression: \"%s\"", - c->remote_user_claim.reg_exp) : + apr_psprintf(r->pool, + " and expression: \"%s\" and replace string: \"%s\"", + c->remote_user_claim.reg_exp, + c->remote_user_claim.replace) : ""); return TRUE; @@ -1985,7 +2000,8 @@ char *url = apr_psprintf(r->pool, "%s%s%s=%s&%s=%s&%s=%s&%s=%s", discover_url, strchr(discover_url, OIDC_CHAR_QUERY) != NULL ? - OIDC_STR_AMP : OIDC_STR_QUERY, + OIDC_STR_AMP : + OIDC_STR_QUERY, OIDC_DISC_RT_PARAM, oidc_util_escape_string(r, current_url), OIDC_DISC_RM_PARAM, method, OIDC_DISC_CB_PARAM, @@ -2217,8 +2233,8 @@ /* send off to the OpenID Connect Provider */ // TODO: maybe show intermediate/progress screen "redirecting to" return oidc_proto_authorization_request(r, provider, login_hint, - oidc_get_redirect_uri(r, c), state, proto_state, id_token_hint, - code_challenge, auth_request_params, path_scope); + oidc_get_redirect_uri_iss(r, c, provider), state, proto_state, + id_token_hint, code_challenge, auth_request_params, path_scope); } /* @@ -2770,8 +2786,9 @@ */ return oidc_authenticate_user(r, c, provider, apr_psprintf(r->pool, "%s?session=iframe_rp", - oidc_get_redirect_uri(r, c)), NULL, id_token_hint, - "none", oidc_dir_cfg_path_auth_request_params(r), + oidc_get_redirect_uri_iss(r, c, provider)), NULL, + id_token_hint, "none", + oidc_dir_cfg_path_auth_request_params(r), oidc_dir_cfg_path_scope(r)); } oidc_debug(r, @@ -2857,7 +2874,8 @@ if (error_code != NULL) return_to = apr_psprintf(r->pool, "%s%serror_code=%s", return_to, strchr(return_to, OIDC_CHAR_QUERY) ? - OIDC_STR_AMP : OIDC_STR_QUERY, + OIDC_STR_AMP : + OIDC_STR_QUERY, oidc_util_escape_string(r, error_code)); /* add the redirect location header */ @@ -3207,7 +3225,7 @@ return rc; - /* initial request to non-redirect URI, check if we have an existing session */ + /* initial request to non-redirect URI, check if we have an existing session */ } else if (session->remote_user != NULL) { /* this is initial request and we already have a session */ diff -Nru libapache2-mod-auth-openidc-2.3.1/src/mod_auth_openidc.h libapache2-mod-auth-openidc-2.3.2/src/mod_auth_openidc.h --- libapache2-mod-auth-openidc-2.3.1/src/mod_auth_openidc.h 2017-07-19 16:01:02.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/mod_auth_openidc.h 2017-08-29 11:31:12.000000000 +0000 @@ -150,7 +150,7 @@ #define OIDC_AUTH_REQUEST_METHOD_POST 1 /* prefix of the cookie that binds the state in the authorization request/response to the browser */ -#define OIDCStateCookiePrefix "mod_auth_openidc_state_" +#define OIDC_STATE_COOKIE_PREFIX "mod_auth_openidc_state_" /* default prefix for information passed in HTTP headers */ #define OIDC_DEFAULT_HEADER_PREFIX "OIDC_" @@ -204,8 +204,8 @@ #define OIDC_COOKIE_EXT_SAME_SITE_LAX "SameSite=Lax" #define OIDC_COOKIE_EXT_SAME_SITE_STRICT "SameSite=Strict" -/* draft-campbell-tokbind-ttrp-00 */ -#define OIDC_TB_CFG_PROVIDED_ENV_VAR "Provided-Token-Binding-ID" +/* https://tools.ietf.org/html/draft-ietf-tokbind-ttrp-01 */ +#define OIDC_TB_CFG_PROVIDED_ENV_VAR "Sec-Provided-Token-Binding-ID" #define OIDC_TOKEN_BINDING_POLICY_DISABLED 0 #define OIDC_TOKEN_BINDING_POLICY_OPTIONAL 1 @@ -277,11 +277,14 @@ char *request_object; int auth_request_method; int token_binding_policy; + + int issuer_specific_redirect_uri; } oidc_provider_t ; typedef struct oidc_remote_user_claim_t { const char *claim_name; const char *reg_exp; + const char *replace; } oidc_remote_user_claim_t; typedef struct oidc_oauth_t { @@ -403,7 +406,8 @@ void oidc_scrub_headers(request_rec *r); void oidc_strip_cookies(request_rec *r); int oidc_content_handler(request_rec *r); -apr_byte_t oidc_get_remote_user(request_rec *r, const char *claim_name, const char *reg_exp, json_t *json, char **request_user); +apr_byte_t oidc_get_remote_user(request_rec *r, const char *claim_name, const char *replace, const char *reg_exp, + json_t *json, char **request_user); #define OIDC_REDIRECT_URI_REQUEST_INFO "info" #define OIDC_REDIRECT_URI_REQUEST_LOGOUT "logout" @@ -477,6 +481,7 @@ #define OIDC_PROTO_CLIENT_SECRET_POST "client_secret_post" #define OIDC_PROTO_CLIENT_SECRET_JWT "client_secret_jwt" #define OIDC_PROTO_PRIVATE_KEY_JWT "private_key_jwt" +#define OIDC_PROTO_ENDPOINT_AUTH_NONE "none" #define OIDC_PROTO_BEARER "Bearer" @@ -672,6 +677,7 @@ const char *oidc_get_current_url_host(request_rec *r); char *oidc_get_current_url(request_rec *r); const char *oidc_get_redirect_uri(request_rec *r, oidc_cfg *c); +const char *oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *c, oidc_provider_t *provider); char *oidc_url_encode(const request_rec *r, const char *str, const char *charsToEncode); char *oidc_normalize_header_name(const request_rec *r, const char *str); void oidc_util_set_cookie(request_rec *r, const char *cookieName, const char *cookieValue, apr_time_t expires, const char *ext); @@ -703,9 +709,11 @@ apr_byte_t oidc_util_spaced_string_contains(apr_pool_t *pool, const char *str, const char *match); apr_byte_t oidc_json_object_get_string(apr_pool_t *pool, json_t *json, const char *name, char **value, const char *default_value); apr_byte_t oidc_json_object_get_int(apr_pool_t *pool, json_t *json, const char *name, int *value, const int default_value); +apr_byte_t oidc_json_object_get_bool(apr_pool_t *pool, json_t *json, const char *name, int *value, const int default_value); char *oidc_util_html_escape(apr_pool_t *pool, const char *input); void oidc_util_table_add_query_encoded_params(apr_pool_t *pool, apr_table_t *table, const char *params); apr_hash_t * oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1, apr_hash_t *k2); +apr_byte_t oidc_util_regexp_substitute(apr_pool_t *pool, const char *input, const char *regexp, const char *replace, char **output, char **error_str); apr_byte_t oidc_util_regexp_first_match(apr_pool_t *pool, const char *input, const char *regexp, char **output, char **error_str); apr_byte_t oidc_util_json_merge(request_rec *r, json_t *src, json_t *dst); int oidc_util_cookie_domain_valid(const char *hostname, char *cookie_domain); @@ -718,6 +726,7 @@ apr_hash_t * oidc_util_merge_symmetric_key(apr_pool_t *pool, apr_hash_t *private_keys, oidc_jwk_t *jwk); const char *oidc_util_get_provided_token_binding_id(const request_rec *r); char *oidc_util_http_query_encoded_url(request_rec *r, const char *url, const apr_table_t *params); +char *oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename); /* HTTP header constants */ #define OIDC_HTTP_HDR_COOKIE "Cookie" diff -Nru libapache2-mod-auth-openidc-2.3.1/src/oauth.c libapache2-mod-auth-openidc-2.3.2/src/oauth.c --- libapache2-mod-auth-openidc-2.3.1/src/oauth.c 2017-07-18 08:23:49.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/oauth.c 2017-08-29 11:31:12.000000000 +0000 @@ -90,14 +90,14 @@ basic_auth, NULL, c->oauth.ssl_validate_server, response, c->http_timeout_long, c->outgoing_proxy, oidc_dir_cfg_pass_cookies(r), - c->oauth.introspection_endpoint_tls_client_cert, - c->oauth.introspection_endpoint_tls_client_key) : + oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_cert), + oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_key)) : oidc_util_http_post_form(r, c->oauth.introspection_endpoint_url, params, basic_auth, NULL, c->oauth.ssl_validate_server, response, c->http_timeout_long, c->outgoing_proxy, oidc_dir_cfg_pass_cookies(r), - c->oauth.introspection_endpoint_tls_client_cert, - c->oauth.introspection_endpoint_tls_client_key); + oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_cert), + oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_key)); } /* @@ -558,7 +558,7 @@ char *remote_user = NULL; if (oidc_get_remote_user(r, c->oauth.remote_user_claim.claim_name, - c->oauth.remote_user_claim.reg_exp, token, &remote_user) == FALSE) { + c->oauth.remote_user_claim.reg_exp, c->oauth.remote_user_claim.replace, token, &remote_user) == FALSE) { oidc_error(r, "" OIDCOAuthRemoteUserClaim " is set to \"%s\", but could not set the remote user based the available claims for the user", c->oauth.remote_user_claim.claim_name); @@ -566,14 +566,12 @@ } r->user = remote_user; - - oidc_debug(r, "set user to \"%s\" based on claim: \"%s\"%s", r->user, - c->oauth.remote_user_claim.claim_name, - c->oauth.remote_user_claim.reg_exp ? - apr_psprintf(r->pool, " and expression: \"%s\"", - c->oauth.remote_user_claim.reg_exp) : - ""); - + oidc_debug(r, "set user to \"%s\" based on claim: \"%s\"%s", r->user, + c->oauth.remote_user_claim.claim_name, + c->oauth.remote_user_claim.reg_exp ? + apr_psprintf(r->pool, " and expression: \"%s\" and replace string: \"%s\"", + c->oauth.remote_user_claim.reg_exp, c->oauth.remote_user_claim.replace) : + ""); return TRUE; } diff -Nru libapache2-mod-auth-openidc-2.3.1/src/parse.c libapache2-mod-auth-openidc-2.3.2/src/parse.c --- libapache2-mod-auth-openidc-2.3.1/src/parse.c 2017-06-29 09:09:41.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/parse.c 2017-08-29 11:31:12.000000000 +0000 @@ -378,6 +378,7 @@ #define OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC "client_secret_basic" #define OIDC_ENDPOINT_AUTH_CLIENT_SECRET_JWT "client_secret_jwt" #define OIDC_ENDPOINT_AUTH_PRIVATE_KEY_JWT "private_key_jwt" +#define OIDC_ENDPOINT_AUTH_NONE "none" /* * check if the provided endpoint authentication method is supported @@ -388,6 +389,7 @@ OIDC_ENDPOINT_AUTH_CLIENT_SECRET_POST, OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC, OIDC_ENDPOINT_AUTH_CLIENT_SECRET_JWT, + OIDC_ENDPOINT_AUTH_NONE, NULL, NULL }; if (has_private_key) diff -Nru libapache2-mod-auth-openidc-2.3.1/src/pcre_subst.c libapache2-mod-auth-openidc-2.3.2/src/pcre_subst.c --- libapache2-mod-auth-openidc-2.3.1/src/pcre_subst.c 1970-01-01 00:00:00.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/pcre_subst.c 2017-08-29 11:31:12.000000000 +0000 @@ -0,0 +1,191 @@ +/************************************************* +* PCRE string replacement * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. +pcre_subst is a wrapper around pcre_exec designed to make it easier to +perform PERL style replacements with PCRE. + +Written by: Bert Driehuis + + Copyright (c) 2000 Bert Driehuis + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +*/ + +#include +#include +#include +#include +#include "pcre_subst.h" + +#define MAXCAPTURE 50 + +#ifdef DEBUG_PCRE_SUBST +static void +dumpstr(const char *str, int len, int start, int end) +{ + int i; + for (i = 0; i < strlen(str); i++) { + if (i >= start && i < end) + putchar(str[i]); + else + putchar('-'); + } + putchar('\n'); +} + +static void +dumpmatch(const char *str, int len, const char *rep, int nmat, const int *ovec) +{ + int i; + printf("%s Input\n", str); + printf("nmat=%d", nmat); + for (i = 0; i < nmat * 2; i++) + printf(" %d", ovec[i]); + printf("\n"); + for (i = 0; i < nmat * 2; i += 2) + dumpstr(str, len, ovec[i], ovec[i+1]); + printf("\n"); +} +#endif + +static int +findreplen(const char *rep, int nmat, const int *replen) +{ + int len = 0; + int val; + char *cp = (char *)rep; + while(*cp) { + if (*cp == '$' && isdigit(cp[1])) { + val = strtoul(&cp[1], &cp, 10); + if (val && val <= nmat + 1) + len += replen[val -1]; + else + fprintf(stderr, "repl %d out of range\n", val); + } else { + cp++; + len++; + } + } + return len; +} + +static void +doreplace(char *out, const char *rep, int nmat, int *replen, const char **repstr) +{ + int val; + char *cp = (char *)rep; + while(*cp) { + if (*cp == '$' && isdigit(cp[1])) { + val = strtoul(&cp[1], &cp, 10); + if (val && val <= nmat + 1) { + strncpy(out, repstr[val - 1], replen[val - 1]); + out += replen[val -1]; + } + } else { + *out++ = *cp++; + } + } +} + +static char * +edit(const char *str, int len, const char *rep, int nmat, const int *ovec) +{ + int i, slen, rlen; + const int *mvec = ovec; + char *res, *cp; + int replen[MAXCAPTURE]; + const char *repstr[MAXCAPTURE]; + nmat--; + ovec += 2; + for (i = 0; i < nmat; i++) { + replen[i] = ovec[i * 2 + 1] - ovec[i * 2]; + repstr[i] = &str[ovec[i * 2]]; +#ifdef DEBUG_PCRE_SUBST + printf(">>>%d %d %.*s\n", i, replen[i], replen[i], repstr[i]); +#endif + } + slen = len; + len -= mvec[1] - mvec[0]; + len += rlen = findreplen(rep, nmat, replen); +#ifdef DEBUG_PCRE_SUBST + printf("resulting length %d (srclen=%d)\n", len, slen); +#endif + cp = res = pcre_malloc(len + 1); + if (mvec[0] > 0) { + strncpy(cp, str, mvec[0]); + cp += mvec[0]; + } + doreplace(cp, rep, nmat, replen, repstr); + cp += rlen; + if (mvec[1] < slen) + strcpy(cp, &str[mvec[1]]); + res[len] = 0; + return res; +} + +char * +pcre_subst(const pcre *ppat, const pcre_extra *extra, const char *str, int len, + int offset, int options, const char *rep) +{ + int nmat; + int ovec[MAXCAPTURE * 3]; + nmat = pcre_exec(ppat, extra, str, len, offset, options, + ovec, sizeof(ovec)); +#ifdef DEBUG_PCRE_SUBST + dumpmatch(str, len, rep, nmat, ovec); +#endif + if (nmat <= 0) + return NULL; + return(edit(str, len, rep, nmat, ovec)); +} + +#ifdef DEBUG_BUILD +int +main() +{ + char *pat = "quick\\s(\\w+)\\s(fox)"; + char *rep = "$1ish $2"; + char *str = "The quick brown foxy"; + char *newstr; + const char *err; + int erroffset; + pcre_extra *extra; + pcre *ppat = pcre_compile(pat, 0, &err, &erroffset, NULL); + if (ppat == NULL) { + fprintf(stderr, "%s at %d\n", err, erroffset); + exit(1); + } + extra = pcre_study(ppat, 0, &err); + if (err != NULL) + fprintf(stderr, "Study %s failed: %s\n", pat, err); + newstr = pcre_subst(ppat, extra, str, strlen(str), 0, 0, rep); + if (newstr) { + printf("Newstr\t%s\n", newstr); + pcre_free(newstr); + } else { + printf("No match\n"); + } + return 0; +} +#endif diff -Nru libapache2-mod-auth-openidc-2.3.1/src/pcre_subst.h libapache2-mod-auth-openidc-2.3.2/src/pcre_subst.h --- libapache2-mod-auth-openidc-2.3.1/src/pcre_subst.h 1970-01-01 00:00:00.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/pcre_subst.h 2017-08-29 11:31:12.000000000 +0000 @@ -0,0 +1,35 @@ +/************************************************* +* PCRE string replacement * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. +pcre_subst is a wrapper around pcre_exec designed to make it easier to +perform PERL style replacements with PCRE. + +Written by: Bert Driehuis + + Copyright (c) 2000 Bert Driehuis + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +*/ + +char *pcre_subst(const pcre *, const pcre_extra *, const char *, int, int, int, const char *); diff -Nru libapache2-mod-auth-openidc-2.3.1/src/proto.c libapache2-mod-auth-openidc-2.3.2/src/proto.c --- libapache2-mod-auth-openidc-2.3.1/src/proto.c 2017-07-18 08:23:49.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/proto.c 2017-09-05 07:46:14.000000000 +0000 @@ -79,15 +79,17 @@ return TRUE; } +#define OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST "copy_from_request" +#define OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST "copy_and_remove_from_request" + /* * indicates wether a request parameter from the authorization request needs to be - * copied to the protected request object based on the settings specified in the - * "copy_from_request" JSON array in the request object + * copied and/or deleted to/from the protected request object based on the settings specified + * in the "copy_from_request"/"copy_and_remove_from_request" JSON array in the request object */ -static apr_byte_t oidc_proto_param_needs_copy(json_t *request_object_config, - const char *parameter_name) { - json_t *copy_from_request = json_object_get(request_object_config, - "copy_from_request"); +static apr_byte_t oidc_proto_param_needs_action(json_t *request_object_config, + const char *parameter_name, const char *action) { + json_t *copy_from_request = json_object_get(request_object_config, action); size_t index = 0; while (index < json_array_size(copy_from_request)) { json_t *value = json_array_get(copy_from_request, index); @@ -105,6 +107,7 @@ request_rec *r; json_t *request_object_config; oidc_jwt_t *request_object; + apr_table_t *params2; } oidc_proto_copy_req_ctx_t; /* @@ -117,7 +120,10 @@ oidc_debug(ctx->r, "processing name: %s, value: %s", name, value); - if (oidc_proto_param_needs_copy(ctx->request_object_config, name)) { + if (oidc_proto_param_needs_action(ctx->request_object_config, name, + OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST) + || oidc_proto_param_needs_action(ctx->request_object_config, name, + OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) { json_t *result = NULL; json_error_t json_error; result = json_loads(value, JSON_DECODE_ANY, &json_error); @@ -129,6 +135,30 @@ json_deep_copy(result)); json_decref(result); } + + if (oidc_proto_param_needs_action(ctx->request_object_config, name, + OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) { + apr_table_set(ctx->params2, name, name); + } + + } + + return 1; +} + +/* + * delete a parameter key/value from the authorizion request if the configuration setting says to remove it + */ +static int oidc_proto_delete_from_request(void* rec, const char* name, + const char* value) { + oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t *) rec; + + oidc_debug(ctx->r, "deleting from query paramters: name: %s, value: %s", + name, value); + + if (oidc_proto_param_needs_action(ctx->request_object_config, name, + OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) { + apr_table_unset(ctx->params2, name); } return 1; @@ -226,10 +256,16 @@ request_object->payload.value.json); /* copy parameters from the authorization request as configured in the .conf file */ - oidc_proto_copy_req_ctx_t data = - { r, request_object_config, request_object }; + apr_table_t *delete_from_query_params = apr_table_make(r->pool, 0); + oidc_proto_copy_req_ctx_t data = { r, request_object_config, request_object, + delete_from_query_params }; apr_table_do(oidc_proto_copy_from_request, &data, params, NULL); + /* delete parameters from the query parameters of the authorization request as configured in the .conf file */ + data.params2 = params; + apr_table_do(oidc_proto_delete_from_request, &data, + delete_from_query_params, NULL); + /* debug logging */ oidc_debug(r, "request object: %s", oidc_util_encode_json_object(r, request_object->payload.value.json, JSON_COMPACT)); @@ -1282,7 +1318,7 @@ oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); - oidc_debug(r, "enter, jwt.header=\"%s\", jwt.payload=\%s\", nonce=%s", + oidc_debug(r, "enter, jwt.header=\"%s\", jwt.payload=\"%s\", nonce=\"%s\"", jwt->header.value.str, jwt->payload.value.str, nonce); /* if a nonce is not passed, we're doing a ("code") flow where the nonce is optional */ @@ -1525,7 +1561,8 @@ "could not parse first element separated by \".\" from input"); return NULL; } - input = apr_pstrndup(r->pool, compact_encoded_jwt, p - compact_encoded_jwt); + input = apr_pstrmemdup(r->pool, compact_encoded_jwt, + strlen(compact_encoded_jwt) - strlen(p)); if (oidc_base64url_decode(r->pool, &result, input) <= 0) { oidc_warn(r, "oidc_base64url_decode returned an error"); return NULL; @@ -1805,12 +1842,26 @@ oidc_debug(r, "token_endpoint_auth=%s", token_endpoint_auth); - // we assume the default is client_secret_basic unless no secret is set - if ((token_endpoint_auth == NULL) && (client_secret == NULL)) - return oidc_proto_endpoint_auth_none(r, client_id, params); + // default is client_secret_basic, but only if a client_secret is set, + // otherwise we are a public client + if ((token_endpoint_auth == NULL) && (client_secret != NULL)) + token_endpoint_auth = OIDC_PROTO_CLIENT_SECRET_BASIC; if ((token_endpoint_auth == NULL) || (apr_strnatcmp(token_endpoint_auth, - OIDC_PROTO_CLIENT_SECRET_BASIC) == 0)) + OIDC_PROTO_ENDPOINT_AUTH_NONE) == 0)) + return oidc_proto_endpoint_auth_none(r, client_id, params); + + // if no client_secret is set and we don't authenticate using private_key_jwt, + // we can only be a public client since the other methods require a client_secret + if ((client_secret == NULL) && (apr_strnatcmp(token_endpoint_auth, + OIDC_PROTO_PRIVATE_KEY_JWT) != 0)) { + oidc_debug(r, + "no client secret set and not using private_key_jwt, assume we are a public client"); + return oidc_proto_endpoint_auth_none(r, client_id, params); + } + + if (apr_strnatcmp(token_endpoint_auth, + OIDC_PROTO_CLIENT_SECRET_BASIC) == 0) return oidc_proto_endpoint_auth_basic(r, client_id, client_secret, basic_auth_str); @@ -1860,8 +1911,10 @@ basic_auth, NULL, provider->ssl_validate_server, &response, cfg->http_timeout_long, cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r), - provider->token_endpoint_tls_client_cert, - provider->token_endpoint_tls_client_key) == FALSE) { + oidc_util_get_full_path(r->pool, + provider->token_endpoint_tls_client_cert), + oidc_util_get_full_path(r->pool, + provider->token_endpoint_tls_client_key)) == FALSE) { oidc_warn(r, "error when calling the token endpoint (%s)", provider->token_endpoint_url); return FALSE; @@ -1924,7 +1977,7 @@ OIDC_PROTO_GRANT_TYPE_AUTHZ_CODE); apr_table_setn(params, OIDC_PROTO_CODE, code); apr_table_set(params, OIDC_PROTO_REDIRECT_URI, - oidc_get_redirect_uri(r, cfg)); + oidc_get_redirect_uri_iss(r, cfg, provider)); if (code_verifier) apr_table_setn(params, OIDC_PROTO_CODE_VERIFIER, code_verifier); diff -Nru libapache2-mod-auth-openidc-2.3.1/src/util.c libapache2-mod-auth-openidc-2.3.2/src/util.c --- libapache2-mod-auth-openidc-2.3.1/src/util.c 2017-07-19 15:41:03.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/src/util.c 2017-08-30 15:14:54.000000000 +0000 @@ -63,6 +63,7 @@ #include "mod_auth_openidc.h" #include +#include "pcre_subst.h" /* hrm, should we get rid of this by adding parameters to the (3) functions? */ extern module AP_MODULE_DECLARE_DATA auth_openidc_module; @@ -337,12 +338,12 @@ const char * const replace[] = { "&", "'", """, ">", "<", }; unsigned int i, j = 0, k, n = 0, len = strlen(chars); - int m = 0; + unsigned int m = 0; char *r = apr_pcalloc(pool, strlen(s) * 6); for (i = 0; i < strlen(s); i++) { for (n = 0; n < len; n++) { if (s[i] == chars[n]) { - m = strlen(replace[n]); + m = (unsigned int)strlen(replace[n]); for (k = 0; k < m; k++) r[j + k] = replace[n][k]; j += m; @@ -511,6 +512,26 @@ return redirect_uri; } +/* + * determine absolute redirect uri that is issuer specific + */ +const char *oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *cfg, + oidc_provider_t *provider) { + const char *redirect_uri = oidc_get_redirect_uri(r, cfg); + if (provider->issuer_specific_redirect_uri != 0) { + redirect_uri = apr_psprintf(r->pool, "%s%s%s=%s", redirect_uri, + strchr(redirect_uri, OIDC_CHAR_QUERY) != NULL ? + OIDC_STR_AMP : + OIDC_STR_QUERY, + OIDC_PROTO_ISS, oidc_util_escape_string(r, provider->issuer)); +// OIDC_PROTO_CLIENT_ID, +// oidc_util_escape_string(r, provider->client_id)); + oidc_debug(r, "determined issuer specific redirect uri: %s", + redirect_uri); + } + return redirect_uri; +} + /* buffer to hold HTTP call responses */ typedef struct oidc_curl_buffer { request_rec *r; @@ -776,7 +797,7 @@ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); oidc_debug(r, "HTTP response code=%ld", response_code); - *response = apr_pstrndup(r->pool, curlBuffer.memory, curlBuffer.size); + *response = apr_pstrmemdup(r->pool, curlBuffer.memory, curlBuffer.size); /* set and log the response */ oidc_debug(r, "response=%s", *response ? *response : ""); @@ -1162,7 +1183,7 @@ return FALSE; /* not sure why we do this, but better be safe than sorry */ - args = apr_pstrndup(r->pool, r->args, strlen(r->args)); + args = apr_pstrmemdup(r->pool, r->args, strlen(r->args)); p = apr_strtok(args, OIDC_STR_AMP, &tokenizer_ctx); do { @@ -1322,6 +1343,14 @@ static char *html_error_template_contents = NULL; /* + * get the full path to a file based on an (already) absolute filename or a filename + * that is relative to the Apache root directory + */ +char *oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename) { + return (abs_or_rel_filename) ? ap_server_root_relative(pool, abs_or_rel_filename) : NULL; +} + +/* * send a user-facing error to the browser */ int oidc_util_html_send_error(request_rec *r, const char *html_template, @@ -1331,6 +1360,8 @@ if (html_template != NULL) { + html_template = oidc_util_get_full_path(r->pool, html_template); + if (html_error_template_contents == NULL) { int rc = oidc_util_file_read(r, html_template, r->server->process->pool, &html_error_template_contents); @@ -1873,6 +1904,22 @@ } /* + * get (optional) boolean from a JSON object + */ +apr_byte_t oidc_json_object_get_bool(apr_pool_t *pool, json_t *json, + const char *name, int *value, const int default_value) { + *value = default_value; + if (json != NULL) { + json_t *v = json_object_get(json, name); + if ((v != NULL) && (json_is_boolean(v))) { + *value = json_is_true(v); + return TRUE; + } + } + return FALSE; +} + +/* * merge two JSON objects */ apr_byte_t oidc_util_json_merge(request_rec *r, json_t *src, json_t *dst) { @@ -2012,6 +2059,46 @@ } /* + * regexp substitute + * Example: + * regex: "^.*([0-9]+).*$" + * replace: "$1" + * text_original: "match 292 numbers" + * text_replaced: "292" + */ + +apr_byte_t oidc_util_regexp_substitute( + apr_pool_t *pool, const char *input, + const char *regexp, const char *replace, char **output, char **error_str) { + + const char *errorptr; + int erroffset; + pcre *preg; + char *substituted; + + preg = pcre_compile(regexp, 0, &errorptr, &erroffset, NULL); + + if (preg == NULL) { + *error_str = apr_psprintf(pool, "pattern [%s] is not a valid regular expression", regexp); + pcre_free(preg); + return FALSE; + } + + substituted = pcre_subst(preg, NULL, input, (int) strlen(input), 0, 0, replace); + if (substituted) { + *output = apr_pstrdup(pool, substituted); + pcre_free(preg); + pcre_free(substituted); + return TRUE; + } else { + *error_str = apr_psprintf(pool,"unknown error could not match string [%s] using pattern [%s] and replace matches in [%s]", + input, regexp, replace); + pcre_free(preg); + } + return FALSE; +} + +/* * regexp match */ #define OIDC_UTIL_REGEXP_MATCH_SIZE 30 @@ -2169,7 +2256,7 @@ } void oidc_util_hdr_in_cookie_set(const request_rec *r, const char *value) { - return oidc_util_hdr_in_set(r, OIDC_HTTP_HDR_COOKIE, value); + oidc_util_hdr_in_set(r, OIDC_HTTP_HDR_COOKIE, value); } const char *oidc_util_hdr_in_user_agent_get(const request_rec *r) { @@ -2213,7 +2300,7 @@ } void oidc_util_hdr_out_location_set(const request_rec *r, const char *value) { - return oidc_util_hdr_out_set(r, OIDC_HTTP_HDR_LOCATION, value); + oidc_util_hdr_out_set(r, OIDC_HTTP_HDR_LOCATION, value); } const char *oidc_util_hdr_out_location_get(const request_rec *r) { diff -Nru libapache2-mod-auth-openidc-2.3.1/test/stub.c libapache2-mod-auth-openidc-2.3.2/test/stub.c --- libapache2-mod-auth-openidc-2.3.1/test/stub.c 2017-07-12 19:14:12.000000000 +0000 +++ libapache2-mod-auth-openidc-2.3.2/test/stub.c 2017-08-09 07:40:18.000000000 +0000 @@ -176,3 +176,7 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r) { return "www.example.com"; } + +AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file) { + return ""; +}