MEDIUM: ssl: {ca,crt}-ignore-err can now use error constant name

The ca-ignore-err and crt-ignore-err directives are now able to use the
openssl X509_V_ERR constant names instead of the numerical values.

This allow a configuration to survive an OpenSSL upgrade, because the
numerical ID can change between versions. For example
X509_V_ERR_INVALID_CA was 24 in OpenSSL 1 and is 79 in OpenSSL 3.

The list of errors must be updated when a new major OpenSSL version is
released.
This commit is contained in:
William Lallemand 2022-11-03 16:31:50 +01:00
parent 9b25982716
commit 960fb74cae
5 changed files with 238 additions and 13 deletions

View File

@ -14084,6 +14084,11 @@ ca-file <cafile>
ca-ignore-err [all|<errorID>,...]
This setting is only available when support for OpenSSL was built in.
Sets a comma separated list of errorIDs to ignore during verify at depth > 0.
It could be a numerical ID, or the constant name (X509_V_ERR) which is
available in the OpenSSL documentation:
https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get_error.html#ERROR-CODES
It is recommended to use the constant name as the numerical value can change
in new version of OpenSSL.
If set to 'all', all errors are ignored. SSL handshake is not aborted if an
error is ignored.
@ -14204,9 +14209,14 @@ crt <cert>
crt-ignore-err <errors>
This setting is only available when support for OpenSSL was built in. Sets a
comma separated list of errorIDs to ignore during verify at depth == 0. If
set to 'all', all errors are ignored. SSL handshake is not aborted if an error
is ignored.
comma separated list of errorIDs to ignore during verify at depth == 0.
It could be a numerical ID, or the constant name (X509_V_ERR) which is
available in the OpenSSL documentation:
https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get_error.html#ERROR-CODES
It is recommended to use the constant name as the numerical value can change
in new version of OpenSSL.
If set to 'all', all errors are ignored. SSL handshake is not aborted if an
error is ignored.
crt-list <file>
This setting is only available when support for OpenSSL was built in. It

View File

@ -41,6 +41,8 @@ int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out);
X509* ssl_sock_get_peer_certificate(SSL *ssl);
unsigned int openssl_version_parser(const char *version);
void exclude_tls_grease(char *input, int len, struct buffer *output);
int x509_v_err_str_to_int(const char *str);
const char *x509_v_err_int_to_str(int code);
#endif /* _HAPROXY_SSL_UTILS_H */
#endif /* USE_OPENSSL */

View File

@ -48,7 +48,7 @@ haproxy h1 -conf {
# crt: certificate of the server
# ca-file: CA used for client authentication request
# crl-file: revocation list for client auth: the client1 certificate is revoked
bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err X509_V_ERR_CERT_REVOKED,X509_V_ERR_CERT_HAS_EXPIRED crl-file ${testdir}/crl-auth.pem
acl cert_expired ssl_c_verify 10
acl cert_revoked ssl_c_verify 23

View File

@ -37,6 +37,7 @@
#include <haproxy/listener.h>
#include <haproxy/openssl-compat.h>
#include <haproxy/ssl_sock.h>
#include <haproxy/ssl_utils.h>
#include <haproxy/tools.h>
#include <haproxy/ssl_ckch.h>
@ -824,7 +825,10 @@ static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct b
static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
int code;
char *s1 = NULL, *s2 = NULL;
char *token = NULL;
char *p = args[cur_arg + 1];
char *str;
unsigned long long *ignerr = conf->crt_ignerr_bitfield;
if (!*p) {
@ -832,6 +836,15 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
return ERR_ALERT | ERR_FATAL;
}
/* copy the string to be able to dump the complete one in case of
* error, because strtok_r is writing \0 inside. */
str = strdup(p);
if (!str) {
memprintf(err, "'%s' : Could not allocate memory", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
if (strcmp(args[cur_arg], "ca-ignore-err") == 0)
ignerr = conf->ca_ignerr_bitfield;
@ -840,19 +853,31 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
return 0;
}
while (p) {
code = atoi(p);
if ((code <= 0) || (code > SSL_MAX_VFY_ERROR_CODE)) {
memprintf(err, "'%s' : ID '%d' out of range (1..%d) in error IDs list '%s'",
args[cur_arg], code, SSL_MAX_VFY_ERROR_CODE, args[cur_arg + 1]);
return ERR_ALERT | ERR_FATAL;
s1 = str;
while ((token = strtok_r(s1, ",", &s2))) {
s1 = NULL;
printf("token: %s\n", token);
if (isdigit((int)*token)) {
code = atoi(token);
if ((code <= 0) || (code > SSL_MAX_VFY_ERROR_CODE)) {
memprintf(err, "'%s' : ID '%d' out of range (1..%d) in error IDs list '%s'",
args[cur_arg], code, SSL_MAX_VFY_ERROR_CODE, args[cur_arg + 1]);
free(str);
return ERR_ALERT | ERR_FATAL;
}
} else {
code = x509_v_err_str_to_int(token);
if (code < 0) {
memprintf(err, "'%s' : error constant '%s' unknown in error IDs list '%s'",
args[cur_arg], token, args[cur_arg + 1]);
free(str);
return ERR_ALERT | ERR_FATAL;
}
}
cert_ignerr_bitfield_set(ignerr, code);
p = strchr(p, ',');
if (p)
p++;
}
free(str);
return 0;
}

View File

@ -417,3 +417,191 @@ void exclude_tls_grease(char *input, int len, struct buffer *output)
if (output->size - output->data > 0 && len - ptr > 0)
output->area[output->data++] = input[ptr];
}
/*
* The following generates an array <x509_v_codes> in which the X509_V_ERR_*
* codes are populated with there string equivalent. Depending on the version
* of the SSL library, some code does not exist, these will be populated as
* "-1" in the array.
*
* The list was taken from
* https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
* and must be updated when new constant are introduced.
*/
/* manual atoi() that only works on the first 10 chars of input (they must all be there) */
#undef _S
#define _S(x) ((x[0]-'0')*1000000000 + \
(x[1]-'0')*100000000 + \
(x[2]-'0')*10000000 + \
(x[3]-'0')*1000000 + \
(x[4]-'0')*100000 + \
(x[5]-'0')*10000 + \
(x[6]-'0')*1000 + \
(x[7]-'0')*100 + \
(x[8]-'0')*10 + \
(x[9]-'0')*1 + \
0)
/* always prepends the sufficient number of leading zeroes to have 10 chars */
#undef _R
#define _R(x) (!x[0] ? _S("0000000000" x) : \
!x[1] ? _S("000000000" x) : \
!x[2] ? _S("00000000" x) : \
!x[3] ? _S("0000000" x) : \
!x[4] ? _S("000000" x) : \
!x[5] ? _S("00000" x) : \
!x[6] ? _S("0000" x) : \
!x[7] ? _S("000" x) : \
!x[8] ? _S("00" x) : \
!x[9] ? _S("0" x) : \
_S("" x))
/* returns the value for an integer-defined macro, otherwise -1
* The extraneous series of "\0" is there to shut up stupid clang which wants to
* evaluate the expression in false branches.
*/
#undef _Q
#define _Q(x) ((#x[0] >= '0' && #x[0] <= '9') ? _R(#x "\0\0\0\0\0\0\0\0\0\0") : -1)
#undef V
#define V(x) { .code = _Q(x), .string = #x }
static const struct x509_v_codes {
int code;
const char *string;
} x509_v_codes[] = {
V(X509_V_OK),
V(X509_V_ERR_UNSPECIFIED),
V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT),
V(X509_V_ERR_UNABLE_TO_GET_CRL),
V(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE),
V(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE),
V(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY),
V(X509_V_ERR_CERT_SIGNATURE_FAILURE),
V(X509_V_ERR_CRL_SIGNATURE_FAILURE),
V(X509_V_ERR_CERT_NOT_YET_VALID),
V(X509_V_ERR_CERT_HAS_EXPIRED),
V(X509_V_ERR_CRL_NOT_YET_VALID),
V(X509_V_ERR_CRL_HAS_EXPIRED),
V(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD),
V(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD),
V(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD),
V(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD),
V(X509_V_ERR_OUT_OF_MEM),
V(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT),
V(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN),
V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY),
V(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE),
V(X509_V_ERR_CERT_CHAIN_TOO_LONG),
V(X509_V_ERR_CERT_REVOKED),
V(X509_V_ERR_NO_ISSUER_PUBLIC_KEY),
V(X509_V_ERR_PATH_LENGTH_EXCEEDED),
V(X509_V_ERR_INVALID_PURPOSE),
V(X509_V_ERR_CERT_UNTRUSTED),
V(X509_V_ERR_CERT_REJECTED),
V(X509_V_ERR_SUBJECT_ISSUER_MISMATCH),
V(X509_V_ERR_AKID_SKID_MISMATCH),
V(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH),
V(X509_V_ERR_KEYUSAGE_NO_CERTSIGN),
V(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER),
V(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION),
V(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN),
V(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION),
V(X509_V_ERR_INVALID_NON_CA),
V(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED),
V(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE),
V(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED),
V(X509_V_ERR_INVALID_EXTENSION),
V(X509_V_ERR_INVALID_POLICY_EXTENSION),
V(X509_V_ERR_NO_EXPLICIT_POLICY),
V(X509_V_ERR_DIFFERENT_CRL_SCOPE),
V(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE),
V(X509_V_ERR_UNNESTED_RESOURCE),
V(X509_V_ERR_PERMITTED_VIOLATION),
V(X509_V_ERR_EXCLUDED_VIOLATION),
V(X509_V_ERR_SUBTREE_MINMAX),
V(X509_V_ERR_APPLICATION_VERIFICATION),
V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE),
V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX),
V(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX),
V(X509_V_ERR_CRL_PATH_VALIDATION_ERROR),
V(X509_V_ERR_PATH_LOOP),
V(X509_V_ERR_SUITE_B_INVALID_VERSION),
V(X509_V_ERR_SUITE_B_INVALID_ALGORITHM),
V(X509_V_ERR_SUITE_B_INVALID_CURVE),
V(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM),
V(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED),
V(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256),
V(X509_V_ERR_HOSTNAME_MISMATCH),
V(X509_V_ERR_EMAIL_MISMATCH),
V(X509_V_ERR_IP_ADDRESS_MISMATCH),
V(X509_V_ERR_DANE_NO_MATCH),
V(X509_V_ERR_EE_KEY_TOO_SMALL),
V(X509_V_ERR_CA_KEY_TOO_SMALL),
V(X509_V_ERR_CA_MD_TOO_WEAK),
V(X509_V_ERR_INVALID_CALL),
V(X509_V_ERR_STORE_LOOKUP),
V(X509_V_ERR_NO_VALID_SCTS),
V(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION),
V(X509_V_ERR_OCSP_VERIFY_NEEDED),
V(X509_V_ERR_OCSP_VERIFY_FAILED),
V(X509_V_ERR_OCSP_CERT_UNKNOWN),
V(X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM),
V(X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH),
V(X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY),
V(X509_V_ERR_INVALID_CA),
V(X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA),
V(X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN),
V(X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA),
V(X509_V_ERR_ISSUER_NAME_EMPTY),
V(X509_V_ERR_SUBJECT_NAME_EMPTY),
V(X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER),
V(X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER),
V(X509_V_ERR_EMPTY_SUBJECT_ALT_NAME),
V(X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL),
V(X509_V_ERR_CA_BCONS_NOT_CRITICAL),
V(X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL),
V(X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL),
V(X509_V_ERR_CA_CERT_MISSING_KEY_USAGE),
V(X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3),
V(X509_V_ERR_EC_KEY_EXPLICIT_PARAMS),
{ 0, NULL },
};
/*
* Return the X509_V_ERR code corresponding to the name of the constant.
* See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
* If not found, return -1
*/
int x509_v_err_str_to_int(const char *str)
{
int i;
for (i = 0; x509_v_codes[i].string; i++) {
if (strcmp(str, x509_v_codes[i].string) == 0) {
return x509_v_codes[i].code;
}
}
return -1;
}
/*
* Return the constant name corresponding to the X509_V_ERR code
* See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
* If not found, return NULL;
*/
const char *x509_v_err_int_to_str(int code)
{
int i;
if (code == -1)
return NULL;
for (i = 0; x509_v_codes[i].string; i++) {
if (x509_v_codes[i].code == code) {
return x509_v_codes[i].string;
}
}
return NULL;
}