[exim-cvs] cvs commit: exim/exim-src/src/pdkim pdkim.c pdkim…

Top Page
Delete this message
Reply to this message
Author: Tom Kistner
Date:  
To: exim-cvs
Subject: [exim-cvs] cvs commit: exim/exim-src/src/pdkim pdkim.c pdkim.h rsa.c rsa.h
tom 2009/03/17 12:57:37 GMT

  Modified files:        (Branch: DEVEL_PDKIM)
    exim-src/src/pdkim   pdkim.c pdkim.h rsa.c rsa.h 
  Log:
  wip


  Revision  Changes    Path
  1.1.2.6   +267 -18   exim/exim-src/src/pdkim/pdkim.c
  1.1.2.5   +147 -62   exim/exim-src/src/pdkim/pdkim.h
  1.1.2.2   +59 -0     exim/exim-src/src/pdkim/rsa.c
  1.1.2.2   +2 -0      exim/exim-src/src/pdkim/rsa.h


  Index: pdkim.c
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.c,v
  retrieving revision 1.1.2.5
  retrieving revision 1.1.2.6
  diff -u -r1.1.2.5 -r1.1.2.6
  --- pdkim.c    27 Feb 2009 17:04:20 -0000    1.1.2.5
  +++ pdkim.c    17 Mar 2009 12:57:37 -0000    1.1.2.6
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/pdkim.c,v 1.1.2.5 2009/02/27 17:04:20 tom Exp $ */
  +/* $Cambridge: exim/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 @@
     "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 @@
                 }
               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 @@
                 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 @@
     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_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 @@
       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 @@
       }
     }


  +  BAIL:
     pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
     return PDKIM_OK;
   };
  @@ -876,8 +1002,9 @@
           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 @@
         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 @@
       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 @@
       }
       /* 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 @@



   /* -------------------------------------------------------------------------- */
  -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 @@
     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 @@


   /* -------------------------------------------------------------------------- */
   int pdkim_set_optional(pdkim_ctx *ctx,
  -                       int input_mode,
                          char *sign_headers,
                          char *identity,
                          int canon_headers,
  @@ -1256,7 +1491,6 @@
       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 @@
   };



  +/* -------------------------------------------------------------------------- */
  +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 @@
       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: pdkim.h
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/pdkim.h,v
  retrieving revision 1.1.2.4
  retrieving revision 1.1.2.5
  diff -u -r1.1.2.4 -r1.1.2.5
  --- pdkim.h    27 Feb 2009 17:04:20 -0000    1.1.2.4
  +++ pdkim.h    17 Mar 2009 12:57:37 -0000    1.1.2.5
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/pdkim.h,v 1.1.2.4 2009/02/27 17:04:20 tom Exp $ */
  +/* $Cambridge: exim/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 @@
   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_CANON_SIMPLE   0
  -#define PDKIM_CANON_RELAXED  1
  -/* extern char *pdkim_canons[]; */
  +#define PDKIM_ALGO_RSA_SHA256     0
  +#define PDKIM_ALGO_RSA_SHA1       1
  +
  +#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 @@
     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=   */
  -
  -  char *selector;                 /* s=   */
  -  char *domain;                   /* d=   */
  -  char *identity;                 /* i=   */
  -
  -  unsigned long created;          /* t=   */
  -  unsigned long expires;          /* x=   */
  -  unsigned long bodylength;       /* l=   */
  +  /* Bits stored in a DKIM signature header --------------------------- */
  +
  +  /* (v=) The version, as an integer. Currently, always "1" */
  +  int version;
  +
  +  /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256
  +     or PDKIM_ALGO_RSA_SHA1 */
  +  int algo;
  +
  +  /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
  +     or PDKIM_CANON_RELAXED */
  +  int canon_headers;
  +
  +  /* (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;


  -  char *headernames;              /* h=   */
  -  char *copiedheaders;            /* z=   */
  +  /* (h=) Colon-separated list of header names that are included in the
  +     signature */
  +  char *headernames;


  -  char *sigdata;                  /* b=   */
  -  char *bodyhash;                 /* bh=  */
  +  /* (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 */
  -
  -  /* 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 */
  +  /* The main verification status. One of:
  +
  +     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.


  -  /* Per-signature helper variables -------------- */
  +        PDKIM_VERIFY_INVALID_PUBKEY_PARSING
  +          (Syntax) error while parsing the retrieved public key record.
  +
  +
  +     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 @@
     /* 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 @@
     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 @@
   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: rsa.c
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/rsa.c,v
  retrieving revision 1.1.2.1
  retrieving revision 1.1.2.2
  diff -u -r1.1.2.1 -r1.1.2.2
  --- rsa.c    24 Feb 2009 13:13:47 -0000    1.1.2.1
  +++ rsa.c    17 Mar 2009 12:57:37 -0000    1.1.2.2
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/rsa.c,v 1.1.2.1 2009/02/24 13:13:47 tom Exp $ */
  +/* $Cambridge: exim/exim-src/src/pdkim/rsa.c,v 1.1.2.2 2009/03/17 12:57:37 tom Exp $ */
   /*
    *  The RSA public-key cryptosystem
    *
  @@ -603,6 +603,65 @@



   /*
  + * 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
    */
   int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen,


  Index: rsa.h
  ===================================================================
  RCS file: /home/cvs/exim/exim-src/src/pdkim/Attic/rsa.h,v
  retrieving revision 1.1.2.1
  retrieving revision 1.1.2.2
  diff -u -r1.1.2.1 -r1.1.2.2
  --- rsa.h    24 Feb 2009 13:13:47 -0000    1.1.2.1
  +++ rsa.h    17 Mar 2009 12:57:37 -0000    1.1.2.2
  @@ -1,4 +1,4 @@
  -/* $Cambridge: exim/exim-src/src/pdkim/rsa.h,v 1.1.2.1 2009/02/24 13:13:47 tom Exp $ */
  +/* $Cambridge: exim/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 @@
    */
   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 );