Fix taint trap in parse_fix_phrase(). Bug 2617
[exim.git] / src / src / acl.c
index 24716f0d10da82133641f471d7fc5d25bdaeb023..185a39d5da84e2c66dc2f2f7ee1b8d108cf97183 100644 (file)
 
 #define CALLOUT_TIMEOUT_DEFAULT 30
 
+/* Default quota cache TTLs */
+
+#define QUOTA_POS_DEFAULT (5*60)
+#define QUOTA_NEG_DEFAULT (60*60)
+
+
 /* ACL verb codes - keep in step with the table of verbs that follows */
 
 enum { ACL_ACCEPT, ACL_DEFER, ACL_DENY, ACL_DISCARD, ACL_DROP, ACL_REQUIRE,
@@ -1180,8 +1186,6 @@ acl_verify_reverse(uschar **user_msgptr, uschar **log_msgptr)
 {
 int rc;
 
-user_msgptr = user_msgptr;  /* stop compiler warning */
-
 /* Previous success */
 
 if (sender_host_name != NULL) return OK;
@@ -1511,14 +1515,14 @@ static verify_type_t verify_type_list[] = {
     { US"certificate",         VERIFY_CERT,            (unsigned)~0,   TRUE,  0 },
     { US"helo",                        VERIFY_HELO,            (unsigned)~0,   TRUE,  0 },
     { US"csa",                 VERIFY_CSA,             (unsigned)~0,   FALSE, 0 },
-    { US"header_syntax",       VERIFY_HDR_SYNTAX,      ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 },
-    { US"not_blind",           VERIFY_NOT_BLIND,       ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 },
-    { US"header_sender",       VERIFY_HDR_SNDR,        ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 },
+    { US"header_syntax",       VERIFY_HDR_SYNTAX,      ACL_BITS_HAVEDATA, TRUE, 0 },
+    { US"not_blind",           VERIFY_NOT_BLIND,       ACL_BITS_HAVEDATA, FALSE, 0 },
+    { US"header_sender",       VERIFY_HDR_SNDR,        ACL_BITS_HAVEDATA, FALSE, 0 },
     { US"sender",              VERIFY_SNDR,            ACL_BIT_MAIL | ACL_BIT_RCPT
                        | ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP,
                                                                                FALSE, 6 },
     { US"recipient",           VERIFY_RCPT,            ACL_BIT_RCPT,   FALSE, 0 },
-    { US"header_names_ascii",  VERIFY_HDR_NAMES_ASCII, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 },
+    { US"header_names_ascii",  VERIFY_HDR_NAMES_ASCII, ACL_BITS_HAVEDATA, TRUE, 0 },
 #ifdef EXPERIMENTAL_ARC
     { US"arc",                 VERIFY_ARC,             ACL_BIT_DATA,   FALSE , 0 },
 #endif
@@ -1556,6 +1560,20 @@ static callout_opt_t callout_opt_list[] = {
 
 
 
+static int
+v_period(const uschar * s, const uschar * arg, uschar ** log_msgptr)
+{
+int period;
+if ((period = readconf_readtime(s, 0, FALSE)) < 0)
+  {
+  *log_msgptr = string_sprintf("bad time value in ACL condition "
+    "\"verify %s\"", arg);
+  }
+return period;
+}
+
+
+
 /* This function implements the "verify" condition. It is called when
 encountered in any ACL, because some tests are almost always permitted. Some
 just don't make sense, and always fail (for example, an attempt to test a host
@@ -1590,6 +1608,8 @@ BOOL defer_ok = FALSE;
 BOOL callout_defer_ok = FALSE;
 BOOL no_details = FALSE;
 BOOL success_on_redirect = FALSE;
+BOOL quota = FALSE;
+int quota_pos_cache = QUOTA_POS_DEFAULT, quota_neg_cache = QUOTA_NEG_DEFAULT;
 address_item *sender_vaddr = NULL;
 uschar *verify_sender_address = NULL;
 uschar *pm_mailfrom = NULL;
@@ -1767,7 +1787,7 @@ switch(vp->value)
 /* Remaining items are optional; they apply to sender and recipient
 verification, including "header sender" verification. */
 
-while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+while ((ss = string_nextinlist(&list, &sep, NULL, 0)))
   {
   if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE;
   else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE;
@@ -1823,12 +1843,8 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
               }
             while (isspace(*opt)) opt++;
            }
-         if (op->timeval && (period = readconf_readtime(opt, 0, FALSE)) < 0)
-           {
-           *log_msgptr = string_sprintf("bad time value in ACL condition "
-             "\"verify %s\"", arg);
+         if (op->timeval && (period = v_period(opt, arg, log_msgptr)) < 0)
            return ERROR;
-           }
 
          switch(op->value)
            {
@@ -1861,6 +1877,38 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
       }
     }
 
+  /* The quota option has sub-options, comma-separated */
+
+  else if (strncmpic(ss, US"quota", 5) == 0)
+    {
+    quota = TRUE;
+    if (*(ss += 5))
+      {
+      while (isspace(*ss)) ss++;
+      if (*ss++ == '=')
+        {
+       const uschar * sublist = ss;
+        int optsep = ',';
+       int period;
+
+        while (isspace(*sublist)) sublist++;
+        for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); )
+         if (Ustrncmp(opt, "cachepos=", 9) == 0)
+           if ((period = v_period(opt += 9, arg, log_msgptr)) < 0)
+             return ERROR;
+           else
+             quota_pos_cache = period;
+         else if (Ustrncmp(opt, "cacheneg=", 9) == 0)
+           if ((period = v_period(opt += 9, arg, log_msgptr)) < 0)
+             return ERROR;
+           else
+             quota_neg_cache = period;
+         else if (Ustrcmp(opt, "no_cache") == 0)
+           quota_pos_cache = quota_neg_cache = 0;
+       }
+      }
+    }
+
   /* Option not recognized */
 
   else
@@ -1879,6 +1927,31 @@ if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster)) ==
   return ERROR;
   }
 
+/* Handle quota verification */
+if (quota)
+  {
+  if (vp->value != VERIFY_RCPT)
+    {
+    *log_msgptr = US"can only verify quota of recipient";
+    return ERROR;
+    }
+
+  if ((rc = verify_quota_call(addr->address,
+             quota_pos_cache, quota_neg_cache, log_msgptr)) != OK)
+    {
+    *basic_errno = errno;
+    if (smtp_return_error_details)
+      {
+      if (!*user_msgptr && *log_msgptr)
+        *user_msgptr = string_sprintf("Rejected after %s: %s",
+           smtp_names[smtp_connection_had[smtp_ch_index-1]], *log_msgptr);
+      if (rc == DEFER) f.acl_temp_details = TRUE;
+      }
+    }
+
+  return rc;
+  }
+
 /* Handle sender-in-header verification. Default the user message to the log
 message if giving out verification details. */
 
@@ -3195,8 +3268,7 @@ for (; cb; cb = cb->next)
              {
              const uschar *pp = p + 6;
              while (*pp) pp++;
-             submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
-               big_buffer, big_buffer_size));
+             submission_name = parse_fix_phrase(p+6, pp-p-6);
              p = pp;
              }
            else break;
@@ -3260,37 +3332,41 @@ for (; cb; cb = cb->next)
            the case where both sides handle prdr and this-node prdr acl
            is "accept" */
            ignored = US"PRDR active";
+         else if (f.deliver_freeze)
+           ignored = US"frozen";
+         else if (f.queue_only_policy)
+           ignored = US"queue-only";
+         else if (fake_response == FAIL)
+           ignored = US"fakereject";
+         else if (rcpt_count != 1)
+           ignored = US"nonfirst rcpt";
+         else if (cutthrough.delivery)
+           ignored = US"repeated";
+         else if (cutthrough.callout_hold_only)
+           {
+           DEBUG(D_acl)
+             debug_printf_indent(" cutthrough request upgrades callout hold\n");
+           cutthrough.callout_hold_only = FALSE;
+           cutthrough.delivery = TRUE; /* control accepted */
+           }
          else
            {
-           if (f.deliver_freeze)
-             ignored = US"frozen";
-           else if (f.queue_only_policy)
-             ignored = US"queue-only";
-           else if (fake_response == FAIL)
-             ignored = US"fakereject";
-           else
+           cutthrough.delivery = TRUE; /* control accepted */
+           while (*p == '/')
              {
-             if (rcpt_count == 1)
+             const uschar * pp = p+1;
+             if (Ustrncmp(pp, "defer=", 6) == 0)
                {
-               cutthrough.delivery = TRUE;     /* control accepted */
-               while (*p == '/')
-                 {
-                 const uschar * pp = p+1;
-                 if (Ustrncmp(pp, "defer=", 6) == 0)
-                   {
-                   pp += 6;
-                   if (Ustrncmp(pp, "pass", 4) == 0) cutthrough.defer_pass = TRUE;
-                   /* else if (Ustrncmp(pp, "spool") == 0) ;   default */
-                   }
-                 else
-                   while (*pp && *pp != '/') pp++;
-                 p = pp;
-                 }
+               pp += 6;
+               if (Ustrncmp(pp, "pass", 4) == 0) cutthrough.defer_pass = TRUE;
+               /* else if (Ustrncmp(pp, "spool") == 0) ;       default */
                }
              else
-               ignored = US"nonfirst rcpt";
+               while (*pp && *pp != '/') pp++;
+             p = pp;
              }
            }
+
          DEBUG(D_acl) if (ignored)
            debug_printf(" cutthrough request ignored on %s item\n", ignored);
          }
@@ -3345,11 +3421,11 @@ for (; cb; cb = cb->next)
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+      uschar *ss = string_nextinlist(&list, &sep, NULL, 0);
       /* Run the dcc backend. */
       rc = dcc_process(&ss);
       /* Modify return code based upon the existence of options. */
-      while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+      while ((ss = string_nextinlist(&list, &sep, NULL, 0)))
         if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
           rc = FAIL;   /* FAIL so that the message is passed to the next ACL */
       }
@@ -3510,7 +3586,7 @@ for (; cb; cb = cb->next)
       int sep = 0;
       const uschar *s = arg;
       uschar * ss;
-      while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)))
+      while ((ss = string_nextinlist(&s, &sep, NULL, 0)))
         {
         if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
         else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC;
@@ -3563,7 +3639,7 @@ for (; cb; cb = cb->next)
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar * ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+      uschar * ss = string_nextinlist(&list, &sep, NULL, 0);
       uschar * opt;
       BOOL defer_ok = FALSE;
       int timeout = 0;
@@ -3668,11 +3744,11 @@ for (; cb; cb = cb->next)
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+      uschar *ss = string_nextinlist(&list, &sep, NULL, 0);
 
       rc = spam(CUSS &ss);
       /* Modify return code based upon the existence of options. */
-      while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+      while ((ss = string_nextinlist(&list, &sep, NULL, 0)))
         if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
           rc = FAIL;   /* FAIL so that the message is passed to the next ACL */
       }