summaryrefslogtreecommitdiff
path: root/src/lib/libtls/tls_verify.c
diff options
context:
space:
mode:
authortb <>2024-12-10 08:40:30 +0000
committertb <>2024-12-10 08:40:30 +0000
commitb3d93b59d26fa80123892302071d566ee8f30930 (patch)
tree2542f39bf15e49eda926e59376de19e797b35c50 /src/lib/libtls/tls_verify.c
parentc0f2dde01da58c00510ac95afabe0df82b7374ec (diff)
downloadopenbsd-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.c97
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
212static int 212static int
213tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, 213tls_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
318int
319tls_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
340static int
341tls_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}