aboutsummaryrefslogtreecommitdiffstats
path: root/src/libstrongswan/plugins/pem
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/pem')
-rw-r--r--src/libstrongswan/plugins/pem/Makefile.am12
-rw-r--r--src/libstrongswan/plugins/pem/pem_builder.c490
-rw-r--r--src/libstrongswan/plugins/pem/pem_builder.h52
-rw-r--r--src/libstrongswan/plugins/pem/pem_plugin.c97
-rw-r--r--src/libstrongswan/plugins/pem/pem_plugin.h47
5 files changed, 698 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/pem/Makefile.am b/src/libstrongswan/plugins/pem/Makefile.am
new file mode 100644
index 000000000..98f356aaf
--- /dev/null
+++ b/src/libstrongswan/plugins/pem/Makefile.am
@@ -0,0 +1,12 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libstrongswan-pem.la
+
+libstrongswan_pem_la_SOURCES = pem_plugin.h pem_plugin.c \
+ pem_builder.c pem_builder.h
+
+libstrongswan_pem_la_LDFLAGS = -module -avoid-version
+
diff --git a/src/libstrongswan/plugins/pem/pem_builder.c b/src/libstrongswan/plugins/pem/pem_builder.c
new file mode 100644
index 000000000..d5bd1bd2d
--- /dev/null
+++ b/src/libstrongswan/plugins/pem/pem_builder.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Copyright (C) 2001-2008 Andreas Steffen
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pem_builder.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <debug.h>
+#include <library.h>
+#include <utils/lexparser.h>
+#include <crypto/hashers/hasher.h>
+#include <crypto/crypters/crypter.h>
+
+#define PKCS5_SALT_LEN 8 /* bytes */
+
+typedef struct private_builder_t private_builder_t;
+
+/**
+ * Builder implementation for PEM decoding
+ */
+struct private_builder_t {
+ /** implements the builder interface */
+ builder_t public;
+ /** credential type we are building */
+ credential_type_t type;
+ /** subtype (keytype, certtype) of the credential we build */
+ int subtype;
+ /** PEM encoding of the credential */
+ chunk_t pem;
+ /** PEM decryption passphrase, if given */
+ chunk_t passphrase;
+ /** supplied callback to read passphrase */
+ chunk_t (*cb)(void *data, int try);
+ /** user data to callback */
+ void *data;
+};
+
+/**
+ * check the presence of a pattern in a character string, skip if found
+ */
+static bool present(char* pattern, chunk_t* ch)
+{
+ u_int len = strlen(pattern);
+
+ if (ch->len >= len && strneq(ch->ptr, pattern, len))
+ {
+ *ch = chunk_skip(*ch, len);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * find a boundary of the form -----tag name-----
+ */
+static bool find_boundary(char* tag, chunk_t *line)
+{
+ chunk_t name = chunk_empty;
+
+ if (!present("-----", line) ||
+ !present(tag, line) ||
+ *line->ptr != ' ')
+ {
+ return FALSE;
+ }
+ *line = chunk_skip(*line, 1);
+
+ /* extract name */
+ name.ptr = line->ptr;
+ while (line->len > 0)
+ {
+ if (present("-----", line))
+ {
+ DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr);
+ return TRUE;
+ }
+ line->ptr++; line->len--; name.len++;
+ }
+ return FALSE;
+}
+
+/*
+ * decrypts a passphrase protected encrypted data block
+ */
+static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg,
+ size_t key_size, chunk_t iv, chunk_t passphrase)
+{
+ hasher_t *hasher;
+ crypter_t *crypter;
+ chunk_t salt = { iv.ptr, PKCS5_SALT_LEN };
+ chunk_t hash;
+ chunk_t decrypted;
+ chunk_t key = {alloca(key_size), key_size};
+ u_int8_t padding, *last_padding_pos, *first_padding_pos;
+
+ /* build key from passphrase and IV */
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+ if (hasher == NULL)
+ {
+ DBG1(" MD5 hash algorithm not available");
+ return NOT_SUPPORTED;
+ }
+ hash.len = hasher->get_hash_size(hasher);
+ hash.ptr = alloca(hash.len);
+ hasher->get_hash(hasher, passphrase, NULL);
+ hasher->get_hash(hasher, salt, hash.ptr);
+ memcpy(key.ptr, hash.ptr, hash.len);
+
+ if (key.len > hash.len)
+ {
+ hasher->get_hash(hasher, hash, NULL);
+ hasher->get_hash(hasher, passphrase, NULL);
+ hasher->get_hash(hasher, salt, hash.ptr);
+ memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
+ }
+ hasher->destroy(hasher);
+
+ /* decrypt blob */
+ crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size);
+ if (crypter == NULL)
+ {
+ DBG1(" %N encryption algorithm not available",
+ encryption_algorithm_names, alg);
+ return NOT_SUPPORTED;
+ }
+ crypter->set_key(crypter, key);
+
+ if (iv.len != crypter->get_block_size(crypter) ||
+ blob->len % iv.len)
+ {
+ crypter->destroy(crypter);
+ DBG1(" data size is not multiple of block size");
+ return PARSE_ERROR;
+ }
+ crypter->decrypt(crypter, *blob, iv, &decrypted);
+ crypter->destroy(crypter);
+ memcpy(blob->ptr, decrypted.ptr, blob->len);
+ chunk_free(&decrypted);
+
+ /* determine amount of padding */
+ last_padding_pos = blob->ptr + blob->len - 1;
+ padding = *last_padding_pos;
+ if (padding > blob->len)
+ {
+ first_padding_pos = blob->ptr;
+ }
+ else
+ {
+ first_padding_pos = last_padding_pos - padding;
+ }
+ /* check the padding pattern */
+ while (--last_padding_pos > first_padding_pos)
+ {
+ if (*last_padding_pos != padding)
+ {
+ DBG1(" invalid passphrase");
+ return INVALID_ARG;
+ }
+ }
+ /* remove padding */
+ blob->len -= padding;
+ return SUCCESS;
+}
+
+/**
+ * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
+ */
+status_t pem_to_bin(chunk_t *blob, private_builder_t *this, bool *pgp)
+{
+ typedef enum {
+ PEM_PRE = 0,
+ PEM_MSG = 1,
+ PEM_HEADER = 2,
+ PEM_BODY = 3,
+ PEM_POST = 4,
+ PEM_ABORT = 5
+ } state_t;
+
+ encryption_algorithm_t alg = ENCR_UNDEFINED;
+ size_t key_size = 0;
+ bool encrypted = FALSE;
+ state_t state = PEM_PRE;
+ chunk_t src = *blob;
+ chunk_t dst = *blob;
+ chunk_t line = chunk_empty;
+ chunk_t iv = chunk_empty;
+ chunk_t passphrase;
+ int try = 0;
+ u_char iv_buf[HASH_SIZE_MD5];
+
+ dst.len = 0;
+ iv.ptr = iv_buf;
+ iv.len = 0;
+
+ while (fetchline(&src, &line))
+ {
+ if (state == PEM_PRE)
+ {
+ if (find_boundary("BEGIN", &line))
+ {
+ state = PEM_MSG;
+ }
+ continue;
+ }
+ else
+ {
+ if (find_boundary("END", &line))
+ {
+ state = PEM_POST;
+ break;
+ }
+ if (state == PEM_MSG)
+ {
+ state = PEM_HEADER;
+ if (memchr(line.ptr, ':', line.len) == NULL)
+ {
+ state = PEM_BODY;
+ }
+ }
+ if (state == PEM_HEADER)
+ {
+ err_t ugh = NULL;
+ chunk_t name = chunk_empty;
+ chunk_t value = chunk_empty;
+
+ /* an empty line separates HEADER and BODY */
+ if (line.len == 0)
+ {
+ state = PEM_BODY;
+ continue;
+ }
+
+ /* we are looking for a parameter: value pair */
+ DBG2(" %.*s", (int)line.len, line.ptr);
+ ugh = extract_parameter_value(&name, &value, &line);
+ if (ugh != NULL)
+ {
+ continue;
+ }
+ if (match("Proc-Type", &name) && *value.ptr == '4')
+ {
+ encrypted = TRUE;
+ }
+ else if (match("DEK-Info", &name))
+ {
+ chunk_t dek;
+
+ if (!extract_token(&dek, ',', &value))
+ {
+ dek = value;
+ }
+ if (match("DES-EDE3-CBC", &dek))
+ {
+ alg = ENCR_3DES;
+ key_size = 24;
+ }
+ else if (match("AES-128-CBC", &dek))
+ {
+ alg = ENCR_AES_CBC;
+ key_size = 16;
+ }
+ else if (match("AES-192-CBC", &dek))
+ {
+ alg = ENCR_AES_CBC;
+ key_size = 24;
+ }
+ else if (match("AES-256-CBC", &dek))
+ {
+ alg = ENCR_AES_CBC;
+ key_size = 32;
+ }
+ else
+ {
+ DBG1(" encryption algorithm '%.s' not supported",
+ dek.len, dek.ptr);
+ return NOT_SUPPORTED;
+ }
+ eat_whitespace(&value);
+ iv = chunk_from_hex(value, iv.ptr);
+ }
+ }
+ else /* state is PEM_BODY */
+ {
+ chunk_t data;
+
+ /* remove any trailing whitespace */
+ if (!extract_token(&data ,' ', &line))
+ {
+ data = line;
+ }
+
+ /* check for PGP armor checksum */
+ if (*data.ptr == '=')
+ {
+ *pgp = TRUE;
+ data.ptr++;
+ data.len--;
+ DBG2(" armor checksum: %.*s", (int)data.len, data.ptr);
+ continue;
+ }
+
+ if (blob->len - dst.len < data.len / 4 * 3)
+ {
+ state = PEM_ABORT;
+ }
+ data = chunk_from_base64(data, dst.ptr);
+
+ dst.ptr += data.len;
+ dst.len += data.len;
+ }
+ }
+ }
+ /* set length to size of binary blob */
+ blob->len = dst.len;
+
+ if (state != PEM_POST)
+ {
+ DBG1(" file coded in unknown format, discarded");
+ return PARSE_ERROR;
+ }
+ if (!encrypted)
+ {
+ return SUCCESS;
+ }
+ if (!this->cb)
+ {
+ DBG1(" missing passphrase");
+ return INVALID_ARG;
+ }
+ while (TRUE)
+ {
+ passphrase = this->cb(this->data, ++try);
+ if (!passphrase.len || !passphrase.ptr)
+ {
+ return INVALID_ARG;
+ }
+ switch (pem_decrypt(blob, alg, key_size, iv, passphrase))
+ {
+ case INVALID_ARG:
+ /* bad passphrase, retry */
+ continue;
+ case SUCCESS:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ }
+}
+
+/**
+ * Implementation of builder_t.build
+ */
+static void *build(private_builder_t *this)
+{
+ bool pgp = FALSE;
+ void *cred = NULL;
+ chunk_t blob;
+ builder_part_t part = BUILD_BLOB_ASN1_DER;
+
+ if (!this->pem.ptr)
+ {
+ free(this);
+ return NULL;
+ }
+ blob = chunk_clone(this->pem);
+ if (pem_to_bin(&blob, this, &pgp) == SUCCESS)
+ {
+ if (pgp)
+ {
+ part = BUILD_BLOB_PGP;
+ }
+ cred = lib->creds->create(lib->creds, this->type, this->subtype,
+ part, blob, BUILD_END);
+ }
+ chunk_clear(&blob);
+ free(this);
+ return cred;
+}
+
+/**
+ * passphrase callback to use if passphrase given
+ */
+static chunk_t given_passphrase_cb(chunk_t *passphrase, int try)
+{
+ if (try > 1)
+ { /* try only once for given passphrases */
+ return chunk_empty;
+ }
+ return *passphrase;
+}
+
+/**
+ * Implementation of builder_t.add
+ */
+static void add(private_builder_t *this, builder_part_t part, ...)
+{
+ va_list args;
+
+ switch (part)
+ {
+ case BUILD_BLOB_PEM:
+ va_start(args, part);
+ this->pem = va_arg(args, chunk_t);
+ va_end(args);
+ break;
+ case BUILD_PASSPHRASE:
+ va_start(args, part);
+ this->passphrase = va_arg(args, chunk_t);
+ va_end(args);
+ if (this->passphrase.len && this->passphrase.ptr)
+ {
+ this->cb = (void*)given_passphrase_cb;
+ this->data = &this->passphrase;
+ }
+ break;
+ case BUILD_PASSPHRASE_CALLBACK:
+ va_start(args, part);
+ this->cb = va_arg(args, chunk_t(*)(void*,int));
+ this->data = va_arg(args, void*);
+ va_end(args);
+ break;
+ default:
+ builder_cancel(&this->public);
+ break;
+ }
+}
+
+/**
+ * Generic PEM builder.
+ */
+static builder_t *pem_builder(credential_type_t type, int subtype)
+{
+ private_builder_t *this = malloc_thing(private_builder_t);
+
+ this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
+ this->public.build = (void*(*)(builder_t *this))build;
+
+ this->type = type;
+ this->subtype = subtype;
+ this->pem = chunk_empty;
+ this->passphrase = chunk_empty;
+ this->cb = NULL;
+ this->data = NULL;
+
+ return &this->public;
+}
+
+/**
+ * Private key PEM builder.
+ */
+builder_t *private_key_pem_builder(key_type_t type)
+{
+ return pem_builder(CRED_PRIVATE_KEY, type);
+}
+
+/**
+ * Public key PEM builder.
+ */
+builder_t *public_key_pem_builder(key_type_t type)
+{
+ return pem_builder(CRED_PUBLIC_KEY, type);
+}
+
+/**
+ * Certificate PEM builder.
+ */
+builder_t *certificate_pem_builder(certificate_type_t type)
+{
+ return pem_builder(CRED_CERTIFICATE, type);
+}
+
diff --git a/src/libstrongswan/plugins/pem/pem_builder.h b/src/libstrongswan/plugins/pem/pem_builder.h
new file mode 100644
index 000000000..a473a2784
--- /dev/null
+++ b/src/libstrongswan/plugins/pem/pem_builder.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup pem_builder pem_builder
+ * @{ @ingroup pem_p
+ */
+
+#ifndef PEM_PRIVATE_KEY_H_
+#define PEM_PRIVATE_KEY_H_
+
+#include <credentials/certificates/certificate.h>
+#include <credentials/credential_factory.h>
+
+/**
+ * Builder for PEM encoded private keys of all kind.
+ *
+ * @param type type of the key
+ * @return builder instance
+ */
+builder_t *private_key_pem_builder(key_type_t type);
+
+/**
+ * Builder for PEM encoded public keys of all kind.
+ *
+ * @param type type of the key
+ * @return builder instance
+ */
+builder_t *public_key_pem_builder(key_type_t type);
+
+/**
+ * Builder for PEM encoded certificates of all kind.
+ *
+ * @param type type of the key
+ * @return builder instance
+ */
+builder_t *certificate_pem_builder(certificate_type_t type);
+
+#endif /** PEM_PRIVATE_KEY_H_ @}*/
+
diff --git a/src/libstrongswan/plugins/pem/pem_plugin.c b/src/libstrongswan/plugins/pem/pem_plugin.c
new file mode 100644
index 000000000..c8505047b
--- /dev/null
+++ b/src/libstrongswan/plugins/pem/pem_plugin.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pem_plugin.h"
+
+#include <library.h>
+#include "pem_builder.h"
+
+typedef struct private_pem_plugin_t private_pem_plugin_t;
+
+/**
+ * private data of pem_plugin
+ */
+struct private_pem_plugin_t {
+
+ /**
+ * public functions
+ */
+ pem_plugin_t public;
+};
+
+/**
+ * Implementation of pem_plugin_t.pemtroy
+ */
+static void destroy(private_pem_plugin_t *this)
+{
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)private_key_pem_builder);
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)public_key_pem_builder);
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)certificate_pem_builder);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ private_pem_plugin_t *this = malloc_thing(private_pem_plugin_t);
+
+ this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ /* register private key PEM decoding builders */
+ lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+ (builder_constructor_t)private_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
+ (builder_constructor_t)private_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_ECDSA,
+ (builder_constructor_t)private_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_DSA,
+ (builder_constructor_t)private_key_pem_builder);
+
+ /* register public key PEM decoding builders */
+ lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
+ (builder_constructor_t)public_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
+ (builder_constructor_t)public_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA,
+ (builder_constructor_t)public_key_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_DSA,
+ (builder_constructor_t)public_key_pem_builder);
+
+ /* register certificate PEM decoding builders */
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_ANY,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_AC,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY,
+ (builder_constructor_t)certificate_pem_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_PGP,
+ (builder_constructor_t)certificate_pem_builder);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/libstrongswan/plugins/pem/pem_plugin.h b/src/libstrongswan/plugins/pem/pem_plugin.h
new file mode 100644
index 000000000..6d39160f9
--- /dev/null
+++ b/src/libstrongswan/plugins/pem/pem_plugin.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup pem_p pem
+ * @ingroup plugins
+ *
+ * @defgroup pem_plugin pem_plugin
+ * @{ @ingroup pem_p
+ */
+
+#ifndef PEM_PLUGIN_H_
+#define PEM_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct pem_plugin_t pem_plugin_t;
+
+/**
+ * Plugin providing support to load credentials in PEM format
+ */
+struct pem_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a pem_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /** PEM_PLUGIN_H_ @}*/