diff --git a/CVE-2022-41859-1.patch b/CVE-2022-41859-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..030a97ba4112d862f63851e36862b2806fdbfa65 --- /dev/null +++ b/CVE-2022-41859-1.patch @@ -0,0 +1,293 @@ +From: Markus Koschany +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; + } diff --git a/CVE-2022-41859-2.patch b/CVE-2022-41859-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..576504262c9bc1f4666a6b21d34294375e0fa242 --- /dev/null +++ b/CVE-2022-41859-2.patch @@ -0,0 +1,1158 @@ +From: Markus Koschany +Date: Thu, 23 Feb 2023 13:46:09 +0100 +Subject: CVE-2022-41859 part2 + +Origin: https://github.com/FreeRADIUS/freeradius-server/commit/9e5e8f2f912ad2da8ac6e176ac3a606333469937 +--- + src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c | 776 +++++++++++++++--------- + 1 file changed, 482 insertions(+), 294 deletions(-) + +diff --git a/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c b/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c +index d94851c..5b11a33 100644 +--- a/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c ++++ b/src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c +@@ -1,7 +1,5 @@ +-/* +- * Copyright (c) Dan Harkins, 2012 +- * +- * Copyright holder grants permission for redistribution and use in source ++/** ++ * copyright holder grants permission for redistribution and use in source + * and binary forms, with or without modification, provided that the + * following conditions are met: + * 1. Redistribution of source code must retain the above copyright +@@ -35,94 +33,228 @@ RCSID("$Id: ae311d50658c4dd72e52a1daa19d87f1faade114 $") + USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + + #include "eap_pwd.h" ++#include "const_time.h" + +-#include +-#include ++static uint8_t allzero[SHA256_DIGEST_LENGTH] = { 0x00 }; + + /* The random function H(x) = HMAC-SHA256(0^32, x) */ +-static void H_Init(HMAC_CTX *ctx) ++static void pwd_hmac_final(HMAC_CTX *hmac_ctx, uint8_t *digest) + { +- uint8_t allzero[SHA256_DIGEST_LENGTH]; ++ unsigned int mdlen = SHA256_DIGEST_LENGTH; ++ HMAC_Final(hmac_ctx, digest, &mdlen); ++// HMAC_CTX_reset(hmac_ctx); ++} + +- memset(allzero, 0, SHA256_DIGEST_LENGTH); ++/* a counter-based KDF based on NIST SP800-108 */ ++static void eap_pwd_kdf(uint8_t *key, int keylen, char const *label, ++ int label_len, uint8_t *result, int result_bit_len) ++{ ++ HMAC_CTX *hmac_ctx; ++ uint8_t digest[SHA256_DIGEST_LENGTH]; ++ uint16_t i, ctr, L; ++ int result_byte_len, len = 0; ++ unsigned int mdlen = SHA256_DIGEST_LENGTH; ++ uint8_t mask = 0xff; + +- HMAC_Init_ex(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256(), NULL); ++ MEM(hmac_ctx = HMAC_CTX_new()); ++ result_byte_len = (result_bit_len + 7) / 8; ++ ++ ctr = 0; ++ L = htons(result_bit_len); ++ while (len < result_byte_len) { ++ ctr++; i = htons(ctr); ++ ++ HMAC_Init_ex(hmac_ctx, key, keylen, EVP_sha256(), NULL); ++ if (ctr > 1) HMAC_Update(hmac_ctx, digest, mdlen); ++ HMAC_Update(hmac_ctx, (uint8_t *) &i, sizeof(uint16_t)); ++ HMAC_Update(hmac_ctx, (uint8_t const *)label, label_len); ++ HMAC_Update(hmac_ctx, (uint8_t *) &L, sizeof(uint16_t)); ++ HMAC_Final(hmac_ctx, digest, &mdlen); ++ if ((len + (int) mdlen) > result_byte_len) { ++ memcpy(result + len, digest, result_byte_len - len); ++ } else { ++ memcpy(result + len, digest, mdlen); ++ } ++ len += mdlen; ++// HMAC_CTX_reset(hmac_ctx); ++ } ++ ++ /* since we're expanding to a bit length, mask off the excess */ ++ if (result_bit_len % 8) { ++ mask <<= (8 - (result_bit_len % 8)); ++ result[result_byte_len - 1] &= mask; ++ } ++ ++ HMAC_CTX_free(hmac_ctx); + } + +-static void H_Update(HMAC_CTX *ctx, uint8_t const *data, int len) ++static BIGNUM *consttime_BN (void) + { +- HMAC_Update(ctx, data, len); ++ BIGNUM *bn; ++ ++ bn = BN_new(); ++ if (bn) BN_set_flags(bn, BN_FLG_CONSTTIME); ++ return bn; + } + +-static void H_Final(HMAC_CTX *ctx, uint8_t *digest) ++/* ++ * compute the legendre symbol in constant time ++ */ ++static int legendre(BIGNUM *a, BIGNUM *p, BN_CTX *bnctx) + { +- unsigned int mdlen = SHA256_DIGEST_LENGTH; ++ int symbol; ++ unsigned int mask; ++ BIGNUM *res, *pm1over2; ++ ++ pm1over2 = consttime_BN(); ++ res = consttime_BN(); ++ ++ if (!BN_sub(pm1over2, p, BN_value_one()) || ++ !BN_rshift1(pm1over2, pm1over2) || ++ !BN_mod_exp_mont_consttime(res, a, pm1over2, p, bnctx, NULL)) { ++ BN_free(pm1over2); ++ BN_free(res); ++ return -2; ++ } + +- HMAC_Final(ctx, digest, &mdlen); ++ symbol = -1; ++ mask = const_time_eq(BN_is_word(res, 1), 1); ++ symbol = const_time_select_int(mask, 1, symbol); ++ mask = const_time_eq(BN_is_zero(res), 1); ++ symbol = const_time_select_int(mask, -1, symbol); ++ ++ BN_free(pm1over2); ++ BN_free(res); ++ ++ return symbol; + } + +-/* a counter-based KDF based on NIST SP800-108 */ +-static int eap_pwd_kdf(uint8_t *key, int keylen, char const *label, int labellen, uint8_t *result, int resultbitlen) ++static void do_equation(EC_GROUP *group, BIGNUM *y2, BIGNUM *x, BN_CTX *bnctx) + { +- HMAC_CTX *hctx = NULL; +- uint8_t digest[SHA256_DIGEST_LENGTH]; +- uint16_t i, ctr, L; +- int resultbytelen, len = 0; +- unsigned int mdlen = SHA256_DIGEST_LENGTH; +- uint8_t mask = 0xff; ++ BIGNUM *p, *a, *b, *tmp1, *pm1; ++ ++ tmp1 = BN_new(); ++ pm1 = BN_new(); ++ p = BN_new(); ++ a = BN_new(); ++ b = BN_new(); ++ EC_GROUP_get_curve(group, p, a, b, bnctx); + +- hctx = HMAC_CTX_new(); +- if (hctx == NULL) { +- DEBUG("failed allocating HMAC context"); +- return -1; ++ BN_sub(pm1, p, BN_value_one()); ++ ++ /* ++ * y2 = x^3 + ax + b ++ */ ++ BN_mod_sqr(tmp1, x, p, bnctx); ++ BN_mod_mul(y2, tmp1, x, p, bnctx); ++ BN_mod_mul(tmp1, a, x, p, bnctx); ++ BN_mod_add_quick(y2, y2, tmp1, p); ++ BN_mod_add_quick(y2, y2, b, p); ++ ++ BN_free(tmp1); ++ BN_free(pm1); ++ BN_free(p); ++ BN_free(a); ++ BN_free(b); ++ ++ return; ++} ++ ++static int is_quadratic_residue(BIGNUM *val, BIGNUM *p, BIGNUM *qr, BIGNUM *qnr, BN_CTX *bnctx) ++{ ++ int offset, check, ret = 0; ++ BIGNUM *r = NULL, *pm1 = NULL, *res = NULL, *qr_or_qnr = NULL; ++ unsigned int mask; ++ unsigned char *qr_bin = NULL, *qnr_bin = NULL, *qr_or_qnr_bin = NULL; ++ ++ if (((r = consttime_BN()) == NULL) || ++ ((res = consttime_BN()) == NULL) || ++ ((qr_or_qnr = consttime_BN()) == NULL) || ++ ((pm1 = consttime_BN()) == NULL)) { ++ ret = -2; ++ goto fail; + } +- resultbytelen = (resultbitlen + 7)/8; +- ctr = 0; +- L = htons(resultbitlen); +- while (len < resultbytelen) { +- ctr++; i = htons(ctr); +- HMAC_Init_ex(hctx, key, keylen, EVP_sha256(), NULL); +- if (ctr > 1) { +- HMAC_Update(hctx, digest, mdlen); +- } +- HMAC_Update(hctx, (uint8_t *) &i, sizeof(uint16_t)); +- HMAC_Update(hctx, (uint8_t const *)label, labellen); +- HMAC_Update(hctx, (uint8_t *) &L, sizeof(uint16_t)); +- HMAC_Final(hctx, digest, &mdlen); +- if ((len + (int) mdlen) > resultbytelen) { +- memcpy(result + len, digest, resultbytelen - len); +- } else { +- memcpy(result + len, digest, mdlen); +- } +- len += mdlen; ++ ++ if (((qr_bin = (unsigned char *)malloc(BN_num_bytes(p))) == NULL) || ++ ((qnr_bin = (unsigned char *)malloc(BN_num_bytes(p))) == NULL) || ++ ((qr_or_qnr_bin = (unsigned char *)malloc(BN_num_bytes(p))) == NULL)) { ++ ret = -2; ++ goto fail; + } +- HMAC_CTX_free(hctx); + +- /* since we're expanding to a bit length, mask off the excess */ +- if (resultbitlen % 8) { +- mask <<= (8 - (resultbitlen % 8)); +- result[resultbytelen - 1] &= mask; ++ /* ++ * we select binary in constant time so make them binary ++ */ ++ memset(qr_bin, 0, BN_num_bytes(p)); ++ memset(qnr_bin, 0, BN_num_bytes(p)); ++ memset(qr_or_qnr_bin, 0, BN_num_bytes(p)); ++ ++ offset = BN_num_bytes(p) - BN_num_bytes(qr); ++ BN_bn2bin(qr, qr_bin + offset); ++ ++ offset = BN_num_bytes(p) - BN_num_bytes(qnr); ++ BN_bn2bin(qnr, qnr_bin + offset); ++ ++ /* ++ * r = (random() mod p-1) + 1 ++ */ ++ BN_sub(pm1, p, BN_value_one()); ++ BN_rand_range(r, pm1); ++ BN_add(r, r, BN_value_one()); ++ ++ BN_copy(res, val); ++ ++ /* ++ * res = val * r * r which ensures res != val but has same quadratic residocity ++ */ ++ BN_mod_mul(res, res, r, p, bnctx); ++ BN_mod_mul(res, res, r, p, bnctx); ++ ++ /* ++ * if r is even (mask is -1) then multiply by qnr and our check is qnr ++ * otherwise multiply by qr and our check is qr ++ */ ++ mask = const_time_is_zero(BN_is_odd(r)); ++ const_time_select_bin(mask, qnr_bin, qr_bin, BN_num_bytes(p), qr_or_qnr_bin); ++ BN_bin2bn(qr_or_qnr_bin, BN_num_bytes(p), qr_or_qnr); ++ BN_mod_mul(res, res, qr_or_qnr, p, bnctx); ++ check = const_time_select_int(mask, -1, 1); ++ ++ if ((ret = legendre(res, p, bnctx)) == -2) { ++ ret = -1; /* just say no it's not */ ++ goto fail; + } ++ mask = const_time_eq(ret, check); ++ ret = const_time_select_int(mask, 1, 0); + +- return 0; ++fail: ++ if (qr_bin != NULL) free(qr_bin); ++ if (qnr_bin != NULL) free(qnr_bin); ++ if (qr_or_qnr_bin != NULL) free(qr_or_qnr_bin); ++ BN_free(r); ++ BN_free(res); ++ BN_free(qr_or_qnr); ++ BN_free(pm1); ++ ++ return ret; + } + +-int compute_password_element (pwd_session_t *session, uint16_t grp_num, ++int compute_password_element (REQUEST *request, pwd_session_t *session, 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) + { +- BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; +- HMAC_CTX *ctx = NULL; +- uint8_t pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr; +- int nid, is_odd, primebitlen, primebytelen, ret = 0; +- +- ctx = HMAC_CTX_new(); +- if (ctx == NULL) { +- DEBUG("failed allocating HMAC context"); +- goto fail; +- } ++ BIGNUM *x_candidate = NULL, *rnd = NULL, *y_sqrd = NULL, *qr = NULL, *qnr = NULL, *y1 = NULL, *y2 = NULL, *y = NULL, *exp = NULL; ++ EVP_MD_CTX *hmac_ctx; ++ EVP_PKEY *hmac_pkey; ++ uint8_t pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, *xbuf = NULL, *pm1buf = NULL, *y1buf = NULL, *y2buf = NULL, *ybuf = NULL, ctr; ++ int nid, is_odd, primebitlen, primebytelen, ret = 0, found = 0, mask; ++ int save, i, rbits, qr_or_qnr, save_is_odd = 0, cmp; ++ unsigned int skip; ++ ++ MEM(hmac_ctx = EVP_MD_CTX_new()); ++ MEM(hmac_pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, allzero, sizeof(allzero))); + + switch (grp_num) { /* from IANA registry for IKE D-H groups */ + case 19: +@@ -159,17 +291,23 @@ int compute_password_element (pwd_session_t *session, uint16_t grp_num, + goto fail; + } + +- if (((rnd = BN_new()) == NULL) || +- ((cofactor = BN_new()) == NULL) || ++ if (((rnd = consttime_BN()) == NULL) || + ((session->pwe = EC_POINT_new(session->group)) == NULL) || +- ((session->order = BN_new()) == NULL) || +- ((session->prime = BN_new()) == NULL) || +- ((x_candidate = BN_new()) == NULL)) { ++ ((session->order = consttime_BN()) == NULL) || ++ ((session->prime = consttime_BN()) == NULL) || ++ ((qr = consttime_BN()) == NULL) || ++ ((qnr = consttime_BN()) == NULL) || ++ ((x_candidate = consttime_BN()) == NULL) || ++ ((y_sqrd = consttime_BN()) == NULL) || ++ ((y1 = consttime_BN()) == NULL) || ++ ((y2 = consttime_BN()) == NULL) || ++ ((y = consttime_BN()) == NULL) || ++ ((exp = consttime_BN()) == NULL)) { + DEBUG("unable to create bignums"); + goto fail; + } + +- if (!EC_GROUP_get_curve_GFp(session->group, session->prime, NULL, NULL, NULL)) { ++ if (!EC_GROUP_get_curve(session->group, session->prime, NULL, NULL, NULL)) { + DEBUG("unable to get prime for GFp curve"); + goto fail; + } +@@ -179,46 +317,80 @@ int compute_password_element (pwd_session_t *session, uint16_t grp_num, + goto fail; + } + +- if (!EC_GROUP_get_cofactor(session->group, cofactor, NULL)) { +- DEBUG("unable to get cofactor for curve"); +- goto fail; +- } +- + primebitlen = BN_num_bits(session->prime); + primebytelen = BN_num_bytes(session->prime); + if ((prfbuf = talloc_zero_array(session, uint8_t, primebytelen)) == NULL) { + DEBUG("unable to alloc space for prf buffer"); + goto fail; + } ++ if ((xbuf = talloc_zero_array(request, uint8_t, primebytelen)) == NULL) { ++ DEBUG("unable to alloc space for x buffer"); ++ goto fail; ++ } ++ if ((pm1buf = talloc_zero_array(request, uint8_t, primebytelen)) == NULL) { ++ DEBUG("unable to alloc space for pm1 buffer"); ++ goto fail; ++ } ++ if ((y1buf = talloc_zero_array(request, uint8_t, primebytelen)) == NULL) { ++ DEBUG("unable to alloc space for y1 buffer"); ++ goto fail; ++ } ++ if ((y2buf = talloc_zero_array(request, uint8_t, primebytelen)) == NULL) { ++ DEBUG("unable to alloc space for y2 buffer"); ++ goto fail; ++ } ++ if ((ybuf = talloc_zero_array(request, uint8_t, primebytelen)) == NULL) { ++ DEBUG("unable to alloc space for y buffer"); ++ goto fail; ++ } ++ ++ ++ /* ++ * derive random quadradic residue and quadratic non-residue ++ */ ++ do { ++ BN_rand_range(qr, session->prime); ++ } while (legendre(qr, session->prime, session->bnctx) != 1); ++ ++ do { ++ BN_rand_range(qnr, session->prime); ++ } while (legendre(qnr, session->prime, session->bnctx) != -1); ++ ++ if (!BN_sub(rnd, session->prime, BN_value_one())) { ++ goto fail; ++ } ++ BN_bn2bin(rnd, pm1buf); ++ ++ save_is_odd = 0; ++ found = 0; ++ memset(xbuf, 0, primebytelen); + ctr = 0; +- while (1) { +- if (ctr > 100) { +- DEBUG("unable to find random point on curve for group %d, something's fishy", grp_num); +- goto fail; +- } ++ while (ctr < 40) { + ctr++; + + /* + * compute counter-mode password value and stretch to prime +- * pwd-seed = H(token | peer-id | server-id | password | +- * counter) ++ * pwd-seed = H(token | peer-id | server-id | password | ++ * counter) + */ +- H_Init(ctx); +- H_Update(ctx, (uint8_t *)token, sizeof(*token)); +- H_Update(ctx, (uint8_t const *)id_peer, id_peer_len); +- H_Update(ctx, (uint8_t const *)id_server, id_server_len); +- H_Update(ctx, (uint8_t const *)password, password_len); +- H_Update(ctx, (uint8_t *)&ctr, sizeof(ctr)); +- H_Final(ctx, pwe_digest); ++ EVP_DigestSignInit(hmac_ctx, NULL, EVP_sha256(), NULL, hmac_pkey); ++ EVP_DigestSignUpdate(hmac_ctx, (uint8_t *)token, sizeof(*token)); ++ EVP_DigestSignUpdate(hmac_ctx, (uint8_t const *)id_peer, id_peer_len); ++ EVP_DigestSignUpdate(hmac_ctx, (uint8_t const *)id_server, id_server_len); ++ EVP_DigestSignUpdate(hmac_ctx, (uint8_t const *)password, password_len); ++ EVP_DigestSignUpdate(hmac_ctx, (uint8_t *)&ctr, sizeof(ctr)); ++ ++ { ++ size_t mdlen = SHA256_DIGEST_LENGTH; ++ ++ EVP_DigestSignFinal(hmac_ctx, pwe_digest, &mdlen); ++ EVP_MD_CTX_reset(hmac_ctx); ++ } + + BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd); +- if (eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH, "EAP-pwd Hunting And Pecking", +- strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) != 0) { +- DEBUG("key derivation function failed"); +- goto fail; +- } ++ eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH, "EAP-pwd Hunting And Pecking", ++ strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen); + +- BN_bin2bn(prfbuf, primebytelen, x_candidate); + /* + * eap_pwd_kdf() returns a string of bits 0..primebitlen but + * BN_bin2bn will treat that string of bits as a big endian +@@ -226,49 +398,86 @@ int compute_password_element (pwd_session_t *session, uint16_t grp_num, + * then excessive bits-- those _after_ primebitlen-- so now + * we have to shift right the amount we masked off. + */ +- if (primebitlen % 8) BN_rshift(x_candidate, x_candidate, (8 - (primebitlen % 8))); +- if (BN_ucmp(x_candidate, session->prime) >= 0) continue; ++ if (primebitlen % 8) { ++ rbits = 8 - (primebitlen % 8); ++ for (i = primebytelen - 1; i > 0; i--) { ++ prfbuf[i] = (prfbuf[i - 1] << (8 - rbits)) | (prfbuf[i] >> rbits); ++ } ++ prfbuf[0] >>= rbits; ++ } ++ BN_bin2bn(prfbuf, primebytelen, x_candidate); + + /* +- * need to unambiguously identify the solution, if there is +- * one... +- */ +- is_odd = BN_is_odd(rnd) ? 1 : 0; ++ * it would've been better if the spec reduced the candidate ++ * modulo the prime but it didn't. So if the candidate >= prime ++ * we need to skip it but still run through the operations below ++ */ ++ cmp = const_time_memcmp(pm1buf, prfbuf, primebytelen); ++ skip = const_time_fill_msb((unsigned int)cmp); + + /* +- * solve the quadratic equation, if it's not solvable then we +- * don't have a point +- */ +- if (!EC_POINT_set_compressed_coordinates_GFp(session->group, session->pwe, x_candidate, is_odd, NULL)) { +- continue; +- } ++ * need to unambiguously identify the solution, if there is ++ * one.. ++ */ ++ is_odd = BN_is_odd(rnd); + + /* +- * If there's a solution to the equation then the point must be +- * on the curve so why check again explicitly? OpenSSL code +- * says this is required by X9.62. We're not X9.62 but it can't +- * hurt just to be sure. +- */ +- if (!EC_POINT_is_on_curve(session->group, session->pwe, NULL)) { +- DEBUG("EAP-pwd: point is not on curve"); +- continue; +- } ++ * check whether x^3 + a*x + b is a quadratic residue ++ * ++ * save the first quadratic residue we find in the loop but do ++ * it in constant time. ++ */ ++ do_equation(session->group, y_sqrd, x_candidate, session->bnctx); ++ qr_or_qnr = is_quadratic_residue(y_sqrd, session->prime, qr, qnr, session->bnctx); + +- if (BN_cmp(cofactor, BN_value_one())) { +- /* make sure the point is not in a small sub-group */ +- if (!EC_POINT_mul(session->group, session->pwe, NULL, session->pwe, +- cofactor, NULL)) { +- DEBUG("EAP-pwd: cannot multiply generator by order"); +- continue; +- } ++ /* ++ * if the candidate >= prime then we want to skip it ++ */ ++ qr_or_qnr = const_time_select(skip, 0, qr_or_qnr); + +- if (EC_POINT_is_at_infinity(session->group, session->pwe)) { +- DEBUG("EAP-pwd: point is at infinity"); +- continue; +- } +- } +- /* if we got here then we have a new generator. */ +- break; ++ /* ++ * if we haven't found PWE yet (found = 0) then mask will be true, ++ * if we have found PWE then mask will be false ++ */ ++ mask = const_time_select(found, 0, -1); ++ ++ /* ++ * save will be 1 if we want to save this value-- i.e. we haven't ++ * found PWE yet and this is a quadratic residue-- and 0 otherwise ++ */ ++ save = const_time_select(mask, qr_or_qnr, 0); ++ ++ /* ++ * mask will be true (-1) if we want to save this and false (0) ++ * otherwise ++ */ ++ mask = const_time_eq(save, 1); ++ ++ const_time_select_bin(mask, prfbuf, xbuf, primebytelen, xbuf); ++ save_is_odd = const_time_select(mask, is_odd, save_is_odd); ++ found = const_time_select(mask, -1, found); ++ } ++ ++ /* ++ * now we can savely construct PWE ++ */ ++ BN_bin2bn(xbuf, primebytelen, x_candidate); ++ do_equation(session->group, y_sqrd, x_candidate, session->bnctx); ++ if ( !BN_add(exp, session->prime, BN_value_one()) || ++ !BN_rshift(exp, exp, 2) || ++ !BN_mod_exp_mont_consttime(y1, y_sqrd, exp, session->prime, session->bnctx, NULL) || ++ !BN_sub(y2, session->prime, y1) || ++ !BN_bn2bin(y1, y1buf) || ++ !BN_bn2bin(y2, y2buf)) { ++ DEBUG("unable to compute y"); ++ goto fail; ++ } ++ mask = const_time_eq(save_is_odd, BN_is_odd(y1)); ++ const_time_select_bin(mask, y1buf, y2buf, primebytelen, ybuf); ++ if (BN_bin2bn(ybuf, primebytelen, y) == NULL || ++ !EC_POINT_set_affine_coordinates(session->group, session->pwe, x_candidate, y, session->bnctx)) { ++ DEBUG("unable to set point coordinate"); ++ goto fail; + } + + session->group_num = grp_num; +@@ -278,78 +487,89 @@ int compute_password_element (pwd_session_t *session, uint16_t grp_num, + } + + /* cleanliness and order.... */ +- BN_clear_free(cofactor); + BN_clear_free(x_candidate); ++ BN_clear_free(y_sqrd); ++ BN_clear_free(qr); ++ BN_clear_free(qnr); + BN_clear_free(rnd); +- talloc_free(prfbuf); +- HMAC_CTX_free(ctx); ++ BN_clear_free(y1); ++ BN_clear_free(y2); ++ BN_clear_free(y); ++ BN_clear_free(exp); ++ ++ if (prfbuf) talloc_free(prfbuf); ++ if (xbuf) talloc_free(xbuf); ++ if (pm1buf) talloc_free(pm1buf); ++ if (y1buf) talloc_free(y1buf); ++ if (y2buf) talloc_free(y2buf); ++ if (ybuf) talloc_free(ybuf); ++ ++ EVP_MD_CTX_free(hmac_ctx); ++ EVP_PKEY_free(hmac_pkey); + + return ret; + } + +-int compute_scalar_element (pwd_session_t *session, BN_CTX *bnctx) { ++int compute_scalar_element(REQUEST *request, pwd_session_t *session, BN_CTX *bn_ctx) ++{ + BIGNUM *mask = NULL; + int ret = -1; + +- if (((session->private_value = BN_new()) == NULL) || +- ((session->my_element = EC_POINT_new(session->group)) == NULL) || +- ((session->my_scalar = BN_new()) == NULL) || +- ((mask = BN_new()) == NULL)) { +- DEBUG2("server scalar allocation failed"); +- goto fail; +- } ++ MEM(session->private_value = BN_new()); ++ MEM(session->my_element = EC_POINT_new(session->group)); ++ MEM(session->my_scalar = BN_new()); ++ ++ MEM(mask = BN_new()); + + if (BN_rand_range(session->private_value, session->order) != 1) { +- DEBUG2("Unable to get randomness for private_value"); +- goto fail; ++ REDEBUG("Unable to get randomness for private_value"); ++ goto error; + } + if (BN_rand_range(mask, session->order) != 1) { +- DEBUG2("Unable to get randomness for mask"); +- goto fail; ++ REDEBUG("Unable to get randomness for mask"); ++ goto error; + } + BN_add(session->my_scalar, session->private_value, mask); +- BN_mod(session->my_scalar, session->my_scalar, session->order, bnctx); ++ BN_mod(session->my_scalar, session->my_scalar, session->order, bn_ctx); + +- if (!EC_POINT_mul(session->group, session->my_element, NULL, session->pwe, mask, bnctx)) { +- DEBUG2("server element allocation failed"); +- goto fail; ++ if (!EC_POINT_mul(session->group, session->my_element, NULL, session->pwe, mask, bn_ctx)) { ++ REDEBUG("Server element allocation failed"); ++ goto error; + } + +- if (!EC_POINT_invert(session->group, session->my_element, bnctx)) { +- DEBUG2("server element inversion failed"); +- goto fail; ++ if (!EC_POINT_invert(session->group, session->my_element, bn_ctx)) { ++ REDEBUG("Server element inversion failed"); ++ goto error; + } + + ret = 0; + +-fail: ++error: + BN_clear_free(mask); + + return ret; + } + +-int process_peer_commit (pwd_session_t *session, uint8_t *in, size_t in_len, BN_CTX *bnctx) ++int process_peer_commit(REQUEST *request, pwd_session_t *session, uint8_t *in, size_t in_len, BN_CTX *bn_ctx) + { +- uint8_t *ptr; +- size_t data_len; +- BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; +- EC_POINT *K = NULL, *point = NULL; +- int res = 1; +- +- if (((session->peer_scalar = BN_new()) == NULL) || +- ((session->k = BN_new()) == NULL) || +- ((cofactor = BN_new()) == NULL) || +- ((x = BN_new()) == NULL) || +- ((y = BN_new()) == NULL) || +- ((point = EC_POINT_new(session->group)) == NULL) || +- ((K = EC_POINT_new(session->group)) == NULL) || +- ((session->peer_element = EC_POINT_new(session->group)) == NULL)) { +- DEBUG2("pwd: failed to allocate room to process peer's commit"); +- goto finish; +- } ++ uint8_t *ptr; ++ size_t data_len; ++ BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; ++ EC_POINT *K = NULL, *point = NULL; ++ int ret = 1; ++ ++ MEM(session->peer_scalar = BN_new()); ++ MEM(session->k = BN_new()); ++ MEM(session->peer_element = EC_POINT_new(session->group)); ++ MEM(point = EC_POINT_new(session->group)); ++ MEM(K = EC_POINT_new(session->group)); ++ ++ MEM(cofactor = BN_new()); ++ MEM(x = BN_new()); ++ MEM(y = BN_new()); + + if (!EC_GROUP_get_cofactor(session->group, cofactor, NULL)) { +- DEBUG2("pwd: unable to get group co-factor"); ++ REDEBUG("Unable to get group co-factor"); + goto finish; + } + +@@ -361,7 +581,7 @@ int process_peer_commit (pwd_session_t *session, uint8_t *in, size_t in_len, BN_ + * Did the peer send enough data? + */ + if (in_len < (2 * data_len + BN_num_bytes(session->order))) { +- DEBUG("pwd: Invalid commit packet"); ++ REDEBUG("Invalid commit packet"); + goto finish; + } + +@@ -377,54 +597,54 @@ int process_peer_commit (pwd_session_t *session, uint8_t *in, size_t in_len, BN_ + if (BN_is_zero(session->peer_scalar) || + BN_is_one(session->peer_scalar) || + BN_cmp(session->peer_scalar, session->order) >= 0) { +- ERROR("Peer's scalar is not within the allowed range"); ++ REDEBUG("Peer's scalar is not within the allowed range"); + goto finish; + } + +- if (!EC_POINT_set_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) { +- DEBUG2("pwd: unable to get coordinates of peer's element"); ++ if (!EC_POINT_set_affine_coordinates(session->group, session->peer_element, x, y, bn_ctx)) { ++ REDEBUG("Unable to get coordinates of peer's element"); + goto finish; + } + + /* validate received element */ +- if (!EC_POINT_is_on_curve(session->group, session->peer_element, bnctx) || ++ if (!EC_POINT_is_on_curve(session->group, session->peer_element, bn_ctx) || + EC_POINT_is_at_infinity(session->group, session->peer_element)) { +- ERROR("Peer's element is not a point on the elliptic curve"); ++ REDEBUG("Peer's element is not a point on the elliptic curve"); + goto finish; + } + + /* check to ensure peer's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(session->group, point, NULL, session->peer_element, cofactor, NULL)) { +- DEBUG2("pwd: unable to multiply element by co-factor"); ++ REDEBUG("Unable to multiply element by co-factor"); + goto finish; + } + + if (EC_POINT_is_at_infinity(session->group, point)) { +- DEBUG2("pwd: peer's element is in small sub-group"); ++ REDEBUG("Peer's element is in small sub-group"); + goto finish; + } + } + + /* detect reflection attacks */ + if (BN_cmp(session->peer_scalar, session->my_scalar) == 0 || +- EC_POINT_cmp(session->group, session->peer_element, session->my_element, bnctx) == 0) { +- ERROR("Reflection attack detected"); ++ EC_POINT_cmp(session->group, session->peer_element, session->my_element, bn_ctx) == 0) { ++ REDEBUG("Reflection attack detected"); + goto finish; + } + + /* compute the shared key, k */ +- if ((!EC_POINT_mul(session->group, K, NULL, session->pwe, session->peer_scalar, bnctx)) || +- (!EC_POINT_add(session->group, K, K, session->peer_element, bnctx)) || +- (!EC_POINT_mul(session->group, K, NULL, K, session->private_value, bnctx))) { +- DEBUG2("pwd: unable to compute shared key, k"); ++ if ((!EC_POINT_mul(session->group, K, NULL, session->pwe, session->peer_scalar, bn_ctx)) || ++ (!EC_POINT_add(session->group, K, K, session->peer_element, bn_ctx)) || ++ (!EC_POINT_mul(session->group, K, NULL, K, session->private_value, bn_ctx))) { ++ REDEBUG("Unable to compute shared key, k"); + goto finish; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(session->group, K, NULL, K, cofactor, NULL)) { +- DEBUG2("pwd: unable to multiply k by co-factor"); ++ REDEBUG("Unable to multiply k by co-factor"); + goto finish; + } + } +@@ -436,15 +656,15 @@ int process_peer_commit (pwd_session_t *session, uint8_t *in, size_t in_len, BN_ + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(session->group, K)) { +- DEBUG2("pwd: k is point-at-infinity!"); ++ REDEBUG("K is point-at-infinity"); + goto finish; + } + +- if (!EC_POINT_get_affine_coordinates_GFp(session->group, K, session->k, NULL, bnctx)) { +- DEBUG2("pwd: unable to get shared secret from K"); ++ if (!EC_POINT_get_affine_coordinates(session->group, K, session->k, NULL, bn_ctx)) { ++ REDEBUG("Unable to get shared secret from K"); + goto finish; + } +- res = 0; ++ ret = 0; + + finish: + EC_POINT_clear_free(K); +@@ -453,36 +673,29 @@ finish: + BN_clear_free(x); + BN_clear_free(y); + +- return res; ++ return ret; + } + +-int compute_server_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) ++int compute_server_confirm(REQUEST *request, pwd_session_t *session, uint8_t *out, BN_CTX *bn_ctx) + { +- BIGNUM *x = NULL, *y = NULL; +- HMAC_CTX *ctx = NULL; +- uint8_t *cruft = NULL; +- int offset, req = -1; +- +- ctx = HMAC_CTX_new(); +- if (ctx == NULL) { +- DEBUG2("pwd: unable to allocate HMAC context!"); +- goto finish; +- } ++ BIGNUM *x = NULL, *y = NULL; ++ HMAC_CTX *hmac_ctx = NULL; ++ uint8_t *cruft = NULL; ++ int offset, req = -1; + + /* + * Each component of the cruft will be at most as big as the prime + */ +- if (((cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) || +- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { +- DEBUG2("pwd: unable to allocate space to compute confirm!"); +- goto finish; +- } ++ MEM(cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))); ++ MEM(x = BN_new()); ++ MEM(y = BN_new()); + + /* + * commit is H(k | server_element | server_scalar | peer_element | + * peer_scalar | ciphersuite) + */ +- H_Init(ctx); ++ MEM(hmac_ctx = HMAC_CTX_new()); ++ HMAC_Init_ex(hmac_ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256(), NULL); + + /* + * Zero the memory each time because this is mod prime math and some +@@ -492,24 +705,24 @@ int compute_server_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + */ + offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k); + BN_bn2bin(session->k, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * next is server element: x, y + */ +- if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y, bnctx)) { +- DEBUG2("pwd: unable to get coordinates of server element"); ++ if (!EC_POINT_get_affine_coordinates(session->group, session->my_element, x, y, bn_ctx)) { ++ REDEBUG("Unable to get coordinates of server element"); + goto finish; + } + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * and server scalar +@@ -517,25 +730,25 @@ int compute_server_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar); + BN_bn2bin(session->my_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); + + /* + * next is peer element: x, y + */ +- if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) { +- DEBUG2("pwd: unable to get coordinates of peer's element"); ++ if (!EC_POINT_get_affine_coordinates(session->group, session->peer_element, x, y, bn_ctx)) { ++ REDEBUG("Unable to get coordinates of peer's element"); + goto finish; + } + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * and peer scalar +@@ -543,52 +756,46 @@ int compute_server_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar); + BN_bn2bin(session->peer_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); + + /* + * finally, ciphersuite + */ +- H_Update(ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); ++ HMAC_Update(hmac_ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); + +- H_Final(ctx, out); ++ pwd_hmac_final(hmac_ctx, out); + + req = 0; ++ + finish: ++ HMAC_CTX_free(hmac_ctx); + talloc_free(cruft); + BN_free(x); + BN_free(y); +- HMAC_CTX_free(ctx); + + return req; + } + +-int compute_peer_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) ++int compute_peer_confirm(REQUEST *request, pwd_session_t *session, uint8_t *out, BN_CTX *bn_ctx) + { +- BIGNUM *x = NULL, *y = NULL; +- HMAC_CTX *ctx = NULL; +- uint8_t *cruft = NULL; +- int offset, req = -1; +- +- ctx = HMAC_CTX_new(); +- if (ctx == NULL) { +- DEBUG2("pwd: unable to allocate HMAC context!"); +- goto finish; +- } ++ BIGNUM *x = NULL, *y = NULL; ++ HMAC_CTX *hmac_ctx = NULL; ++ uint8_t *cruft = NULL; ++ int offset, req = -1; + + /* + * Each component of the cruft will be at most as big as the prime + */ +- if (((cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) || +- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { +- DEBUG2("pwd: unable to allocate space to compute confirm!"); +- goto finish; +- } ++ MEM(cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))); ++ MEM(x = BN_new()); ++ MEM(y = BN_new()); + + /* + * commit is H(k | server_element | server_scalar | peer_element | + * peer_scalar | ciphersuite) + */ +- H_Init(ctx); ++ MEM(hmac_ctx = HMAC_CTX_new()); ++ HMAC_Init_ex(hmac_ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256(), NULL); + + /* + * Zero the memory each time because this is mod prime math and some +@@ -598,25 +805,25 @@ int compute_peer_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + */ + offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k); + BN_bn2bin(session->k, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * then peer element: x, y + */ +- if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) { +- DEBUG2("pwd: unable to get coordinates of peer's element"); ++ if (!EC_POINT_get_affine_coordinates(session->group, session->peer_element, x, y, bn_ctx)) { ++ REDEBUG("Unable to get coordinates of peer's element"); + goto finish; + } + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * and peer scalar +@@ -624,24 +831,24 @@ int compute_peer_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar); + BN_bn2bin(session->peer_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); + + /* + * then server element: x, y + */ +- if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y, bnctx)) { +- DEBUG2("pwd: unable to get coordinates of server element"); ++ if (!EC_POINT_get_affine_coordinates(session->group, session->my_element, x, y, bn_ctx)) { ++ REDEBUG("Unable to get coordinates of server element"); + goto finish; + } + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + + /* + * and server scalar +@@ -649,94 +856,75 @@ int compute_peer_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx) + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar); + BN_bn2bin(session->my_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); + + /* + * finally, ciphersuite + */ +- H_Update(ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); ++ HMAC_Update(hmac_ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); + +- H_Final(ctx, out); ++ pwd_hmac_final(hmac_ctx, out); + + req = 0; + finish: ++ HMAC_CTX_free(hmac_ctx); + talloc_free(cruft); + BN_free(x); + BN_free(y); +- HMAC_CTX_free(ctx); + + return req; + } + +-int compute_keys (pwd_session_t *session, uint8_t *peer_confirm, uint8_t *msk, uint8_t *emsk) ++int compute_keys(UNUSED REQUEST *request, pwd_session_t *session, uint8_t *peer_confirm, uint8_t *msk, uint8_t *emsk) + { +- HMAC_CTX *ctx = NULL; +- uint8_t mk[SHA256_DIGEST_LENGTH], *cruft = NULL; +- uint8_t session_id[SHA256_DIGEST_LENGTH + 1]; +- uint8_t msk_emsk[128]; /* 64 each */ +- int offset, ret = -1; +- +- ctx = HMAC_CTX_new(); +- if (ctx == NULL) { +- DEBUG2("pwd: unable to allocate HMAC context!"); +- goto finish; +- } ++ HMAC_CTX *hmac_ctx; ++ uint8_t mk[SHA256_DIGEST_LENGTH], *cruft; ++ uint8_t session_id[SHA256_DIGEST_LENGTH + 1]; ++ uint8_t msk_emsk[128]; /* 64 each */ ++ int offset; + +- if ((cruft = talloc_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) { +- DEBUG2("pwd: unable to allocate space to compute keys"); +- goto finish; +- } ++ MEM(cruft = talloc_array(session, uint8_t, BN_num_bytes(session->prime))); ++ MEM(hmac_ctx = HMAC_CTX_new()); + + /* + * first compute the session-id = TypeCode | H(ciphersuite | scal_p | + * scal_s) + */ + session_id[0] = PW_EAP_PWD; +- H_Init(ctx); +- H_Update(ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); ++ HMAC_Init_ex(hmac_ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256(), NULL); ++ HMAC_Update(hmac_ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar); + memset(cruft, 0, BN_num_bytes(session->prime)); + BN_bn2bin(session->peer_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); + offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar); + memset(cruft, 0, BN_num_bytes(session->prime)); + BN_bn2bin(session->my_scalar, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->order)); +- H_Final(ctx, (uint8_t *)&session_id[1]); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->order)); ++ pwd_hmac_final(hmac_ctx, (uint8_t *)&session_id[1]); + + /* then compute MK = H(k | commit-peer | commit-server) */ +- H_Init(ctx); ++ HMAC_Init_ex(hmac_ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256(), NULL); + + memset(cruft, 0, BN_num_bytes(session->prime)); + offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k); + BN_bn2bin(session->k, cruft + offset); +- H_Update(ctx, cruft, BN_num_bytes(session->prime)); ++ HMAC_Update(hmac_ctx, cruft, BN_num_bytes(session->prime)); + +- H_Update(ctx, peer_confirm, SHA256_DIGEST_LENGTH); ++ HMAC_Update(hmac_ctx, peer_confirm, SHA256_DIGEST_LENGTH); + +- H_Update(ctx, session->my_confirm, SHA256_DIGEST_LENGTH); ++ HMAC_Update(hmac_ctx, session->my_confirm, SHA256_DIGEST_LENGTH); + +- H_Final(ctx, mk); ++ pwd_hmac_final(hmac_ctx, mk); + + /* stretch the mk with the session-id to get MSK | EMSK */ +- if (eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH, (char const *)session_id, +- SHA256_DIGEST_LENGTH + 1, msk_emsk, +- /* it's bits, ((64 + 64) * 8) */ +- 1024) != 0) { +- DEBUG("key derivation function failed"); +- goto finish; +- } ++ eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH, (char const *)session_id, ++ SHA256_DIGEST_LENGTH + 1, msk_emsk, 1024); /* it's bits, ((64 + 64) * 8) */ + + memcpy(msk, msk_emsk, 64); + memcpy(emsk, msk_emsk + 64, 64); + +- ret = 0; +-finish: ++ HMAC_CTX_free(hmac_ctx); + talloc_free(cruft); +- HMAC_CTX_free(ctx); +- return ret; ++ return 0; + } +- +- +- +- diff --git a/freeradius.spec b/freeradius.spec index ea4b33e251cd43d6b9a4a093c3bc54365bcd663c..71a5bc2db02310b0dfdc942e88acec21f7ae6c02 100644 --- a/freeradius.spec +++ b/freeradius.spec @@ -4,7 +4,7 @@ Name: freeradius Version: 3.0.15 -Release: 26 +Release: 27 Summary: Remote Authentication Dial-In User Service License: GPLv2+ and LGPLv2+ @@ -26,6 +26,10 @@ Patch6006: Fix-radeapclient-option-q.patch Patch6007: radsqlrelay-actually-do-something-in-debug-mode.patch patch6008: CVE-2022-41860.patch patch6009: CVE-2022-41861.patch +# https://github.com/FreeRADIUS/freeradius-server/commit/6f0e0aca4f4e614eea4ce10e226aed73ed4ab68b +Patch6010: CVE-2022-41859-1.patch +# https://github.com/FreeRADIUS/freeradius-server/commit/9e5e8f2f912ad2da8ac6e176ac3a606333469937 +Patch6011: CVE-2022-41859-2.patch BuildRequires: autoconf gdbm-devel openssl openssl-devel pam-devel zlib-devel net-snmp-devel BuildRequires: net-snmp-utils readline-devel libpcap-devel systemd-units libtalloc-devel @@ -455,6 +459,9 @@ exit 0 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/mods-available/ldap %changelog +* Fri Dec 15 2023 yaoxin - 3.0.15-27 +- Fix CVE-2022-41859 + * Wed Nov 08 2023 xu_ping <707078654@qq.com> - 3.0.15-26 - remove rpath for some elf file.