From: Markus Koschany <apo@debian.org>
Date: Thu, 23 Feb 2023 11:57:52 +0100
Subject: CVE-2022-41859 part1

Origin: https://github.com/FreeRADIUS/freeradius-server/commit/6f0e0aca4f4e614eea4ce10e226aed73ed4ab68b
---
 src/modules/rlm_eap/types/rlm_eap_pwd/const_time.h | 190 +++++++++++++++++++++
 src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h    |  12 +-
 .../rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c        |  12 +-
 3 files changed, 202 insertions(+), 12 deletions(-)
 create mode 100644 src/modules/rlm_eap/types/rlm_eap_pwd/const_time.h

diff --git a/src/modules/rlm_eap/types/rlm_eap_pwd/const_time.h b/src/modules/rlm_eap/types/rlm_eap_pwd/const_time.h
new file mode 100644
index 0000000..b717dd5
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_pwd/const_time.h
@@ -0,0 +1,190 @@
+/*
+ * Helper functions for constant time operations
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * These helper functions can be used to implement logic that needs to minimize
+ * externally visible differences in execution path by avoiding use of branches,
+ * avoiding early termination or other time differences, and forcing same memory
+ * access pattern regardless of values.
+ */
+
+#ifndef CONST_TIME_H
+#define CONST_TIME_H
+
+
+#if defined(__clang__)
+#define NO_UBSAN_UINT_OVERFLOW                                        \
+	__attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define NO_UBSAN_UINT_OVERFLOW
+#endif
+
+/**
+ * const_time_fill_msb - Fill all bits with MSB value
+ * @param	val Input value
+ * @return	Value with all the bits set to the MSB of the input val
+ */
+static inline unsigned int const_time_fill_msb(unsigned int val)
+{
+	/* Move the MSB to LSB and multiple by -1 to fill in all bits. */
+	return (val >> (sizeof(val) * 8 - 1)) * ~0U;
+}
+
+
+/* @return	-1 if val is zero; 0 if val is not zero */
+static inline unsigned int const_time_is_zero(unsigned int val)
+	NO_UBSAN_UINT_OVERFLOW
+{
+	/* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
+	return const_time_fill_msb(~val & (val - 1));
+}
+
+
+/* @return	-1 if a == b; 0 if a != b */
+static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
+{
+	return const_time_is_zero(a ^ b);
+}
+
+
+/* @return	-1 if a == b; 0 if a != b */
+static inline unsigned char const_time_eq_u8(unsigned int a, unsigned int b)
+{
+	return (unsigned char) const_time_eq(a, b);
+}
+
+
+/**
+ * const_time_eq_bin - Constant time memory comparison
+ * @param	a First buffer to compare
+ * @param	b Second buffer to compare
+ * @param	len Number of octets to compare
+ * @return	-1 if buffers are equal, 0 if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time or memory access pattern could provide external
+ * observer information about the location of the difference in the memory
+ * buffers. The return value does not behave like memcmp(), i.e.,
+ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
+ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
+ * contents of the compared memory buffers, but only on the total compared
+ * length.
+ */
+static inline unsigned int const_time_eq_bin(const void *a, const void *b,
+					     size_t len)
+{
+	const unsigned char *aa = a;
+	const unsigned char *bb = b;
+	size_t i;
+	unsigned char res = 0;
+
+	for (i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return const_time_is_zero(res);
+}
+
+
+/**
+ * const_time_select - Constant time unsigned int selection
+ * @param	mask 0 (false) or -1 (true) to identify which value to select
+ * @param	true_val Value to select for the true case
+ * @param	false_val Value to select for the false case
+ * @return	true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned int const_time_select(unsigned int mask,
+					     unsigned int true_val,
+					     unsigned int false_val)
+{
+	return (mask & true_val) | (~mask & false_val);
+}
+
+
+/**
+ * const_time_select_int - Constant time int selection
+ * @param	mask 0 (false) or -1 (true) to identify which value to select
+ * @param	true_val Value to select for the true case
+ * @param	false_val Value to select for the false case
+ * @return	true_val if mask == -1, false_val if mask == 0
+ */
+static inline int const_time_select_int(unsigned int mask, int true_val,
+					int false_val)
+{
+	return (int) const_time_select(mask, (unsigned int) true_val,
+				       (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_u8 - Constant time u8 selection
+ * @param	mask 0 (false) or -1 (true) to identify which value to select
+ * @param	true_val Value to select for the true case
+ * @param	false_val Value to select for the false case
+ * @return	true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned char const_time_select_u8(unsigned char mask, unsigned char true_val, unsigned char false_val)
+{
+	return (unsigned char) const_time_select(mask, true_val, false_val);
+}
+
+
+/**
+ * const_time_select_s8 - Constant time s8 selection
+ * @param	mask 0 (false) or -1 (true) to identify which value to select
+ * @param	true_val Value to select for the true case
+ * @param	false_val Value to select for the false case
+ * @return	true_val if mask == -1, false_val if mask == 0
+ */
+static inline char const_time_select_s8(char mask, char true_val, char false_val)
+{
+	return (char) const_time_select(mask, (unsigned int) true_val,
+				      (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_bin - Constant time binary buffer selection copy
+ * @param	mask 0 (false) or -1 (true) to identify which value to copy
+ * @param	true_val Buffer to copy for the true case
+ * @param	false_val Buffer to copy for the false case
+ * @param	len Number of octets to copy
+ * @param	dst Destination buffer for the copy
+ *
+ * This function copies the specified buffer into the destination buffer using
+ * operations with identical memory access pattern regardless of which buffer
+ * is being copied.
+ */
+static inline void const_time_select_bin(unsigned char mask, const unsigned char *true_val,
+					 const unsigned char *false_val, size_t len,
+					 unsigned char *dst)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
+}
+
+
+static inline int const_time_memcmp(const void *a, const void *b, size_t len)
+{
+	const unsigned char *aa = a;
+	const unsigned char *bb = b;
+	int diff, res = 0;
+	unsigned int mask;
+
+	if (len == 0)
+		return 0;
+	do {
+		len--;
+		diff = (int) aa[len] - (int) bb[len];
+		mask = const_time_is_zero((unsigned int) diff);
+		res = const_time_select_int(mask, res, diff);
+	} while (len);
+
+	return res;
+}
+
+#endif /* CONST_TIME_H */
diff --git a/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h b/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h
index ca12778..8c5d55b 100644
--- a/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h
+++ b/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h
@@ -104,16 +104,16 @@ typedef struct _pwd_session_t {
     uint8_t my_confirm[SHA256_DIGEST_LENGTH];
 } pwd_session_t;
 
-int compute_password_element(pwd_session_t *sess, uint16_t grp_num,
+int compute_password_element(REQUEST *request, pwd_session_t *sess, uint16_t grp_num,
 			     char const *password, int password_len,
 			     char const *id_server, int id_server_len,
 			     char const *id_peer, int id_peer_len,
 			     uint32_t *token);
-int compute_scalar_element(pwd_session_t *sess, BN_CTX *bnctx);
-int process_peer_commit (pwd_session_t *sess, uint8_t *in, size_t in_len, BN_CTX *bnctx);
-int compute_server_confirm(pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
-int compute_peer_confirm(pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
-int compute_keys(pwd_session_t *sess, uint8_t *peer_confirm,
+int compute_scalar_element(REQUEST *request, pwd_session_t *sess, BN_CTX *bnctx);
+int process_peer_commit(REQUEST *request, pwd_session_t *sess, uint8_t *in, size_t in_len, BN_CTX *bnctx);
+int compute_server_confirm(REQUEST *request, pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
+int compute_peer_confirm(REQUEST *request, pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
+int compute_keys(REQUEST *request, pwd_session_t *sess, uint8_t *peer_confirm,
 		 uint8_t *msk, uint8_t *emsk);
 #ifdef PRINTBUF
 void print_buf(char *str, uint8_t *buf, int len);
diff --git a/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c b/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c
index 90ebe5c..745c4cf 100644
--- a/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c
+++ b/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c
@@ -477,7 +477,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 			return 0;
 		}
 
-		if (compute_password_element(session, session->group_num,
+		if (compute_password_element(request, session, session->group_num,
 			     		     pw->data.strvalue, strlen(pw->data.strvalue),
 					     inst->server_id, strlen(inst->server_id),
 					     session->peer_id, strlen(session->peer_id),
@@ -491,7 +491,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 		/*
 		 * compute our scalar and element
 		 */
-		if (compute_scalar_element(session, session->bnctx)) {
+		if (compute_scalar_element(request, session, session->bnctx)) {
 			DEBUG2("failed to compute server's scalar and element");
 			return 0;
 		}
@@ -546,7 +546,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 		/*
 		 * process the peer's commit and generate the shared key, k
 		 */
-		if (process_peer_commit(session, in, in_len, session->bnctx)) {
+		if (process_peer_commit(request, session, in, in_len, session->bnctx)) {
 			RDEBUG2("failed to process peer's commit");
 			return 0;
 		}
@@ -554,7 +554,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 		/*
 		 * compute our confirm blob
 		 */
-		if (compute_server_confirm(session, session->my_confirm, session->bnctx)) {
+		if (compute_server_confirm(request, session, session->my_confirm, session->bnctx)) {
 			ERROR("rlm_eap_pwd: failed to compute confirm!");
 			return 0;
 		}
@@ -585,7 +585,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 			RDEBUG2("pwd exchange is incorrect: not commit!");
 			return 0;
 		}
-		if (compute_peer_confirm(session, peer_confirm, session->bnctx)) {
+		if (compute_peer_confirm(request, session, peer_confirm, session->bnctx)) {
 			RDEBUG2("pwd exchange cannot compute peer's confirm");
 			return 0;
 		}
@@ -593,7 +593,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
 			RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
 			return 0;
 		}
-		if (compute_keys(session, peer_confirm, msk, emsk)) {
+		if (compute_keys(request, session, peer_confirm, msk, emsk)) {
 			RDEBUG2("pwd exchange cannot generate (E)MSK!");
 			return 0;
 		}
