/* * 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 */ #include "config.h" #include "compat.h" #include "base64.h" #include "buffer.h" #include "debug.h" #include "pem.h" #include #include #include #include #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 = strndup (pref, suff - pref); return_val_if_fail (*type != NULL, NULL); } /* 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); }