summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@gnome.org>2012-12-17 11:04:42 +0100
committerStef Walter <stefw@gnome.org>2013-02-05 14:54:53 +0100
commita286df75050db8b306685cb22e491d11be842584 (patch)
tree4b13620f177f0bdb4f716a3b72876cad9db92c8e
parent5147d71466455b3d087b3f3a7472a35e8216c55a (diff)
downloadp11-kit-a286df75050db8b306685cb22e491d11be842584.tar.gz
Add support for parsing PEM files
-rw-r--r--build/certs/Makefile.am1
-rw-r--r--common/Makefile.am2
-rw-r--r--common/base64.c192
-rw-r--r--common/base64.h53
-rw-r--r--common/pem.c241
-rw-r--r--common/pem.h50
-rw-r--r--common/tests/Makefile.am14
-rw-r--r--common/tests/test-pem.c254
-rw-r--r--trust/Makefile.am3
-rw-r--r--trust/parser.c38
-rw-r--r--trust/tests/files/cacert3.pem42
-rw-r--r--trust/tests/test-parser.c26
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);