@@ -40,6 +40,7 @@ use URI::QueryParam;
4040use Crypt::OpenSSL::RSA;
4141use Crypt::OpenSSL::X509;
4242use File::Slurp qw/ read_file / ;
43+ use URI::Encode qw/ uri_decode/ ;
4344
4445=head2 new( ... )
4546
@@ -79,6 +80,18 @@ sha1, sha224, sha256, sha384, sha512
7980
8081sha1 is current default but will change by version 44
8182
83+ =item B<sls_force_lcase_url_encoding >
84+
85+ Specifies that the IdP requires the encoding of a URL to be in lowercase.
86+ Necessary for a HTTP-Redirect of a LogoutResponse from Azure in particular.
87+ True (1) or False (0). Some web frameworks and underlying http requests assume
88+ that the encoding should be in the standard uppercase (%2F not %2f)
89+
90+ =item B<sls_double_encoded_response >
91+
92+ Specifies that the IdP response sent to the HTTP-Redirect is double encoded.
93+ The double encoding requires it to be decoded prior to processing.
94+
8295=back
8396
8497=cut
@@ -88,6 +101,8 @@ has 'cert' => (isa => 'Str', is => 'ro', required => 1);
88101has ' url' => (isa => Uri, is => ' ro' , required => 1, coerce => 1);
89102has ' param' => (isa => ' Str' , is => ' ro' , required => 1);
90103has ' sig_hash' => (isa => ' Str' , is => ' ro' , required => 0);
104+ has ' sls_force_lcase_url_encoding' => (isa => ' Bool' , is => ' ro' , required => 0);
105+ has ' sls_double_encoded_response' => (isa => ' Bool' , is => ' ro' , required => 0);
91106
92107=head2 sign( $request, $relaystate )
93108
@@ -162,6 +177,32 @@ sub verify {
162177 my $cert = Crypt::OpenSSL::X509-> new_from_string($self -> cert);
163178 my $rsa_pub = Crypt::OpenSSL::RSA-> new_public_key($cert -> pubkey);
164179
180+ my $signed ;
181+ my $saml_request ;
182+ my $sig = $u -> query_param_delete(' Signature' );
183+
184+ # Some IdPs (PingIdentity) seem to double encode the LogoutResponse URL
185+ if (defined $self -> sls_double_encoded_response and $self -> sls_double_encoded_response == 1) {
186+ # if ($sigalg =~ m/%/) {
187+ $signed = uri_decode($u -> query);
188+ $sig = uri_decode($sig );
189+ $sigalg = uri_decode($sigalg );
190+ $saml_request = uri_decode($u -> query_param($self -> param));
191+ } else {
192+ $signed = $u -> query;
193+ $saml_request = $u -> query_param($self -> param);
194+ }
195+
196+ # What can we say about this one Microsoft Azure uses lower case in the
197+ # URL encoding %2f not %2F. As it is signed as %2f the resulting signed
198+ # needs to change it to lowercase if the application layer reencoded the URL.
199+ if (defined $self -> sls_force_lcase_url_encoding and $self -> sls_force_lcase_url_encoding == 1) {
200+ # TODO: This is a hack.
201+ $signed =~ s / (%..)/ lc($1 )/ ge ;
202+ }
203+
204+ $sig = decode_base64($sig );
205+
165206 if ($sigalg eq ' http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' ) {
166207 $rsa_pub -> use_sha256_hash;
167208 } elsif ($sigalg eq ' http://www.w3.org/2001/04/xmldsig-more#rsa-sha224' ) {
@@ -176,12 +217,10 @@ sub verify {
176217 die " Unsupported Signature Algorithim: $sigalg " ;
177218 }
178219
179- my $sig = decode_base64($u -> query_param_delete(' Signature' ));
180- my $signed = $u -> query;
181220 die " bad sig" unless $rsa_pub -> verify($signed , $sig );
182221
183222 # unpack the SAML request
184- my $deflated = decode_base64($u -> query_param( $self -> param) );
223+ my $deflated = decode_base64($saml_request );
185224 my $request = ' ' ;
186225 rawinflate \$deflated => \$request ;
187226
0 commit comments