wip
authorTom Kistner <tom@duncanthrax.net>
Tue, 17 Mar 2009 12:57:37 +0000 (12:57 +0000)
committerTom Kistner <tom@duncanthrax.net>
Tue, 17 Mar 2009 12:57:37 +0000 (12:57 +0000)
src/src/pdkim/pdkim.c
src/src/pdkim/pdkim.h
src/src/pdkim/rsa.c
src/src/pdkim/rsa.h

index 836df263898ee8c9914919496416eecdd11a8671..0fefd9acecf311260ec1bdd49e2254662eacd2cd 100644 (file)
@@ -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);
   }
index 77f74560bc25059325b93e5715fc94298ba80d69..8463312b691a90306619ec7149e6ee232bddca96 100644 (file)
@@ -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:"\
 
 
 /* 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
 #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 *);
 
index 99c703d95f44d45ea5f28a416fdc6dde84b6e18c..06068aedea53fa1c6c74f07f0cfe688e1a0c6e45 100644 (file)
@@ -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
  */
index 05c903ece21ef8b28e3094f540d78e73e8fcdb18..a91a5ec5374f505da34bdb936a4db7951b5675e7 100644 (file)
@@ -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 );