diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am | 1 | ||||
-rw-r--r-- | common/oid.h | 18 | ||||
-rw-r--r-- | common/tests/Makefile.am | 1 | ||||
-rw-r--r-- | common/tests/test-utf8.c | 252 | ||||
-rw-r--r-- | common/tests/test-x509.c | 81 | ||||
-rw-r--r-- | common/utf8.c | 328 | ||||
-rw-r--r-- | common/utf8.h | 53 | ||||
-rw-r--r-- | common/x509.c | 136 | ||||
-rw-r--r-- | common/x509.h | 16 |
9 files changed, 886 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index 145627c..96000dd 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -46,6 +46,7 @@ libp11_data_la_SOURCES = \ openssl.asn openssl.asn.h \ pem.c pem.h \ pkix.asn pkix.asn.h \ + utf8.c utf8.h \ x509.c x509.h \ $(NULL) diff --git a/common/oid.h b/common/oid.h index 08b3feb..96b7a27 100644 --- a/common/oid.h +++ b/common/oid.h @@ -48,6 +48,24 @@ bool p11_oid_equal (const void *oid_one, int p11_oid_length (const unsigned char *oid); /* + * 2.5.4.3: CN or commonName + */ +static const unsigned char P11_OID_CN[] = + { 0x06, 0x03, 0x55, 0x04, 0x03, }; + +/* + * 2.5.4.10: O or organization + */ +static const unsigned char P11_OID_O[] = + { 0x06, 0x03, 0x55, 0x04, 0x0a, }; + +/* + * 2.5.4.11: OU or organizationalUnit + */ +static const unsigned char P11_OID_OU[] = + { 0x06, 0x03, 0x55, 0x04, 0x0b, }; + +/* * Our support of certificate extensions and so on is not limited to what is * listed here. This is simply the OIDs used by the parsing code that generates * backwards compatible PKCS#11 objects for NSS and the like. diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index ceb0d47..3ef4471 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -38,6 +38,7 @@ CHECK_PROGS += \ test-checksum \ test-pem \ test-oid \ + test-utf8 \ test-x509 \ $(NULL) diff --git a/common/tests/test-utf8.c b/common/tests/test-utf8.c new file mode 100644 index 0000000..d34f597 --- /dev/null +++ b/common/tests/test-utf8.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2013, Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" +#include "CuTest.h" + +#include "utf8.h" + +#include <stdio.h> + +#define ELEMS(x) (sizeof (x) / sizeof (x[0])) + +static void +test_ucs2be (CuTest *cu) +{ + char *output; + size_t length; + int i; + + struct { + const char *output; + size_t output_len; + const unsigned char input[100]; + size_t input_len; + } fixtures[] = { + { "This is a test", 14, + { 0x00, 'T', 0x00, 'h', 0x00, 'i', 0x00, 's', 0x00, ' ', 0x00, 'i', 0x00, 's', 0x00, ' ', + 0x00, 'a', 0x00, ' ', 0x00, 't', 0x00, 'e', 0x00, 's', 0x00, 't' }, 28, + }, + { "V\303\266gel", 6, + { 0x00, 'V', 0x00, 0xF6, 0x00, 'g', 0x00, 'e', 0x00, 'l' }, 10, + }, + { "M\303\244nwich \340\264\205", 12, + { 0x00, 'M', 0x00, 0xE4, 0x00, 'n', 0x00, 'w', 0x00, 'i', 0x00, 'c', 0x00, 'h', + 0x00, ' ', 0x0D, 0x05 }, 18, + } + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + output = p11_utf8_for_ucs2be (fixtures[i].input, + fixtures[i].input_len, + &length); + + CuAssertIntEquals (cu, fixtures[i].output_len, length); + CuAssertStrEquals (cu, fixtures[i].output, output); + } +} + +static void +test_ucs2be_fail (CuTest *cu) +{ + char *output; + size_t length; + int i; + + struct { + const unsigned char input[100]; + size_t input_len; + } fixtures[] = { + { { 0x00, 'T', 0x00, 'h', 0x00, 'i', 0x00, }, 7 /* truncated */ } + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + output = p11_utf8_for_ucs2be (fixtures[i].input, + fixtures[i].input_len, + &length); + CuAssertPtrEquals (cu, NULL, output); + } +} + +static void +test_ucs4be (CuTest *cu) +{ + char *output; + size_t length; + int i; + + struct { + const char *output; + size_t output_len; + const unsigned char input[100]; + size_t input_len; + } fixtures[] = { + { "This is a test", 14, + { 0x00, 0x00, 0x00, 'T', + 0x00, 0x00, 0x00, 'h', + 0x00, 0x00, 0x00, 'i', + 0x00, 0x00, 0x00, 's', + 0x00, 0x00, 0x00, ' ', + 0x00, 0x00, 0x00, 'i', + 0x00, 0x00, 0x00, 's', + 0x00, 0x00, 0x00, ' ', + 0x00, 0x00, 0x00, 'a', + 0x00, 0x00, 0x00, ' ', + 0x00, 0x00, 0x00, 't', + 0x00, 0x00, 0x00, 'e', + 0x00, 0x00, 0x00, 's', + 0x00, 0x00, 0x00, 't', + }, 56, + }, + { "Fun \360\220\214\231", 8, + { 0x00, 0x00, 0x00, 'F', + 0x00, 0x00, 0x00, 'u', + 0x00, 0x00, 0x00, 'n', + 0x00, 0x00, 0x00, ' ', + 0x00, 0x01, 0x03, 0x19, /* U+10319: looks like an antenna */ + }, 20, + } + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + output = p11_utf8_for_ucs4be (fixtures[i].input, + fixtures[i].input_len, + &length); + + CuAssertIntEquals (cu, fixtures[i].output_len, length); + CuAssertStrEquals (cu, fixtures[i].output, output); + } +} + +static void +test_ucs4be_fail (CuTest *cu) +{ + char *output; + size_t length; + int i; + + struct { + const unsigned char input[100]; + size_t input_len; + } fixtures[] = { + { { 0x00, 0x00, 'T', + }, 7 /* truncated */ }, + { { 0x00, 0x00, 0x00, 'F', + 0x00, 0x00, 0x00, 'u', + 0x00, 0x00, 0x00, 'n', + 0x00, 0x00, 0x00, ' ', + 0xD8, 0x00, 0xDF, 0x19, + }, 20, + } + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + output = p11_utf8_for_ucs4be (fixtures[i].input, + fixtures[i].input_len, + &length); + CuAssertPtrEquals (cu, NULL, output); + } +} + +static void +test_utf8 (CuTest *cu) +{ + bool ret; + int i; + + struct { + const char *input; + size_t input_len; + } fixtures[] = { + { "This is a test", 14 }, + { "Good news everyone", -1 }, + { "Fun \360\220\214\231", -1 }, + { "Fun invalid here: \xfe", 4 }, /* but limited length */ + { "V\303\266gel", 6, }, + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + ret = p11_utf8_validate (fixtures[i].input, + fixtures[i].input_len); + CuAssertIntEquals (cu, true, ret); + } +} + +static void +test_utf8_fail (CuTest *cu) +{ + bool ret; + int i; + + struct { + const char *input; + size_t input_len; + } fixtures[] = { + { "This is a test\x80", 15 }, + { "Good news everyone\x88", -1 }, + { "Bad \xe0v following chars should be |0x80", -1 }, + { "Truncated \xe0", -1 }, + }; + + for (i = 0; i < ELEMS (fixtures); i++) { + ret = p11_utf8_validate (fixtures[i].input, + fixtures[i].input_len); + CuAssertIntEquals (cu, false, ret); + } +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + SUITE_ADD_TEST (suite, test_ucs2be); + SUITE_ADD_TEST (suite, test_ucs2be_fail); + SUITE_ADD_TEST (suite, test_ucs4be); + SUITE_ADD_TEST (suite, test_ucs4be_fail); + SUITE_ADD_TEST (suite, test_utf8); + SUITE_ADD_TEST (suite, test_utf8_fail); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} diff --git a/common/tests/test-x509.c b/common/tests/test-x509.c index 0341ed9..6da26bf 100644 --- a/common/tests/test-x509.c +++ b/common/tests/test-x509.c @@ -44,6 +44,8 @@ #include <stdio.h> #include <string.h> +#define ELEMS(x) (sizeof (x) / sizeof (x[0])) + struct { p11_dict *asn1_defs; } test; @@ -335,6 +337,83 @@ test_parse_extension_not_found (CuTest *cu) teardown (cu); } +static void +test_directory_string (CuTest *tc) +{ + struct { + unsigned char input[100]; + int input_len; + char *output; + int output_len; + } fixtures[] = { + /* UTF8String */ + { { 0x0c, 0x0f, 0xc3, 0x84, ' ', 'U', 'T', 'F', '8', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', }, 17, + "\xc3\x84 UTF8 string ", 15, + }, + + /* NumericString */ + { { 0x12, 0x04, '0', '1', '2', '3', }, 6, + "0123", 4, + }, + + /* IA5String */ + { { 0x16, 0x04, ' ', 'A', 'B', ' ', }, 6, + " AB ", 4 + }, + + /* TeletexString */ + { { 0x14, 0x07, 'A', ' ', ' ', 'n', 'i', 'c', 'e' }, 9, + "A nice", 7 + }, + + /* PrintableString */ + { { 0x13, 0x07, 'A', ' ', ' ', 'n', 'i', 'c', 'e' }, 9, + "A nice", 7, + }, + + /* UniversalString */ + { { 0x1c, 0x14, 0x00, 0x00, 0x00, 'F', 0x00, 0x00, 0x00, 'u', + 0x00, 0x00, 0x00, 'n', 0x00, 0x00, 0x00, ' ', 0x00, 0x01, 0x03, 0x19, }, 22, + "Fun \xf0\x90\x8c\x99", 8 + }, + + /* BMPString */ + { { 0x1e, 0x0a, 0x00, 'V', 0x00, 0xF6, 0x00, 'g', 0x00, 'e', 0x00, 'l' }, 12, + "V\xc3\xb6gel", 6 + }, + }; + + char *string; + bool unknown; + size_t length; + int i; + + for (i = 0; i < ELEMS (fixtures); i++) { + string = p11_x509_parse_directory_string (fixtures[i].input, + fixtures[i].input_len, + &unknown, &length); + CuAssertPtrNotNull (tc, string); + CuAssertIntEquals (tc, false, unknown); + + CuAssertIntEquals (tc, fixtures[i].output_len, length); + CuAssertStrEquals (tc, fixtures[i].output, string); + } +} + +static void +test_directory_string_unknown (CuTest *tc) +{ + /* Not a valid choice in DirectoryString */ + unsigned char input[] = { 0x05, 0x07, 'A', ' ', ' ', 'n', 'i', 'c', 'e' }; + char *string; + bool unknown = false; + size_t length; + + string = p11_x509_parse_directory_string (input, sizeof (input), &unknown, &length); + CuAssertPtrEquals (tc, NULL, string); + CuAssertIntEquals (tc, true, unknown); +} + int main (void) { @@ -349,6 +428,8 @@ main (void) SUITE_ADD_TEST (suite, test_parse_key_usage); SUITE_ADD_TEST (suite, test_parse_extension); SUITE_ADD_TEST (suite, test_parse_extension_not_found); + SUITE_ADD_TEST (suite, test_directory_string); + SUITE_ADD_TEST (suite, test_directory_string_unknown); CuSuiteRun (suite); CuSuiteSummary (suite, output); diff --git a/common/utf8.c b/common/utf8.c new file mode 100644 index 0000000..5ce6889 --- /dev/null +++ b/common/utf8.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2013, Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#include "config.h" + +#include "buffer.h" +#include "debug.h" +#include "utf8.h" + +#include <assert.h> +#include <stddef.h> +#include <string.h> + +/* + * Some parts come from FreeBSD utf8.c + * + * Copyright (c) 2002-2004 Tim J. Robbins + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +static ssize_t +utf8_to_wchar (const char *str, + size_t len, + wchar_t *wc) +{ + int ch, i, mask, want; + wchar_t lbound, wch; + + assert (str != NULL); + assert (len > 0); + assert (wc != NULL); + + if (((ch = (unsigned char)*str) & ~0x7f) == 0) { + /* Fast path for plain ASCII characters. */ + *wc = ch; + return 1; + } + + /* + * Determine the number of octets that make up this character + * from the first octet, and a mask that extracts the + * interesting bits of the first octet. We already know + * the character is at least two bytes long. + * + * We also specify a lower bound for the character code to + * detect redundant, non-"shortest form" encodings. For + * example, the sequence C0 80 is _not_ a legal representation + * of the null character. This enforces a 1-to-1 mapping + * between character codes and their multibyte representations. + */ + ch = (unsigned char)*str; + if ((ch & 0xe0) == 0xc0) { + mask = 0x1f; + want = 2; + lbound = 0x80; + } else if ((ch & 0xf0) == 0xe0) { + mask = 0x0f; + want = 3; + lbound = 0x800; + } else if ((ch & 0xf8) == 0xf0) { + mask = 0x07; + want = 4; + lbound = 0x10000; + } else if ((ch & 0xfc) == 0xf8) { + mask = 0x03; + want = 5; + lbound = 0x200000; + } else if ((ch & 0xfe) == 0xfc) { + mask = 0x01; + want = 6; + lbound = 0x4000000; + } else { + /* + * Malformed input; input is not UTF-8. + */ + return -1; + } + + if (want > len) { + /* Incomplete multibyte sequence. */ + return -1; + } + + /* + * Decode the octet sequence representing the character in chunks + * of 6 bits, most significant first. + */ + wch = (unsigned char)*str++ & mask; + for (i = 1; i < want; i++) { + if ((*str & 0xc0) != 0x80) { + /* + * Malformed input; bad characters in the middle + * of a character. + */ + return -1; + } + wch <<= 6; + wch |= *str++ & 0x3f; + } + if (wch < lbound) { + /* + * Malformed input; redundant encoding. + */ + return -1; + } + + *wc = wch; + return want; +} + +static size_t +utf8_for_wchar (wchar_t wc, + char *str, + size_t len) +{ + unsigned char lead; + int i, want; + + assert (str != NULL); + assert (len >= 6); + + if ((wc & ~0x7f) == 0) { + /* Fast path for plain ASCII characters. */ + *str = (char)wc; + return 1; + } + + /* + * Determine the number of octets needed to represent this character. + * We always output the shortest sequence possible. Also specify the + * first few bits of the first octet, which contains the information + * about the sequence length. + */ + if ((wc & ~0x7ff) == 0) { + lead = 0xc0; + want = 2; + } else if ((wc & ~0xffff) == 0) { + lead = 0xe0; + want = 3; + } else if ((wc & ~0x1fffff) == 0) { + lead = 0xf0; + want = 4; + } else if ((wc & ~0x3ffffff) == 0) { + lead = 0xf8; + want = 5; + } else if ((wc & ~0x7fffffff) == 0) { + lead = 0xfc; + want = 6; + } else { + return -1; + } + + assert (want <= len); + + /* + * Output the octets representing the character in chunks + * of 6 bits, least significant last. The first octet is + * a special case because it contains the sequence length + * information. + */ + for (i = want - 1; i > 0; i--) { + str[i] = (wc & 0x3f) | 0x80; + wc >>= 6; + } + *str = (wc & 0xff) | lead; + return want; +} + +static ssize_t +ucs2be_to_wchar (const unsigned char *str, + size_t len, + wchar_t *wc) +{ + assert (str != NULL); + assert (len != 0); + assert (wc != NULL); + + if (len < 2) + return -1; + + *wc = (str[0] << 8 | str[1]); + return 2; +} + +static ssize_t +ucs4be_to_wchar (const unsigned char *str, + size_t len, + wchar_t *wc) +{ + assert (str != NULL); + assert (len != 0); + assert (wc != NULL); + + if (len < 4) + return -1; + + *wc = (str[0] << 24 | str[1] << 16 | str[2] << 8 | str[3]); + return 4; +} + +bool +p11_utf8_validate (const char *str, + ssize_t len) +{ + wchar_t dummy; + ssize_t ret; + + if (len < 0) + len = strlen (str); + + while (len > 0) { + ret = utf8_to_wchar (str, len, &dummy); + if (ret < 0) + return false; + str += ret; + len -= ret; + } + + return true; +} + +static char * +utf8_for_convert (ssize_t (* convert) (const unsigned char *, size_t, wchar_t *), + const unsigned char *str, + size_t num_bytes, + size_t *ret_len) +{ + p11_buffer buf; + char block[6]; + wchar_t wc; + ssize_t ret; + + assert (convert); + + if (!p11_buffer_init_null (&buf, num_bytes)) + return_val_if_reached (NULL); + + while (num_bytes != 0) { + ret = (convert) (str, num_bytes, &wc); + if (ret < 0) { + p11_buffer_uninit (&buf); + return NULL; + } + + str += ret; + num_bytes -= ret; + + ret = utf8_for_wchar (wc, block, 6); + if (ret < 0) { + p11_buffer_uninit (&buf); + return NULL; + } + p11_buffer_add (&buf, block, ret); + } + + return_val_if_fail (p11_buffer_ok (&buf), NULL); + return p11_buffer_steal (&buf, ret_len); +} + +char * +p11_utf8_for_ucs2be (const unsigned char *str, + size_t num_bytes, + size_t *ret_len) +{ + assert (str != NULL); + return utf8_for_convert (ucs2be_to_wchar, str, num_bytes, ret_len); +} + +char * +p11_utf8_for_ucs4be (const unsigned char *str, + size_t num_bytes, + size_t *ret_len) +{ + assert (str != NULL); + return utf8_for_convert (ucs4be_to_wchar, str, num_bytes, ret_len); +} diff --git a/common/utf8.h b/common/utf8.h new file mode 100644 index 0000000..8efa66f --- /dev/null +++ b/common/utf8.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013, Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@redhat.com> + */ + +#ifndef P11_UTF8_H_ +#define P11_UTF8_H_ + +#include "compat.h" + +#include <sys/types.h> + +bool p11_utf8_validate (const char *str, + ssize_t len); + +char * p11_utf8_for_ucs2be (const unsigned char *str, + size_t num_bytes, + size_t *ret_len); + +char * p11_utf8_for_ucs4be (const unsigned char *str, + size_t num_bytes, + size_t *ret_len); + +#endif /* P11_UTF8_H_ */ diff --git a/common/x509.c b/common/x509.c index bfb49df..46e3bd9 100644 --- a/common/x509.c +++ b/common/x509.c @@ -38,6 +38,7 @@ #define P11_DEBUG_FLAG P11_DEBUG_TRUST #include "debug.h" #include "oid.h" +#include "utf8.h" #include "x509.h" #include <stdlib.h> @@ -209,3 +210,138 @@ p11_x509_parse_extended_key_usage (p11_dict *asn1_defs, return ekus; } + +char * +p11_x509_parse_directory_string (const unsigned char *input, + size_t input_len, + bool *unknown_string, + size_t *string_len) +{ + unsigned long tag; + unsigned char cls; + int tag_len; + int len_len; + const void *octets; + long octet_len; + int ret; + + ret = asn1_get_tag_der (input, input_len, &cls, &tag_len, &tag); + return_val_if_fail (ret == ASN1_SUCCESS, NULL); + + octet_len = asn1_get_length_der (input + tag_len, input_len - tag_len, &len_len); + return_val_if_fail (octet_len >= 0, false); + return_val_if_fail (tag_len + len_len + octet_len == input_len, NULL); + + octets = input + tag_len + len_len; + + if (unknown_string) + *unknown_string = false; + + /* The following strings are the ones we normalize */ + switch (tag) { + case 12: /* UTF8String */ + case 18: /* NumericString */ + case 22: /* IA5String */ + case 20: /* TeletexString */ + case 19: /* PrintableString */ + if (!p11_utf8_validate (octets, octet_len)) + return NULL; + if (string_len) + *string_len = octet_len; + return strndup (octets, octet_len); + + case 28: /* UniversalString */ + return p11_utf8_for_ucs4be (octets, octet_len, string_len); + + case 30: /* BMPString */ + return p11_utf8_for_ucs2be (octets, octet_len, string_len); + + /* Just pass through all the non-string types */ + default: + if (unknown_string) + *unknown_string = true; + return NULL; + } + +} + +char * +p11_x509_parse_dn_name (p11_dict *asn_defs, + const unsigned char *der, + size_t der_len, + const unsigned char *oid) +{ + node_asn *asn; + char *part; + + asn = p11_asn1_decode (asn_defs, "PKIX1.Name", der, der_len, NULL); + if (asn == NULL) + return NULL; + + part = p11_x509_lookup_dn_name (asn, NULL, der, der_len, oid); + asn1_delete_structure (&asn); + return part; +} + +char * +p11_x509_lookup_dn_name (node_asn *asn, + const char *dn_field, + const unsigned char *der, + size_t der_len, + const unsigned char *oid) +{ + unsigned char *value; + char field[128]; + int value_len; + char *part; + int i, j; + int start; + int end; + int ret; + + for (i = 1; true; i++) { + for (j = 1; true; j++) { + snprintf (field, sizeof (field), "%s%srdnSequence.?%d.?%d.type", + dn_field, dn_field ? "." : "", i, j); + + ret = asn1_der_decoding_startEnd (asn, der, der_len, field, &start, &end); + + /* No more dns */ + if (ret == ASN1_ELEMENT_NOT_FOUND) + break; + + return_val_if_fail (ret == ASN1_SUCCESS, NULL); + + /* Make sure it's a straightforward oid with certain assumptions */ + if (!p11_oid_simple (der + start, (end - start) + 1)) + continue; + + /* The one we're lookin for? */ + if (!p11_oid_equal (der + start, oid)) + continue; + + snprintf (field, sizeof (field), "%s%srdnSequence.?%d.?%d.value", + dn_field, dn_field ? "." : "", i, j); + + value_len = 0; + ret = asn1_read_value (asn, field, NULL, &value_len); + return_val_if_fail (ret == ASN1_MEM_ERROR, NULL); + + value = malloc (value_len + 1); + return_val_if_fail (value != NULL, NULL); + + ret = asn1_read_value (asn, field, value, &value_len); + return_val_if_fail (ret == ASN1_SUCCESS, false); + + part = p11_x509_parse_directory_string (value, value_len, NULL, NULL); + free (value); + + return part; + } + + if (j == 1) + break; + } + + return NULL; +} diff --git a/common/x509.h b/common/x509.h index 2ec5eb8..cbfc574 100644 --- a/common/x509.h +++ b/common/x509.h @@ -60,4 +60,20 @@ p11_array * p11_x509_parse_extended_key_usage (p11_dict *asn1_defs, const unsigned char *ext_der, size_t ext_len); +char * p11_x509_parse_dn_name (p11_dict *asn_defs, + const unsigned char *der, + size_t der_len, + const unsigned char *oid); + +char * p11_x509_lookup_dn_name (node_asn *asn, + const char *dn_field, + const unsigned char *der, + size_t der_len, + const unsigned char *oid); + +char * p11_x509_parse_directory_string (const unsigned char *input, + size_t input_len, + bool *unknown_string, + size_t *string_len); + #endif /* P11_X509_H_ */ |