294 lines
11 KiB
Diff
294 lines
11 KiB
Diff
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;
|
|
}
|