DKIM: use string-allocate facilities for DNS lookup
[exim.git] / src / src / pdkim / pdkim.c
index 1420b1a795d32944f624bfecd2277f0a24033bc1..679607dbd46d4abcf174023227ff8153bd4dd3a7 100644 (file)
@@ -224,13 +224,13 @@ static void
 pdkim_strtrim(gstring * str)
 {
 uschar * p = str->s;
-uschar * q = p + str->ptr;
+uschar * q;
 
 while (*p == '\t' || *p == ' ')                /* dump the leading whitespace */
   { str->size--; str->ptr--; str->s++; }
 
 while (  str->ptr > 0
-      && (q = str->s + str->ptr - 1),  *q == '\t' || *q == ' '
+      && ((q = str->s + str->ptr - 1),  (*q == '\t' || *q == ' '))
       )
   str->ptr--;                          /* dump trailing whitespace */
 
@@ -250,16 +250,19 @@ pdkim_free_ctx(pdkim_ctx *ctx)
 /* -------------------------------------------------------------------------- */
 /* Matches the name of the passed raw "header" against
    the passed colon-separated "tick", and invalidates
-   the entry in tick. Returns OK or fail-code */
-/*XXX might be safer done using a pdkim_stringlist for "tick" */
+   the entry in tick.  Entries can be prefixed for multi- or over-signing,
+   in which case do not invalidate.
+
+   Returns OK for a match, or fail-code
+*/
 
 static int
 header_name_match(const uschar * header, uschar * tick)
 {
-uschar * hname;
-uschar * lcopy;
-uschar * p;
-uschar * q;
+const uschar * ticklist = tick;
+int sep = ':';
+BOOL multisign;
+uschar * hname, * p, * ele;
 uschar * hcolon = Ustrchr(header, ':');                /* Get header name */
 
 if (!hcolon)
@@ -268,27 +271,22 @@ if (!hcolon)
 /* if we had strncmpic() we wouldn't need this copy */
 hname = string_copyn(header, hcolon-header);
 
-/* Copy tick-off list locally, so we can punch zeroes into it */
-p = lcopy = string_copy(tick);
-
-for (q = Ustrchr(p, ':'); q; q = Ustrchr(p, ':'))
+while (p = US ticklist, ele = string_nextinlist(&ticklist, &sep, NULL, 0))
   {
-  *q = '\0';
-  if (strcmpic(p, hname) == 0)
-    goto found;
-
-  p = q+1;
+  switch (*ele)
+  {
+  case '=': case '+':  multisign = TRUE; ele++; break;
+  default:             multisign = FALSE; break;
   }
 
-if (strcmpic(p, hname) == 0)
-  goto found;
-
+  if (strcmpic(ele, hname) == 0)
+    {
+    if (!multisign)
+      *p = '_';        /* Invalidate this header name instance in tick-off list */
+    return PDKIM_OK;
+    }
+  }
 return PDKIM_FAIL;
-
-found:
-  /* Invalidate header name instance in tick-off list */
-  tick[p-lcopy] = '_';
-  return PDKIM_OK;
 }
 
 
@@ -492,7 +490,12 @@ for (p = raw_hdr; ; p++)
 
     if (c == ';' || c == '\0')
       {
-      if (cur_tag && cur_val)
+      /* We must have both tag and value, and tags must be one char except
+      for the possibility of "bh". */
+
+      if (  cur_tag && cur_val
+        && (cur_tag->ptr == 1 || *cur_tag->s == 'b')
+        )
         {
        (void) string_from_gstring(cur_val);
        pdkim_strtrim(cur_val);
@@ -502,8 +505,14 @@ for (p = raw_hdr; ; p++)
        switch (*cur_tag->s)
          {
          case 'b':
-           pdkim_decode_base64(cur_val->s,
-                           cur_tag->s[1] == 'h' ? &sig->bodyhash : &sig->sighash);
+           switch (cur_tag->s[1])
+             {
+             case '\0': pdkim_decode_base64(cur_val->s, &sig->sighash); break;
+             case 'h':  if (cur_tag->ptr == 2)
+                          pdkim_decode_base64(cur_val->s, &sig->bodyhash);
+                        break;
+             default:   break;
+             }
            break;
          case 'v':
              /* We only support version 1, and that is currently the
@@ -645,7 +654,8 @@ while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0)))
   }
 
 /* Set fallback defaults */
-if (!pub->version    ) pub->version     = string_copy(PDKIM_PUB_RECORD_VERSION);
+if (!pub->version)
+  pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
 else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0)
   {
   DEBUG(D_acl) debug_printf(" Bad v= field\n");
@@ -1296,10 +1306,7 @@ pdkim_pubkey * p;
 
 dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
 
-dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
-memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
-
-if (  ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK 
+if (  !(dns_txt_reply = ctx->dns_txt_callback(CS dns_txt_name))
    || dns_txt_reply[0] == '\0'
    )
   {
@@ -1445,11 +1452,17 @@ for (sig = ctx->sig; sig; sig = sig->next)
        }
       }
 
-    /* Any headers we wanted to sign but were not present must also be listed */
+    /* Any headers we wanted to sign but were not present must also be listed.
+    Ignore elements that have been ticked-off or are marked as never-oversign. */
+
     l = sig->sign_headers;
     while((s = string_nextinlist(&l, &sep, NULL, 0)))
-      if (*s != '_')
+      {
+      if (*s == '+')                   /* skip oversigning marker */
+        s++;
+      if (*s != '_' && *s != '=')
        g = string_append_listele(g, ':', s);
+      }
     sig->headernames = string_from_gstring(g);
 
     /* Create signature header with b= omitted */
@@ -1697,7 +1710,7 @@ return PDKIM_OK;
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT pdkim_ctx *
-pdkim_init_verify(int(*dns_txt_callback)(char *, char *), BOOL dot_stuffing)
+pdkim_init_verify(uschar * (*dns_txt_callback)(char *), BOOL dot_stuffing)
 {
 pdkim_ctx * ctx;
 
@@ -1801,7 +1814,7 @@ return;
 
 void
 pdkim_init_context(pdkim_ctx * ctx, BOOL dot_stuffed,
-  int(*dns_txt_callback)(char *, char *))
+  uschar * (*dns_txt_callback)(char *))
 {
 memset(ctx, 0, sizeof(pdkim_ctx));
 ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;