diff options
author | tb <> | 2024-12-10 08:40:30 +0000 |
---|---|---|
committer | tb <> | 2024-12-10 08:40:30 +0000 |
commit | b3d93b59d26fa80123892302071d566ee8f30930 (patch) | |
tree | 2542f39bf15e49eda926e59376de19e797b35c50 /src/lib/libtls/tls_verify.c | |
parent | c0f2dde01da58c00510ac95afabe0df82b7374ec (diff) | |
download | openbsd-b3d93b59d26fa80123892302071d566ee8f30930.tar.gz openbsd-b3d93b59d26fa80123892302071d566ee8f30930.tar.bz2 openbsd-b3d93b59d26fa80123892302071d566ee8f30930.zip |
Provide tls_peer_cert_common_name()
There is currently no sane way of getting your hands on the common name or
subject alternative name of the peer certificate from libtls. It is possible
to extract it from the peer cert's PEM by hand, but that way lies madness.
While the common name is close to being deprecated in the webpki, it is
still the de facto standard to identify client certs. It would be nice to
have a way to access the subject alternative names as well, but this is a
lot more difficult to expose in a clean and sane C interface due to its
multivaluedness.
Initial diff from henning, with input from beck, jsing and myself
henning and bluhm have plans of using this in syslogd.
ok beck
Diffstat (limited to 'src/lib/libtls/tls_verify.c')
-rw-r--r-- | src/lib/libtls/tls_verify.c | 97 |
1 files changed, 77 insertions, 20 deletions
diff --git a/src/lib/libtls/tls_verify.c b/src/lib/libtls/tls_verify.c index 6b2a4fb82a..2935278383 100644 --- a/src/lib/libtls/tls_verify.c +++ b/src/lib/libtls/tls_verify.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_verify.c,v 1.31 2024/11/12 22:50:06 tb Exp $ */ | 1 | /* $OpenBSD: tls_verify.c,v 1.32 2024/12/10 08:40:30 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> |
4 | * | 4 | * |
@@ -210,19 +210,22 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name, | |||
210 | } | 210 | } |
211 | 211 | ||
212 | static int | 212 | static int |
213 | tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | 213 | tls_get_common_name_internal(X509 *cert, char **out_common_name, |
214 | int *cn_match) | 214 | unsigned int *out_tlserr, const char **out_errstr) |
215 | { | 215 | { |
216 | unsigned char *utf8_bytes = NULL; | 216 | unsigned char *utf8_bytes = NULL; |
217 | X509_NAME *subject_name; | 217 | X509_NAME *subject_name; |
218 | char *common_name = NULL; | 218 | char *common_name = NULL; |
219 | union tls_addr addrbuf; | ||
220 | int common_name_len; | 219 | int common_name_len; |
221 | ASN1_STRING *data; | 220 | ASN1_STRING *data; |
222 | int lastpos = -1; | 221 | int lastpos = -1; |
223 | int rv = -1; | 222 | int rv = -1; |
224 | 223 | ||
225 | *cn_match = 0; | 224 | *out_tlserr = TLS_ERROR_UNKNOWN; |
225 | *out_errstr = "unknown"; | ||
226 | |||
227 | free(*out_common_name); | ||
228 | *out_common_name = NULL; | ||
226 | 229 | ||
227 | subject_name = X509_get_subject_name(cert); | 230 | subject_name = X509_get_subject_name(cert); |
228 | if (subject_name == NULL) | 231 | if (subject_name == NULL) |
@@ -244,10 +247,10 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
244 | * more than one CN fed to us in the subject, treating the | 247 | * more than one CN fed to us in the subject, treating the |
245 | * certificate as hostile. | 248 | * certificate as hostile. |
246 | */ | 249 | */ |
247 | tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, | 250 | *out_tlserr = TLS_ERROR_UNKNOWN; |
248 | "error verifying name '%s': " | 251 | *out_errstr = "error getting common name: " |
249 | "Certificate subject contains multiple Common Name fields, " | 252 | "Certificate subject contains multiple Common Name fields, " |
250 | "probably a malicious or malformed certificate", name); | 253 | "probably a malicious or malformed certificate"; |
251 | goto err; | 254 | goto err; |
252 | } | 255 | } |
253 | 256 | ||
@@ -257,10 +260,10 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
257 | * Fail if we cannot encode the CN bytes as UTF-8. | 260 | * Fail if we cannot encode the CN bytes as UTF-8. |
258 | */ | 261 | */ |
259 | if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) { | 262 | if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) { |
260 | tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, | 263 | *out_tlserr = TLS_ERROR_UNKNOWN; |
261 | "error verifying name '%s': " | 264 | *out_errstr = "error getting common name: " |
262 | "Common Name field cannot be encoded as a UTF-8 string, " | 265 | "Common Name field cannot be encoded as a UTF-8 string, " |
263 | "probably a malicious certificate", name); | 266 | "probably a malicious certificate"; |
264 | goto err; | 267 | goto err; |
265 | } | 268 | } |
266 | /* | 269 | /* |
@@ -268,30 +271,85 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
268 | * must be between 1 and 64 bytes long. | 271 | * must be between 1 and 64 bytes long. |
269 | */ | 272 | */ |
270 | if (common_name_len < 1 || common_name_len > 64) { | 273 | if (common_name_len < 1 || common_name_len > 64) { |
271 | tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, | 274 | *out_tlserr = TLS_ERROR_UNKNOWN; |
272 | "error verifying name '%s': " | 275 | *out_errstr = "error getting common name: " |
273 | "Common Name field has invalid length, " | 276 | "Common Name field has invalid length, " |
274 | "probably a malicious certificate", name); | 277 | "probably a malicious certificate"; |
275 | goto err; | 278 | goto err; |
276 | } | 279 | } |
277 | /* | 280 | /* |
278 | * Fail if the resulting text contains a NUL byte. | 281 | * Fail if the resulting text contains a NUL byte. |
279 | */ | 282 | */ |
280 | if (memchr(utf8_bytes, 0, common_name_len) != NULL) { | 283 | if (memchr(utf8_bytes, 0, common_name_len) != NULL) { |
281 | tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, | 284 | *out_tlserr = TLS_ERROR_UNKNOWN; |
282 | "error verifying name '%s': " | 285 | *out_errstr = "error getting common name: " |
283 | "NUL byte in Common Name field, " | 286 | "NUL byte in Common Name field, " |
284 | "probably a malicious certificate", name); | 287 | "probably a malicious certificate"; |
285 | goto err; | 288 | goto err; |
286 | } | 289 | } |
287 | 290 | ||
288 | common_name = strndup(utf8_bytes, common_name_len); | 291 | common_name = strndup(utf8_bytes, common_name_len); |
289 | if (common_name == NULL) { | 292 | if (common_name == NULL) { |
290 | tls_set_error(ctx, TLS_ERROR_OUT_OF_MEMORY, | 293 | *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; |
291 | "out of memory"); | 294 | *out_errstr = "out of memory"; |
295 | goto err; | ||
296 | } | ||
297 | |||
298 | *out_common_name = common_name; | ||
299 | common_name = NULL; | ||
300 | |||
301 | done: | ||
302 | if (*out_common_name == NULL) | ||
303 | *out_common_name = strdup(""); | ||
304 | if (*out_common_name == NULL) { | ||
305 | *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; | ||
306 | *out_errstr = "out of memory"; | ||
292 | goto err; | 307 | goto err; |
293 | } | 308 | } |
294 | 309 | ||
310 | rv = 0; | ||
311 | |||
312 | err: | ||
313 | free(utf8_bytes); | ||
314 | free(common_name); | ||
315 | return rv; | ||
316 | } | ||
317 | |||
318 | int | ||
319 | tls_get_common_name(struct tls *ctx, X509 *cert, const char *in_name, | ||
320 | char **out_common_name) | ||
321 | { | ||
322 | unsigned int errcode = TLS_ERROR_UNKNOWN; | ||
323 | const char *errstr = "unknown"; | ||
324 | |||
325 | if (tls_get_common_name_internal(cert, out_common_name, &errcode, | ||
326 | &errstr) == -1) { | ||
327 | const char *name = in_name; | ||
328 | const char *space = " "; | ||
329 | |||
330 | if (name == NULL) | ||
331 | name = space = ""; | ||
332 | |||
333 | tls_set_errorx(ctx, errcode, "%s%s%s", name, space, errstr); | ||
334 | return -1; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int | ||
341 | tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | ||
342 | int *cn_match) | ||
343 | { | ||
344 | char *common_name = NULL; | ||
345 | union tls_addr addrbuf; | ||
346 | int rv = -1; | ||
347 | |||
348 | if (tls_get_common_name(ctx, cert, name, &common_name) == -1) | ||
349 | goto err; | ||
350 | if (strlen(common_name) == 0) | ||
351 | goto done; | ||
352 | |||
295 | /* | 353 | /* |
296 | * We don't want to attempt wildcard matching against IP addresses, | 354 | * We don't want to attempt wildcard matching against IP addresses, |
297 | * so perform a simple comparison here. | 355 | * so perform a simple comparison here. |
@@ -310,7 +368,6 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
310 | rv = 0; | 368 | rv = 0; |
311 | 369 | ||
312 | err: | 370 | err: |
313 | free(utf8_bytes); | ||
314 | free(common_name); | 371 | free(common_name); |
315 | return rv; | 372 | return rv; |
316 | } | 373 | } |