From 5b12ca9a7b9e95b39512836fd0e43ed7c880fbbd Mon Sep 17 00:00:00 2001 From: Tom Kistner Date: Tue, 17 Mar 2009 12:57:37 +0000 Subject: [PATCH] wip --- src/src/pdkim/pdkim.c | 287 +++++++++++++++++++++++++++++++++++++++--- src/src/pdkim/pdkim.h | 203 +++++++++++++++++++++--------- src/src/pdkim/rsa.c | 61 ++++++++- src/src/pdkim/rsa.h | 4 +- 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 @@ -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 * @@ -602,6 +602,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 */ 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 ); -- 2.30.2