]> granicus.if.org Git - p11-kit/commitdiff
Add support for parsing PEM files
authorStef Walter <stefw@gnome.org>
Mon, 17 Dec 2012 10:04:42 +0000 (11:04 +0100)
committerStef Walter <stefw@gnome.org>
Tue, 5 Feb 2013 13:54:53 +0000 (14:54 +0100)
12 files changed:
build/certs/Makefile.am
common/Makefile.am
common/base64.c [new file with mode: 0644]
common/base64.h [new file with mode: 0644]
common/pem.c [new file with mode: 0644]
common/pem.h [new file with mode: 0644]
common/tests/Makefile.am
common/tests/test-pem.c [new file with mode: 0644]
trust/Makefile.am
trust/parser.c
trust/tests/files/cacert3.pem [new file with mode: 0644]
trust/tests/test-parser.c

index 03dca0d42ad5d0f3c9b80062f2d3948ccfe86aa4..b0439a4f347ed662610a47c12a6f760db3b5dc5e 100644 (file)
@@ -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
index 5f2852e30e8583c546c275177e107c5536b9bc40..00c043bc86b46f0e9d2796b236c9f3153e56b36f 100644 (file)
@@ -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 (file)
index 0000000..7e66933
--- /dev/null
@@ -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 (file)
index 0000000..4a2d1e7
--- /dev/null
@@ -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 (file)
index 0000000..3d3d284
--- /dev/null
@@ -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 (file)
index 0000000..1e88f1f
--- /dev/null
@@ -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_ */
index be92e8dce9290f4dd7d3d3753d8b11eb28c32428..ac2ab384b47e5a46362fe4a8f7b88ee7d5630ed5 100644 (file)
@@ -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 (file)
index 0000000..65a78d8
--- /dev/null
@@ -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;
+}
index 413bb51330f5c85cb60eb496be8a0ddd71be5d75..75684cce2c5f7ab7c6540dd81afa6ec65910d1e9 100644 (file)
@@ -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 \
index ef72474633a89cfb46014331c0b25bcdb4169e99..65d7855536fde41fbfeaa1affa097d076f8886ff 100644 (file)
@@ -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 (file)
index 0000000..087ca0e
--- /dev/null
@@ -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-----
index c224669a29075cd39ff861a17fdd52fbda44f8a1..0a0a9d12acea82e295de385aef7937e71601fbca 100644 (file)
@@ -106,6 +106,31 @@ test_parse_der_certificate (CuTest *cu)
        teardown (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)
 {
@@ -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);