diff options
-rw-r--r-- | build/certs/Makefile.am | 1 | ||||
-rw-r--r-- | common/Makefile.am | 2 | ||||
-rw-r--r-- | common/base64.c | 192 | ||||
-rw-r--r-- | common/base64.h | 53 | ||||
-rw-r--r-- | common/pem.c | 241 | ||||
-rw-r--r-- | common/pem.h | 50 | ||||
-rw-r--r-- | common/tests/Makefile.am | 14 | ||||
-rw-r--r-- | common/tests/test-pem.c | 254 | ||||
-rw-r--r-- | trust/Makefile.am | 3 | ||||
-rw-r--r-- | trust/parser.c | 38 | ||||
-rw-r--r-- | trust/tests/files/cacert3.pem | 42 | ||||
-rw-r--r-- | trust/tests/test-parser.c | 26 |
12 files changed, 910 insertions, 6 deletions
diff --git a/build/certs/Makefile.am b/build/certs/Makefile.am index 03dca0d..b0439a4 100644 --- a/build/certs/Makefile.am +++ b/build/certs/Makefile.am @@ -8,6 +8,7 @@ TRUST = $(top_srcdir)/trust/tests prepare-certs: cp -v cacert3.der $(TRUST)/anchors cp -v cacert3.der $(TRUST)/files + openssl x509 -in cacert3.der -inform DER -out $(TRUST)/files/cacert3.pem cp -v cacert-ca.der $(TRUST)/certificates cp -v cacert-ca.der $(TRUST)/files cp -v self-server.der $(TRUST)/files diff --git a/common/Makefile.am b/common/Makefile.am index 5f2852e..00c043b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -39,7 +39,9 @@ noinst_LTLIBRARIES += \ $(NULL) libp11_data_la_SOURCES = \ + base64.c base64.h \ checksum.c checksum.h \ + pem.c pem.h \ pkix.asn pkix.asn.h \ $(NULL) diff --git a/common/base64.c b/common/base64.c new file mode 100644 index 0000000..7e66933 --- /dev/null +++ b/common/base64.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "config.h" + +#include "base64.h" + +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const char Pad64 = '='; + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +p11_b64_pton (const char *src, + size_t length, + unsigned char *target, + size_t targsize) +{ + int tarindex, state, ch; + char *pos; + const char *end; + + state = 0; + tarindex = 0; + end = src + length; + + for (;;) { + src++; + if (src == end) { + ch = 0; + break; + } + + ch = *src; + if (isspace ((unsigned char) ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr (Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t) tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex + 1] = ((pos - Base64) & 0x0f) + << 4; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t) tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex + 1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t) tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void) NULL; src != end; ch = *src++) + if (!isspace((unsigned char) ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (src++; src != end; ch = *src++) + if (!isspace((unsigned char) ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/common/base64.h b/common/base64.h new file mode 100644 index 0000000..4a2d1e7 --- /dev/null +++ b/common/base64.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef P11_BASE64_H_ +#define P11_BASE64_H_ + +#include <sys/types.h> + +int p11_b64_pton (const char *src, + size_t length, + unsigned char *target, + size_t targsize); + +#endif /* P11_BASE64_H_ */ diff --git a/common/pem.c b/common/pem.c new file mode 100644 index 0000000..3d3d284 --- /dev/null +++ b/common/pem.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2012 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 "compat.h" +#include "base64.h" +#include "debug.h" +#include "pem.h" + +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#define ARMOR_SUFF "-----" +#define ARMOR_SUFF_L 5 +#define ARMOR_PREF_BEGIN "-----BEGIN " +#define ARMOR_PREF_BEGIN_L 11 +#define ARMOR_PREF_END "-----END " +#define ARMOR_PREF_END_L 9 + +enum { + NONE = 0, + TRUSTED_CERTIFICATE, + CERTIFICATE +}; + +static const char * +pem_find_begin (const char *data, + size_t n_data, + char **type) +{ + const char *pref, *suff; + + /* Look for a prefix */ + pref = strnstr ((char *)data, ARMOR_PREF_BEGIN, n_data); + if (!pref) + return NULL; + + n_data -= (pref - data) + ARMOR_PREF_BEGIN_L; + data = pref + ARMOR_PREF_BEGIN_L; + + /* Look for the end of that begin */ + suff = strnstr ((char *)data, ARMOR_SUFF, n_data); + if (!suff) + return NULL; + + /* Make sure on the same line */ + if (memchr (pref, '\n', suff - pref)) + return NULL; + + if (type) { + pref += ARMOR_PREF_BEGIN_L; + assert (suff > pref); + *type = malloc (suff - pref + 1); + return_val_if_fail (*type != NULL, NULL); + memcpy (*type, pref, suff - pref); + (*type)[suff - pref] = 0; + } + + /* The byte after this ---BEGIN--- */ + return suff + ARMOR_SUFF_L; +} + +static const char * +pem_find_end (const char *data, + size_t n_data, + const char *type) +{ + const char *pref; + size_t n_type; + + /* Look for a prefix */ + pref = strnstr (data, ARMOR_PREF_END, n_data); + if (!pref) + return NULL; + + n_data -= (pref - data) + ARMOR_PREF_END_L; + data = pref + ARMOR_PREF_END_L; + + /* Next comes the type string */ + n_type = strlen (type); + if (n_type > n_data || strncmp ((char *)data, type, n_type) != 0) + return NULL; + + n_data -= n_type; + data += n_type; + + /* Next comes the suffix */ + if (ARMOR_SUFF_L > n_data && strncmp ((char *)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0) + return NULL; + + /* The end of the data */ + return pref; +} + +static unsigned char * +pem_parse_block (const char *data, + size_t n_data, + size_t *n_decoded) +{ + const char *x, *hbeg, *hend; + const char *p, *end; + unsigned char *decoded; + size_t length; + int ret; + + assert (data != NULL); + assert (n_data != 0); + assert (n_decoded != NULL); + + p = data; + end = p + n_data; + + hbeg = hend = NULL; + + /* Try and find a pair of blank lines with only white space between */ + while (hend == NULL) { + x = memchr (p, '\n', end - p); + if (!x) + break; + ++x; + while (isspace (*x)) { + /* Found a second line, with only spaces between */ + if (*x == '\n') { + hbeg = data; + hend = x; + break; + /* Found a space between two lines */ + } else { + ++x; + } + } + + /* Try next line */ + p = x; + } + + /* Headers found? */ + if (hbeg && hend) { + data = hend; + n_data = end - data; + } + + length = (n_data * 3) / 4 + 1; + decoded = malloc (length); + return_val_if_fail (decoded != NULL, 0); + + ret = p11_b64_pton (data, n_data, decoded, length); + if (ret < 0) { + free (decoded); + return NULL; + } + + /* No need to parse headers for our use cases */ + + *n_decoded = ret; + return decoded; +} + +unsigned int +p11_pem_parse (const char *data, + size_t n_data, + p11_pem_sink sink, + void *user_data) +{ + const char *beg, *end; + unsigned int nfound = 0; + unsigned char *decoded = NULL; + size_t n_decoded = 0; + char *type; + + assert (data != NULL); + + while (n_data > 0) { + + /* This returns the first character after the PEM BEGIN header */ + beg = pem_find_begin (data, n_data, &type); + if (beg == NULL) + break; + + assert (type != NULL); + + /* This returns the character position before the PEM END header */ + end = pem_find_end (beg, n_data - (beg - data), type); + if (end == NULL) { + free (type); + break; + } + + if (beg != end) { + decoded = pem_parse_block (beg, end - beg, &n_decoded); + if (decoded) { + if (sink != NULL) + (sink) (type, decoded, n_decoded, user_data); + ++nfound; + } + } + + free (type); + + /* Try for another block */ + end += ARMOR_SUFF_L; + n_data -= (const char *)end - (const char *)data; + data = end; + } + + return nfound; +} diff --git a/common/pem.h b/common/pem.h new file mode 100644 index 0000000..1e88f1f --- /dev/null +++ b/common/pem.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 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_PEM_H_ +#define P11_PEM_H_ + +#include <sys/types.h> + +typedef void (*p11_pem_sink) (const char *type, + const unsigned char *contents, + size_t length, + void *user_data); + +unsigned int p11_pem_parse (const char *input, + size_t length, + p11_pem_sink sink, + void *user_data); + +#endif /* P11_PEM_H_ */ diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index be92e8d..ac2ab38 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -12,9 +12,6 @@ INCLUDES = \ $(CUTEST_CFLAGS) LDADD = \ - $(top_builddir)/common/libp11-library.la \ - $(top_builddir)/common/libp11-compat.la \ - $(CUTEST_LIBS) \ $(NULL) CHECK_PROGS = \ @@ -33,11 +30,13 @@ if WITH_ASN1 LDADD += \ $(top_builddir)/common/libp11-data.la \ - $(LIBTASN1_LIBS) + $(LIBTASN1_LIBS) \ $(NULL) CHECK_PROGS += \ - test-checksum + test-checksum \ + test-pem \ + $(NULL) noinst_PROGRAMS += \ frob-ku \ @@ -46,3 +45,8 @@ noinst_PROGRAMS += \ $(NULL) endif # WITH_ASN1 + +LDADD += \ + $(top_builddir)/common/libp11-library.la \ + $(top_builddir)/common/libp11-compat.la \ + $(CUTEST_LIBS) diff --git a/common/tests/test-pem.c b/common/tests/test-pem.c new file mode 100644 index 0000000..65a78d8 --- /dev/null +++ b/common/tests/test-pem.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2012 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@gnome.org> + */ + +#include "config.h" +#include "CuTest.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "compat.h" +#include "pem.h" + +struct { + const char *input; + struct { + const char *type; + const char *data; + unsigned int length; + } output[8]; +} success_fixtures[] = { + { + /* one block */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----", + { + { + "BLOCK1", + "\x69\x83\x4d\x5e\xab\x21\x95\x5c\x42\x76\x8f\x10\x7c\xa7\x97\x87" + "\x71\x94\xcd\xdf\xf2\x9f\x82\xd8\x21\x58\x10\xaf\x1e\x1a", + 30, + }, + { + NULL, + } + } + }, + + { + /* one block, with header */ + "-----BEGIN BLOCK1-----\n" + "Header1: value1 \n" + " Header2: value2\n" + "\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----", + { + { + "BLOCK1", + "\x69\x83\x4d\x5e\xab\x21\x95\x5c\x42\x76\x8f\x10\x7c\xa7\x97\x87" + "\x71\x94\xcd\xdf\xf2\x9f\x82\xd8\x21\x58\x10\xaf\x1e\x1a", + 30, + }, + { + NULL, + } + } + }, + + { + /* two blocks, junk data */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----\n" + "blah blah\n" + "-----BEGIN TWO-----\n" + "oy5L157C671HyJMCf9FiK9prvPZfSch6V4EoUfylFoI1Bq6SbL53kg==\n" + "-----END TWO-----\n" + "trailing data", + { + { + "BLOCK1", + "\x69\x83\x4d\x5e\xab\x21\x95\x5c\x42\x76\x8f\x10\x7c\xa7\x97\x87" + "\x71\x94\xcd\xdf\xf2\x9f\x82\xd8\x21\x58\x10\xaf\x1e\x1a", + 30, + }, + { + "TWO", + "\xa3\x2e\x4b\xd7\x9e\xc2\xeb\xbd\x47\xc8\x93\x02\x7f\xd1\x62\x2b" + "\xda\x6b\xbc\xf6\x5f\x49\xc8\x7a\x57\x81\x28\x51\xfc\xa5\x16\x82" + "\x35\x06\xae\x92\x6c\xbe\x77\x92", + 40 + }, + { + NULL, + } + } + }, + + { + NULL, + } +}; + +typedef struct { + CuTest *cu; + int input_index; + int output_index; + int parsed; +} SuccessClosure; + +static void +on_parse_pem_success (const char *type, + const unsigned char *contents, + size_t length, + void *user_data) +{ + SuccessClosure *cl = user_data; + + CuAssertIntEquals (cl->cu, success_fixtures[cl->input_index].output[cl->output_index].length, length); + CuAssertTrue (cl->cu, memcmp (success_fixtures[cl->input_index].output[cl->output_index].data, contents, + success_fixtures[cl->input_index].output[cl->output_index].length) == 0); + + cl->output_index++; + cl->parsed++; +} + +static void +test_pem_success (CuTest *cu) +{ + SuccessClosure cl; + int ret; + int i; + int j; + + for (i = 0; success_fixtures[i].input != NULL; i++) { + cl.cu = cu; + cl.input_index = i; + cl.output_index = 0; + cl.parsed = 0; + + ret = p11_pem_parse (success_fixtures[i].input, strlen (success_fixtures[i].input), + on_parse_pem_success, &cl); + + CuAssertTrue (cu, success_fixtures[i].output[cl.output_index].type == NULL); + + /* Count number of outputs, return from p11_pem_parse() should match */ + for (j = 0; success_fixtures[i].output[j].type != NULL; j++); + CuAssertIntEquals (cu, j, ret); + CuAssertIntEquals (cu, ret, cl.parsed); + } +} + +const char *failure_fixtures[] = { + /* too short at end of opening line */ + "-----BEGIN BLOCK1---\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----", + + /* truncated */ + "-----BEGIN BLOCK1---", + + /* no ending */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n", + + /* wrong ending */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK2-----", + + /* wrong ending */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END INVALID-----", + + /* too short at end of ending line */ + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1---", + + /* invalid base64 data */ + "-----BEGIN BLOCK1-----\n" + "!!!!NNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----", + + NULL, +}; + +static void +on_parse_pem_failure (const char *type, + const unsigned char *contents, + size_t length, + void *user_data) +{ + CuTest *cu = user_data; + CuAssertTrue (cu, false && "not reached"); +} + +static void +test_pem_failure (CuTest *cu) +{ + int ret; + int i; + + for (i = 0; failure_fixtures[i] != NULL; i++) { + ret = p11_pem_parse (failure_fixtures[i], strlen (failure_fixtures[i]), + on_parse_pem_failure, cu); + CuAssertIntEquals (cu, 0, ret); + } +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + SUITE_ADD_TEST (suite, test_pem_success); + SUITE_ADD_TEST (suite, test_pem_failure); + + 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/trust/Makefile.am b/trust/Makefile.am index 413bb51..75684cc 100644 --- a/trust/Makefile.am +++ b/trust/Makefile.am @@ -30,7 +30,8 @@ p11_kit_trust_la_LIBADD = \ $(top_builddir)/common/libp11-data.la \ $(top_builddir)/common/libp11-library.la \ $(top_builddir)/common/libp11-compat.la \ - $(LIBTASN1_LIBS) + $(LIBTASN1_LIBS) \ + $(NULL) p11_kit_trust_la_LDFLAGS = \ -no-undefined -module -avoid-version \ diff --git a/trust/parser.c b/trust/parser.c index ef72474..65d7855 100644 --- a/trust/parser.c +++ b/trust/parser.c @@ -42,6 +42,7 @@ #include "library.h" #include "module.h" #include "parser.h" +#include "pem.h" #include "pkcs11x.h" #include <libtasn1.h> @@ -992,7 +993,44 @@ parse_der_x509_certificate (p11_parser *parser, return P11_PARSE_SUCCESS; } +static void +on_pem_block (const char *type, + const unsigned char *contents, + size_t length, + void *user_data) +{ + p11_parser *parser = user_data; + int ret; + + if (strcmp (type, "CERTIFICATE") == 0) { + ret = parse_der_x509_certificate (parser, contents, length); + + } else { + p11_debug ("Saw unsupported or unrecognized PEM block of type %s", type); + ret = P11_PARSE_SUCCESS; + } + + if (ret != P11_PARSE_SUCCESS) + p11_message ("Couldn't parse PEM block of type %s", type); +} + +static int +parse_pem_certificates (p11_parser *parser, + const unsigned char *data, + size_t length) +{ + int num; + + num = p11_pem_parse ((const char *)data, length, on_pem_block, parser); + + if (num == 0) + return P11_PARSE_UNRECOGNIZED; + + return P11_PARSE_SUCCESS; +} + static parser_func all_parsers[] = { + parse_pem_certificates, parse_der_x509_certificate, NULL, }; diff --git a/trust/tests/files/cacert3.pem b/trust/tests/files/cacert3.pem new file mode 100644 index 0000000..087ca0e --- /dev/null +++ b/trust/tests/files/cacert3.pem @@ -0,0 +1,42 @@ +-----BEGIN CERTIFICATE----- +MIIHWTCCBUGgAwIBAgIDCkGKMA0GCSqGSIb3DQEBCwUAMHkxEDAOBgNVBAoTB1Jv +b3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAGA1UEAxMZ +Q0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSc3VwcG9y +dEBjYWNlcnQub3JnMB4XDTExMDUyMzE3NDgwMloXDTIxMDUyMDE3NDgwMlowVDEU +MBIGA1UEChMLQ0FjZXJ0IEluYy4xHjAcBgNVBAsTFWh0dHA6Ly93d3cuQ0FjZXJ0 +Lm9yZzEcMBoGA1UEAxMTQ0FjZXJ0IENsYXNzIDMgUm9vdDCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAKtJNRFIfNImflOUz0Op3SjXQiqL84d4GVh8D57a +iX3h++tykA10oZZkq5+gJJlz2uJVdscXe/UErEa4w75/ZI0QbCTzYZzA8pD6Ueb1 +aQFjww9W4kpCz+JEjCUoqMV5CX1GuYrz6fM0KQhF5Byfy5QEHIGoFLOYZcRD7E6C +jQnRvapbjZLQ7N6QxX8KwuPr5jFaXnQ+lzNZ6MMDPWAzv/fRb0fEze5ig1JuLgia +pNkVGJGmhZJHsK5I6223IeyFGmhyNav/8BBdwPSUp2rVO5J+TJAFfpPBLIukjmJ0 +FXFuC3ED6q8VOJrU0gVyb4z5K+taciX5OUbjchs+BMNkJyIQKopPWKcDrb60LhPt +XapI19V91Cp7XPpGBFDkzA5CW4zt2/LP/JaT4NsRNlRiNDiPDGCbO5dWOK3z0luL +oFvqTpa4fNfVoIZwQNORKbeiPK31jLvPGpKK5DR7wNhsX+kKwsOnIJpa3yxdUly6 +R9Wb7yQocDggL9V/KcCyQQNokszgnMyXS0XvOhAKq3A6mJVwrTWx6oUrpByAITGp +rmB6gCZIALgBwJNjVSKRPFbnr9s6JfOPMVTqJouBWfmh0VMRxXudA/Z0EeBtsSw/ +LIaRmXGapneLNGDRFLQsrJ2vjBDTn8Rq+G8T/HNZ92ZCdB6K4/jc0m+YnMtHmJVA +BfvpAgMBAAGjggINMIICCTAdBgNVHQ4EFgQUdahxYEyIE/B42Yl3tW3Fid+8sXow +gaMGA1UdIwSBmzCBmIAUFrUyG9TH8+DmjvO90rA67rI5GNGhfaR7MHkxEDAOBgNV +BAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAG +A1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS +c3VwcG9ydEBjYWNlcnQub3JnggEAMA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUH +AQEEUTBPMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggr +BgEFBQcwAoYcaHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBB +MD8GCCsGAQQBgZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9y +Zy9pbmRleC5waHA/aWQ9MTAwNAYJYIZIAYb4QgEIBCcWJWh0dHA6Ly93d3cuQ0Fj +ZXJ0Lm9yZy9pbmRleC5waHA/aWQ9MTAwUAYJYIZIAYb4QgENBEMWQVRvIGdldCB5 +b3VyIG93biBjZXJ0aWZpY2F0ZSBmb3IgRlJFRSwgZ28gdG8gaHR0cDovL3d3dy5D +QWNlcnQub3JnMA0GCSqGSIb3DQEBCwUAA4ICAQApKIWuRKm5r6R5E/CooyuXYPNc +7uMvwfbiZqARrjY3OnYVBFPqQvX56sAV2KaC2eRhrnILKVyQQ+hBsuF32wITRHhH +Va9Y/MyY9kW50SD42CEH/m2qc9SzxgfpCYXMO/K2viwcJdVxjDm1Luq+GIG6sJO4 +D+Pm1yaMMVpyA4RS5qb1MyJFCsgLDYq4Nm+QCaGrvdfVTi5xotSu+qdUK+s1jVq3 +VIgv7nSf7UgWyg1I0JTTrKSi9iTfkuO960NAkW4cGI5WtIIS86mTn9S8nK2cde5a +lxuV53QtHA+wLJef+6kzOXrnAzqSjiL2jA3k2X4Ndhj3AfnvlpaiVXPAPHG0HRpW +Q7fDCo1y/OIQCQtBzoyUoPkD/XFzS4pXM+WOdH4VAQDmzEoc53+VGS3FpQyLu7Xt +hbNc09+4ufLKxw0BFKxwWMWMjTPUnWajGlCVI/xI4AZDEtnNp4Y5LzZyo4AQ5OHz +0ctbGsDkgJp8E3MGT9ujayQKurMcvEp4u+XjdTilSKeiHq921F73OIZWWonO1sOn +ebJSoMbxhbQljPI/lrMQ2Y1sVzufb4Y6GIIiNsiwkTjbKqGTqoQ/9SdlrnPVyNXT +d+pLncdBu8fA46A/5H2kjXPmEkvfoXNzczqA6NXLji/L6hOn1kGLrPo8idck9U60 +4GGSt/M3mMS+lqO3ig== +-----END CERTIFICATE----- diff --git a/trust/tests/test-parser.c b/trust/tests/test-parser.c index c224669..0a0a9d1 100644 --- a/trust/tests/test-parser.c +++ b/trust/tests/test-parser.c @@ -107,6 +107,31 @@ test_parse_der_certificate (CuTest *cu) } static void +test_parse_pem_certificate (CuTest *cu) +{ + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *attr; + int ret; + + setup (cu); + + ret = p11_parse_file (test.parser, SRCDIR "/files/cacert3.pem", + 0, on_parse_object, cu); + CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); + + /* Should have gotten certificate and a trust object */ + CuAssertIntEquals (cu, 2, test.objects->num); + + attrs = test.objects->elem[0]; + test_check_cacert3_ca (cu, attrs, NULL); + + attr = p11_attrs_find (attrs, CKA_TRUSTED); + CuAssertPtrEquals (cu, NULL, attr); + + teardown (cu); +} + +static void test_parse_anchor (CuTest *cu) { CK_ATTRIBUTE *attrs; @@ -294,6 +319,7 @@ main (void) p11_message_quiet (); SUITE_ADD_TEST (suite, test_parse_der_certificate); + SUITE_ADD_TEST (suite, test_parse_pem_certificate); SUITE_ADD_TEST (suite, test_parse_anchor); SUITE_ADD_TEST (suite, test_parse_no_sink); SUITE_ADD_TEST (suite, test_parse_invalid_file); |