summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Kistner <tom@duncanthrax.net>2009-03-17 12:57:37 +0000
committerTom Kistner <tom@duncanthrax.net>2009-03-17 12:57:37 +0000
commit5b12ca9a7b9e95b39512836fd0e43ed7c880fbbd (patch)
treed5f12cc5ffd1fac3fd470ef4fd8f4d4d769988d2
parente4f169e895baded9cd6eb690ed36829f4fb0553f (diff)
downloadexim4-5b12ca9a7b9e95b39512836fd0e43ed7c880fbbd.tar.gz
wip
-rw-r--r--src/src/pdkim/pdkim.c287
-rw-r--r--src/src/pdkim/pdkim.h203
-rw-r--r--src/src/pdkim/rsa.c61
-rw-r--r--src/src/pdkim/rsa.h4
4 files changed, 475 insertions, 80 deletions
diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c
index 836df2638..0fefd9ace 100644
--- a/src/src/pdkim/pdkim.c
+++ b/src/src/pdkim/pdkim.c
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.1.2.5 2009/02/27 17:04:20 tom Exp $ */
+/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.1.2.6 2009/03/17 12:57:37 tom Exp $ */
/* pdkim.c */
#include <stdlib.h>
@@ -26,6 +26,15 @@ char *pdkim_canons[] = {
"relaxed",
NULL
};
+char *pdkim_hashes[] = {
+ "sha256",
+ "sha1",
+ NULL
+};
+char *pdkim_keytypes[] = {
+ "rsa",
+ NULL
+};
typedef struct pdkim_combined_canon_entry {
char *str;
@@ -478,14 +487,10 @@ pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
}
break;
case 's':
- sig->selector = malloc(strlen(cur_val->str)+1);
- if (sig->selector == NULL) break;
- strcpy(sig->selector, cur_val->str);
+ sig->selector = strdup(cur_val->str);
break;
case 'd':
- sig->domain = malloc(strlen(cur_val->str)+1);
- if (sig->domain == NULL) break;
- strcpy(sig->domain, cur_val->str);
+ sig->domain = strdup(cur_val->str);
break;
case 'i':
sig->identity = pdkim_decode_qp(cur_val->str);
@@ -500,9 +505,7 @@ pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
sig->bodylength = strtoul(cur_val->str,NULL,10);
break;
case 'h':
- sig->headernames = malloc(strlen(cur_val->str)+1);
- if (sig->headernames == NULL) break;
- strcpy(sig->headernames, cur_val->str);
+ sig->headernames = strdup(cur_val->str);
break;
case 'z':
sig->copiedheaders = pdkim_decode_qp(cur_val->str);
@@ -537,8 +540,8 @@ pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
if (!(sig->domain && (*(sig->domain) != '\0') &&
sig->selector && (*(sig->selector) != '\0') &&
sig->headernames && (*(sig->headernames) != '\0') &&
- sig->bodyhash && (*(sig->bodyhash) != '\0') &&
- sig->sigdata && (*(sig->sigdata) != '\0') &&
+ sig->bodyhash &&
+ sig->sigdata &&
sig->version)) {
pdkim_free_sig(sig);
return NULL;
@@ -564,6 +567,125 @@ pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
}
+/* -------------------------------------------------------------------------- */
+pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) {
+ pdkim_pubkey *pub ;
+ char *p;
+ pdkim_str *cur_tag = NULL;
+ pdkim_str *cur_val = NULL;
+ int where = PDKIM_HDR_LIMBO;
+
+ pub = malloc(sizeof(pdkim_pubkey));
+ if (pub == NULL) return NULL;
+ memset(pub,0,sizeof(pdkim_pubkey));
+
+ p = raw_record;
+
+ while (*p != '\0') {
+
+ /* Ignore FWS */
+ if ( (*p == '\r') || (*p == '\n') )
+ goto NEXT_CHAR;
+
+ if (where == PDKIM_HDR_LIMBO) {
+ /* In limbo, just wait for a tag-char to appear */
+ if (!((*p >= 'a') && (*p <= 'z')))
+ goto NEXT_CHAR;
+
+ where = PDKIM_HDR_TAG;
+ }
+
+ if (where == PDKIM_HDR_TAG) {
+ if (cur_tag == NULL)
+ cur_tag = pdkim_strnew(NULL);
+
+ if ((*p >= 'a') && (*p <= 'z'))
+ pdkim_strncat(cur_tag,p,1);
+
+ if (*p == '=') {
+ where = PDKIM_HDR_VALUE;
+ goto NEXT_CHAR;
+ }
+ }
+
+ if (where == PDKIM_HDR_VALUE) {
+ if (cur_val == NULL)
+ cur_val = pdkim_strnew(NULL);
+
+ if ( (*p == '\r') || (*p == '\n') )
+ goto NEXT_CHAR;
+
+ if (*p == ';') {
+ if (cur_tag->len > 0) {
+ pdkim_strtrim(cur_val);
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream)
+ fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str);
+ #endif
+ switch (cur_tag->str[0]) {
+ case 'v':
+ /* This tag isn't evaluated because:
+ - We only support version DKIM1.
+ - Which is the default for this value (set below)
+ - Other versions are currently not specified. */
+ break;
+ case 'h':
+ pub->hashes = strdup(cur_val->str);
+ break;
+ case 'g':
+ pub->granularity = strdup(cur_val->str);
+ break;
+ case 'n':
+ pub->notes = pdkim_decode_qp(cur_val->str);
+ break;
+ case 'p':
+ pub->key = pdkim_decode_base64(cur_val->str,&(pub->key_len));
+ break;
+ case 'k':
+ pub->hashes = strdup(cur_val->str);
+ break;
+ case 's':
+ pub->srvtype = strdup(cur_val->str);
+ break;
+ case 't':
+ if (strchr(cur_val->str,'t') != NULL) pub->testing = 1;
+ if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1;
+ break;
+ default:
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream)
+ fprintf(ctx->debug_stream, "Unknown tag encountered\n");
+ #endif
+ break;
+ }
+ }
+ pdkim_strclear(cur_tag);
+ pdkim_strclear(cur_val);
+ where = PDKIM_HDR_LIMBO;
+ goto NEXT_CHAR;
+ }
+ else pdkim_strncat(cur_val,p,1);
+ }
+
+ NEXT_CHAR:
+ p++;
+ }
+
+ /* Set fallback defaults */
+ if (pub->version == NULL) pub->version = strdup(PDKIM_PUB_RECORD_VERSION);
+ if (pub->granularity == NULL) pub->granularity = strdup("*");
+ if (pub->keytype == NULL) pub->keytype = strdup("rsa");
+ if (pub->srvtype == NULL) pub->srvtype = strdup("*");
+
+ /* p= is required */
+ if (pub->key == NULL) {
+ pdkim_free_pubkey(pub);
+ return NULL;
+ }
+
+ return pub;
+}
+
/* -------------------------------------------------------------------------- */
int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
@@ -759,6 +881,9 @@ int pdkim_header_complete(pdkim_ctx *ctx) {
ctx->cur_header->len--;
}
+ ctx->num_headers++;
+ if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
+
/* Traverse all signatures */
while (sig != NULL) {
@@ -822,6 +947,7 @@ int pdkim_header_complete(pdkim_ctx *ctx) {
}
}
+ BAIL:
pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
return PDKIM_OK;
};
@@ -876,8 +1002,9 @@ int pdkim_feed (pdkim_ctx *ctx,
ctx->cur_header = pdkim_strnew(NULL);
if (ctx->cur_header == NULL) return PDKIM_ERR_OOM;
}
- if (pdkim_strncat(ctx->cur_header,&data[p],1) == NULL)
- return PDKIM_ERR_OOM;
+ if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN)
+ if (pdkim_strncat(ctx->cur_header,&data[p],1) == NULL)
+ return PDKIM_ERR_OOM;
}
}
return PDKIM_OK;
@@ -1048,7 +1175,7 @@ int pdkim_feed_finish(pdkim_ctx *ctx, char **signature) {
if (sig->canon_body == PDKIM_CANON_RELAXED)
rh = pdkim_relax_header(p->value,1); /* cook header for relaxed canon */
else
- rh = strdup(p->value); /* just copy it for simple canon */
+ rh = strdup(p->value); /* just copy it for simple canon */
if (rh == NULL) return PDKIM_ERR_OOM;
@@ -1123,6 +1250,8 @@ int pdkim_feed_finish(pdkim_ctx *ctx, char **signature) {
if (ctx->mode == PDKIM_MODE_SIGN) {
rsa_context rsa;
+ rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+
/* Perform private key operation */
if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey,
strlen(sig->rsa_privkey), NULL, 0) != 0) {
@@ -1160,7 +1289,107 @@ int pdkim_feed_finish(pdkim_ctx *ctx, char **signature) {
}
/* VERIFICATION ----------------------------------------------------------- */
else {
+ rsa_context rsa;
+ char *dns_txt_name, *dns_txt_reply;
+
+ rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+
+ dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN);
+ if (dns_txt_name == NULL) return PDKIM_ERR_OOM;
+ dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN);
+ if (dns_txt_reply == NULL) {
+ free(dns_txt_name);
+ return PDKIM_ERR_OOM;
+ }
+ memset(dns_txt_reply,0,PDKIM_DNS_TXT_MAX_RECLEN);
+ memset(dns_txt_name ,0,PDKIM_DNS_TXT_MAX_NAMELEN);
+
+ if (snprintf(dns_txt_name,PDKIM_DNS_TXT_MAX_NAMELEN,
+ "%s._domainkey.%s.",
+ sig->selector,sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN) {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE;
+ goto NEXT_VERIFY;
+ };
+
+ if ((ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK) ||
+ (dns_txt_reply[0] == '\0')) {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
+ goto NEXT_VERIFY;
+ }
+
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,
+ "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ fprintf(ctx->debug_stream,"Raw record: ");
+ pdkim_quoteprint(ctx->debug_stream, dns_txt_reply, strlen(dns_txt_reply), 1);
+ }
+ #endif
+
+ sig->pubkey = pdkim_parse_pubkey_record(ctx,dns_txt_reply);
+ if (sig->pubkey == NULL) {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,"Error while parsing public key record\n");
+ fprintf(ctx->debug_stream,
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+ #endif
+ goto NEXT_VERIFY;
+ }
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+ #endif
+
+ if (rsa_parse_public_key(&rsa,
+ (unsigned char *)sig->pubkey->key,
+ sig->pubkey->key_len) != 0) {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+ goto NEXT_VERIFY;
+ }
+
+ /* Check the signature */
+ if (rsa_pkcs1_verify(&rsa,
+ RSA_PUBLIC,
+ ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
+ RSA_SHA1:RSA_SHA256),
+ 0,
+ (unsigned char *)headerhash,
+ (unsigned char *)sig->sigdata) != 0) {
+ sig->verify_status = PDKIM_VERIFY_FAIL;
+ sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream, "PDKIM [%s] signature did NOT verify OK\n",
+ sig->domain);
+ }
+ #endif
+ goto NEXT_VERIFY;
+ }
+
+ /* We have a winner! */
+ sig->verify_status = PDKIM_VERIFY_PASS;
+
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream, "PDKIM [%s] signature verified OK\n",
+ sig->domain);
+ }
+ #endif
+
+ NEXT_VERIFY:
+ rsa_free(&rsa);
+ free(dns_txt_name);
+ free(dns_txt_reply);
}
sig = sig->next;
@@ -1171,17 +1400,23 @@ int pdkim_feed_finish(pdkim_ctx *ctx, char **signature) {
/* -------------------------------------------------------------------------- */
-pdkim_ctx *pdkim_init_verify(void) {
+pdkim_ctx *pdkim_init_verify(int input_mode,
+ int(*dns_txt_callback)(char *, char *)
+ ) {
pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
if (ctx == NULL) return NULL;
memset(ctx,0,sizeof(pdkim_ctx));
ctx->mode = PDKIM_MODE_VERIFY;
+ ctx->input_mode = input_mode;
+ ctx->dns_txt_callback = dns_txt_callback;
+
return ctx;
}
/* -------------------------------------------------------------------------- */
-pdkim_ctx *pdkim_init_sign(char *domain,
+pdkim_ctx *pdkim_init_sign(int input_mode,
+ char *domain,
char *selector,
char *rsa_privkey) {
pdkim_ctx *ctx;
@@ -1199,6 +1434,7 @@ pdkim_ctx *pdkim_init_sign(char *domain,
memset(sig,0,sizeof(pdkim_signature));
ctx->mode = PDKIM_MODE_SIGN;
+ ctx->input_mode = input_mode;
ctx->sig = sig;
ctx->sig->domain = malloc(strlen(domain)+1);
@@ -1230,7 +1466,6 @@ void pdkim_set_debug_stream(pdkim_ctx *ctx,
/* -------------------------------------------------------------------------- */
int pdkim_set_optional(pdkim_ctx *ctx,
- int input_mode,
char *sign_headers,
char *identity,
int canon_headers,
@@ -1256,7 +1491,6 @@ int pdkim_set_optional(pdkim_ctx *ctx,
strcpy(ctx->sig->sign_headers, sign_headers);
}
- ctx->input_mode = input_mode;
ctx->sig->canon_headers = canon_headers;
ctx->sig->canon_body = canon_body;
ctx->sig->bodylength = bodylength;
@@ -1268,6 +1502,19 @@ int pdkim_set_optional(pdkim_ctx *ctx,
};
+/* -------------------------------------------------------------------------- */
+void pdkim_free_pubkey(pdkim_pubkey *pub) {
+ if (pub) {
+ if (pub->version != NULL) free(pub->version);
+ if (pub->granularity != NULL) free(pub->granularity);
+ if (pub->hashes != NULL) free(pub->hashes);
+ if (pub->keytype != NULL) free(pub->keytype);
+ if (pub->srvtype != NULL) free(pub->srvtype);
+ if (pub->notes != NULL) free(pub->notes);
+ if (pub->key != NULL) free(pub->key);
+ free(pub);
+ }
+}
/* -------------------------------------------------------------------------- */
@@ -1293,6 +1540,8 @@ void pdkim_free_sig(pdkim_signature *sig) {
if (sig->rsa_privkey != NULL) free(sig->rsa_privkey);
if (sig->sign_headers != NULL) free(sig->sign_headers);
+ if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey);
+
free(sig);
if (next != NULL) pdkim_free_sig(next);
}
diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h
index 77f74560b..8463312b6 100644
--- a/src/src/pdkim/pdkim.h
+++ b/src/src/pdkim/pdkim.h
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/pdkim/pdkim.h,v 1.1.2.4 2009/02/27 17:04:20 tom Exp $ */
+/* $Cambridge: exim/src/src/pdkim/pdkim.h,v 1.1.2.5 2009/03/17 12:57:37 tom Exp $ */
/* pdkim.h */
#include "sha1.h"
@@ -6,8 +6,14 @@
#include "rsa.h"
#include "base64.h"
-#define PDKIM_SIGNATURE_VERSION "1"
-#define PDKIM_MAX_BODY_LINE_LEN 1024
+#define PDKIM_SIGNATURE_VERSION "1"
+#define PDKIM_PUB_RECORD_VERSION "DKIM1"
+
+#define PDKIM_MAX_HEADER_LEN 65536
+#define PDKIM_MAX_HEADERS 512
+#define PDKIM_MAX_BODY_LINE_LEN 1024
+#define PDKIM_DNS_TXT_MAX_NAMELEN 1024
+#define PDKIM_DNS_TXT_MAX_RECLEN 4096
#define PDKIM_DEBUG
#define PDKIM_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
"Message-ID:To:Cc:MIME-Version:Content-Type:"\
@@ -20,12 +26,13 @@
/* Function success / error codes */
-#define PDKIM_OK 0
-#define PDKIM_FAIL -1
-#define PDKIM_ERR_OOM -100
-#define PDKIM_ERR_RSA_PRIVKEY -101
-#define PDKIM_ERR_RSA_SIGNING -102
-#define PDKIM_ERR_LONG_LINE -103
+#define PDKIM_OK 0
+#define PDKIM_FAIL -1
+#define PDKIM_ERR_OOM -100
+#define PDKIM_ERR_RSA_PRIVKEY -101
+#define PDKIM_ERR_RSA_SIGNING -102
+#define PDKIM_ERR_LONG_LINE -103
+#define PDKIM_ERR_BUFFER_TOO_SMALL -104
/* Main verification status */
#define PDKIM_VERIFY_NONE 0
@@ -34,14 +41,12 @@
#define PDKIM_VERIFY_PASS 3
/* Extended verification status */
-#define PDKIM_VERIFY_FAIL_NONE 0
#define PDKIM_VERIFY_FAIL_BODY 1
#define PDKIM_VERIFY_FAIL_MESSAGE 2
-
-
-
-
+#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE 3
+#define PDKIM_VERIFY_INVALID_BUFFER_SIZE 4
+#define PDKIM_VERIFY_INVALID_PUBKEY_PARSING 5
#ifdef PDKIM_DEBUG
@@ -67,15 +72,17 @@ char *pdkim_strncat(pdkim_str *, char *, int);
void pdkim_strfree(pdkim_str *);
#define PDKIM_QUERYMETHOD_DNS_TXT 0
-/* extern char *pdkim_querymethods[]; */
-#define PDKIM_ALGO_RSA_SHA256 0
-#define PDKIM_ALGO_RSA_SHA1 1
-/* extern char *pdkim_algos[]; */
+#define PDKIM_ALGO_RSA_SHA256 0
+#define PDKIM_ALGO_RSA_SHA1 1
-#define PDKIM_CANON_SIMPLE 0
-#define PDKIM_CANON_RELAXED 1
-/* extern char *pdkim_canons[]; */
+#define PDKIM_CANON_SIMPLE 0
+#define PDKIM_CANON_RELAXED 1
+
+#define PDKIM_HASH_SHA256 0
+#define PDKIM_HASH_SHA1 1
+
+#define PDKIM_KEYTYPE_RSA 0
/* -------------------------------------------------------------------------- */
@@ -84,64 +91,138 @@ typedef struct pdkim_pubkey {
char *version; /* v= */
char *granularity; /* g= */
- int num_hash_algos;
- int **hash_algos; /* h= */
-
- int keytype; /* k= */
- int srvtype; /* s= */
-
+ char *hashes; /* h= */
+ char *keytype; /* k= */
+ char *srvtype; /* s= */
char *notes; /* n= */
+
char *key; /* p= */
+ int key_len;
- int testing; /* t=y */
- int no_subdomaining; /* t=s */
+ int testing; /* t=y */
+ int no_subdomaining; /* t=s */
} pdkim_pubkey;
/* -------------------------------------------------------------------------- */
/* Signature as it appears in a DKIM-Signature header */
typedef struct pdkim_signature {
- /* Bits stored in a DKIM signature header ------ */
- int version; /* v= */
- int algo; /* a= */
- int canon_headers; /* c=x/ */
- int canon_body; /* c=/x */
- int querymethod; /* q= */
+ /* Bits stored in a DKIM signature header --------------------------- */
- char *selector; /* s= */
- char *domain; /* d= */
- char *identity; /* i= */
+ /* (v=) The version, as an integer. Currently, always "1" */
+ int version;
- unsigned long created; /* t= */
- unsigned long expires; /* x= */
- unsigned long bodylength; /* l= */
+ /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256
+ or PDKIM_ALGO_RSA_SHA1 */
+ int algo;
- char *headernames; /* h= */
- char *copiedheaders; /* z= */
+ /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
+ or PDKIM_CANON_RELAXED */
+ int canon_headers;
- char *sigdata; /* b= */
- char *bodyhash; /* bh= */
+ /* (c=/x) Body canonicalization method. Either PDKIM_CANON_SIMPLE
+ or PDKIM_CANON_RELAXED */
+ int canon_body;
+ /* (q=) Query Method. Currently, only PDKIM_QUERYMETHOD_DNS_TXT
+ is specified */
+ int querymethod;
+
+ /* (s=) The selector string as given in the signature */
+ char *selector;
+
+ /* (d=) The domain as given in the signature */
+ char *domain;
+
+ /* (i=) The identity as given in the signature */
+ char *identity;
+
+ /* (t=) Timestamp of signature creation */
+ unsigned long created;
+
+ /* (x=) Timestamp of expiry of signature */
+ unsigned long expires;
+
+ /* (l=) Amount of hashed body bytes (after canonicalization) */
+ unsigned long bodylength;
+
+ /* (h=) Colon-separated list of header names that are included in the
+ signature */
+ char *headernames;
+
+ /* (z=) */
+ char *copiedheaders;
+
+ /* (b=) Decoded raw signature data, along with its length in bytes */
+ char *sigdata;
int sigdata_len;
+
+ /* (bh=) Decoded raw body hash data, along with its length in bytes */
+ char *bodyhash;
int bodyhash_len;
- /* Signing specific ---------------------------- */
- char *rsa_privkey; /* Private RSA key */
- char *sign_headers; /* To-be-signed header names */
+ /* The main verification status. One of:
- /* Verification specific ----------------------- */
- pdkim_pubkey pubkey; /* Public key used to verify this signature. */
- int headernames_pos; /* Current position in header name list */
- char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */
- void *next; /* Pointer to next signature in list. */
- int verify_status; /* Verification result */
- int verify_ext_status; /* Extended verification result */
+ PDKIM_VERIFY_NONE Verification was not attempted. This status
+ should not appear.
+
+ PDKIM_VERIFY_INVALID There was an error while trying to verify
+ the signature. A more precise description
+ is available in verify_ext_status.
+
+ PDKIM_VERIFY_FAIL Verification failed because either the body
+ hash did not match, or the signature verification
+ failed. This probably means the message was
+ modified. Check verify_ext_status for the
+ exact reason.
+
+ PDKIM_VERIFY_PASS Verification succeeded.
+ */
+ int verify_status;
+
+
+ /* Extended verification status. Depending on the value of verify_status,
+ it can contain:
+
+ For verify_status == PDKIM_VERIFY_INVALID:
+
+ PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE
+ Unable to retrieve a public key container.
+
+ PDKIM_VERIFY_INVALID_BUFFER_SIZE
+ Either the DNS name constructed to retrieve the public key record
+ does not fit into PDKIM_DNS_TXT_MAX_NAMELEN bytes, or the retrieved
+ record is longer than PDKIM_DNS_TXT_MAX_RECLEN bytes.
+
+ PDKIM_VERIFY_INVALID_PUBKEY_PARSING
+ (Syntax) error while parsing the retrieved public key record.
- /* Per-signature helper variables -------------- */
+
+ For verify_status == PDKIM_VERIFY_FAIL:
+
+ PDKIM_VERIFY_FAIL_BODY
+ PDKIM_VERIFY_FAIL_MESSAGE
+
+
+ */
+ int verify_ext_status;
+
+
+
+ pdkim_pubkey *pubkey; /* Public key used to verify this signature. */
+ void *next; /* Pointer to next signature in list. */
+
+ /* Per-signature helper variables ----------------------------------- */
sha1_context sha1_body;
sha2_context sha2_body;
unsigned long signed_body_bytes;
pdkim_stringlist *headers;
+ /* Signing specific ------------------------------------------------- */
+ char *rsa_privkey; /* Private RSA key */
+ char *sign_headers; /* To-be-signed header names */
+ /* Verification specific -------------------------------------------- */
+ int headernames_pos; /* Current position in header name list */
+ char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */
} pdkim_signature;
@@ -164,6 +245,9 @@ typedef struct pdkim_ctx {
/* One (signing) or several chained (verification) signatures */
pdkim_signature *sig;
+ /* Callback for dns/txt query method (verification only) */
+ int(*dns_txt_callback)(char *, char *);
+
/* Coder's little helpers */
pdkim_str *cur_header;
char linebuf[PDKIM_MAX_BODY_LINE_LEN];
@@ -172,6 +256,7 @@ typedef struct pdkim_ctx {
int seen_eod;
int past_headers;
int num_buffered_crlf;
+ int num_headers;
#ifdef PDKIM_DEBUG
/* A FILE pointer. When not NULL, debug output will be generated
@@ -197,19 +282,19 @@ int pdkim_feed_finish (pdkim_ctx *, char **);
char *pdkim_create_header (pdkim_signature *, int);
pdkim_ctx
- *pdkim_init_sign (char *, char *, char *);
+ *pdkim_init_sign (int, char *, char *, char *);
pdkim_ctx
- *pdkim_init_verify (void);
+ *pdkim_init_verify (int, int(*dns_txt_callback)(char *, char *));
int pdkim_set_optional (pdkim_ctx *,
- int,
char *, char *,
int, int,
unsigned long, int,
unsigned long,
unsigned long);
+void pdkim_free_pubkey (pdkim_pubkey *);
void pdkim_free_sig (pdkim_signature *);
void pdkim_free_ctx (pdkim_ctx *);
diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c
index 99c703d95..06068aede 100644
--- a/src/src/pdkim/rsa.c
+++ b/src/src/pdkim/rsa.c
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/pdkim/rsa.c,v 1.1.2.1 2009/02/24 13:13:47 tom Exp $ */
+/* $Cambridge: exim/src/src/pdkim/rsa.c,v 1.1.2.2 2009/03/17 12:57:37 tom Exp $ */
/*
* The RSA public-key cryptosystem
*
@@ -603,6 +603,65 @@ static int asn1_get_mpi( unsigned char **p,
/*
+ * Parse a public RSA key
+
+OpenSSL RSA public key ASN1 container
+ 0:d=0 hl=3 l= 159 cons: SEQUENCE
+ 3:d=1 hl=2 l= 13 cons: SEQUENCE
+ 5:d=2 hl=2 l= 9 prim: OBJECT:rsaEncryption
+ 16:d=2 hl=2 l= 0 prim: NULL
+ 18:d=1 hl=3 l= 141 prim: BIT STRING:RSAPublicKey (below)
+
+RSAPublicKey ASN1 container
+ 0:d=0 hl=3 l= 137 cons: SEQUENCE
+ 3:d=1 hl=3 l= 129 prim: INTEGER:Public modulus
+135:d=1 hl=2 l= 3 prim: INTEGER:Public exponent
+*/
+
+int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen )
+{
+ unsigned char *p, *end;
+ int ret, len;
+
+ p = buf;
+ end = buf+buflen;
+
+ if( ( ret = asn1_get_tag( &p, end, &len,
+ ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) {
+ return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+ }
+
+ if( ( ret = asn1_get_tag( &p, end, &len,
+ ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) == 0 ) {
+ /* Skip over embedded rsaEncryption Object */
+ p+=len;
+
+ /* The RSAPublicKey ASN1 container is wrapped in a BIT STRING */
+ if( ( ret = asn1_get_tag( &p, end, &len,
+ ASN1_BIT_STRING ) ) != 0 ) {
+ return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+ }
+
+ /* Limit range to that BIT STRING */
+ end = p + len;
+ p++;
+
+ if( ( ret = asn1_get_tag( &p, end, &len,
+ ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) {
+ return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+ }
+ }
+
+ if ( ( ( ret = asn1_get_mpi( &p, end, &(rsa->N) ) ) == 0 ) &&
+ ( ( ret = asn1_get_mpi( &p, end, &(rsa->E) ) ) == 0 ) ) {
+ rsa->len = mpi_size( &rsa->N );
+ return 0;
+ }
+
+ return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret );
+}
+
+/*
* Parse a private RSA key
*/
int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen,
diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h
index 05c903ece..a91a5ec53 100644
--- a/src/src/pdkim/rsa.h
+++ b/src/src/pdkim/rsa.h
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/pdkim/rsa.h,v 1.1.2.1 2009/02/24 13:13:47 tom Exp $ */
+/* $Cambridge: exim/src/src/pdkim/rsa.h,v 1.1.2.2 2009/03/17 12:57:37 tom Exp $ */
/**
* \file rsa.h
*
@@ -343,6 +343,8 @@ int rsa_pkcs1_verify( rsa_context *ctx,
*/
void rsa_free( rsa_context *ctx );
+int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen );
+
int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen,
unsigned char *pwd, int pwdlen );