summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbeck <>2023-05-29 11:54:50 +0000
committerbeck <>2023-05-29 11:54:50 +0000
commit3244a31e011c814d3d65b8b757a2dd3d28c542e2 (patch)
tree3ee54daae8ea3ccd2187904d345b85cb009a53dd
parent26e7d501966e054b9e377033e8b93db0c4e42412 (diff)
downloadopenbsd-3244a31e011c814d3d65b8b757a2dd3d28c542e2.tar.gz
openbsd-3244a31e011c814d3d65b8b757a2dd3d28c542e2.tar.bz2
openbsd-3244a31e011c814d3d65b8b757a2dd3d28c542e2.zip
Make X509_NAME_get_text_by[NID|OBJ] safer.
This is an un-revert with nits of the previously landed change to do this which broke libtls. libtls has now been changed to not use this function. This change ensures that if something is returned it is "text" (UTF-8) and a C string not containing a NUL byte. Historically callers to this function assume the result is text and a C string however the OpenSSL version simply hands them the bytes from an ASN1_STRING and expects them to know bad things can happen which they almost universally do not check for. Partly inspired by goings on in boringssl. ok jsing@ tb@
-rw-r--r--src/lib/libcrypto/man/X509_NAME_get_index_by_NID.330
-rw-r--r--src/lib/libcrypto/x509/x509name.c38
-rw-r--r--src/regress/lib/libcrypto/x509/x509_asn1.c79
3 files changed, 125 insertions, 22 deletions
diff --git a/src/lib/libcrypto/man/X509_NAME_get_index_by_NID.3 b/src/lib/libcrypto/man/X509_NAME_get_index_by_NID.3
index 20730fb52a..a2ceb10eb5 100644
--- a/src/lib/libcrypto/man/X509_NAME_get_index_by_NID.3
+++ b/src/lib/libcrypto/man/X509_NAME_get_index_by_NID.3
@@ -1,4 +1,4 @@
1.\" $OpenBSD: X509_NAME_get_index_by_NID.3,v 1.15 2023/05/03 08:10:23 beck Exp $ 1.\" $OpenBSD: X509_NAME_get_index_by_NID.3,v 1.16 2023/05/29 11:54:50 beck Exp $
2.\" OpenSSL aebb9aac Jul 19 09:27:53 2016 -0400 2.\" OpenSSL aebb9aac Jul 19 09:27:53 2016 -0400
3.\" 3.\"
4.\" This file was written by Dr. Stephen Henson <steve@openssl.org>. 4.\" This file was written by Dr. Stephen Henson <steve@openssl.org>.
@@ -49,7 +49,7 @@
49.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 49.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50.\" OF THE POSSIBILITY OF SUCH DAMAGE. 50.\" OF THE POSSIBILITY OF SUCH DAMAGE.
51.\" 51.\"
52.Dd $Mdocdate: May 3 2023 $ 52.Dd $Mdocdate: May 29 2023 $
53.Dt X509_NAME_GET_INDEX_BY_NID 3 53.Dt X509_NAME_GET_INDEX_BY_NID 3
54.Os 54.Os
55.Sh NAME 55.Sh NAME
@@ -136,22 +136,32 @@ run from 0 to
136.Fn X509_NAME_get_text_by_NID 136.Fn X509_NAME_get_text_by_NID
137and 137and
138.Fn X509_NAME_get_text_by_OBJ 138.Fn X509_NAME_get_text_by_OBJ
139retrieve the "text" from the first entry in 139retrieve the bytes encoded as UTF-8 from the first entry in
140.Fa name 140.Fa name
141which matches 141which matches
142.Fa nid 142.Fa nid
143or 143or
144.Fa obj . 144.Fa obj .
145At most
146.Fa len
147bytes will be written and the text written to
148.Fa buf
149will be NUL terminated.
150If 145If
151.Fa buf 146.Fa buf
152is 147is
153.Dv NULL , 148.Dv NULL ,
154nothing is written, but the return value is calculated as usual. 149nothing is written, but the return value is calculated as usual.
150If
151.Fa buf
152is not
153.Dv NULL ,
154no more than
155.Fa len
156bytes will be written and the text written to
157.Fa buf
158will be NUL terminated.
159.Pp
160If
161.Fa len
162is not large enough to hold the NUL byte terminated UTF-8 encoding of
163the text, or if the UTF-8 encoding of the text would contains a NUL
164byte, no data will be written and the call will return failure.
155.Pp 165.Pp
156All relevant 166All relevant
157.Dv NID_* 167.Dv NID_*
@@ -189,8 +199,8 @@ if the index is invalid.
189.Fn X509_NAME_get_text_by_NID 199.Fn X509_NAME_get_text_by_NID
190and 200and
191.Fn X509_NAME_get_text_by_OBJ 201.Fn X509_NAME_get_text_by_OBJ
192return the length of the output string written, not counting the 202return the length of the output UTF-8 string written, not counting the
193terminating NUL, or -1 if no match is found. 203terminating NUL, or -1 in the case of an error or no match being found.
194.Pp 204.Pp
195In some cases of failure of 205In some cases of failure of
196.Fn X509_NAME_get_index_by_NID 206.Fn X509_NAME_get_index_by_NID
diff --git a/src/lib/libcrypto/x509/x509name.c b/src/lib/libcrypto/x509/x509name.c
index ecdf473ea9..d2df06ccc6 100644
--- a/src/lib/libcrypto/x509/x509name.c
+++ b/src/lib/libcrypto/x509/x509name.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: x509name.c,v 1.34 2023/05/03 08:10:23 beck Exp $ */ 1/* $OpenBSD: x509name.c,v 1.35 2023/05/29 11:54:50 beck Exp $ */
2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3 * All rights reserved. 3 * All rights reserved.
4 * 4 *
@@ -66,6 +66,7 @@
66#include <openssl/stack.h> 66#include <openssl/stack.h>
67#include <openssl/x509.h> 67#include <openssl/x509.h>
68 68
69#include "bytestring.h"
69#include "x509_local.h" 70#include "x509_local.h"
70 71
71int 72int
@@ -84,21 +85,38 @@ int
84X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, char *buf, 85X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, char *buf,
85 int len) 86 int len)
86{ 87{
87 int i; 88 unsigned char *text = NULL;
88 ASN1_STRING *data; 89 ASN1_STRING *data;
90 int i, text_len;
91 int ret = -1;
92 CBS cbs;
89 93
90 i = X509_NAME_get_index_by_OBJ(name, obj, -1); 94 i = X509_NAME_get_index_by_OBJ(name, obj, -1);
91 if (i < 0) 95 if (i < 0)
92 return (-1); 96 goto err;
93 data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); 97 data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
94 i = (data->length > (len - 1)) ? (len - 1) : data->length; 98 /*
95 if (buf == NULL) 99 * Fail if we cannot encode as UTF-8, or if the UTF-8 encoding of the
96 return (data->length); 100 * string contains a 0 byte, because mortal callers seldom handle the
97 if (i >= 0) { 101 * length difference correctly.
98 memcpy(buf, data->data, i); 102 */
99 buf[i] = '\0'; 103 if ((text_len = ASN1_STRING_to_UTF8(&text, data)) < 0)
104 goto err;
105 CBS_init(&cbs, text, text_len);
106 if (CBS_contains_zero_byte(&cbs))
107 goto err;
108 /* We still support the "pass NULL to find out how much" API */
109 if (buf != NULL) {
110 if (len <= 0 || !CBS_write_bytes(&cbs, buf, len - 1, NULL))
111 goto err;
112 /* It must be a C string */
113 buf[text_len] = '\0';
100 } 114 }
101 return (i); 115 ret = text_len;
116
117 err:
118 free(text);
119 return (ret);
102} 120}
103LCRYPTO_ALIAS(X509_NAME_get_text_by_OBJ); 121LCRYPTO_ALIAS(X509_NAME_get_text_by_OBJ);
104 122
diff --git a/src/regress/lib/libcrypto/x509/x509_asn1.c b/src/regress/lib/libcrypto/x509/x509_asn1.c
index d96a51880e..4c038c9bbf 100644
--- a/src/regress/lib/libcrypto/x509/x509_asn1.c
+++ b/src/regress/lib/libcrypto/x509/x509_asn1.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: x509_asn1.c,v 1.18 2023/05/03 08:10:23 beck Exp $ */ 1/* $OpenBSD: x509_asn1.c,v 1.19 2023/05/29 11:54:50 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2023 Job Snijders <job@openbsd.org> 3 * Copyright (c) 2023 Job Snijders <job@openbsd.org>
4 * 4 *
@@ -512,13 +512,88 @@ test_x509_req_setters(void)
512 return failed; 512 return failed;
513} 513}
514 514
515int main(void) 515static const struct testcase {
516 char *data;
517 int len;
518 int len_to_pass;
519 int encode_type;
520 int expected_result;
521 char *expected_string;
522} testCases[] = {
523 /* should work */
524 {"fozzie", 6, 80, MBSTRING_ASC, 6, "fozzie"},
525 /* should work */
526 {"fozzie", 6, -1, MBSTRING_ASC, 6, ""},
527 /* should fail, truncation */
528 {"muppet", 6, 5, MBSTRING_ASC, -1, ""},
529 /* should fail, contains 0 byte */
530 {"g\0nzo", 5, 80, MBSTRING_ASC, -1, ""},
531 /* should fail, can't encode as utf-8 */
532 {"\x30\x00", 2, 80, V_ASN1_SEQUENCE, -1, ""},
533};
534
535#define NUM_TEST_CASES (sizeof(testCases) / sizeof(testCases[0]))
536
537static int
538test_x509_name_get(void)
539{
540 int failed = 0;
541 size_t i;
542
543 for (i = 0; i < NUM_TEST_CASES; i++) {
544 const struct testcase *test = testCases + i;
545 X509_NAME_ENTRY *entry = NULL;
546 X509_NAME *name = NULL;
547 char textbuf[80];
548 int result;
549
550 textbuf[0] = '\0';
551 if ((name = X509_NAME_new()) == NULL)
552 err(1, "X509_NAME_new");
553 if ((entry = X509_NAME_ENTRY_new()) == NULL)
554 err(1, "X509_NAME_ENTRY_new");
555 if (!X509_NAME_ENTRY_set_object(entry,
556 OBJ_nid2obj(NID_commonName)))
557 err(1, "X509_NAME_ENTRY_set_object");
558 if (!X509_NAME_ENTRY_set_data(entry, test->encode_type,
559 test->data, test->len))
560 err(1, "X509_NAME_ENTRY_set_data");
561 if (!X509_NAME_add_entry(name, entry, -1, 0))
562 err(1, "X509_NAME_add_entry");
563 if (test->len_to_pass == -1)
564 result = X509_NAME_get_text_by_NID(name, NID_commonName,
565 NULL, 0);
566 else
567 result = X509_NAME_get_text_by_NID(name, NID_commonName,
568 textbuf, test->len_to_pass);
569 if (result != test->expected_result) {
570 fprintf(stderr,
571 "Test %zu X509_GET_text_by_NID returned %d,"
572 "expected %d\n", i, result, test->expected_result);
573 failed++;
574 }
575 if (result != -1 &&
576 strcmp(test->expected_string, textbuf) != 0) {
577 fprintf(stderr,
578 "Test %zu, X509_GET_text_by_NID returned bytes do"
579 "not match \n", i);
580 failed++;
581 }
582 X509_NAME_ENTRY_free(entry);
583 X509_NAME_free(name);
584 }
585 return failed;
586}
587
588int
589main(void)
516{ 590{
517 int failed = 0; 591 int failed = 0;
518 592
519 failed |= test_x509_setters(); 593 failed |= test_x509_setters();
520 /* failed |= */ test_x509_crl_setters(); 594 /* failed |= */ test_x509_crl_setters();
521 /* failed |= */ test_x509_req_setters(); 595 /* failed |= */ test_x509_req_setters();
596 failed |= test_x509_name_get();
522 597
523 OPENSSL_cleanup(); 598 OPENSSL_cleanup();
524 599