From 25f7b470a2ac7a1989850f10e0f0eaff70f064c6 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sat, 16 Nov 2019 12:47:04 -0800
Subject: [PATCH 1/9] Allow external redirects via the image proxy secret as a
 validation method.

---
 Sources/LogInOut.php | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index 84cd1b8b90d..e7af908b97b 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -27,7 +27,7 @@
  */
 function Login()
 {
-	global $txt, $context, $scripturl, $user_info;
+	global $txt, $context, $scripturl, $user_info, $image_proxy_secret;
 
 	// You are already logged in, go take a tour of the boards
 	if (!empty($user_info['id']))
@@ -60,6 +60,9 @@ function Login()
 	// Set the login URL - will be used when the login process is done (but careful not to send us to an attachment).
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
+	// This came from a valid hashed return url.  Or something that knows our secrets...
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) != $_REQUEST['return_hash'])
+		$_SESSION['login_url'] = urldecode($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
 
@@ -145,7 +148,7 @@ function Login2()
 			redirectexit(empty($user_settings['tfa_secret']) ? '' : 'action=logintfa');
 		elseif (!empty($_SESSION['login_url']) && (strpos($_SESSION['login_url'], 'http://') === false && strpos($_SESSION['login_url'], 'https://') === false))
 		{
-			unset ($_SESSION['login_url']);
+			unset($_SESSION['login_url']);
 			redirectexit(empty($user_settings['tfa_secret']) ? '' : 'action=logintfa');
 		}
 		elseif (!empty($user_settings['tfa_secret']))

From 4ca130edc64fcba7349f2c49009c383caf9b6d0a Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sat, 16 Nov 2019 13:17:17 -0800
Subject: [PATCH 2/9] Add the ability to confirm a logout by specifying logout
 with no session var.  Ensuring that we can use redirects from outside SMF if
 needed.

---
 Sources/LogInOut.php                       | 25 ++++++++++++++++++-
 Themes/default/Login.template.php          | 29 ++++++++++++++++++++++
 Themes/default/languages/Login.english.php |  5 ++++
 3 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index e7af908b97b..2fa1c310eaf 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -648,8 +648,31 @@ function Logout($internal = false, $redirect = true)
 {
 	global $sourcedir, $user_info, $user_settings, $context, $smcFunc, $cookiename, $modSettings;
 
+	// They decided to cancel a logout?
+	if (!$internal && isset($_POST['cancel']) && isset($_GET[$context['session_var']]))
+		redirectexit(!empty($_SESSION['logout_return']) ? $_SESSION['logout_return'] : '');
+	// Prompt to logout?
+	elseif (!$internal && !isset($_GET[$context['session_var']]))
+	{
+		loadLanguage('Login');
+		loadTemplate('Login');
+		$context['sub_template'] = 'logout';
+
+		// This came from a valid hashed return url.  Or something that knows our secrets...
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) != $_REQUEST['return_hash'])
+		{
+			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
+			$_SESSION['logout_return'] = $_SESSION['logout_url'];
+		}
+		// Setup the return address.
+		else
+			$_SESSION['logout_return'] = $_SESSION['old_url'];
+
+		// Don't go any further.
+		return;
+	}
 	// Make sure they aren't being auto-logged out.
-	if (!$internal)
+	elseif (!$internal && isset($_GET[$context['session_var']]))
 		checkSession('get');
 
 	require_once($sourcedir . '/Subs-Auth.php');
diff --git a/Themes/default/Login.template.php b/Themes/default/Login.template.php
index 1193f4a31e7..8e75ed4b970 100644
--- a/Themes/default/Login.template.php
+++ b/Themes/default/Login.template.php
@@ -448,4 +448,33 @@ function template_resend()
 		</form>';
 }
 
+/**
+ * Confirm a logout.
+ */
+function template_logout()
+{
+	global $context, $settings, $scripturl, $modSettings, $txt;
+
+	// This isn't that much... just like normal login but with a message at the top.
+	echo '
+	<form action="', $scripturl . '?action=logout;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogout" id="frmLogout">
+		<div class="logout">
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['logout_confirm'], '</h3>
+			</div>
+			<div class="roundframe">
+				<p class="information centertext">
+					', $txt['logout_notice'], '
+				</p>
+
+				<p class="centertext">
+					<input type="submit" value="', $txt['logout'], '" class="button">
+					<input type="submit" name="cancel" value="', $txt['logout_return'], '" class="button">
+				</p>
+
+			</p>
+		</div><!-- .logout -->
+	</form>';
+}
+
 ?>
\ No newline at end of file
diff --git a/Themes/default/languages/Login.english.php b/Themes/default/languages/Login.english.php
index 72ce680b829..baa3f4ee700 100644
--- a/Themes/default/languages/Login.english.php
+++ b/Themes/default/languages/Login.english.php
@@ -140,4 +140,9 @@
 
 $txt['registration_errors_occurred'] = 'The following errors were detected in your registration. Please correct them to continue:';
 
+// Logout
+$txt['logout_confirm'] = 'Are you sure you want to log out?';
+$txt['logout_notice'] = 'You are about to be logged out of the forum and continue browsing as a guest?';
+$txt['logout_return'] = 'Stay logged in and return to browsing as a member.';
+
 ?>
\ No newline at end of file

From 353e58cea84e5055142ada98e69a1703de361344 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sat, 16 Nov 2019 13:25:05 -0800
Subject: [PATCH 3/9] Wrong Punctuation

---
 Themes/default/languages/Login.english.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Themes/default/languages/Login.english.php b/Themes/default/languages/Login.english.php
index baa3f4ee700..46848bf623a 100644
--- a/Themes/default/languages/Login.english.php
+++ b/Themes/default/languages/Login.english.php
@@ -142,7 +142,7 @@
 
 // Logout
 $txt['logout_confirm'] = 'Are you sure you want to log out?';
-$txt['logout_notice'] = 'You are about to be logged out of the forum and continue browsing as a guest?';
+$txt['logout_notice'] = 'You are about to be logged out of the forum and continue browsing as a guest!';
 $txt['logout_return'] = 'Stay logged in and return to browsing as a member.';
 
 ?>
\ No newline at end of file

From 9da5ec5f259410e1ae06f64be8c9c49e958dea44 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sat, 18 Jan 2020 08:11:15 -0800
Subject: [PATCH 4/9] Oops, thats bad

---
 Sources/LogInOut.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index f0b9cb678a2..66c5b7b1651 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -61,7 +61,7 @@ function Login()
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
 	// This came from a valid hashed return url.  Or something that knows our secrets...
-	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) != $_REQUEST['return_hash'])
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) == $_REQUEST['return_hash'])
 		$_SESSION['login_url'] = urldecode($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
@@ -659,7 +659,7 @@ function Logout($internal = false, $redirect = true)
 		$context['sub_template'] = 'logout';
 
 		// This came from a valid hashed return url.  Or something that knows our secrets...
-		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) != $_REQUEST['return_hash'])
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) == $_REQUEST['return_hash'])
 		{
 			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
 			$_SESSION['logout_return'] = $_SESSION['logout_url'];

From 505929c18b092a1303317e03aec29c20ca28d222 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sun, 19 Jan 2020 15:50:55 -0800
Subject: [PATCH 5/9] Use hmac_hash

---
 Sources/LogInOut.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index 66c5b7b1651..e08fcec4820 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -61,7 +61,7 @@ function Login()
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
 	// This came from a valid hashed return url.  Or something that knows our secrets...
-	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) == $_REQUEST['return_hash'])
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', $_REQUEST['return_to'], $image_proxy_secret) == $_REQUEST['return_hash'])
 		$_SESSION['login_url'] = urldecode($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
@@ -659,7 +659,7 @@ function Logout($internal = false, $redirect = true)
 		$context['sub_template'] = 'logout';
 
 		// This came from a valid hashed return url.  Or something that knows our secrets...
-		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && md5($_REQUEST['return_to'] . $image_proxy_secret) == $_REQUEST['return_hash'])
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', $_REQUEST['return_to'], $image_proxy_secret) == $_REQUEST['return_hash'])
 		{
 			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
 			$_SESSION['logout_return'] = $_SESSION['logout_url'];

From 4864c781cba7665ab9b0540004d30d5c99d805e1 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sun, 19 Jan 2020 15:53:39 -0800
Subject: [PATCH 6/9] Roundframe fix

---
 Themes/default/Login.template.php | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Themes/default/Login.template.php b/Themes/default/Login.template.php
index f01adcbfcb7..7a750d6db58 100644
--- a/Themes/default/Login.template.php
+++ b/Themes/default/Login.template.php
@@ -471,8 +471,7 @@ function template_logout()
 					<input type="submit" value="', $txt['logout'], '" class="button">
 					<input type="submit" name="cancel" value="', $txt['logout_return'], '" class="button">
 				</p>
-				</div>
-			</p>
+			</div>
 		</div><!-- .logout -->
 	</form>';
 }

From a85fd0d7b2ad666472f9de9a04888848c089b745 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sun, 25 Oct 2020 14:29:28 -0700
Subject: [PATCH 7/9] Fix a issue where url encoding can prevent the hash from
 matching If the member is logged in, we still want to redirect.

---
 Sources/LogInOut.php | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index 0fd1c570fc0..016a4de3d30 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -31,7 +31,13 @@ function Login()
 
 	// You are already logged in, go take a tour of the boards
 	if (!empty($user_info['id']))
-		redirectexit();
+	{
+ 		// This came from a valid hashed return url.  Or something that knows our secrets...
+ 		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
+			redirectexit(base64_decode($_REQUEST['return_to']));
+		else
+			redirectexit();
+	}
 
 	// We need to load the Login template/language file.
 	loadLanguage('Login');
@@ -61,8 +67,8 @@ function Login()
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
 	// This came from a valid hashed return url.  Or something that knows our secrets...
-	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', $_REQUEST['return_to'], $image_proxy_secret) == $_REQUEST['return_hash'])
-		$_SESSION['login_url'] = urldecode($_REQUEST['return_to']);
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
+		$_SESSION['login_url'] = base64_decode($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
 
@@ -676,7 +682,7 @@ function Logout($internal = false, $redirect = true)
 		$context['sub_template'] = 'logout';
 
 		// This came from a valid hashed return url.  Or something that knows our secrets...
-		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', $_REQUEST['return_to'], $image_proxy_secret) == $_REQUEST['return_hash'])
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
 		{
 			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
 			$_SESSION['logout_return'] = $_SESSION['logout_url'];

From 794e4525c3d021e5cc474f912ba00227b86d1ef8 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sun, 25 Oct 2020 14:35:39 -0700
Subject: [PATCH 8/9] Use get_auth_secret() instead of $image_proxy_secret

---
 Sources/LogInOut.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index 016a4de3d30..9d3e5a972f9 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -27,13 +27,13 @@
  */
 function Login()
 {
-	global $txt, $context, $scripturl, $user_info, $image_proxy_secret;
+	global $txt, $context, $scripturl, $user_info;
 
 	// You are already logged in, go take a tour of the boards
 	if (!empty($user_info['id']))
 	{
  		// This came from a valid hashed return url.  Or something that knows our secrets...
- 		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
+ 		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
 			redirectexit(base64_decode($_REQUEST['return_to']));
 		else
 			redirectexit();
@@ -67,7 +67,7 @@ function Login()
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
 	// This came from a valid hashed return url.  Or something that knows our secrets...
-	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
 		$_SESSION['login_url'] = base64_decode($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
@@ -682,7 +682,7 @@ function Logout($internal = false, $redirect = true)
 		$context['sub_template'] = 'logout';
 
 		// This came from a valid hashed return url.  Or something that knows our secrets...
-		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), $image_proxy_secret) == $_REQUEST['return_hash'])
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
 		{
 			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
 			$_SESSION['logout_return'] = $_SESSION['logout_url'];

From c1d1550c1790d1a7d8510286b011a404266e9221 Mon Sep 17 00:00:00 2001
From: jdarwood007 <unmonitored+github@sleepycode.com>
Date: Sat, 6 Mar 2021 11:41:05 -0800
Subject: [PATCH 9/9] Do not use base64 for urls

---
 Sources/LogInOut.php | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Sources/LogInOut.php b/Sources/LogInOut.php
index 3b8ae30cc2c..4c15456bf57 100644
--- a/Sources/LogInOut.php
+++ b/Sources/LogInOut.php
@@ -33,8 +33,8 @@ function Login()
 	if (!empty($user_info['id']))
 	{
  		// This came from a valid hashed return url.  Or something that knows our secrets...
- 		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
-			redirectexit(base64_decode($_REQUEST['return_to']));
+ 		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', un_htmlspecialchars($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
+			redirectexit(un_htmlspecialchars($_REQUEST['return_to']));
 		else
 			redirectexit();
 	}
@@ -67,8 +67,8 @@ function Login()
 	if (isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0)
 		$_SESSION['login_url'] = $_SESSION['old_url'];
 	// This came from a valid hashed return url.  Or something that knows our secrets...
-	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
-		$_SESSION['login_url'] = base64_decode($_REQUEST['return_to']);
+	elseif (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', un_htmlspecialchars($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
+		$_SESSION['login_url'] = un_htmlspecialchars($_REQUEST['return_to']);
 	elseif (isset($_SESSION['login_url']) && strpos($_SESSION['login_url'], 'dlattach') !== false)
 		unset($_SESSION['login_url']);
 
@@ -685,9 +685,9 @@ function Logout($internal = false, $redirect = true)
 		$context['sub_template'] = 'logout';
 
 		// This came from a valid hashed return url.  Or something that knows our secrets...
-		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', base64_decode($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
+		if (!empty($_REQUEST['return_hash']) && !empty($_REQUEST['return_to']) && hash_hmac('sha1', un_htmlspecialchars($_REQUEST['return_to']), get_auth_secret()) == $_REQUEST['return_hash'])
 		{
-			$_SESSION['logout_url'] = urldecode($_REQUEST['return_to']);
+			$_SESSION['logout_url'] = un_htmlspecialchars($_REQUEST['return_to']);
 			$_SESSION['logout_return'] = $_SESSION['logout_url'];
 		}
 		// Setup the return address.
