summaryrefslogtreecommitdiff
path: root/trust/pem.c
diff options
context:
space:
mode:
Diffstat (limited to 'trust/pem.c')
-rw-r--r--trust/pem.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/trust/pem.c b/trust/pem.c
new file mode 100644
index 0000000..7fe0076
--- /dev/null
+++ b/trust/pem.c
@@ -0,0 +1,290 @@
+/*
+ * 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 "buffer.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 (decoded);
+ }
+ }
+
+ free (type);
+
+ /* Try for another block */
+ end += ARMOR_SUFF_L;
+ n_data -= (const char *)end - (const char *)data;
+ data = end;
+ }
+
+ return nfound;
+}
+
+bool
+p11_pem_write (const unsigned char *contents,
+ size_t length,
+ const char *type,
+ p11_buffer *buf)
+{
+ size_t estimate;
+ size_t prefix;
+ char *target;
+ int len;
+
+ return_val_if_fail (contents || !length, false);
+ return_val_if_fail (type, false);
+ return_val_if_fail (buf, false);
+
+ /* Estimate from base64 data. Algorithm from Glib reference */
+ estimate = length * 4 / 3 + 7;
+ estimate += estimate / 64 + 1;
+
+ p11_buffer_add (buf, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L);
+ p11_buffer_add (buf, type, -1);
+ p11_buffer_add (buf, ARMOR_SUFF, ARMOR_SUFF_L);
+
+ prefix = buf->len;
+ target = p11_buffer_append (buf, estimate);
+ return_val_if_fail (target != NULL, NULL);
+
+ /*
+ * OpenSSL is absolutely certain that it wants its PEM base64
+ * lines to be 64 characters in len.
+ */
+
+ len = p11_b64_ntop (contents, length, target, estimate, 64);
+
+ assert (len > 0);
+ assert (len <= estimate);
+ buf->len = prefix + len;
+
+ p11_buffer_add (buf, "\n", 1);
+ p11_buffer_add (buf, ARMOR_PREF_END, ARMOR_PREF_END_L);
+ p11_buffer_add (buf, type, -1);
+ p11_buffer_add (buf, ARMOR_SUFF, ARMOR_SUFF_L);
+ p11_buffer_add (buf, "\n", 1);
+
+ return p11_buffer_ok (buf);
+}