Expansions: add ${sha3:<string>} item
[exim.git] / src / src / expand.c
index 5189cdcfed5af230a5f80d60467f1271c1ec807c..1484a30272275b11ec14a718a0566c8f7cd18aef 100644 (file)
@@ -231,6 +231,7 @@ static uschar *op_table_main[] = {
   US"s",
   US"sha1",
   US"sha256",
+  US"sha3",
   US"stat",
   US"str2b64",
   US"strlen",
@@ -273,6 +274,7 @@ enum {
   EOP_S,
   EOP_SHA1,
   EOP_SHA256,
+  EOP_SHA3,
   EOP_STAT,
   EOP_STR2B64,
   EOP_STRLEN,
@@ -2506,7 +2508,6 @@ switch(cond_type)
     checking for them individually. */
 
     if (!isalpha(name[0]) && yield != NULL)
-      {
       if (sub[i][0] == 0)
         {
         num[i] = 0;
@@ -2518,7 +2519,6 @@ switch(cond_type)
         num[i] = expanded_string_integer(sub[i], FALSE);
         if (expand_string_message != NULL) return NULL;
         }
-      }
     }
 
   /* Result not required */
@@ -2686,7 +2686,7 @@ switch(cond_type)
       uschar digest[16];
 
       md5_start(&base);
-      md5_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest);
+      md5_end(&base, sub[0], Ustrlen(sub[0]), digest);
 
       /* If the length that we are comparing against is 24, the MD5 digest
       is expressed as a base64 string. This is the way LDAP does it. However,
@@ -2695,7 +2695,7 @@ switch(cond_type)
 
       if (sublen == 24)
         {
-        uschar *coded = b64encode((uschar *)digest, 16);
+        uschar *coded = b64encode(digest, 16);
         DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n"
           "  subject=%s\n  crypted=%s\n", coded, sub[1]+5);
         tempcond = (Ustrcmp(coded, sub[1]+5) == 0);
@@ -2721,11 +2721,11 @@ switch(cond_type)
     else if (strncmpic(sub[1], US"{sha1}", 6) == 0)
       {
       int sublen = Ustrlen(sub[1]+6);
-      sha1 base;
+      hctx h;
       uschar digest[20];
 
-      sha1_start(&base);
-      sha1_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest);
+      sha1_start(&h);
+      sha1_end(&h, sub[0], Ustrlen(sub[0]), digest);
 
       /* If the length that we are comparing against is 28, assume the SHA1
       digest is expressed as a base64 string. If the length is 40, assume a
@@ -2733,7 +2733,7 @@ switch(cond_type)
 
       if (sublen == 28)
         {
-        uschar *coded = b64encode((uschar *)digest, 20);
+        uschar *coded = b64encode(digest, 20);
         DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n"
           "  subject=%s\n  crypted=%s\n", coded, sub[1]+6);
         tempcond = (Ustrcmp(coded, sub[1]+6) == 0);
@@ -3338,7 +3338,7 @@ chash_start(int type, void *base)
 if (type == HMAC_MD5)
   md5_start((md5 *)base);
 else
-  sha1_start((sha1 *)base);
+  sha1_start((hctx *)base);
 }
 
 static void
@@ -3347,7 +3347,7 @@ chash_mid(int type, void *base, uschar *string)
 if (type == HMAC_MD5)
   md5_mid((md5 *)base, string);
 else
-  sha1_mid((sha1 *)base, string);
+  sha1_mid((hctx *)base, string);
 }
 
 static void
@@ -3356,7 +3356,7 @@ chash_end(int type, void *base, uschar *string, int length, uschar *digest)
 if (type == HMAC_MD5)
   md5_end((md5 *)base, string, length, digest);
 else
-  sha1_end((sha1 *)base, string, length, digest);
+  sha1_end((hctx *)base, string, length, digest);
 }
 
 
@@ -3415,8 +3415,7 @@ prvs_hmac_sha1(uschar *address, uschar *key, uschar *key_num, uschar *daystamp)
 {
 uschar *hash_source, *p;
 int size = 0,offset = 0,i;
-sha1 sha1_base;
-void *use_base = &sha1_base;
+hctx h;
 uschar innerhash[20];
 uschar finalhash[20];
 uschar innerkey[64];
@@ -3445,13 +3444,13 @@ for (i = 0; i < Ustrlen(key); i++)
   outerkey[i] ^= key[i];
   }
 
-chash_start(HMAC_SHA1, use_base);
-chash_mid(HMAC_SHA1, use_base, innerkey);
-chash_end(HMAC_SHA1, use_base, hash_source, offset, innerhash);
+chash_start(HMAC_SHA1, &h);
+chash_mid(HMAC_SHA1, &h, innerkey);
+chash_end(HMAC_SHA1, &h, hash_source, offset, innerhash);
 
-chash_start(HMAC_SHA1, use_base);
-chash_mid(HMAC_SHA1, use_base, outerkey);
-chash_end(HMAC_SHA1, use_base, innerhash, 20, finalhash);
+chash_start(HMAC_SHA1, &h);
+chash_mid(HMAC_SHA1, &h, outerkey);
+chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash);
 
 p = finalhash_hex;
 for (i = 0; i < 3; i++)
@@ -3851,8 +3850,8 @@ expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left,
 {
 int ptr = 0;
 int size = Ustrlen(string)+ 64;
-int item_type;
 uschar *yield = store_get(size);
+int item_type;
 const uschar *s = string;
 uschar *save_expand_nstring[EXPAND_MAXN+1];
 int save_expand_nlength[EXPAND_MAXN+1];
@@ -5144,7 +5143,7 @@ while (*s != 0)
       {
       uschar *sub[3];
       md5 md5_base;
-      sha1 sha1_base;
+      hctx sha1_ctx;
       void *use_base;
       int type, i;
       int hashlen;      /* Number of octets for the hash algorithm's output */
@@ -5176,7 +5175,7 @@ while (*s != 0)
       else if (Ustrcmp(sub[0], "sha1") == 0)
         {
         type = HMAC_SHA1;
-        use_base = &sha1_base;
+        use_base = &sha1_ctx;
         hashlen = 20;
         hashblocklen = 64;
         }
@@ -5359,7 +5358,7 @@ while (*s != 0)
     case EITEM_EXTRACT:
       {
       int i;
-      int j = 2;
+      int j;
       int field_number = 1;
       BOOL field_number_set = FALSE;
       uschar *save_lookup_value = lookup_value;
@@ -5369,12 +5368,12 @@ while (*s != 0)
 
       /* While skipping we cannot rely on the data for expansions being
       available (eg. $item) hence cannot decide on numeric vs. keyed.
-      Just read as many arguments as there are. */
+      Read a maximum of 5 arguments (inclding the yes/no) */
 
       if (skipping)
        {
         while (isspace(*s)) s++;
-        while (*s == '{')
+        for (j = 5; j > 0 && *s == '{'; j--)
          {
           if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
            goto EXPAND_FAILED;                                 /*{*/
@@ -5385,6 +5384,13 @@ while (*s != 0)
            }
          while (isspace(*s)) s++;
          }
+       if (  Ustrncmp(s, "fail", 4) == 0
+          && (s[4] == '}' || s[4] == ' ' || s[4] == '\t' || !s[4])
+          )
+         {
+         s += 4;
+         while (isspace(*s)) s++;
+         }
        if (*s != '}')
          {
          expand_string_message = US"missing '}' closing extract";
@@ -5392,7 +5398,7 @@ while (*s != 0)
          }
        }
 
-      else for (i = 0; i < j; i++) /* Read the proper number of arguments */
+      else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
         {
         while (isspace(*s)) s++;
         if (*s == '{')                                                 /*}*/
@@ -5402,7 +5408,7 @@ while (*s != 0)
           if (*s++ != '}')
            {
            expand_string_message = string_sprintf(
-             US"missing '}' closing arg %d of extract", i+1);
+             "missing '}' closing arg %d of extract", i+1);
            goto EXPAND_FAILED_CURLY;
            }
 
@@ -5448,7 +5454,7 @@ while (*s != 0)
         else
          {
          expand_string_message = string_sprintf(
-           US"missing '{' for arg %d of extract", i+1);
+           "missing '{' for arg %d of extract", i+1);
          goto EXPAND_FAILED_CURLY;
          }
         }
@@ -5505,7 +5511,7 @@ while (*s != 0)
         if (*s != '{')                                 /*}*/
          {
          expand_string_message = string_sprintf(
-           US"missing '{' for arg %d of listextract", i+1);
+           "missing '{' for arg %d of listextract", i+1);
          goto EXPAND_FAILED_CURLY;
          }
 
@@ -5514,7 +5520,7 @@ while (*s != 0)
        if (*s++ != '}')
          {
          expand_string_message = string_sprintf(
-           US"missing '}' closing arg %d of listextract", i+1);
+           "missing '}' closing arg %d of listextract", i+1);
          goto EXPAND_FAILED_CURLY;
          }
 
@@ -6351,29 +6357,78 @@ while (*s != 0)
        else
 #endif
          {
-         sha1 base;
+         hctx h;
          uschar digest[20];
          int j;
          char st[41];
-         sha1_start(&base);
-         sha1_end(&base, sub, Ustrlen(sub), digest);
+         sha1_start(&h);
+         sha1_end(&h, sub, Ustrlen(sub), digest);
          for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]);
-         yield = string_cat(yield, &size, &ptr, US st);
+         yield = string_catn(yield, &size, &ptr, US st, 40);
          }
         continue;
 
       case EOP_SHA256:
-#ifdef SUPPORT_TLS
+#ifdef EXIM_HAVE_SHA2
        if (vp && *(void **)vp->value)
          {
          uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value);
          yield = string_cat(yield, &size, &ptr, cp);
          }
        else
+         {
+         hctx h;
+         blob b;
+         char st[3];
+
+         exim_sha_init(&h, HASH_SHA256);
+         exim_sha_update(&h, sub, Ustrlen(sub));
+         exim_sha_finish(&h, &b);
+         while (b.len-- > 0)
+           {
+           sprintf(st, "%02X", *b.data++);
+           yield = string_catn(yield, &size, &ptr, US st, 2);
+           }
+         }
+#else
+         expand_string_message = US"sha256 only supported with TLS";
 #endif
-         expand_string_message = US"sha256 only supported for certificates";
         continue;
 
+      case EOP_SHA3:
+#ifdef EXIM_HAVE_SHA3
+       {
+       hctx h;
+       blob b;
+       char st[3];
+       hashmethod m = !arg ? HASH_SHA3_256
+         : Ustrcmp(arg, "224") == 0 ? HASH_SHA3_224
+         : Ustrcmp(arg, "256") == 0 ? HASH_SHA3_256
+         : Ustrcmp(arg, "384") == 0 ? HASH_SHA3_384
+         : Ustrcmp(arg, "512") == 0 ? HASH_SHA3_512
+         : HASH_BADTYPE;
+
+       if (m == HASH_BADTYPE)
+         {
+         expand_string_message = US"unrecognised sha3 variant";
+         goto EXPAND_FAILED;
+         }
+
+       exim_sha_init(&h, m);
+       exim_sha_update(&h, sub, Ustrlen(sub));
+       exim_sha_finish(&h, &b);
+       while (b.len-- > 0)
+         {
+         sprintf(st, "%02X", *b.data++);
+         yield = string_catn(yield, &size, &ptr, US st, 2);
+         }
+       }
+        continue;
+#else
+       expand_string_message = US"sha3 only supported with GnuTLS 3.5.0 +";
+       goto EXPAND_FAILED;
+#endif
+
       /* Convert hex encoding to base64 encoding */
 
       case EOP_HEX2B64:
@@ -7624,6 +7679,29 @@ return OK;
 
 
 
+/* Avoid potentially exposing a password in a string about to be logged */
+
+uschar *
+expand_hide_passwords(uschar * s)
+{
+return (  (  Ustrstr(s, "failed to expand") != NULL
+         || Ustrstr(s, "expansion of ")    != NULL
+         ) 
+       && (  Ustrstr(s, "mysql")   != NULL
+         || Ustrstr(s, "pgsql")   != NULL
+         || Ustrstr(s, "redis")   != NULL
+         || Ustrstr(s, "sqlite")  != NULL
+         || Ustrstr(s, "ldap:")   != NULL
+         || Ustrstr(s, "ldaps:")  != NULL
+         || Ustrstr(s, "ldapi:")  != NULL
+         || Ustrstr(s, "ldapdn:") != NULL
+         || Ustrstr(s, "ldapm:")  != NULL
+       )  ) 
+  ? US"Temporary internal error" : s;
+}
+
+
+
 
 /*************************************************
 **************************************************