-/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.1.2.5 2009/02/27 17:04:20 tom Exp $ */
-/* pdkim.c */
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2009 Tom Kistner <tom@duncanthrax.net>
+ *
+ * http://duncanthrax.net/pdkim/
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.1.2.12 2009/04/09 19:18:11 tom Exp $ */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <strings.h>
#include <ctype.h>
-#include <unistd.h>
+
#include "pdkim.h"
+#include "sha1.h"
+#include "sha2.h"
+#include "rsa.h"
+#include "base64.h"
+
+#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_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
+ "Message-ID:To:Cc:MIME-Version:Content-Type:"\
+ "Content-Transfer-Encoding:Content-ID:"\
+ "Content-Description:Resent-Date:Resent-From:"\
+ "Resent-Sender:Resent-To:Resent-Cc:"\
+ "Resent-Message-ID:In-Reply-To:References:"\
+ "List-Id:List-Help:List-Unsubscribe:"\
+ "List-Subscribe:List-Post:List-Owner:List-Archive"
+
+/* -------------------------------------------------------------------------- */
+struct pdkim_stringlist {
+ char *value;
+ void *next;
+};
+
+#define PDKIM_STR_ALLOC_FRAG 256
+struct pdkim_str {
+ char *str;
+ unsigned int len;
+ unsigned int allocated;
+};
/* -------------------------------------------------------------------------- */
/* A bunch of list constants */
"relaxed",
NULL
};
+char *pdkim_hashes[] = {
+ "sha256",
+ "sha1",
+ NULL
+};
+char *pdkim_keytypes[] = {
+ "rsa",
+ NULL
+};
typedef struct pdkim_combined_canon_entry {
char *str;
/* -------------------------------------------------------------------------- */
-/* Various debugging functions */
+/* Print debugging functions */
#ifdef PDKIM_DEBUG
void pdkim_quoteprint(FILE *stream, char *data, int len, int lf) {
int i;
for (i=0;i<len;i++) {
int c = p[i];
- fprintf(stream,"%02x ",c);
+ fprintf(stream,"%02x",c);
}
if (lf)
fputc('\n',stream);
pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
if (new_entry == NULL) return NULL;
memset(new_entry,0,sizeof(pdkim_stringlist));
- new_entry->value = malloc(strlen(str)+1);
+ new_entry->value = strdup(str);
if (new_entry->value == NULL) return NULL;
- strcpy(new_entry->value,str);
if (base != NULL) {
pdkim_stringlist *last = base;
while (last->next != NULL) { last = last->next; };
if (cstr) strcpy(p->str,cstr);
return p;
};
-char *pdkim_strcat(pdkim_str *str, char *cstr) {
- return pdkim_strncat(str, cstr, strlen(cstr));
-};
char *pdkim_strncat(pdkim_str *str, char *data, int len) {
if ((str->allocated - str->len) < (len+1)) {
/* Extend the buffer */
str->str[str->len] = '\0';
return str->str;
};
+char *pdkim_strcat(pdkim_str *str, char *cstr) {
+ return pdkim_strncat(str, cstr, strlen(cstr));
+};
char *pdkim_numcat(pdkim_str *str, unsigned long num) {
char minibuf[20];
snprintf(minibuf,20,"%lu",num);
};
+
+/* -------------------------------------------------------------------------- */
+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);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+void pdkim_free_sig(pdkim_signature *sig) {
+ if (sig) {
+ pdkim_signature *next = (pdkim_signature *)sig->next;
+
+ pdkim_stringlist *e = sig->headers;
+ while(e != NULL) {
+ pdkim_stringlist *c = e;
+ if (e->value != NULL) free(e->value);
+ e = e->next;
+ free(c);
+ }
+
+ if (sig->sigdata != NULL) free(sig->sigdata);
+ if (sig->bodyhash != NULL) free(sig->bodyhash);
+ if (sig->selector != NULL) free(sig->selector);
+ if (sig->domain != NULL) free(sig->domain);
+ if (sig->identity != NULL) free(sig->identity);
+ if (sig->headernames != NULL) free(sig->headernames);
+ if (sig->copiedheaders != NULL) free(sig->copiedheaders);
+ if (sig->rsa_privkey != NULL) free(sig->rsa_privkey);
+ if (sig->sign_headers != NULL) free(sig->sign_headers);
+ if (sig->signature_header != NULL) free(sig->signature_header);
+ if (sig->sha1_body != NULL) free(sig->sha1_body);
+ if (sig->sha2_body != NULL) free(sig->sha2_body);
+
+ if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey);
+
+ free(sig);
+ if (next != NULL) pdkim_free_sig(next);
+ }
+};
+
+
+/* -------------------------------------------------------------------------- */
+DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) {
+ if (ctx) {
+ pdkim_free_sig(ctx->sig);
+ pdkim_strfree(ctx->cur_header);
+ free(ctx);
+ }
+};
+
+
/* -------------------------------------------------------------------------- */
/* Matches the name of the passed raw "header" against
the passed colon-separated "list", starting at entry
if (hname == NULL) return PDKIM_ERR_OOM;
memset(hname,0,(hcolon-header)+1);
strncpy(hname,header,(hcolon-header));
- lcopy = malloc(strlen(list)+1);
+ lcopy = strdup(list);
if (lcopy == NULL) {
free(hname);
return PDKIM_ERR_OOM;
}
- strcpy(lcopy,list);
p = lcopy;
q = strchr(p,':');
while (q != NULL) {
sig = malloc(sizeof(pdkim_signature));
if (sig == NULL) return NULL;
memset(sig,0,sizeof(pdkim_signature));
+ sig->bodylength = -1;
sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1);
if (sig->rawsig_no_b_val == NULL) {
p = raw_hdr;
q = sig->rawsig_no_b_val;
- while (*p != '\0') {
+ while (1) {
/* Ignore FWS */
if ( (*p == '\r') || (*p == '\n') )
if (cur_val == NULL)
cur_val = pdkim_strnew(NULL);
- if ( (*p == '\r') || (*p == '\n') )
+ if ( (*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t') )
goto NEXT_CHAR;
- if (*p == ';') {
+ if ( (*p == ';') || (*p == '\0') ) {
if (cur_tag->len > 0) {
pdkim_strtrim(cur_val);
#ifdef PDKIM_DEBUG
}
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);
sig->expires = strtoul(cur_val->str,NULL,10);
break;
case 'l':
- sig->bodylength = strtoul(cur_val->str,NULL,10);
+ sig->bodylength = strtol(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);
}
NEXT_CHAR:
+ if (*p == '\0') break;
if (!in_b_val) {
*q = *p;
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;
}
*q = '\0';
+ /* Chomp raw header. The final newline must not be added to the signature. */
+ q--;
+ while( (q > sig->rawsig_no_b_val) && ((*q == '\r') || (*q == '\n')) ) {
+ *q = '\0'; q--;
+ }
+
#ifdef PDKIM_DEBUG
if (ctx->debug_stream) {
fprintf(ctx->debug_stream,
}
#endif
- sha1_starts(&(sig->sha1_body));
- sha2_starts(&(sig->sha2_body),0);
+ sig->sha1_body = malloc(sizeof(sha1_context));
+ if (sig->sha1_body == NULL) {
+ pdkim_free_sig(sig);
+ return NULL;
+ }
+ sig->sha2_body = malloc(sizeof(sha2_context));
+ if (sig->sha2_body == NULL) {
+ pdkim_free_sig(sig);
+ return NULL;
+ }
+
+ sha1_starts(sig->sha1_body);
+ sha2_starts(sig->sha2_body,0);
return sig;
}
+/* -------------------------------------------------------------------------- */
+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 (1) {
+
+ /* 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 == ';') || (*p == '\0') ) {
+ 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:
+ if (*p == '\0') break;
+ 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) {
}
/* Make sure we don't exceed the to-be-signed body length */
- if (sig->bodylength &&
+ if ((sig->bodylength >= 0) &&
((sig->signed_body_bytes+(unsigned long)canon_len) > sig->bodylength))
canon_len = (sig->bodylength - sig->signed_body_bytes);
if (canon_len > 0) {
if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_update(&(sig->sha1_body),(unsigned char *)canon_data,canon_len);
+ sha1_update(sig->sha1_body,(unsigned char *)canon_data,canon_len);
else
- sha2_update(&(sig->sha2_body),(unsigned char *)canon_data,canon_len);
+ sha2_update(sig->sha2_body,(unsigned char *)canon_data,canon_len);
sig->signed_body_bytes += canon_len;
#ifdef PDKIM_DEBUG
if (ctx->debug_stream!=NULL)
/* Finish hashes */
unsigned char bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */
if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_finish(&(sig->sha1_body),bh);
+ sha1_finish(sig->sha1_body,bh);
else
- sha2_finish(&(sig->sha2_body),bh);
+ sha2_finish(sig->sha2_body,bh);
#ifdef PDKIM_DEBUG
if (ctx->debug_stream) {
/* If bodylength limit is set, and we have received less bytes
than the requested amount, effectively remove the limit tag. */
- if (sig->signed_body_bytes < sig->bodylength) sig->bodylength = 0;
+ if (sig->signed_body_bytes < sig->bodylength) sig->bodylength = -1;
}
/* VERIFICATION --------------------------------------------------------- */
else {
ctx->cur_header->len--;
}
+ ctx->num_headers++;
+ if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
+
/* Traverse all signatures */
while (sig != NULL) {
+ pdkim_stringlist *list;
/* SIGNING -------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN) {
}
/* Add header to the signed headers list */
- pdkim_stringlist *list = pdkim_append_stringlist(sig->headers,
- ctx->cur_header->str);
+ list = pdkim_append_stringlist(sig->headers,
+ ctx->cur_header->str);
if (list == NULL) return PDKIM_ERR_OOM;
sig->headers = list;
(strncasecmp(ctx->cur_header->str,
DKIM_SIGNATURE_HEADERNAME,
strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) ) {
+ pdkim_signature *new_sig;
/* Create and chain new signature block */
#ifdef PDKIM_DEBUG
if (ctx->debug_stream)
fprintf(ctx->debug_stream,
"PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
#endif
- pdkim_signature *new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str);
+ new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str);
if (new_sig != NULL) {
pdkim_signature *last_sig = ctx->sig;
if (last_sig == NULL) {
}
}
+ BAIL:
pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
return PDKIM_OK;
};
/* -------------------------------------------------------------------------- */
#define HEADER_BUFFER_FRAG_SIZE 256
-int pdkim_feed (pdkim_ctx *ctx,
+DLLEXPORT int pdkim_feed (pdkim_ctx *ctx,
char *data,
int len) {
int p;
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;
goto BAIL;
}
}
- if (sig->bodylength > 0) {
+ if (sig->bodylength >= 0) {
if (!( pdkim_strcat(hdr,"l=") &&
pdkim_numcat(hdr,sig->bodylength) &&
pdkim_strcat(hdr,";") ) ) {
/* -------------------------------------------------------------------------- */
-int pdkim_feed_finish(pdkim_ctx *ctx, char **signature) {
+DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) {
pdkim_signature *sig = ctx->sig;
pdkim_str *headernames = NULL; /* Collected signed header names */
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;
if (sig->algo == PDKIM_ALGO_RSA_SHA1) {
sha1_update(&(sha1_headers),(unsigned char *)sig_hdr,strlen(sig_hdr));
sha1_finish(&(sha1_headers),(unsigned char *)headerhash);
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
+ pdkim_hexprint(ctx->debug_stream, headerhash, 20, 1);
+ }
+ #endif
}
else {
sha2_update(&(sha2_headers),(unsigned char *)sig_hdr,strlen(sig_hdr));
sha2_finish(&(sha2_headers),(unsigned char *)headerhash);
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
+ pdkim_hexprint(ctx->debug_stream, headerhash, 32, 1);
+ }
+ #endif
}
free(sig_hdr);
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) {
}
#endif
- /* Recreate signature header with b= included, return it to the caller */
- if (signature != NULL) {
- *signature = pdkim_create_header(ctx->sig,1);
- if (*signature == NULL) return PDKIM_ERR_OOM;
- }
+ sig->signature_header = pdkim_create_header(ctx->sig,1);
+ if (sig->signature_header == NULL) return PDKIM_ERR_OOM;
}
/* 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;
}
+ /* If requested, set return pointer to signature(s) */
+ if (return_signatures != NULL) {
+ *return_signatures = ctx->sig;
+ }
+
return PDKIM_OK;
}
/* -------------------------------------------------------------------------- */
-pdkim_ctx *pdkim_init_verify(void) {
+DLLEXPORT 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->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN);
+ if (ctx->linebuf == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
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,
+DLLEXPORT pdkim_ctx *pdkim_init_sign(int input_mode,
+ char *domain,
char *selector,
char *rsa_privkey) {
pdkim_ctx *ctx;
+ pdkim_signature *sig;
if (!domain || !selector || !rsa_privkey) return NULL;
ctx = malloc(sizeof(pdkim_ctx));
if (ctx == NULL) return NULL;
memset(ctx,0,sizeof(pdkim_ctx));
- pdkim_signature *sig = malloc(sizeof(pdkim_signature));
+
+ ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN);
+ if (ctx->linebuf == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ sig = malloc(sizeof(pdkim_signature));
if (sig == NULL) {
+ free(ctx->linebuf);
free(ctx);
return NULL;
}
memset(sig,0,sizeof(pdkim_signature));
+ sig->bodylength = -1;
ctx->mode = PDKIM_MODE_SIGN;
+ ctx->input_mode = input_mode;
ctx->sig = sig;
- ctx->sig->domain = malloc(strlen(domain)+1);
- ctx->sig->selector = malloc(strlen(selector)+1);
- ctx->sig->rsa_privkey = malloc(strlen(rsa_privkey)+1);
+ ctx->sig->domain = strdup(domain);
+ ctx->sig->selector = strdup(selector);
+ ctx->sig->rsa_privkey = strdup(rsa_privkey);
if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) {
pdkim_free_ctx(ctx);
return NULL;
}
- strcpy(ctx->sig->domain, domain);
- strcpy(ctx->sig->selector, selector);
- strcpy(ctx->sig->rsa_privkey, rsa_privkey);
+ ctx->sig->sha1_body = malloc(sizeof(sha1_context));
+ if (ctx->sig->sha1_body == NULL) {
+ pdkim_free_ctx(ctx);
+ return NULL;
+ }
+ sha1_starts(ctx->sig->sha1_body);
- sha1_starts(&(ctx->sig->sha1_body));
- sha2_starts(&(ctx->sig->sha2_body),0);
+ ctx->sig->sha2_body = malloc(sizeof(sha2_context));
+ if (ctx->sig->sha2_body == NULL) {
+ pdkim_free_ctx(ctx);
+ return NULL;
+ }
+ sha2_starts(ctx->sig->sha2_body,0);
return ctx;
};
#ifdef PDKIM_DEBUG
/* -------------------------------------------------------------------------- */
-void pdkim_set_debug_stream(pdkim_ctx *ctx,
+DLLEXPORT void pdkim_set_debug_stream(pdkim_ctx *ctx,
FILE *debug_stream) {
ctx->debug_stream = debug_stream;
};
#endif
/* -------------------------------------------------------------------------- */
-int pdkim_set_optional(pdkim_ctx *ctx,
- int input_mode,
+DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx,
char *sign_headers,
char *identity,
int canon_headers,
int canon_body,
- unsigned long bodylength,
+ long bodylength,
int algo,
unsigned long created,
unsigned long expires) {
if (identity != NULL) {
- ctx->sig->identity = malloc(strlen(identity)+1);
- if (!ctx->sig->identity) {
- return PDKIM_ERR_OOM;
- }
- strcpy(ctx->sig->identity, identity);
+ ctx->sig->identity = strdup(identity);
+ if (ctx->sig->identity == NULL) return PDKIM_ERR_OOM;
}
if (sign_headers != NULL) {
- ctx->sig->sign_headers = malloc(strlen(sign_headers)+1);
- if (!ctx->sig->sign_headers) {
- return PDKIM_ERR_OOM;
- }
- strcpy(ctx->sig->sign_headers, sign_headers);
+ ctx->sig->sign_headers = strdup(sign_headers);
+ if (ctx->sig->sign_headers == NULL) return PDKIM_ERR_OOM;
}
- ctx->input_mode = input_mode;
ctx->sig->canon_headers = canon_headers;
ctx->sig->canon_body = canon_body;
ctx->sig->bodylength = bodylength;
return PDKIM_OK;
};
-
-
-
-
-/* -------------------------------------------------------------------------- */
-void pdkim_free_sig(pdkim_signature *sig) {
- if (sig) {
- pdkim_signature *next = (pdkim_signature *)sig->next;
-
- pdkim_stringlist *e = sig->headers;
- while(e != NULL) {
- pdkim_stringlist *c = e;
- if (e->value != NULL) free(e->value);
- e = e->next;
- free(c);
- }
-
- if (sig->sigdata != NULL) free(sig->sigdata);
- if (sig->bodyhash != NULL) free(sig->bodyhash);
- if (sig->selector != NULL) free(sig->selector);
- if (sig->domain != NULL) free(sig->domain);
- if (sig->identity != NULL) free(sig->identity);
- if (sig->headernames != NULL) free(sig->headernames);
- if (sig->copiedheaders != NULL) free(sig->copiedheaders);
- if (sig->rsa_privkey != NULL) free(sig->rsa_privkey);
- if (sig->sign_headers != NULL) free(sig->sign_headers);
-
- free(sig);
- if (next != NULL) pdkim_free_sig(next);
- }
-};
-
-
-/* -------------------------------------------------------------------------- */
-void pdkim_free_ctx(pdkim_ctx *ctx) {
- if (ctx) {
- pdkim_free_sig(ctx->sig);
- pdkim_strfree(ctx->cur_header);
- free(ctx);
- }
-};