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 | |
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')
-rw-r--r-- | src/lib/libtls/tls.h | 3 | ||||
-rw-r--r-- | src/lib/libtls/tls_conninfo.c | 14 | ||||
-rw-r--r-- | src/lib/libtls/tls_internal.h | 5 | ||||
-rw-r--r-- | src/lib/libtls/tls_peer.c | 10 | ||||
-rw-r--r-- | src/lib/libtls/tls_verify.c | 97 |
5 files changed, 105 insertions, 24 deletions
diff --git a/src/lib/libtls/tls.h b/src/lib/libtls/tls.h index 6b36886dc3..5a3a6254ab 100644 --- a/src/lib/libtls/tls.h +++ b/src/lib/libtls/tls.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls.h,v 1.67 2024/08/02 15:00:01 tb Exp $ */ | 1 | /* $OpenBSD: tls.h,v 1.68 2024/12/10 08:40:30 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -200,6 +200,7 @@ int tls_close(struct tls *_ctx); | |||
200 | int tls_peer_cert_provided(struct tls *_ctx); | 200 | int tls_peer_cert_provided(struct tls *_ctx); |
201 | int tls_peer_cert_contains_name(struct tls *_ctx, const char *_name); | 201 | int tls_peer_cert_contains_name(struct tls *_ctx, const char *_name); |
202 | 202 | ||
203 | const char *tls_peer_cert_common_name(struct tls *_ctx); | ||
203 | const char *tls_peer_cert_hash(struct tls *_ctx); | 204 | const char *tls_peer_cert_hash(struct tls *_ctx); |
204 | const char *tls_peer_cert_issuer(struct tls *_ctx); | 205 | const char *tls_peer_cert_issuer(struct tls *_ctx); |
205 | const char *tls_peer_cert_subject(struct tls *_ctx); | 206 | const char *tls_peer_cert_subject(struct tls *_ctx); |
diff --git a/src/lib/libtls/tls_conninfo.c b/src/lib/libtls/tls_conninfo.c index bf525170f1..8fb56c92b7 100644 --- a/src/lib/libtls/tls_conninfo.c +++ b/src/lib/libtls/tls_conninfo.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_conninfo.c,v 1.27 2024/03/26 06:31:22 jsing Exp $ */ | 1 | /* $OpenBSD: tls_conninfo.c,v 1.28 2024/12/10 08:40:30 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> | 4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> |
@@ -119,6 +119,14 @@ tls_get_peer_cert_subject(struct tls *ctx, char **subject) | |||
119 | } | 119 | } |
120 | 120 | ||
121 | static int | 121 | static int |
122 | tls_get_peer_cert_common_name(struct tls *ctx, char **common_name) | ||
123 | { | ||
124 | if (ctx->ssl_peer_cert == NULL) | ||
125 | return (-1); | ||
126 | return tls_get_common_name(ctx, ctx->ssl_peer_cert, NULL, common_name); | ||
127 | } | ||
128 | |||
129 | static int | ||
122 | tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, | 130 | tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, |
123 | time_t *notafter) | 131 | time_t *notafter) |
124 | { | 132 | { |
@@ -158,6 +166,9 @@ tls_get_peer_cert_info(struct tls *ctx) | |||
158 | goto err; | 166 | goto err; |
159 | if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) | 167 | if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) |
160 | goto err; | 168 | goto err; |
169 | if (tls_get_peer_cert_common_name(ctx, | ||
170 | &ctx->conninfo->common_name) == -1) | ||
171 | goto err; | ||
161 | if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, | 172 | if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, |
162 | &ctx->conninfo->notafter) == -1) | 173 | &ctx->conninfo->notafter) == -1) |
163 | goto err; | 174 | goto err; |
@@ -298,6 +309,7 @@ tls_conninfo_free(struct tls_conninfo *conninfo) | |||
298 | free(conninfo->servername); | 309 | free(conninfo->servername); |
299 | free(conninfo->version); | 310 | free(conninfo->version); |
300 | 311 | ||
312 | free(conninfo->common_name); | ||
301 | free(conninfo->hash); | 313 | free(conninfo->hash); |
302 | free(conninfo->issuer); | 314 | free(conninfo->issuer); |
303 | free(conninfo->subject); | 315 | free(conninfo->subject); |
diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h index 5ff48ed7c9..8e566a34e0 100644 --- a/src/lib/libtls/tls_internal.h +++ b/src/lib/libtls/tls_internal.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_internal.h,v 1.85 2024/03/26 06:24:52 joshua Exp $ */ | 1 | /* $OpenBSD: tls_internal.h,v 1.86 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 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
@@ -129,6 +129,7 @@ struct tls_conninfo { | |||
129 | int session_resumed; | 129 | int session_resumed; |
130 | char *version; | 130 | char *version; |
131 | 131 | ||
132 | char *common_name; | ||
132 | char *hash; | 133 | char *hash; |
133 | char *issuer; | 134 | char *issuer; |
134 | char *subject; | 135 | char *subject; |
@@ -238,6 +239,8 @@ struct tls_config *tls_config_new_internal(void); | |||
238 | struct tls *tls_new(void); | 239 | struct tls *tls_new(void); |
239 | struct tls *tls_server_conn(struct tls *ctx); | 240 | struct tls *tls_server_conn(struct tls *ctx); |
240 | 241 | ||
242 | int tls_get_common_name(struct tls *_ctx, X509 *_cert, const char *_in_name, | ||
243 | char **_out_common_name); | ||
241 | int tls_check_name(struct tls *ctx, X509 *cert, const char *servername, | 244 | int tls_check_name(struct tls *ctx, X509 *cert, const char *servername, |
242 | int *match); | 245 | int *match); |
243 | int tls_configure_server(struct tls *ctx); | 246 | int tls_configure_server(struct tls *ctx); |
diff --git a/src/lib/libtls/tls_peer.c b/src/lib/libtls/tls_peer.c index ec97a30838..6d63a529f2 100644 --- a/src/lib/libtls/tls_peer.c +++ b/src/lib/libtls/tls_peer.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_peer.c,v 1.8 2017/04/10 17:11:13 jsing Exp $ */ | 1 | /* $OpenBSD: tls_peer.c,v 1.9 2024/12/10 08:40:30 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> | 4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> |
@@ -24,6 +24,14 @@ | |||
24 | #include "tls_internal.h" | 24 | #include "tls_internal.h" |
25 | 25 | ||
26 | const char * | 26 | const char * |
27 | tls_peer_cert_common_name(struct tls *ctx) | ||
28 | { | ||
29 | if (ctx->conninfo == NULL) | ||
30 | return (NULL); | ||
31 | return (ctx->conninfo->common_name); | ||
32 | } | ||
33 | |||
34 | const char * | ||
27 | tls_peer_cert_hash(struct tls *ctx) | 35 | tls_peer_cert_hash(struct tls *ctx) |
28 | { | 36 | { |
29 | if (ctx->conninfo == NULL) | 37 | if (ctx->conninfo == NULL) |
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 | } |