summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am1
-rw-r--r--common/oid.h18
-rw-r--r--common/tests/Makefile.am1
-rw-r--r--common/tests/test-utf8.c252
-rw-r--r--common/tests/test-x509.c81
-rw-r--r--common/utf8.c328
-rw-r--r--common/utf8.h53
-rw-r--r--common/x509.c136
-rw-r--r--common/x509.h16
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_ */