diff options
author | Stef Walter <stefw@gnome.org> | 2012-12-17 11:04:42 +0100 |
---|---|---|
committer | Stef Walter <stefw@gnome.org> | 2013-02-05 14:54:53 +0100 |
commit | a286df75050db8b306685cb22e491d11be842584 (patch) | |
tree | 4b13620f177f0bdb4f716a3b72876cad9db92c8e /common/pem.c | |
parent | 5147d71466455b3d087b3f3a7472a35e8216c55a (diff) |
Add support for parsing PEM files
Diffstat (limited to 'common/pem.c')
-rw-r--r-- | common/pem.c | 241 |
1 files changed, 241 insertions, 0 deletions
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; +} |