summaryrefslogtreecommitdiff
path: root/src/pluto/pem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/pem.c')
-rw-r--r--src/pluto/pem.c485
1 files changed, 74 insertions, 411 deletions
diff --git a/src/pluto/pem.c b/src/pluto/pem.c
index 5ebe4b576..646447c1a 100644
--- a/src/pluto/pem.c
+++ b/src/pluto/pem.c
@@ -1,5 +1,6 @@
/* Loading of PEM encoded files with optional encryption
- * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur
+ * Copyright (C) 2001-2009 Andreas Steffen
+ * HSR 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
@@ -10,8 +11,6 @@
* 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.
- *
- * RCSID $Id: pem.c 3252 2007-10-06 21:24:50Z andreas $
*/
/* decrypt a PEM encoded data block using DES-EDE3-CBC
@@ -27,437 +26,101 @@
#include <sys/types.h>
#include <freeswan.h>
-#define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in <des.h> */
-#include <libdes/des.h>
+
+#include <library.h>
+#include <asn1/pem.h>
#include "constants.h"
#include "defs.h"
#include "log.h"
-#include "md5.h"
#include "whack.h"
#include "pem.h"
-/*
- * check the presence of a pattern in a character string
- */
-static bool
-present(const char* pattern, chunk_t* ch)
-{
- u_int pattern_len = strlen(pattern);
-
- if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0)
- {
- ch->ptr += pattern_len;
- ch->len -= pattern_len;
- return TRUE;
- }
- return FALSE;
-}
-
-/*
- * compare string with chunk
- */
-static bool
-match(const char *pattern, const chunk_t *ch)
-{
- return ch->len == strlen(pattern) &&
- strncmp(pattern, ch->ptr, ch->len) == 0;
-}
-
-/*
- * find a boundary of the form -----tag name-----
- */
-static bool
-find_boundary(const char* tag, chunk_t *line)
-{
- chunk_t name = empty_chunk;
-
- if (!present("-----", line))
- return FALSE;
- if (!present(tag, line))
- return FALSE;
- if (*line->ptr != ' ')
- return FALSE;
- line->ptr++; line->len--;
-
- /* extract name */
- name.ptr = line->ptr;
- while (line->len > 0)
- {
- if (present("-----", line))
- {
- DBG(DBG_PARSING,
- DBG_log(" -----%s %.*s-----",
- tag, (int)name.len, name.ptr);
- )
- return TRUE;
- }
- line->ptr++; line->len--; name.len++;
- }
- return FALSE;
-}
-
-/*
- * eat whitespace
- */
-static void
-eat_whitespace(chunk_t *src)
-{
- while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t'))
- {
- src->ptr++; src->len--;
- }
-}
-
-/*
- * extracts a token ending with a given termination symbol
- */
-static bool
-extract_token(chunk_t *token, char termination, chunk_t *src)
-{
- u_char *eot = memchr(src->ptr, termination, src->len);
-
- /* initialize empty token */
- *token = empty_chunk;
-
- if (eot == NULL) /* termination symbol not found */
- return FALSE;
-
- /* extract token */
- token->ptr = src->ptr;
- token->len = (u_int)(eot - src->ptr);
-
- /* advance src pointer after termination symbol */
- src->ptr = eot + 1;
- src->len -= (token->len + 1);
-
- return TRUE;
-}
-
-/*
- * extracts a name: value pair from the PEM header
- */
-static bool
-extract_parameter(chunk_t *name, chunk_t *value, chunk_t *line)
-{
- DBG(DBG_PARSING,
- DBG_log(" %.*s", (int)line->len, line->ptr);
- )
-
- /* extract name */
- if (!extract_token(name,':', line))
- return FALSE;
-
- eat_whitespace(line);
-
- /* extract value */
- *value = *line;
- return TRUE;
-}
-
-/*
- * fetches a new line terminated by \n or \r\n
- */
-static bool
-fetchline(chunk_t *src, chunk_t *line)
-{
- if (src->len == 0) /* end of src reached */
- return FALSE;
-
- if (extract_token(line, '\n', src))
- {
- if (line->len > 0 && *(line->ptr + line->len -1) == '\r')
- line->len--; /* remove optional \r */
- }
- else /*last line ends without newline */
- {
- *line = *src;
- src->ptr += src->len;
- src->len = 0;
- }
- return TRUE;
-}
-
-/*
- * decrypts a DES-EDE-CBC encrypted data block
- */
-static bool
-pem_decrypt_3des(chunk_t *blob, chunk_t *iv, const char *passphrase)
-{
- MD5_CTX context;
- u_char digest[MD5_DIGEST_SIZE];
- u_char des_iv[DES_CBC_BLOCK_SIZE];
- u_char key[24];
- des_cblock *deskey = (des_cblock *)key;
- des_key_schedule ks[3];
- u_char padding, *last_padding_pos, *first_padding_pos;
-
- /* Convert passphrase to 3des key */
- MD5Init(&context);
- MD5Update(&context, passphrase, strlen(passphrase));
- MD5Update(&context, iv->ptr, iv->len);
- MD5Final(digest, &context);
-
- memcpy(key, digest, MD5_DIGEST_SIZE);
-
- MD5Init(&context);
- MD5Update(&context, digest, MD5_DIGEST_SIZE);
- MD5Update(&context, passphrase, strlen(passphrase));
- MD5Update(&context, iv->ptr, iv->len);
- MD5Final(digest, &context);
-
- memcpy(key + MD5_DIGEST_SIZE, digest, 24 - MD5_DIGEST_SIZE);
-
- (void) des_set_key(&deskey[0], ks[0]);
- (void) des_set_key(&deskey[1], ks[1]);
- (void) des_set_key(&deskey[2], ks[2]);
-
- /* decrypt data block */
- memcpy(des_iv, iv->ptr, DES_CBC_BLOCK_SIZE);
- des_ede3_cbc_encrypt((des_cblock *)blob->ptr, (des_cblock *)blob->ptr,
- blob->len, ks[0], ks[1], ks[2], (des_cblock *)des_iv, FALSE);
-
- /* determine amount of padding */
- last_padding_pos = blob->ptr + blob->len - 1;
- padding = *last_padding_pos;
- first_padding_pos = (padding > blob->len)?
- blob->ptr : last_padding_pos - padding;
-
- /* check the padding pattern */
- while (--last_padding_pos > first_padding_pos)
- {
- if (*last_padding_pos != padding)
- return FALSE;
- }
-
- /* remove padding */
- blob->len -= padding;
- return TRUE;
-}
-
-/*
- * optionally prompts for a passphrase before decryption
- * currently we support DES-EDE3-CBC, only
+/**
+ * Converts a PEM encoded file into its binary form
+ * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
+ * RFC 934 Message Encapsulation, January 1985
*/
-static err_t
-pem_decrypt(chunk_t *blob, chunk_t *iv, prompt_pass_t *pass, const char* label)
+err_t pemtobin(chunk_t *blob, prompt_pass_t *pass, const char* label, bool *pgp)
{
- DBG(DBG_CRYPT,
- DBG_log(" decrypting file using 'DES-EDE3-CBC'");
- )
- if (iv->len != DES_CBC_BLOCK_SIZE)
- return "size of DES-EDE3-CBC IV is not 8 bytes";
-
- if (pass == NULL)
- return "no passphrase available";
+ chunk_t password = chunk_empty;
- /* do we prompt for the passphrase? */
- if (pass->prompt && pass->fd != NULL_FD)
- {
- int i;
- chunk_t blob_copy;
- err_t ugh = "invalid passphrase, too many trials";
-
- whack_log(RC_ENTERSECRET, "need passphrase for '%s'", label);
-
- for (i = 0; i < MAX_PROMPT_PASS_TRIALS; i++)
+ /* do we prompt for the passphrase? */
+ if (pass && pass->prompt && pass->fd != NULL_FD)
{
- int n;
-
- if (i > 0)
- whack_log(RC_ENTERSECRET, "invalid passphrase, please try again");
-
- n = read(pass->fd, pass->secret, PROMPT_PASS_LEN);
-
- if (n == -1)
- {
- err_t ugh = "read(whackfd) failed";
-
- whack_log(RC_LOG_SERIOUS,ugh);
- return ugh;
- }
+ int i;
+ chunk_t blob_copy;
+ err_t ugh = "invalid passphrase, too many trials";
+ status_t status;
- pass->secret[n-1] = '\0';
-
- if (strlen(pass->secret) == 0)
- {
- err_t ugh = "no passphrase entered, aborted";
+ whack_log(RC_ENTERSECRET, "need passphrase for '%s'", label);
+ for (i = 0; i < MAX_PROMPT_PASS_TRIALS; i++)
+ {
+ int n;
+
+ if (i > 0)
+ {
+ whack_log(RC_ENTERSECRET, "invalid passphrase, please try again");
+ }
+ n = read(pass->fd, pass->secret, PROMPT_PASS_LEN);
+
+ if (n == -1)
+ {
+ err_t ugh = "read(whackfd) failed";
+
+ whack_log(RC_LOG_SERIOUS,ugh);
+ return ugh;
+ }
+
+ pass->secret[n-1] = '\0';
+
+ if (strlen(pass->secret) == 0)
+ {
+ err_t ugh = "no passphrase entered, aborted";
+
+ whack_log(RC_LOG_SERIOUS, ugh);
+ return ugh;
+ }
+
+ blob_copy = chunk_clone(*blob);
+ password = chunk_create(pass->secret, strlen(pass->secret));
+
+ status = pem_to_bin(blob, password, pgp);
+ if (status != INVALID_ARG)
+ {
+ if (status == SUCCESS)
+ {
+ whack_log(RC_SUCCESS, "valid passphrase");
+ }
+ else
+ {
+ whack_log(RC_LOG_SERIOUS, "%N, aborted", status_names, status);
+ }
+ free(blob_copy.ptr);
+ return NULL;
+ }
+
+ /* blob is useless after wrong decryption, restore the original */
+ free(blob->ptr);
+ *blob = blob_copy;
+ }
whack_log(RC_LOG_SERIOUS, ugh);
return ugh;
- }
-
- clonetochunk(blob_copy, blob->ptr, blob->len, "blob copy");
-
- if (pem_decrypt_3des(blob, iv, pass->secret))
- {
- whack_log(RC_SUCCESS, "valid passphrase");
- pfree(blob_copy.ptr);
- return NULL;
- }
-
- /* blob is useless after wrong decryption, restore the original */
- pfree(blob->ptr);
- *blob = blob_copy;
- }
- whack_log(RC_LOG_SERIOUS, ugh);
- return ugh;
- }
- else
- {
- if (pem_decrypt_3des(blob, iv, pass->secret))
- return NULL;
- else
- return "invalid passphrase";
- }
-}
-
-/* Converts a PEM encoded file into its binary form
- *
- * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
- * RFC 934 Message Encapsulation, January 1985
- */
-err_t
-pemtobin(chunk_t *blob, prompt_pass_t *pass, const char* label, bool *pgp)
-{
- typedef enum {
- PEM_PRE = 0,
- PEM_MSG = 1,
- PEM_HEADER = 2,
- PEM_BODY = 3,
- PEM_POST = 4,
- PEM_ABORT = 5
- } state_t;
-
- bool encrypted = FALSE;
-
- state_t state = PEM_PRE;
-
- chunk_t src = *blob;
- chunk_t dst = *blob;
- chunk_t line = empty_chunk;
- chunk_t iv = empty_chunk;
-
- u_char iv_buf[MAX_DIGEST_LEN];
-
- /* zero size of converted blob */
- dst.len = 0;
-
- /* zero size of IV */
- iv.ptr = iv_buf;
- iv.len = 0;
-
- while (fetchline(&src, &line))
- {
- if (state == PEM_PRE)
- {
- if (find_boundary("BEGIN", &line))
- {
- *pgp = FALSE;
- state = PEM_MSG;
- }
- continue;
}
else
{
- if (find_boundary("END", &line))
- {
- state = PEM_POST;
- break;
- }
- if (state == PEM_MSG)
- {
- state = (memchr(line.ptr, ':', line.len) == NULL)?
- PEM_BODY : PEM_HEADER;
- }
- if (state == PEM_HEADER)
- {
- chunk_t name = empty_chunk;
- chunk_t value = empty_chunk;
-
- /* an empty line separates HEADER and BODY */
- if (line.len == 0)
- {
- state = PEM_BODY;
- continue;
- }
-
- /* we are looking for a name: value pair */
- if (!extract_parameter(&name, &value, &line))
- continue;
-
- if (match("Proc-Type", &name) && *value.ptr == '4')
- encrypted = TRUE;
- else if (match("DEK-Info", &name))
+ if (pass)
{
- const char *ugh = NULL;
- size_t len = 0;
- chunk_t dek;
-
- if (!extract_token(&dek, ',', &value))
- dek = value;
-
- /* we support DES-EDE3-CBC encrypted files, only */
- if (!match("DES-EDE3-CBC", &dek))
- return "we support DES-EDE3-CBC encrypted files, only";
-
- eat_whitespace(&value);
- ugh = ttodata(value.ptr, value.len, 16,
- iv.ptr, MAX_DIGEST_LEN, &len);
- if (ugh)
- return "error in IV";
-
- iv.len = len;
+ password = chunk_create(pass->secret, strlen(pass->secret));
}
- }
- else /* state is PEM_BODY */
- {
- const char *ugh = NULL;
- size_t len = 0;
- chunk_t data;
-
- /* remove any trailing whitespace */
- if (!extract_token(&data ,' ', &line))
- data = line;
-
- /* check for PGP armor checksum */
- if (*data.ptr == '=')
+ if (pem_to_bin(blob, password, pgp) == SUCCESS)
{
- *pgp = TRUE;
- data.ptr++;
- data.len--;
- DBG(DBG_PARSING,
- DBG_log(" Armor checksum: %.*s", (int)data.len, data.ptr);
- )
- continue;
- }
-
- ugh = ttodata(data.ptr, data.len, 64,
- dst.ptr, blob->len - dst.len, &len);
- if (ugh)
- {
- DBG(DBG_PARSING,
- DBG_log(" %s", ugh);
- )
- state = PEM_ABORT;
- break;
+ return NULL;
}
else
{
- dst.ptr += len;
- dst.len += len;
+ return "pem to bin conversion failed";
}
- }
- }
- }
- /* set length to size of binary blob */
- blob->len = dst.len;
-
- if (state != PEM_POST)
- return "file coded in unknown format, discarded";
-
- if (encrypted)
- return pem_decrypt(blob, &iv, pass, label);
- else
- return NULL;
+ }
}