tom 2009/02/26 16:07:36 GMT
Modified files: (Branch: DEVEL_PDKIM)
exim-src/src/pdkim pdkim.c pdkim.h
Log:
wip
Revision Changes Path
1.1.2.4 +397 -39 exim/exim-src/src/pdkim/pdkim.c
1.1.2.3 +16 -22 exim/exim-src/src/pdkim/pdkim.h
Index: pdkim.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.c,v
retrieving revision 1.1.2.3
retrieving revision 1.1.2.4
diff -u -r1.1.2.3 -r1.1.2.4
--- pdkim.c 25 Feb 2009 12:52:58 -0000 1.1.2.3
+++ pdkim.c 26 Feb 2009 16:07:36 -0000 1.1.2.4
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/pdkim/pdkim.c,v 1.1.2.3 2009/02/25 12:52:58 tom Exp $ */
+/* $Cambridge: exim/exim-src/src/pdkim/pdkim.c,v 1.1.2.4 2009/02/26 16:07:36 tom Exp $ */
/* pdkim.c */
#include <stdlib.h>
@@ -13,15 +13,33 @@
/* -------------------------------------------------------------------------- */
/* A bunch of list constants */
char *pdkim_querymethods[] = {
- "dns/txt"
+ "dns/txt",
+ NULL
};
char *pdkim_algos[] = {
"rsa-sha256",
- "rsa-sha1"
+ "rsa-sha1",
+ NULL
};
char *pdkim_canons[] = {
"simple",
- "relaxed"
+ "relaxed",
+ NULL
+};
+
+typedef struct pdkim_combined_canon_entry {
+ char *str;
+ int canon_headers;
+ int canon_body;
+} pdkim_combined_canon_entry;
+pdkim_combined_canon_entry pdkim_combined_canons[] = {
+ { "simple/simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
+ { "simple/relaxed", PDKIM_CANON_SIMPLE, PDKIM_CANON_RELAXED },
+ { "relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
+ { "relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED },
+ { "simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
+ { "relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
+ { NULL, 0, 0 }
};
@@ -114,6 +132,24 @@
snprintf(minibuf,20,"%lu",num);
return pdkim_strcat(str,minibuf);
};
+char *pdkim_strtrim(pdkim_str *str) {
+ char *p = str->str;
+ char *q = str->str;
+ while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++;
+ while (*p != '\0') {*q = *p; q++; p++;};
+ *q = '\0';
+ while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) {
+ *q = '\0';
+ q--;
+ }
+ str->len = strlen(str->str);
+ return str->str;
+};
+char *pdkim_strclear(pdkim_str *str) {
+ str->str[0] = '\0';
+ str->len = 0;
+ return str->str;
+};
void pdkim_strfree(pdkim_str *str) {
if (str == NULL) return;
if (str->str != NULL) free(str->str);
@@ -210,6 +246,285 @@
/* -------------------------------------------------------------------------- */
+#define PDKIM_QP_ERROR_DECODE -1
+char *pdkim_decode_qp_char(char *qp_p, int *c) {
+ char *initial_pos = qp_p;
+
+ /* Advance one char */
+ qp_p++;
+
+ /* Check for two hex digits and decode them */
+ if (isxdigit(*qp_p) && isxdigit(qp_p[1])) {
+ /* Do hex conversion */
+ if (isdigit(*qp_p)) {*c = *qp_p - '0';}
+ else {*c = toupper(*qp_p) - 'A' + 10;};
+ *c <<= 4;
+ if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';}
+ else {*c |= toupper(qp_p[1]) - 'A' + 10;};
+ return qp_p + 2;
+ };
+
+ /* Illegal char here */
+ *c = PDKIM_QP_ERROR_DECODE;
+ return initial_pos;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_qp(char *str) {
+ int nchar = 0;
+ char *q;
+ char *p = str;
+ char *n = malloc(strlen(p)+1);
+ if (n == NULL) return NULL;
+ *n = '\0';
+ q = n;
+ while (*p != '\0') {
+ if (*p == '=') {
+ p = pdkim_decode_qp_char(p,&nchar);
+ if (nchar >= 0) {
+ *q = nchar;
+ q++;
+ continue;
+ }
+ }
+ else {
+ *q = *p;
+ q++;
+ }
+ p++;
+ }
+ return n;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_base64(char *str) {
+ int dlen = 0;
+ char *res;
+
+ base64_decode(NULL, &dlen, str, strlen(str));
+ res = malloc(dlen+1);
+ if (res == NULL) return NULL;
+ if (base64_decode(res,&dlen,str,strlen(str)) != 0) {
+ free(res);
+ return NULL;
+ }
+ return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+#define PDKIM_HDR_LIMBO 0
+#define PDKIM_HDR_TAG 1
+#define PDKIM_HDR_VALUE 2
+pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
+ pdkim_signature *sig ;
+ char *rawsig_no_b_val;
+ char *p,*q;
+ pdkim_str *cur_tag = NULL;
+ pdkim_str *cur_val = NULL;
+ int past_hname = 0;
+ int in_b_val = 0;
+ int where = PDKIM_HDR_LIMBO;
+ int i;
+
+ sig = malloc(sizeof(pdkim_signature));
+ if (sig == NULL) return NULL;
+ memset(sig,0,sizeof(pdkim_signature));
+
+ sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1);
+ if (sig->rawsig_no_b_val == NULL) {
+ free(sig);
+ return NULL;
+ }
+
+ p = raw_hdr;
+ q = sig->rawsig_no_b_val;
+
+ while (*p != '\0') {
+
+ /* Ignore FWS */
+ if ( (*p == '\r') || (*p == '\n') )
+ goto NEXT_CHAR;
+
+ /* Fast-forward through header name */
+ if (!past_hname) {
+ if (*p == ':') past_hname = 1;
+ 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 == '=') {
+ if (strcmp(cur_tag->str,"b") == 0) {
+ *q = '='; q++;
+ in_b_val = 1;
+ }
+ 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 'b':
+ switch (cur_tag->str[1]) {
+ case 'h':
+ sig->bodyhash = pdkim_decode_base64(cur_val->str);
+ break;
+ default:
+ sig->sigdata = pdkim_decode_base64(cur_val->str);
+ break;
+ }
+ break;
+ case 'v':
+ if (strcmp(cur_val->str,PDKIM_SIGNATURE_VERSION) == 0) {
+ /* We only support version 1, and that is currently the
+ only version there is. */
+ sig->version = 1;
+ }
+ break;
+ case 'a':
+ i = 0;
+ while (pdkim_algos[i] != NULL) {
+ if (strcmp(cur_val->str,pdkim_algos[i]) == 0 ) {
+ sig->algo = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ case 'c':
+ i = 0;
+ while (pdkim_combined_canons[i].str != NULL) {
+ if (strcmp(cur_val->str,pdkim_combined_canons[i].str) == 0 ) {
+ sig->canon_headers = pdkim_combined_canons[i].canon_headers;
+ sig->canon_body = pdkim_combined_canons[i].canon_body;
+ break;
+ }
+ i++;
+ }
+ break;
+ case 'q':
+ i = 0;
+ while (pdkim_querymethods[i] != NULL) {
+ if (strcmp(cur_val->str,pdkim_querymethods[i]) == 0 ) {
+ sig->querymethod = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ case 's':
+ sig->selector = malloc(strlen(cur_val->str)+1);
+ if (sig->selector == NULL) break;
+ strcpy(sig->selector, 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);
+ break;
+ case 'i':
+ sig->identity = pdkim_decode_qp(cur_val->str);
+ break;
+ case 't':
+ sig->created = strtoul(cur_val->str,NULL,10);
+ break;
+ case 'x':
+ sig->expires = strtoul(cur_val->str,NULL,10);
+ break;
+ case 'l':
+ 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);
+ break;
+ case 'z':
+ sig->copiedheaders = pdkim_decode_qp(cur_val->str);
+ 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);
+ in_b_val = 0;
+ where = PDKIM_HDR_LIMBO;
+ goto NEXT_CHAR;
+ }
+ else pdkim_strncat(cur_val,p,1);
+ }
+
+ NEXT_CHAR:
+
+ if (!in_b_val) {
+ *q = *p;
+ q++;
+ }
+ p++;
+ }
+
+ /* Make sure the most important bits are there. */
+ if (!(sig->domain && (*(sig->domain) != '\0') &&
+ sig->selector && (*(sig->selector) != '\0') &&
+ sig->headernames && (*(sig->headernames) != '\0') &&
+ sig->version)) {
+ pdkim_free_signature(sig);
+ return NULL;
+ }
+
+ *q = '\0';
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,
+ "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ pdkim_quoteprint(ctx->debug_stream,
+ sig->rawsig_no_b_val,
+ strlen(sig->rawsig_no_b_val), 1);
+ fprintf(ctx->debug_stream,
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+ #endif
+ return sig;
+}
+
+
+
+/* -------------------------------------------------------------------------- */
int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
pdkim_signature *sig = ctx->sig;
/* Cache relaxed version of data */
@@ -322,7 +637,6 @@
/* VERIFICATION --------------------------------------------------------- */
else {
-
}
@@ -381,6 +695,7 @@
/* -------------------------------------------------------------------------- */
/* Callback from pdkim_feed below for processing complete headers */
+#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
int pdkim_header_complete(pdkim_ctx *ctx) {
pdkim_signature *sig = ctx->sig;
@@ -412,7 +727,41 @@
sig = sig->next;
}
- ctx->cur_header->len = 0; /* Re-use existing pdkim_str */
+
+ /* DKIM-Signature: headers are added to the verification list */
+ if ( (ctx->mode == PDKIM_MODE_VERIFY) &&
+ (strncasecmp(ctx->cur_header->str,
+ DKIM_SIGNATURE_HEADERNAME,
+ strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) ) {
+ /* 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);
+ if (new_sig != NULL) {
+ pdkim_signature *last_sig = ctx->sig;
+ if (last_sig == NULL) {
+ ctx->sig = new_sig;
+ }
+ else {
+ while (last_sig->next != NULL) { last_sig = last_sig->next; };
+ last_sig->next = new_sig;
+ }
+ }
+ else {
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,"Error while parsing signature header\n");
+ fprintf(ctx->debug_stream,
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+ #endif
+ }
+ }
+
+ pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
return PDKIM_OK;
};
@@ -475,55 +824,55 @@
/* -------------------------------------------------------------------------- */
-pdkim_str *pdkim_create_header(pdkim_ctx *ctx, int final) {
+pdkim_str *pdkim_create_header(pdkim_signature *sig, int final) {
pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
if (hdr == NULL) return NULL;
/* Required and static bits */
if (
- pdkim_strcat(hdr,"; a=") &&
- pdkim_strcat(hdr,pdkim_algos[ctx->sig->algo]) &&
- pdkim_strcat(hdr,"; q=") &&
- pdkim_strcat(hdr,pdkim_querymethods[ctx->sig->querymethod]) &&
- pdkim_strcat(hdr,"; c=") &&
- pdkim_strcat(hdr,pdkim_canons[ctx->sig->canon_headers]) &&
- pdkim_strcat(hdr,"/") &&
- pdkim_strcat(hdr,pdkim_canons[ctx->sig->canon_body]) &&
- pdkim_strcat(hdr,"; d=") &&
- pdkim_strcat(hdr,ctx->sig->domain) &&
- pdkim_strcat(hdr,"; s=") &&
- pdkim_strcat(hdr,ctx->sig->selector) &&
- pdkim_strcat(hdr,";\r\n\th=") &&
- pdkim_strcat(hdr,ctx->sig->headernames) &&
- pdkim_strcat(hdr,"; bh=") &&
- pdkim_strcat(hdr,ctx->sig->bodyhash) &&
+ pdkim_strcat(hdr,"; a=") &&
+ pdkim_strcat(hdr,pdkim_algos[sig->algo]) &&
+ pdkim_strcat(hdr,"; q=") &&
+ pdkim_strcat(hdr,pdkim_querymethods[sig->querymethod]) &&
+ pdkim_strcat(hdr,"; c=") &&
+ pdkim_strcat(hdr,pdkim_canons[sig->canon_headers]) &&
+ pdkim_strcat(hdr,"/") &&
+ pdkim_strcat(hdr,pdkim_canons[sig->canon_body]) &&
+ pdkim_strcat(hdr,"; d=") &&
+ pdkim_strcat(hdr,sig->domain) &&
+ pdkim_strcat(hdr,"; s=") &&
+ pdkim_strcat(hdr,sig->selector) &&
+ pdkim_strcat(hdr,";\r\n\th=") &&
+ pdkim_strcat(hdr,sig->headernames) &&
+ pdkim_strcat(hdr,"; bh=") &&
+ pdkim_strcat(hdr,sig->bodyhash) &&
pdkim_strcat(hdr,";\r\n\t")
) {
/* Optional bits */
- if (ctx->sig->identity != NULL) {
- if (!( pdkim_strcat(hdr,"i=") &&
- pdkim_strcat(hdr,ctx->sig->identity) &&
+ if (sig->identity != NULL) {
+ if (!( pdkim_strcat(hdr,"i=") &&
+ pdkim_strcat(hdr,sig->identity) &&
pdkim_strcat(hdr,";") ) ) {
return NULL;
}
}
- if (ctx->sig->created > 0) {
- if (!( pdkim_strcat(hdr,"t=") &&
- pdkim_numcat(hdr,ctx->sig->created) &&
+ if (sig->created > 0) {
+ if (!( pdkim_strcat(hdr,"t=") &&
+ pdkim_numcat(hdr,sig->created) &&
pdkim_strcat(hdr,";") ) ) {
return NULL;
}
}
- if (ctx->sig->expires > 0) {
- if (!( pdkim_strcat(hdr,"x=") &&
- pdkim_numcat(hdr,ctx->sig->expires) &&
+ if (sig->expires > 0) {
+ if (!( pdkim_strcat(hdr,"x=") &&
+ pdkim_numcat(hdr,sig->expires) &&
pdkim_strcat(hdr,";") ) ) {
return NULL;
}
}
- if (ctx->sig->bodylength > 0) {
- if (!( pdkim_strcat(hdr,"l=") &&
- pdkim_numcat(hdr,ctx->sig->bodylength) &&
+ if (sig->bodylength > 0) {
+ if (!( pdkim_strcat(hdr,"l=") &&
+ pdkim_numcat(hdr,sig->bodylength) &&
pdkim_strcat(hdr,";") ) ) {
return NULL;
}
@@ -535,8 +884,8 @@
/* Preliminary or final version? */
if (final) {
if (
- pdkim_strcat(hdr,"b=") &&
- pdkim_strcat(hdr,ctx->sig->sigdata) &&
+ pdkim_strcat(hdr,"b=") &&
+ pdkim_strcat(hdr,sig->sigdata) &&
pdkim_strcat(hdr,";")
) return hdr;
}
@@ -636,7 +985,7 @@
pdkim_strfree(headernames);
/* Create signature header with b= omitted */
- hdr = pdkim_create_header(ctx,0);
+ hdr = pdkim_create_header(ctx->sig,0);
if (hdr == NULL) return PDKIM_ERR_OOM;
/* If necessary, perform relaxed canon */
@@ -723,7 +1072,7 @@
/* Recreate signature header with b= included */
pdkim_strfree(hdr);
- hdr = pdkim_create_header(ctx,1);
+ hdr = pdkim_create_header(ctx->sig,1);
if (hdr == NULL) return PDKIM_ERR_OOM;
#ifdef PDKIM_DEBUG
@@ -748,6 +1097,16 @@
/* -------------------------------------------------------------------------- */
+pdkim_ctx *pdkim_init_verify(void) {
+ pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
+ if (ctx == NULL) return NULL;
+ memset(ctx,0,sizeof(pdkim_ctx));
+ ctx->mode = PDKIM_MODE_VERIFY;
+ return ctx;
+}
+
+
+/* -------------------------------------------------------------------------- */
pdkim_ctx *pdkim_init_sign(char *domain,
char *selector,
char *rsa_privkey) {
@@ -785,7 +1144,6 @@
sha2_starts(&(ctx->sig->sha2_body),0);
return ctx;
-
};
#ifdef PDKIM_DEBUG
Index: pdkim.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.h,v
retrieving revision 1.1.2.2
retrieving revision 1.1.2.3
diff -u -r1.1.2.2 -r1.1.2.3
--- pdkim.h 24 Feb 2009 15:57:55 -0000 1.1.2.2
+++ pdkim.h 26 Feb 2009 16:07:36 -0000 1.1.2.3
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/pdkim/pdkim.h,v 1.1.2.2 2009/02/24 15:57:55 tom Exp $ */
+/* $Cambridge: exim/exim-src/src/pdkim/pdkim.h,v 1.1.2.3 2009/02/26 16:07:36 tom Exp $ */
/* pdkim.h */
#include "sha1.h"
@@ -86,7 +86,7 @@
/* Signature as it appears in a DKIM-Signature header */
typedef struct pdkim_signature {
- /* Bits stored in a DKIM signature header */
+ /* Bits stored in a DKIM signature header ------ */
int version; /* v= */
int algo; /* a= */
int canon_headers; /* c=x/ */
@@ -107,31 +107,22 @@
char *headernames; /* h= */
char *copiedheaders; /* z= */
- /* Public key used to verify this signature.
- (Verification only) */
- pdkim_pubkey pubkey;
-
- /* Private RSA key used to create this signature */
- char *rsa_privkey;
-
- /* Header field names to include in the signature,
- colon separated. When NULL, the recommended defaults
- from RFC 4871 are used. */
- char *sign_headers;
- /* Per-signature helper variables */
+ /* Signing specific ---------------------------- */
+ char *rsa_privkey; /* Private RSA key */
+ char *sign_headers; /* To-be-signed header names */
+
+ /* Verification specific ----------------------- */
+ pdkim_pubkey pubkey; /* Public key used to verify this signature. */
+ int verify_result; /* Verification result */
+ char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */
+ 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;
-
- /* Verification specific */
- int verify_result;
-
- /* Pointer to next signature in list.
- (Always NULL for signing) */
- void *next;
-
} pdkim_signature;
@@ -185,11 +176,14 @@
int pdkim_feed_finish (pdkim_ctx *, char **);
pdkim_str
- *pdkim_create_header (pdkim_ctx *, int);
+ *pdkim_create_header (pdkim_signature *, int);
pdkim_ctx
*pdkim_init_sign (char *, char *, char *);
+pdkim_ctx
+ *pdkim_init_verify (void);
+
int pdkim_set_optional (pdkim_ctx *,
int,
char *, char *,