-/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.1.2.8 2009/03/17 16:20:13 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.14 2009/04/30 19:15:48 tom Exp $ */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <strings.h>
#include <ctype.h>
-#include <unistd.h>
#include "pdkim.h"
#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_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
"Message-ID:To:Cc:MIME-Version:Content-Type:"\
"Content-Transfer-Encoding:Content-ID:"\
"List-Id:List-Help:List-Unsubscribe:"\
"List-Subscribe:List-Post:List-Owner:List-Archive"
-
+/* -------------------------------------------------------------------------- */
struct pdkim_stringlist {
char *value;
void *next;
unsigned int allocated;
};
-
-
/* -------------------------------------------------------------------------- */
/* A bunch of list constants */
char *pdkim_querymethods[] = {
for (i=0;i<len;i++) {
int c = p[i];
- fprintf(stream,"%02x ",c);
+ fprintf(stream,"%02x",c);
}
if (lf)
fputc('\n',stream);
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->hnames_check != NULL) free(sig->hnames_check);
if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey);
/* -------------------------------------------------------------------------- */
-void pdkim_free_ctx(pdkim_ctx *ctx) {
+DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) {
if (ctx) {
pdkim_free_sig(ctx->sig);
pdkim_strfree(ctx->cur_header);
"start". Returns the position of the header name in
the list. */
int header_name_match(char *header,
- char *list,
- int start) {
+ char *tick,
+ int do_tick) {
char *hname;
char *lcopy;
char *p;
char *q;
- int pos = 0;
int rc = PDKIM_FAIL;
+
+ /* Get header name */
char *hcolon = strchr(header,':');
if (hcolon == NULL) return rc; /* This isn't a header */
hname = malloc((hcolon-header)+1);
if (hname == NULL) return PDKIM_ERR_OOM;
memset(hname,0,(hcolon-header)+1);
strncpy(hname,header,(hcolon-header));
- lcopy = strdup(list);
+
+ /* Copy tick-off list locally, so we can punch zeroes into it */
+ lcopy = strdup(tick);
if (lcopy == NULL) {
free(hname);
return PDKIM_ERR_OOM;
q = strchr(p,':');
while (q != NULL) {
*q = '\0';
- if (pos >= start) {
- if (strcasecmp(p,hname) == 0) {
- rc = pos;
- goto BAIL;
- }
+
+ if (strcasecmp(p,hname) == 0) {
+ rc = PDKIM_OK;
+ /* Invalidate header name instance in tick-off list */
+ if (do_tick) tick[p-lcopy] = '_';
+ goto BAIL;
}
+
p = q+1;
q = strchr(p,':');
- pos++;
}
- if (pos >= start) {
- if (strcasecmp(p,hname) == 0)
- rc = pos;
+
+ if (strcasecmp(p,hname) == 0) {
+ rc = PDKIM_OK;
+ /* Invalidate header name instance in tick-off list */
+ if (do_tick) tick[p-lcopy] = '_';
}
+
BAIL:
free(hname);
free(lcopy);
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
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 = strdup(cur_val->str);
}
NEXT_CHAR:
+ if (*p == '\0') break;
if (!in_b_val) {
*q = *p;
return NULL;
}
+ /* Copy header list to 'tick-off' header list */
+ sig->hnames_check = strdup(sig->headernames);
+
*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,
p = raw_record;
- while (*p != '\0') {
+ while (1) {
/* Ignore FWS */
if ( (*p == '\r') || (*p == '\n') )
if ( (*p == '\r') || (*p == '\n') )
goto NEXT_CHAR;
- if (*p == ';') {
+ if ( (*p == ';') || (*p == '\0') ) {
if (cur_tag->len > 0) {
pdkim_strtrim(cur_val);
#ifdef PDKIM_DEBUG
}
NEXT_CHAR:
+ if (*p == '\0') break;
p++;
}
}
/* 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 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 {
/* Traverse all signatures */
while (sig != NULL) {
+ pdkim_stringlist *list;
/* SIGNING -------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN) {
if (header_name_match(ctx->cur_header->str,
sig->sign_headers?
sig->sign_headers:
- PDKIM_DEFAULT_SIGN_HEADERS, 0) < 0) goto NEXT_SIG;
+ PDKIM_DEFAULT_SIGN_HEADERS, 0) != PDKIM_OK) goto NEXT_SIG;
}
/* VERIFICATION --------------------------------------------------------- */
else {
- int rc = header_name_match(ctx->cur_header->str,
- sig->headernames,
- sig->headernames_pos);
- /* Header is not included or out-of-sequence */
- if (rc < 0) goto NEXT_SIG;
- sig->headernames_pos = rc;
+ /* Header is not included or all instances were already 'ticked off' */
+ if (header_name_match(ctx->cur_header->str,
+ sig->hnames_check, 1) != PDKIM_OK) goto NEXT_SIG;
}
/* 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) {
/* -------------------------------------------------------------------------- */
#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;
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, pdkim_signature **return_signatures) {
+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)
+ if (sig->canon_headers == 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 */
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);
/* -------------------------------------------------------------------------- */
-pdkim_ctx *pdkim_init_verify(int input_mode,
+DLLEXPORT pdkim_ctx *pdkim_init_verify(int input_mode,
int(*dns_txt_callback)(char *, char *)
) {
pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
/* -------------------------------------------------------------------------- */
-pdkim_ctx *pdkim_init_sign(int input_mode,
+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;
return NULL;
}
- pdkim_signature *sig = malloc(sizeof(pdkim_signature));
+ 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;
#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,
+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) {