Various SASL fixes.
[exim.git] / src / src / expand.c
index 06e0eb0ced0d6dd09124c67242aabe00699cb88b..54501de0b92497984038cefbb9c90b7fb281a183 100644 (file)
@@ -1,5 +1,3 @@
-/* $Cambridge: exim/src/src/expand.c,v 1.108 2010/06/07 08:42:15 pdp Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
@@ -15,7 +13,7 @@
 
 /* Recursively called function */
 
-static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL);
+static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL);
 
 #ifdef STAND_ALONE
 #ifndef SUPPORT_CRYPTEQ
@@ -260,6 +258,8 @@ static uschar *cond_table[] = {
   US"gei",
   US"gt",
   US"gti",
+  US"inlist",
+  US"inlisti",
   US"isip",
   US"isip4",
   US"isip6",
@@ -303,6 +303,8 @@ enum {
   ECOND_STR_GEI,
   ECOND_STR_GT,
   ECOND_STR_GTI,
+  ECOND_INLIST,
+  ECOND_INLISTI,
   ECOND_ISIP,
   ECOND_ISIP4,
   ECOND_ISIP6,
@@ -389,6 +391,9 @@ static var_entry var_table[] = {
   { "authenticated_id",    vtype_stringptr,   &authenticated_id },
   { "authenticated_sender",vtype_stringptr,   &authenticated_sender },
   { "authentication_failed",vtype_int,        &authentication_failed },
+#ifdef WITH_CONTENT_SCAN
+  { "av_failed",           vtype_int,         &av_failed },
+#endif
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   { "bmi_alt_location",    vtype_stringptr,   &bmi_alt_location },
   { "bmi_base64_tracker_verdict", vtype_stringptr, &bmi_base64_tracker_verdict },
@@ -606,6 +611,7 @@ static var_entry var_table[] = {
   { "srs_status",          vtype_stringptr,   &srs_status },
 #endif
   { "thisaddress",         vtype_stringptr,   &filter_thisaddress },
+  { "tls_bits",            vtype_int,         &tls_bits },
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
   { "tls_peerdn",          vtype_stringptr,   &tls_peerdn },
@@ -1698,7 +1704,7 @@ for (i = 0; i < n; i++)
     sub[i] = NULL;
     break;
     }
-  sub[i] = expand_string_internal(s+1, TRUE, &s, skipping);
+  sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
   if (sub[i] == NULL) return 3;
   if (*s++ != '}') return 1;
   while (isspace(*s)) s++;
@@ -1770,6 +1776,7 @@ eval_condition(uschar *s, BOOL *yield)
 BOOL testfor = TRUE;
 BOOL tempcond, combined_cond;
 BOOL *subcondptr;
+BOOL sub2_honour_dollar = TRUE;
 int i, rc, cond_type, roffset;
 int num[2];
 struct stat statbuf;
@@ -1902,7 +1909,7 @@ switch(cond_type)
   while (isspace(*s)) s++;
   if (*s != '{') goto COND_FAILED_CURLY_START;
 
-  sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL);
+  sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE);
   if (sub[0] == NULL) return NULL;
   if (*s++ != '}') goto COND_FAILED_CURLY_END;
 
@@ -2015,22 +2022,30 @@ switch(cond_type)
   /* symbolic operators for numeric and string comparison, and a number of
   other operators, all requiring two arguments.
 
+  crypteq:           encrypts plaintext and compares against an encrypted text,
+                       using crypt(), crypt16(), MD5 or SHA-1
+  inlist/inlisti:    checks if first argument is in the list of the second
   match:             does a regular expression match and sets up the numerical
                        variables if it succeeds
   match_address:     matches in an address list
   match_domain:      matches in a domain list
   match_ip:          matches a host list that is restricted to IP addresses
   match_local_part:  matches in a local part list
-  crypteq:           encrypts plaintext and compares against an encrypted text,
-                       using crypt(), crypt16(), MD5 or SHA-1
   */
 
-  case ECOND_MATCH:
   case ECOND_MATCH_ADDRESS:
   case ECOND_MATCH_DOMAIN:
   case ECOND_MATCH_IP:
   case ECOND_MATCH_LOCAL_PART:
+#ifndef EXPAND_LISTMATCH_RHS
+    sub2_honour_dollar = FALSE;
+#endif
+    /* FALLTHROUGH */
+
   case ECOND_CRYPTEQ:
+  case ECOND_INLIST:
+  case ECOND_INLISTI:
+  case ECOND_MATCH:
 
   case ECOND_NUM_L:     /* Numerical comparisons */
   case ECOND_NUM_LE:
@@ -2052,6 +2067,13 @@ switch(cond_type)
 
   for (i = 0; i < 2; i++)
     {
+    /* Sometimes, we don't expand substrings; too many insecure configurations
+    created using match_address{}{} and friends, where the second param
+    includes information from untrustworthy sources. */
+    BOOL honour_dollar = TRUE;
+    if ((i > 0) && !sub2_honour_dollar)
+      honour_dollar = FALSE;
+
     while (isspace(*s)) s++;
     if (*s != '{')
       {
@@ -2060,7 +2082,8 @@ switch(cond_type)
         "after \"%s\"", name);
       return NULL;
       }
-    sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL);
+    sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL,
+        honour_dollar);
     if (sub[i] == NULL) return NULL;
     if (*s++ != '}') goto COND_FAILED_CURLY_END;
 
@@ -2319,6 +2342,7 @@ switch(cond_type)
       }
 
     else   /* {crypt} or {crypt16} and non-{ at start */
+           /* }-for-text-editors */
       {
       int which = 0;
       uschar *coded;
@@ -2365,6 +2389,30 @@ switch(cond_type)
       }
     break;
     #endif  /* SUPPORT_CRYPTEQ */
+
+    case ECOND_INLIST:
+    case ECOND_INLISTI:
+      {
+      int sep = 0;
+      BOOL found = FALSE;
+      uschar *save_iterate_item = iterate_item;
+      int (*compare)(const uschar *, const uschar *);
+
+      if (cond_type == ECOND_INLISTI)
+        compare = strcmpic;
+      else
+        compare = (int (*)(const uschar *, const uschar *)) strcmp;
+
+      while ((iterate_item = string_nextinlist(&sub[1], &sep, NULL, 0)) != NULL)
+        if (compare(sub[0], iterate_item) == 0)
+          {
+          found = TRUE;
+          break;
+          }
+      iterate_item = save_iterate_item;
+      *yield = found;
+      }
+
     }   /* Switch for comparison conditions */
 
   return s;    /* End of comparison conditions */
@@ -2436,7 +2484,7 @@ switch(cond_type)
 
     while (isspace(*s)) s++;
     if (*s++ != '{') goto COND_FAILED_CURLY_START;
-    sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL));
+    sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL), TRUE);
     if (sub[0] == NULL) return NULL;
     if (*s++ != '}') goto COND_FAILED_CURLY_END;
 
@@ -2558,7 +2606,7 @@ switch(cond_type)
        "value \"%s\"", t);
       return NULL;
       }
-    if (yield != NULL) *yield = (boolvalue != 0);
+    if (yield != NULL) *yield = (boolvalue == testfor);
     return s;
     }
 
@@ -2718,7 +2766,7 @@ if (*s++ != '{') goto FAILED_CURLY;
 want this string. Set skipping in the call in the fail case (this will always
 be the case if we were already skipping). */
 
-sub1 = expand_string_internal(s, TRUE, &s, !yes);
+sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE);
 if (sub1 == NULL && (yes || !expand_string_forcedfail)) goto FAILED;
 expand_string_forcedfail = FALSE;
 if (*s++ != '}') goto FAILED_CURLY;
@@ -2743,7 +2791,7 @@ already skipping. */
 while (isspace(*s)) s++;
 if (*s == '{')
   {
-  sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping);
+  sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE);
   if (sub2 == NULL && (!yes || !expand_string_forcedfail)) goto FAILED;
   expand_string_forcedfail = FALSE;
   if (*s++ != '}') goto FAILED_CURLY;
@@ -3106,18 +3154,47 @@ if (*error == NULL)
     int op = *s++;
     int y = eval_op_unary(&s, decimal, error);
     if (*error != NULL) break;
-    if (op == '*') x *= y;
-      else if (op == '/')
+    /* SIGFPE both on div/mod by zero and on INT_MIN / -1, which would give
+     * a value of INT_MAX+1. Note that INT_MIN * -1 gives INT_MIN for me, which
+     * is a bug somewhere in [gcc 4.2.1, FreeBSD, amd64].  In fact, -N*-M where
+     * -N*M is INT_MIN will yielf INT_MIN.
+     * Since we don't support floating point, this is somewhat simpler.
+     * Ideally, we'd return an error, but since we overflow for all other
+     * arithmetic, consistency suggests otherwise, but what's the correct value
+     * to use?  There is none.
+     * The C standard guarantees overflow for unsigned arithmetic but signed
+     * overflow invokes undefined behaviour; in practice, this is overflow
+     * except for converting INT_MIN to INT_MAX+1.  We also can't guarantee
+     * that long/longlong larger than int are available, or we could just work
+     * with larger types.  We should consider whether to guarantee 32bit eval
+     * and 64-bit working variables, with errors returned.  For now ...
+     * So, the only SIGFPEs occur with a non-shrinking div/mod, thus -1; we
+     * can just let the other invalid results occur otherwise, as they have
+     * until now.  For this one case, we can coerce.
+     */
+    if (y == -1 && x == INT_MIN && op != '*')
+      {
+      DEBUG(D_expand)
+        debug_printf("Integer exception dodging: %d%c-1 coerced to %d\n",
+            INT_MIN, op, INT_MAX);
+      x = INT_MAX;
+      continue;
+      }
+    if (op == '*')
+      x *= y;
+    else
+      {
+      if (y == 0)
         {
-        if (y == 0)
-          {
-          *error = US"divide by zero";
-          x = 0;
-          break;
-          }
-        x /= y;
+        *error = (op == '/') ? US"divide by zero" : US"modulo by zero";
+        x = 0;
+        break;
         }
-      else x %= y;
+      if (op == '/')
+        x /= y;
+      else
+        x %= y;
+      }
     }
   }
 *sptr = s;
@@ -3277,6 +3354,8 @@ Arguments:
                  expansion is placed here (typically used with ket_ends)
   skipping       TRUE for recursive calls when the value isn't actually going
                  to be used (to allow for optimisation)
+  honour_dollar  TRUE if $ is to be expanded,
+                 FALSE if it's just another character
 
 Returns:         NULL if expansion fails:
                    expand_string_forcedfail is set TRUE if failure was forced
@@ -3286,7 +3365,7 @@ Returns:         NULL if expansion fails:
 
 static uschar *
 expand_string_internal(uschar *string, BOOL ket_ends, uschar **left,
-  BOOL skipping)
+  BOOL skipping, BOOL honour_dollar)
 {
 int ptr = 0;
 int size = Ustrlen(string)+ 64;
@@ -3342,7 +3421,7 @@ while (*s != 0)
 
   if (ket_ends && *s == '}') break;
 
-  if (*s != '$')
+  if (*s != '$' || !honour_dollar)
     {
     yield = string_cat(yield, &size, &ptr, s++, 1);
     continue;
@@ -3558,7 +3637,7 @@ while (*s != 0)
       while (isspace(*s)) s++;
       if (*s == '{')
         {
-        key = expand_string_internal(s+1, TRUE, &s, skipping);
+        key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
         if (key == NULL) goto EXPAND_FAILED;
         if (*s++ != '}') goto EXPAND_FAILED_CURLY;
         while (isspace(*s)) s++;
@@ -3624,7 +3703,7 @@ while (*s != 0)
       first. */
 
       if (*s != '{') goto EXPAND_FAILED_CURLY;
-      filename = expand_string_internal(s+1, TRUE, &s, skipping);
+      filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
       if (filename == NULL) goto EXPAND_FAILED;
       if (*s++ != '}') goto EXPAND_FAILED_CURLY;
       while (isspace(*s)) s++;
@@ -4293,7 +4372,7 @@ while (*s != 0)
 
       if (*s == '{')
         {
-        if (expand_string_internal(s+1, TRUE, &s, TRUE) == NULL)
+        if (expand_string_internal(s+1, TRUE, &s, TRUE, TRUE) == NULL)
           goto EXPAND_FAILED;
         if (*s++ != '}') goto EXPAND_FAILED_CURLY;
         while (isspace(*s)) s++;
@@ -4308,7 +4387,7 @@ while (*s != 0)
       SOCK_FAIL:
       if (*s != '{') goto EXPAND_FAILED;
       DEBUG(D_any) debug_printf("%s\n", expand_string_message);
-      arg = expand_string_internal(s+1, TRUE, &s, FALSE);
+      arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE);
       if (arg == NULL) goto EXPAND_FAILED;
       yield = string_cat(yield, &size, &ptr, arg, Ustrlen(arg));
       if (*s++ != '}') goto EXPAND_FAILED_CURLY;
@@ -4337,7 +4416,7 @@ while (*s != 0)
 
       while (isspace(*s)) s++;
       if (*s != '{') goto EXPAND_FAILED_CURLY;
-      arg = expand_string_internal(s+1, TRUE, &s, skipping);
+      arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
       if (arg == NULL) goto EXPAND_FAILED;
       while (isspace(*s)) s++;
       if (*s++ != '}') goto EXPAND_FAILED_CURLY;
@@ -4374,13 +4453,24 @@ while (*s != 0)
 
         (void)close(fd_in);
 
+        /* Read the pipe to get the command's output into $value (which is kept
+        in lookup_value). Read during execution, so that if the output exceeds
+        the OS pipe buffer limit, we don't block forever. */
+
+        f = fdopen(fd_out, "rb");
+        sigalrm_seen = FALSE;
+        alarm(60);
+        lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL);
+        alarm(0);
+        (void)fclose(f);
+
         /* Wait for the process to finish, applying the timeout, and inspect its
         return code for serious disasters. Simple non-zero returns are passed on.
         */
 
-        if ((runrc = child_close(pid, 60)) < 0)
+        if (sigalrm_seen == TRUE || (runrc = child_close(pid, 30)) < 0)
           {
-          if (runrc == -256)
+          if (sigalrm_seen == TRUE || runrc == -256)
             {
             expand_string_message = string_sprintf("command timed out");
             killpg(pid, SIGKILL);       /* Kill the whole process group */
@@ -4396,14 +4486,6 @@ while (*s != 0)
 
           goto EXPAND_FAILED;
           }
-
-        /* Read the pipe to get the command's output into $value (which is kept
-        in lookup_value). */
-
-        f = fdopen(fd_out, "rb");
-        lookup_value = NULL;
-        lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL);
-        (void)fclose(f);
         }
 
       /* Process the yes/no strings; $value may be useful in both cases */
@@ -4765,7 +4847,7 @@ while (*s != 0)
         while (isspace(*s)) s++;
         if (*s == '{')
           {
-          sub[i] = expand_string_internal(s+1, TRUE, &s, skipping);
+          sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
           if (sub[i] == NULL) goto EXPAND_FAILED;
           if (*s++ != '}') goto EXPAND_FAILED_CURLY;
 
@@ -4860,7 +4942,7 @@ while (*s != 0)
       while (isspace(*s)) s++;
       if (*s++ != '{') goto EXPAND_FAILED_CURLY;
 
-      list = expand_string_internal(s, TRUE, &s, skipping);
+      list = expand_string_internal(s, TRUE, &s, skipping, TRUE);
       if (list == NULL) goto EXPAND_FAILED;
       if (*s++ != '}') goto EXPAND_FAILED_CURLY;
 
@@ -4868,7 +4950,7 @@ while (*s != 0)
         {
         while (isspace(*s)) s++;
         if (*s++ != '{') goto EXPAND_FAILED_CURLY;
-        temp = expand_string_internal(s, TRUE, &s, skipping);
+        temp = expand_string_internal(s, TRUE, &s, skipping, TRUE);
         if (temp == NULL) goto EXPAND_FAILED;
         lookup_value = temp;
         if (*s++ != '}') goto EXPAND_FAILED_CURLY;
@@ -4892,7 +4974,7 @@ while (*s != 0)
         }
       else
         {
-        temp = expand_string_internal(s, TRUE, &s, TRUE);
+        temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE);
         }
 
       if (temp == NULL)
@@ -4951,7 +5033,7 @@ while (*s != 0)
 
         else
           {
-          temp = expand_string_internal(expr, TRUE, NULL, skipping);
+          temp = expand_string_internal(expr, TRUE, NULL, skipping, TRUE);
           if (temp == NULL)
             {
             iterate_item = save_iterate_item;
@@ -5133,7 +5215,7 @@ while (*s != 0)
     {
     int c;
     uschar *arg = NULL;
-    uschar *sub = expand_string_internal(s+1, TRUE, &s, skipping);
+    uschar *sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE);
     if (sub == NULL) goto EXPAND_FAILED;
     s++;
 
@@ -5208,7 +5290,7 @@ while (*s != 0)
 
       case EOP_EXPAND:
         {
-        uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping);
+        uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE);
         if (expanded == NULL)
           {
           expand_string_message =
@@ -5976,7 +6058,7 @@ expand_string(uschar *string)
 search_find_defer = FALSE;
 malformed_header = FALSE;
 return (Ustrpbrk(string, "$\\") == NULL)? string :
-  expand_string_internal(string, FALSE, NULL, FALSE);
+  expand_string_internal(string, FALSE, NULL, FALSE, TRUE);
 }