#
# Security fix for XSS in captive portal on destination_url (#1296)
# Licensed under the GPLv2
# Olivier Bilodeau <obilodeau@inverse.ca>
#
# old_revision [92f9741dafd035ed1617b8ebb8d6a467cb0f1edb]
#
# patch "pf/html/admin/guest-management.cgi"
#  from [7f0645f6fba65e71319e75d3fe975814b6dc7352]
#    to [28786140f7056c61fc636fba5cf3109f37fe97f7]
# 
# patch "pf/html/captive-portal/email_activation.cgi"
#  from [0dbe63ed920d8b3f67348bf5893b7ef236d33820]
#    to [fc9ff657da26302a5b9d292793ff931c7a3c4e45]
# 
# patch "pf/html/captive-portal/guest-selfregistration.cgi"
#  from [e3645e6533120c8018610a41203fd0b765d2f9b9]
#    to [43f6c3041ca37d6c35be040df38f4e5a7c857d8e]
# 
# patch "pf/html/captive-portal/mobile-confirmation.cgi"
#  from [d7e3eb2ee5c10c5265360b90f194ef8d69acb1b4]
#    to [6c51e46868369e02352b252a1d575aa96b07eff4]
# 
# patch "pf/html/captive-portal/redir.cgi"
#  from [e0bf0a3211b6bee491750d730c7e3b565df879c1]
#    to [5d83de81f112b11d7a3b3ae6bbd2adf32230a07b]
# 
# patch "pf/html/captive-portal/register.cgi"
#  from [3febfe1673d7033d33183a2922567b3628b7e2c0]
#    to [611fce68b5e446da2fecebc462dced7f1d91eba5]
# 
# patch "pf/html/common/pf.js"
#  from [6ec3ebf4178037fcae138993b102a6613383ed1c]
#    to [9249a03a53d92fb76df43908d5573fc3a0d1b6a0]
# 
# patch "pf/lib/pf/web/guest.pm"
#  from [6e48b4451ad79308568d3856beea063bf96c715d]
#    to [d6a8d2d19ecb2eab9f3f5542f853ba4c3d5d0217]
# 
# patch "pf/lib/pf/web.pm"
#  from [03e5c31ba0f2b29b6d63f0c60ff7c088b821723b]
#    to [cc3030b1085242182f269fbdaba3b51264942854]
#
============================================================
--- pf/html/admin/guest-management.cgi	7f0645f6fba65e71319e75d3fe975814b6dc7352
+++ pf/html/admin/guest-management.cgi	28786140f7056c61fc636fba5cf3109f37fe97f7
@@ -41,7 +41,6 @@ my $ip              = $cgi->remote_addr(
 
 my $result;
 my $ip              = $cgi->remote_addr();
-my $destination_url = $cgi->param("destination_url");
 my $enable_menu     = $cgi->param("enable_menu");
 my $mac             = ip2mac($ip);
 my %params;
============================================================
--- pf/html/captive-portal/email_activation.cgi	0dbe63ed920d8b3f67348bf5893b7ef236d33820
+++ pf/html/captive-portal/email_activation.cgi	fc9ff657da26302a5b9d292793ff931c7a3c4e45
@@ -34,7 +34,6 @@ my $ip              = $cgi->remote_addr(
 
 my $result;
 my $ip              = $cgi->remote_addr();
-my $destination_url = $cgi->param("destination_url");
 my $enable_menu     = $cgi->param("enable_menu");
 my $mac             = ip2mac($ip);
 my %params;
============================================================
--- pf/html/captive-portal/guest-selfregistration.cgi	e3645e6533120c8018610a41203fd0b765d2f9b9
+++ pf/html/captive-portal/guest-selfregistration.cgi	43f6c3041ca37d6c35be040df38f4e5a7c857d8e
@@ -10,9 +10,11 @@ use CGI::Session;
 use CGI;
 use CGI::Carp qw( fatalsToBrowser );
 use CGI::Session;
+use HTML::Entities qw(decode_entities);
 use Log::Log4perl;
 use Readonly;
 use POSIX;
+use URI::Escape qw(uri_escape uri_unescape);
 
 use pf::class;
 use pf::config;
@@ -42,7 +44,8 @@ my $ip              = $cgi->remote_addr(
 
 my $result;
 my $ip              = $cgi->remote_addr();
-my $destination_url = $cgi->param("destination_url") || $Config{'trapping'}{'redirecturl'};
+my $destination_url = decode_entities(uri_unescape($cgi->param("destination_url"))) 
+    || $Config{'trapping'}{'redirecturl'};
 my $enable_menu     = $cgi->param("enable_menu");
 my $mac             = ip2mac($ip);
 my %params;
@@ -106,7 +109,7 @@ if (defined($params{'mode'}) && $params{
           $logger->info("registration url = $destination_url");
         }
         else {
-          print $cgi->redirect("/captive-portal?destination_url=$destination_url");
+          print $cgi->redirect("/captive-portal?destination_url=".uri_escape($destination_url));
           $logger->info("more violations yet to come for $mac");
         }
       }
============================================================
--- pf/html/captive-portal/mobile-confirmation.cgi	d7e3eb2ee5c10c5265360b90f194ef8d69acb1b4
+++ pf/html/captive-portal/mobile-confirmation.cgi	6c51e46868369e02352b252a1d575aa96b07eff4
@@ -14,8 +14,10 @@ use CGI::Session;
 use CGI::Carp qw( fatalsToBrowser );
 use CGI;
 use CGI::Session;
+use HTML::Entities qw(decode_entities);
 use Log::Log4perl;
 use POSIX;
+use URI::Escape qw(uri_escape uri_unescape);
 
 use pf::config;
 use pf::iplog;
@@ -40,8 +42,7 @@ my $mac             = ip2mac($ip);
 
 my $ip              = $cgi->remote_addr;
 my $mac             = ip2mac($ip);
-my $destination_url = $cgi->param("destination_url");
-
+my $destination_url = decode_entities(uri_unescape($cgi->param("destination_url")));
 $destination_url = $Config{'trapping'}{'redirecturl'} if (!$destination_url);
 
 if (!valid_mac($mac)) {
@@ -128,7 +129,7 @@ if ($cgi->param("pin")) { # && $session-
       $logger->info("registration url = $destination_url");
 
     } else {
-      print $cgi->redirect("/captive-portal?destination_url=$destination_url");
+      print $cgi->redirect('/captive-portal?destination_url=' . uri_escape($destination_url));
       $logger->info("more violations yet to come for $mac");
     }
 } elsif (defined($cgi->param("action")) && $cgi->param("action") eq 'Confirm') {
============================================================
--- pf/html/captive-portal/redir.cgi	e0bf0a3211b6bee491750d730c7e3b565df879c1
+++ pf/html/captive-portal/redir.cgi	5d83de81f112b11d7a3b3ae6bbd2adf32230a07b
@@ -12,7 +12,9 @@ use CGI::Session;
 use CGI;
 use CGI::Carp qw( fatalsToBrowser );
 use CGI::Session;
+use HTML::Entities qw(decode_entities);
 use Log::Log4perl;
+use URI::Escape qw(uri_escape uri_unescape);
 
 use pf::class;
 use pf::config;
@@ -37,7 +39,7 @@ my $ip              = pf::web::get_clien
 
 my $result;
 my $ip              = pf::web::get_client_ip($cgi);
-my $destination_url = $cgi->param("destination_url") || $Config{'trapping'}{'redirecturl'};
+my $destination_url = decode_entities(uri_unescape($cgi->param("destination_url"))) || $Config{'trapping'}{'redirecturl'};
 my $enable_menu     = $cgi->param("enable_menu");
 my $mac             = ip2mac($ip);
 my %tags;
@@ -115,7 +117,7 @@ if (defined($node_info) && $node_info->{
   if ($cgi->https()) {
     print $cgi->redirect(
         "http://".$Config{'general'}{'hostname'}.".".$Config{'general'}{'domain'}
-        ."/captive-portal?destination_url=$destination_url"
+        .'/captive-portal?destination_url=' . uri_escape($destination_url)
     );
   } else {
     pf::web::generate_pending_page($cgi, $session, $destination_url, $mac);
============================================================
--- pf/html/captive-portal/register.cgi	3febfe1673d7033d33183a2922567b3628b7e2c0
+++ pf/html/captive-portal/register.cgi	611fce68b5e446da2fecebc462dced7f1d91eba5
@@ -12,7 +12,9 @@ use CGI::Session;
 use CGI::Carp qw( fatalsToBrowser );
 use CGI;
 use CGI::Session;
+use HTML::Entities qw(decode_entities);
 use Log::Log4perl;
+use URI::Escape qw(uri_escape uri_unescape);
 use strict;
 use warnings;
 
@@ -38,8 +40,7 @@ my $mac             = ip2mac($ip);
 
 my $ip              = pf::web::get_client_ip($cgi);
 my $mac             = ip2mac($ip);
-my $destination_url = $cgi->param("destination_url");
-
+my $destination_url = decode_entities(uri_unescape($cgi->param("destination_url")));
 $destination_url = $Config{'trapping'}{'redirecturl'} if (!$destination_url);
 
 if (!valid_mac($mac)) {
@@ -99,14 +100,14 @@ if (defined($params{'username'}) && $par
     if ($cgi->https()) {  
       print $cgi->redirect(
         "http://".$Config{'general'}{'hostname'}.".".$Config{'general'}{'domain'}
-        ."/access?destination_url=$destination_url"
+        .'/access?destination_url=' . uri_escape($destination_url)
       );
     } else {
       pf::web::generate_release_page($cgi, $session, $destination_url, $mac);
     }
     exit(0);
   } else {
-    print $cgi->redirect("/captive-portal?destination_url=$destination_url");
+    print $cgi->redirect('/captive-portal?destination_url=' . uri_escape($destination_url));
     $logger->info("more violations yet to come for $mac");
   }
 
@@ -151,7 +152,7 @@ if (defined($params{'username'}) && $par
   if ($cgi->https()) {
     print $cgi->redirect(
       "http://".$Config{'general'}{'hostname'}.".".$Config{'general'}{'domain'}
-      ."/access?destination_url=$destination_url"
+      .'/access?destination_url=' . uri_escape($destination_url)
     );
   } else {
     pf::web::generate_release_page($cgi, $session, $destination_url, $mac);
============================================================
--- pf/html/common/pf.js	6ec3ebf4178037fcae138993b102a6613383ed1c
+++ pf/html/common/pf.js	9249a03a53d92fb76df43908d5573fc3a0d1b6a0
@@ -62,7 +62,7 @@ function networkAccessCallback(destinati
     }
 
     // Chrome 10 / Safari 5 / IE9 (sometimes) flawless
-    top.location.replace(destination_url);
+    top.location.replace(destination_url.unescapeHTML());
 }
 
 /**
@@ -71,7 +71,7 @@ function performRedirect(destination_url
  Simple wrapper to redirect the browser. The wrapper enables us to call the redirect with .delay().
  */
 function performRedirect(destination_url) {
-    top.location.replace(destination_url);
+    top.location.replace(destination_url.unescapeHTML());
 }
 
 /**
============================================================
--- pf/lib/pf/web/guest.pm	6e48b4451ad79308568d3856beea063bf96c715d
+++ pf/lib/pf/web/guest.pm	d6a8d2d19ecb2eab9f3f5542f853ba4c3d5d0217
@@ -94,7 +94,7 @@ sub generate_selfregistration_page {
         logo            => $Config{'general'}{'logo'},
         i18n            => \&i18n,
         deadline        => $Config{'registration'}{'skip_deadline'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
             { name => i18n('MAC'), value => $mac }
@@ -491,7 +491,7 @@ sub generate_login_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         deadline        => $Config{'registration'}{'skip_deadline'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         i18n            => \&i18n,
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
@@ -800,7 +800,7 @@ sub generate_sms_confirmation_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         i18n            => \&i18n,
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         post_uri        => $post_uri,
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
============================================================
--- pf/lib/pf/web.pm	03e5c31ba0f2b29b6d63f0c60ff7c088b821723b
+++ pf/lib/pf/web.pm	cc3030b1085242182f269fbdaba3b51264942854
@@ -108,7 +108,7 @@ sub generate_release_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         timer           => $Config{'trapping'}{'redirtimer'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         redirect_url => $Config{'trapping'}{'redirecturl'},
         i18n => \&i18n,
         initial_delay => $CAPTIVE_PORTAL{'NET_DETECT_INITIAL_DELAY'},
@@ -151,7 +151,7 @@ sub generate_scan_start_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         timer           => $Config{'scan'}{'duration'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         i18n => \&i18n,
         txt_message     => sprintf(
             i18n("system scan in progress"),
@@ -185,7 +185,7 @@ sub generate_login_page {
     my $vars = {
         i18n            => \&i18n,
         logo            => $Config{'general'}{'logo'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
             { name => i18n('MAC'), value => $mac }
@@ -225,7 +225,7 @@ sub generate_enabler_page {
     textdomain("packetfence");
     my $vars = {
         logo            => $Config{'general'}{'logo'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         violation_id    => $violation_id,
         enable_text     => $enable_text,
         i18n            => \&i18n
@@ -247,7 +247,7 @@ sub generate_redirect_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         violation_url   => $violation_url,
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         i18n            => \&i18n,
     };
 
@@ -303,7 +303,7 @@ sub generate_scan_status_page {
         i18n             => \&i18n,
         txt_message      => sprintf(i18n('scan in progress contact support if too long'), $scan_start_time),
         txt_auto_refresh => sprintf(i18n('automatically refresh'), $refresh_timer),
-        destination_url  => $destination_url,
+        destination_url  => encode_entities($destination_url),
         refresh_timer    => $refresh_timer,
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
@@ -559,7 +559,7 @@ sub generate_registration_page {
     my $vars = {
         logo            => $Config{'general'}{'logo'},
         deadline        => $Config{'registration'}{'skip_deadline'},
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         i18n            => \&i18n,
         list_help_info  => [
             { name => i18n('IP'),  value => $ip },
@@ -612,7 +612,7 @@ sub generate_pending_page {
             { name => i18n('IP'),  value => $ip },
             { name => i18n('MAC'), value => $mac }
         ],
-        destination_url => $destination_url,
+        destination_url => encode_entities($destination_url),
         redirect_url => $Config{'trapping'}{'redirecturl'},
         initial_delay => $CAPTIVE_PORTAL{'NET_DETECT_PENDING_INITIAL_DELAY'},
         retry_delay => $CAPTIVE_PORTAL{'NET_DETECT_PENDING_RETRY_DELAY'},
