diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm index 25125fc961b721870d90d04fe4127cd8a964e9ab..ac968cf8660297258674ad87e6cd4f3440582174 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm @@ -16,6 +16,7 @@ use Apache2::Filter; use APR::Table; use Apache2::Const -compile => qw(FORBIDDEN HTTP_UNAUTHORIZED REDIRECT OK DECLINED DONE SERVER_ERROR AUTH_REQUIRED HTTP_SERVICE_UNAVAILABLE); +use URI; use base 'Lemonldap::NG::Handler::Main'; use constant FORBIDDEN => Apache2::Const::FORBIDDEN; @@ -166,7 +167,7 @@ sub redirectFilter { $f->r->status( $class->REDIRECT ); $f->r->status_line("303 See Other"); $f->r->headers_out->unset('Location'); - $f->r->err_headers_out->set( 'Location' => $url ); + $f->r->err_headers_out->set( 'Location' => URI->new($url)->as_string ); $f->ctx(1); } while ( $f->read( my $buffer, 1024 ) ) { diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm index 27e436cbd35c19f92ea93a08df295ff2b8410870..4fdbaf54b1f36b1e14738d6ee15ca6cda381848f 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm @@ -9,6 +9,7 @@ use strict; #use AutoLoader 'AUTOLOAD'; use MIME::Base64; +use URI; use URI::Escape; use Lemonldap::NG::Common::Session; @@ -690,7 +691,7 @@ sub _buildUrl { ) ? '' : ":$portString"; my $url = "http" . ( $_https ? "s" : "" ) . "://$realvhost$portString$s"; $class->logger->debug("Build URL $url"); - return $url; + return URI->new($url)->as_string; } ## @rmethod protected int isUnprotected() diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm index 0c68efacce0afe764ecd24828f6a58eb614586b1..3915677e7f2e2cbc1da9aaa6aad09bb46a033486 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CDC.pm @@ -9,6 +9,7 @@ use strict; use Mouse; use MIME::Base64; use Lemonldap::NG::Common::FormEncode; +use URI; our $VERSION = '2.0.6'; @@ -163,7 +164,10 @@ sub handler { ); # Redirect - return [ 302, [ Location => $urldc, $req->spliceHdrs ], [] ]; + return [ + 302, [ Location => URI->new($urldc)->as_string, $req->spliceHdrs ], + [] + ]; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm index e4ddaf1c9d9540cb27b89838eff6105e62a642ec..5b8227d3b8788890dc51cf34ea04950286b831b5 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm @@ -17,6 +17,7 @@ use Lemonldap::NG::Portal::Main::Constants qw( PE_UNAUTHORIZEDPARTNER PE_CAS_SERVICE_NOT_ALLOWED ); +use URI; our $VERSION = '2.0.15'; @@ -101,7 +102,8 @@ sub storeEnvAndCheckGateway { if ( $self->_gatewayAllowedRedirect( $req, $service ) ) { $self->logger->debug( "Gateway mode requested, redirect without authentication"); - $req->response( [ 302, [ Location => $service ], [] ] ); + $req->response( + [ 302, [ Location => URI->new($service)->as_string ], [] ] ); for my $s ( $self->ipath, $self->ipath . 'Path' ) { $self->logger->debug("Removing $s from pdata") if delete $req->pdata->{$s}; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm index 6ee0ef46eb05d942f4b14fec3a7130cfb617b532..331d157125969d6dc3b524df4b2b4872efd55b1c 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm @@ -24,6 +24,7 @@ use URI; use URI::QueryParam; use Mouse; use Crypt::URandom; +use URI; use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_REDIRECT PE_ERROR); @@ -2274,7 +2275,7 @@ sub buildLogoutResponse { $response_url .= build_urlencoded( state => $state ); } - return $response_url; + return URI->new($response_url)->as_string; } # Create session_state parameter diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm index 9da80841590698212ed57df30315c6ac0f4589d3..5b769a8d9f177915a62e37bc63b9d19d98d1b3e6 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm @@ -2669,7 +2669,7 @@ sub sendLogoutResponseToServiceProvider { # Redirect user to response URL my $slo_url = $logout->msg_url; - return [ 302, [ Location => $slo_url ], [] ]; + return [ 302, [ Location => URI->new($slo_url)->as_string ], [] ]; } # HTTP-POST diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm index 03266c7f1cf2ad819937ce3feb0f33620d607be2..38a80b24734ee901c4f38236958579e52f76497f 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm @@ -145,6 +145,7 @@ sub controlUrl { $req->{urldc} =~ s/[\r\n]//sg; } } + $req->{urldc} = URI->new( $req->{urldc} )->as_string; # For logout request, test if Referer comes from an authorized site my $tmp = ( diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm index 68264409115ef08bd258adc8712f5fc8b6421e46..15de5844d179a0e7edbc02c9151618fac1478213 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -440,7 +440,14 @@ sub autoRedirect { $req->data->{redirectFormMethod} = "get"; } else { - return [ 302, [ Location => $req->{urldc}, $req->spliceHdrs ], [] ]; + return [ + 302, + [ + Location => URI->new( $req->{urldc} )->as_string, + $req->spliceHdrs + ], + [] + ]; } } my ( $tpl, $prms ) = $self->display($req); @@ -1308,7 +1315,7 @@ sub cspGetHost { my ( $self, $url ) = @_; return unless $url; my $uri = $url; - unless (blessed($uri) && $uri->isa("URI") ) { + unless ( blessed($uri) && $uri->isa("URI") ) { $uri = URI->new($uri); } my $scheme = $uri->scheme || ""; diff --git a/lemonldap-ng-portal/t/03-XSS-protection.t b/lemonldap-ng-portal/t/03-XSS-protection.t index 8cf19f42bb9a14050272cb763da1d5034fccb25c..2dcc378bac520666a0513135ca65e1a072fe862b 100644 --- a/lemonldap-ng-portal/t/03-XSS-protection.t +++ b/lemonldap-ng-portal/t/03-XSS-protection.t @@ -5,8 +5,7 @@ use IO::String; require 't/test-lib.pm'; -my $client = LLNG::Manager::Test->new( - { +my $client = LLNG::Manager::Test->new( { ini => { logLevel => 'error', useSafeJail => 1, @@ -21,21 +20,25 @@ my @tests = ( '' => 0, 'Empty', # 2 http://test1.example.com/ - 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==' => 1, 'Protected virtual host', + 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==' => 'http://test1.example.com/', + 'Protected virtual host', # 3 http://test1.example.com - 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t' => 1, 'Missing / in URL', + 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t' => 'http://test1.example.com', + 'Missing / in URL', # 4 http://test1.example.com:8000/test - 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAvdGVzdA==' => 1, + 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAvdGVzdA==' => + 'http://test1.example.com:8000/test', 'Non default port', # 5 http://test1.example.com:8000/ - 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAv' => 1, + 'aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tOjgwMDAv' => + 'http://test1.example.com:8000/', 'Non default port with missing /', # 6 http://t.example2.com/test - 'aHR0cDovL3QuZXhhbXBsZTIuY29tL3Rlc3Q=' => 1, + 'aHR0cDovL3QuZXhhbXBsZTIuY29tL3Rlc3Q=' => 'http://t.example2.com/test', 'Undeclared virtual host in trusted domain', # 7 http://testexample2.com/ @@ -49,7 +52,7 @@ my @tests = ( . ' "example3.com" is trusted, but domain "*.example3.com" not)', # 9 http://example3.com/ - 'aHR0cDovL2V4YW1wbGUzLmNvbS8K' => 1, + 'aHR0cDovL2V4YW1wbGUzLmNvbS8K' => 'http://example3.com/', 'Undeclared virtual host with trusted domain name', # 10 http://t.example.com/test @@ -87,6 +90,21 @@ my @tests = ( 'aHR0cHM6Ly90ZXN0MS5leGFtcGxlLmNvbTp0ZXN0QGhhY2tlci5jb20=' => 0, 'userinfo trick', + # 22 url=https://hacker.com\@@test1.example.com/ + 'aHR0cHM6Ly9oYWNrZXIuY29tXEBAdGVzdDEuZXhhbXBsZS5jb20v' => + 'https://hacker.com%5C@@test1.example.com/', + 'Good reencoding (2931)', + + # 23 url=https://hacker.com:\@@test1.example.com/ + 'aHR0cHM6Ly9oYWNrZXIuY29tOlxAQHRlc3QxLmV4YW1wbGUuY29tLw==' => + 'https://hacker.com:%5C@@test1.example.com/', + 'Good reencoding (2931)', + + # 24 url='https://hacker.com\anything@test1.example.com/' + 'aHR0cHM6Ly9oYWNrZXIuY29tXGFueXRoaW5nQHRlc3QxLmV4YW1wbGUuY29tLw==' => + 'https://hacker.com%5Canything@test1.example.com/', + 'Good reencoding (2931)', + # LOGOUT TESTS 'LOGOUT', @@ -97,7 +115,7 @@ my @tests = ( # 19 url=http://www.toto.com/, good referer 'aHR0cDovL3d3dy50b3RvLmNvbS8=', - 'http://test1.example.com/' => 1, + 'http://test1.example.com/' => 'http://www.toto.com/', 'Logout required by good site', # 20 url=http://www?