From 32ca4f6d3167d08fc985d66fe48f453954596f87 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sun, 3 Feb 2013 23:26:10 +0100 Subject: Use the CN, OU or O of certificates to generate a label * This is in cases where the certificate information does not already have a friendly name or alias. --- common/Makefile.am | 1 + common/oid.h | 18 +++ common/tests/Makefile.am | 1 + common/tests/test-utf8.c | 252 ++++++++++++++++++++++++++++++++++ common/tests/test-x509.c | 81 +++++++++++ common/utf8.c | 328 +++++++++++++++++++++++++++++++++++++++++++++ common/utf8.h | 53 ++++++++ common/x509.c | 136 +++++++++++++++++++ common/x509.h | 16 +++ tools/extract-openssl.c | 70 +++------- tools/tests/Makefile.am | 7 - tools/tests/test-openssl.c | 16 ++- tools/tests/test-utf8.c | 252 ---------------------------------- tools/utf8.c | 328 --------------------------------------------- tools/utf8.h | 53 -------- trust/parser.c | 51 ++++--- trust/tests/test-parser.c | 22 +-- 17 files changed, 951 insertions(+), 734 deletions(-) create mode 100644 common/tests/test-utf8.c create mode 100644 common/utf8.c create mode 100644 common/utf8.h delete mode 100644 tools/tests/test-utf8.c delete mode 100644 tools/utf8.c delete mode 100644 tools/utf8.h 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 + */ + +#include "config.h" +#include "CuTest.h" + +#include "utf8.h" + +#include + +#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 #include +#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 + */ + +#include "config.h" + +#include "buffer.h" +#include "debug.h" +#include "utf8.h" + +#include +#include +#include + +/* + * 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 + */ + +#ifndef P11_UTF8_H_ +#define P11_UTF8_H_ + +#include "compat.h" + +#include + +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 @@ -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_ */ diff --git a/tools/extract-openssl.c b/tools/extract-openssl.c index e59d313..fb87cd6 100644 --- a/tools/extract-openssl.c +++ b/tools/extract-openssl.c @@ -59,7 +59,7 @@ /* These functions are declared with a global scope for testing */ void p11_openssl_canon_string (char *str, - long *len); + size_t *len); bool p11_openssl_canon_string_der (p11_buffer *der); @@ -356,7 +356,7 @@ p11_extract_openssl_bundle (P11KitIter *iter, void p11_openssl_canon_string (char *str, - long *len) + size_t *len) { bool nsp; bool sp; @@ -394,64 +394,24 @@ p11_openssl_canon_string (char *str, bool p11_openssl_canon_string_der (p11_buffer *der) { - unsigned char *input = der->data; - int input_len = der->len; - unsigned char *output; - unsigned long tag; - unsigned char cls; - size_t conv_len; - int tag_len; - int len_len; - void *octets; - long octet_len; + char *string; + size_t length; int output_len; - void *conv = NULL; + int len_len; + bool unknown_string; + unsigned char *output; int len; - int ret; - - ret = asn1_get_tag_der (input, input_len, &cls, &tag_len, &tag); - return_val_if_fail (ret == ASN1_SUCCESS, false); - 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, false); - - octets = input + tag_len + len_len; - - /* 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 false; - break; - - case 28: /* UniversalString */ - octets = conv = p11_utf8_for_ucs4be (octets, octet_len, &conv_len); - if (conv == NULL) - return false; - octet_len = conv_len; - break; - - case 30: /* BMPString */ - octets = conv = p11_utf8_for_ucs2be (octets, octet_len, &conv_len); - if (conv == NULL) - return false; - octet_len = conv_len; - break; + string = p11_x509_parse_directory_string (der->data, der->len, &unknown_string, &length); /* Just pass through all the non-string types */ - default: - return true; - } + if (string == NULL) + return unknown_string; - p11_openssl_canon_string (octets, &octet_len); + p11_openssl_canon_string (string, &length); - asn1_length_der (octet_len, NULL, &len_len); - output_len = 1 + len_len + octet_len; + asn1_length_der (length, NULL, &len_len); + output_len = 1 + len_len + length; if (!p11_buffer_reset (der, output_len)) return_val_if_reached (false); @@ -461,10 +421,10 @@ p11_openssl_canon_string_der (p11_buffer *der) output[0] = 12; /* UTF8String */ len = output_len - 1; - asn1_octet_der (octets, octet_len, output + 1, &len); + asn1_octet_der ((unsigned char *)string, length, output + 1, &len); assert (len == output_len - 1); - free (conv); + free (string); return true; } diff --git a/tools/tests/Makefile.am b/tools/tests/Makefile.am index e50836d..4239a41 100644 --- a/tools/tests/Makefile.am +++ b/tools/tests/Makefile.am @@ -37,7 +37,6 @@ libtestcommon_la_SOURCES = \ test.c test.h CHECK_PROGS = \ - test-utf8 \ test-save \ test-extract \ test-x509 \ @@ -79,12 +78,6 @@ test_openssl_SOURCES = \ $(TOOLS)/extract-info.c \ $(TOOLS)/extract-openssl.c \ $(TOOLS)/save.c \ - $(TOOLS)/utf8.c \ - $(NULL) - -test_utf8_SOURCES = \ - test-utf8.c \ - $(TOOLS)/utf8.c \ $(NULL) endif # WITH_ASN1 diff --git a/tools/tests/test-openssl.c b/tools/tests/test-openssl.c index a48220d..d242b50 100644 --- a/tools/tests/test-openssl.c +++ b/tools/tests/test-openssl.c @@ -373,7 +373,7 @@ test_file_without (CuTest *tc) } /* From extract-openssl.c */ -void p11_openssl_canon_string (char *str, long *len); +void p11_openssl_canon_string (char *str, size_t *len); static void test_canon_string (CuTest *tc) @@ -392,21 +392,23 @@ test_canon_string (CuTest *tc) }; char *str; - long len; - long out; + size_t len; + size_t out; int i; for (i = 0; i < ELEMS (fixtures); i++) { - len = fixtures[i].input_len; - if (len < 0) + if (fixtures[i].input_len < 0) len = strlen (fixtures[i].input); + else + len = fixtures[i].input_len; str = strndup (fixtures[i].input, len); p11_openssl_canon_string (str, &len); - out = fixtures[i].output_len; - if (out < 0) + if (fixtures[i].output_len < 0) out = strlen (fixtures[i].output); + else + out = fixtures[i].output_len; CuAssertIntEquals (tc, out, len); CuAssertStrEquals (tc, fixtures[i].output, str); diff --git a/tools/tests/test-utf8.c b/tools/tests/test-utf8.c deleted file mode 100644 index d34f597..0000000 --- a/tools/tests/test-utf8.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 - */ - -#include "config.h" -#include "CuTest.h" - -#include "utf8.h" - -#include - -#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/tools/utf8.c b/tools/utf8.c deleted file mode 100644 index 5ce6889..0000000 --- a/tools/utf8.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * 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 - */ - -#include "config.h" - -#include "buffer.h" -#include "debug.h" -#include "utf8.h" - -#include -#include -#include - -/* - * 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/tools/utf8.h b/tools/utf8.h deleted file mode 100644 index 8efa66f..0000000 --- a/tools/utf8.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 - */ - -#ifndef P11_UTF8_H_ -#define P11_UTF8_H_ - -#include "compat.h" - -#include - -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/trust/parser.c b/trust/parser.c index f6da728..6229d09 100644 --- a/trust/parser.c +++ b/trust/parser.c @@ -69,7 +69,7 @@ struct _p11_parser { /* Set during a parse */ p11_parser_sink sink; void *sink_data; - const char *probable_label; + const char *basename; int flags; /* Parsing state */ @@ -152,12 +152,11 @@ static CK_ATTRIBUTE * build_object (p11_parser *parser, CK_OBJECT_CLASS vclass, CK_BYTE *vid, - const char *explicit_label) + const char *vlabel) { CK_ATTRIBUTE *attrs = NULL; CK_BBOOL vtrue = CK_TRUE; CK_BBOOL vfalse = CK_FALSE; - const char *vlabel; CK_ATTRIBUTE klass = { CKA_CLASS, &vclass, sizeof (vclass) }; CK_ATTRIBUTE token = { CKA_TOKEN, &vtrue, sizeof (vtrue) }; @@ -166,7 +165,8 @@ build_object (p11_parser *parser, CK_ATTRIBUTE id = { CKA_ID, vid, ID_LENGTH }; CK_ATTRIBUTE label = { CKA_LABEL, }; - vlabel = explicit_label ? (char *)explicit_label : parser->probable_label; + if (!vlabel) + vlabel = parser->basename; if (vlabel) { label.pValue = (void *)vlabel; label.ulValueLen = strlen (vlabel); @@ -277,6 +277,7 @@ build_x509_certificate (p11_parser *parser, CK_ATTRIBUTE *attrs; CK_CERTIFICATE_TYPE vx509 = CKC_X_509; CK_BYTE vchecksum[3]; + char *label; CK_DATE vstart; CK_DATE vend; @@ -321,8 +322,18 @@ build_x509_certificate (p11_parser *parser, if (!calc_element (cert, data, length, "tbsCertificate.serialNumber", &serial_number)) serial_number.type = CKA_INVALID; - attrs = build_object (parser, CKO_CERTIFICATE, vid, NULL); + label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", + parser->cert_der, parser->cert_len, P11_OID_CN); + if (!label) + label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", + parser->cert_der, parser->cert_len, P11_OID_OU); + if (!label) + label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", + parser->cert_der, parser->cert_len, P11_OID_O); + + attrs = build_object (parser, CKO_CERTIFICATE, vid, label); return_val_if_fail (attrs != NULL, NULL); + free (label); attrs = p11_attrs_build (attrs, &certificate_type, &certificate_category, &check_value, &trusted, &distrusted, &start_date, &end_date, @@ -852,7 +863,7 @@ parse_openssl_trusted_certificate (p11_parser *parser, { CK_ATTRIBUTE *attrs; CK_BYTE vid[ID_LENGTH]; - const char *old_label = NULL; + CK_ATTRIBUTE *attr; char *label = NULL; node_asn *cert; node_asn *aux; @@ -883,6 +894,12 @@ parse_openssl_trusted_certificate (p11_parser *parser, begin_parsing (parser, cert, data, cert_len); + /* The CKA_ID links related objects */ + id_generate (parser, vid); + + attrs = build_x509_certificate (parser, vid, cert, data, cert_len); + return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); + /* Pull the label out of the CertAux */ len = 0; ret = asn1_read_value (aux, "alias", NULL, &len); @@ -893,16 +910,13 @@ parse_openssl_trusted_certificate (p11_parser *parser, ret = asn1_read_value (aux, "alias", label, &len); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - old_label = parser->probable_label; - parser->probable_label = label; + attr = p11_attrs_find (attrs, CKA_LABEL); + assert (attr != NULL); + free (attr->pValue); + attr->pValue = label; + attr->ulValueLen = strlen (label); } - /* The CKA_ID links related objects */ - id_generate (parser, vid); - - attrs = build_x509_certificate (parser, vid, cert, data, cert_len); - return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); - ret = build_openssl_extensions (parser, attrs, aux, data + cert_len, length - cert_len); return_val_if_fail (ret == P11_PARSE_SUCCESS, ret); @@ -911,11 +925,6 @@ parse_openssl_trusted_certificate (p11_parser *parser, asn1_delete_structure (&cert); asn1_delete_structure (&aux); - if (label) { - parser->probable_label = old_label; - free (label); - } - return P11_PARSE_SUCCESS; } @@ -1002,7 +1011,7 @@ p11_parse_memory (p11_parser *parser, return_val_if_fail (parser->sink == NULL, P11_PARSE_FAILURE); base = basename (filename); - parser->probable_label = base; + parser->basename = base; parser->sink = sink; parser->sink_data = sink_data; parser->flags = flags; @@ -1019,7 +1028,7 @@ p11_parse_memory (p11_parser *parser, break; } - parser->probable_label = NULL; + parser->basename = NULL; parser->sink = NULL; parser->sink_data = NULL; parser->flags = 0; diff --git a/trust/tests/test-parser.c b/trust/tests/test-parser.c index a504cab..52092d0 100644 --- a/trust/tests/test-parser.c +++ b/trust/tests/test-parser.c @@ -530,7 +530,7 @@ test_parse_with_key_usage (CuTest *cu) { CKA_PRIVATE, &vfalse, sizeof (vfalse) }, { CKA_MODIFIABLE, &vfalse, sizeof (vfalse) }, { CKA_CLASS, &klass, sizeof (klass) }, - { CKA_LABEL, "self-signed-with-ku.der", 23 }, + { CKA_LABEL, "self-signed-with-ku.example.com", 31 }, { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) }, { CKA_CERTIFICATE_CATEGORY, &category, sizeof (category) }, { CKA_CHECK_VALUE, "d/\x9c", 3 }, @@ -545,7 +545,7 @@ test_parse_with_key_usage (CuTest *cu) }; CK_ATTRIBUTE nss_trust[] = { - { CKA_LABEL, "self-signed-with-ku.der", 23 }, + { CKA_LABEL, "self-signed-with-ku.example.com", 31 }, { CKA_CLASS, &trust_object, sizeof (trust_object), }, { CKA_CERT_SHA1_HASH, "d/\x9c=\xbc\x9a\x7f\x91\xc7wT\t`\x86\xe2\x8e\x8f\xa8J\x12", 20 }, { CKA_CERT_MD5_HASH, "\xb1N=\x16\x12?dz\x97\x81""By/\xcc\x97\x82", 16 }, @@ -613,7 +613,7 @@ test_parse_anchor (CuTest *cu) CK_X_ASSERTION_TYPE anchored_certificate = CKT_X_ANCHORED_CERTIFICATE; CK_ATTRIBUTE nss_trust[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_object, sizeof (trust_object), }, { CKA_CERT_SHA1_HASH, "\xad\x7c\x3f\x64\xfc\x44\x39\xfe\xf4\xe9\x0b\xe8\xf4\x7c\x6c\xfa\x8a\xad\xfd\xce", 20 }, { CKA_CERT_MD5_HASH, "\xf7\x25\x12\x82\x4e\x67\xb5\xd0\x8d\x92\xb7\x7c\x0b\x86\x7a\x42", 16 }, @@ -639,7 +639,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE server_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -648,7 +648,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE client_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -657,7 +657,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE code_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -666,7 +666,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE email_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -675,7 +675,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE ipsec_system_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -684,7 +684,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE ipsec_tunnel_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -693,7 +693,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE ipsec_user_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, @@ -702,7 +702,7 @@ test_parse_anchor (CuTest *cu) }; CK_ATTRIBUTE stamping_anchor[] = { - { CKA_LABEL, "cacert3.der", 11 }, + { CKA_LABEL, "CAcert Class 3 Root", 19 }, { CKA_CLASS, &trust_assertion, sizeof (trust_assertion) }, { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) }, { CKA_X_ASSERTION_TYPE, &anchored_certificate, sizeof (anchored_certificate) }, -- cgit v1.1