Support timeout option on malware=
[exim.git] / src / src / acl.c
index 386754fcf9920abc6ef6d9f1ff77b92dc56a2779..8fdae039074a29ccffef64cdec4a2503ee1b72fc 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2013 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -397,7 +397,7 @@ static unsigned int cond_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* add_header */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
   #endif
     (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
@@ -412,7 +412,7 @@ static unsigned int cond_forbids[] = {
   (1<<ACL_WHERE_AUTH)|                             /* bmi_optin */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
   #endif
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
@@ -434,9 +434,9 @@ static unsigned int cond_forbids[] = {
   #ifdef EXPERIMENTAL_DCC
   (unsigned int)
   ~((1<<ACL_WHERE_DATA)|                           /* dcc */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
@@ -450,9 +450,9 @@ static unsigned int cond_forbids[] = {
   #ifdef WITH_OLD_DEMIME
   (unsigned int)
   ~((1<<ACL_WHERE_DATA)|                           /* demime */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
@@ -474,7 +474,7 @@ static unsigned int cond_forbids[] = {
 
   (unsigned int)
   ~((1<<ACL_WHERE_RCPT)                            /* domains */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     |(1<<ACL_WHERE_PRDR)
   #endif
     ),
@@ -491,7 +491,7 @@ static unsigned int cond_forbids[] = {
 
   (unsigned int)
   ~((1<<ACL_WHERE_RCPT)                             /* local_parts */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     |(1<<ACL_WHERE_PRDR)
   #endif
     ),
@@ -505,9 +505,9 @@ static unsigned int cond_forbids[] = {
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
   ~((1<<ACL_WHERE_DATA)|                           /* malware */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
@@ -526,9 +526,9 @@ static unsigned int cond_forbids[] = {
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
   ~((1<<ACL_WHERE_DATA)|                           /* regex */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP)|
     (1<<ACL_WHERE_MIME)),
   #endif
@@ -536,7 +536,7 @@ static unsigned int cond_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* remove_header */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
   #endif
     (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
@@ -559,9 +559,9 @@ static unsigned int cond_forbids[] = {
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
   ~((1<<ACL_WHERE_DATA)|                           /* spam */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
@@ -608,9 +608,9 @@ static unsigned int control_forbids[] = {
 
   #ifndef DISABLE_DKIM
   (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_disable_verify */
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_NOTSMTP_START),
   #endif
 
@@ -674,17 +674,17 @@ static unsigned int control_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* fakedefer */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_MIME)),
 
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* fakereject */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
-  #ifdef EXPERIMENTAL_PRDR
+  #ifndef DISABLE_PRDR
     (1<<ACL_WHERE_PRDR)|
-  #endif /* EXPERIMENTAL_PRDR */
+  #endif
     (1<<ACL_WHERE_MIME)),
 
   (1<<ACL_WHERE_NOTSMTP)|                          /* no_multiline */
@@ -1550,7 +1550,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
   assertion: legitimate SMTP clients are all explicitly authorized with CSA
   SRV records of their own. */
 
-  if (found != domain)
+  if (Ustrcmp(found, domain) != 0)
     {
     if (port & 1)
       return t->data.val = CSA_FAIL_EXPLICIT;
@@ -1614,6 +1614,7 @@ else
 DNS_LOOKUP_AGAIN:
 #endif
 
+lookup_dnssec_authenticated = NULL;
 switch (dns_lookup(&dnsa, target, type, NULL))
   {
   /* If something bad happened (most commonly DNS_AGAIN), defer. */
@@ -2351,7 +2352,10 @@ rate measurement as opposed to rate limiting. */
 
 sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
 if (sender_rate_limit == NULL)
+  {
   limit = -1.0;
+  ss = NULL;   /* compiler quietening */
+  }
 else
   {
   limit = Ustrtod(sender_rate_limit, &ss);
@@ -2857,9 +2861,9 @@ uschar *portstr;
 uschar *portend;
 host_item *h;
 int portnum;
-int host_af;
 int len;
 int r, s;
+uschar * errstr;
 
 hostname = string_nextinlist(&arg, &sep, NULL, 0);
 portstr = string_nextinlist(&arg, &sep, NULL, 0);
@@ -2906,14 +2910,18 @@ if (r == HOST_FIND_FAILED || r == HOST_FIND_AGAIN)
 HDEBUG(D_acl)
   debug_printf("udpsend [%s]:%d %s\n", h->address, portnum, arg);
 
-host_af = (Ustrchr(h->address, ':') == NULL)? AF_INET:AF_INET6;
-r = s = ip_socket(SOCK_DGRAM, host_af);
-if (r < 0) goto defer;
-r = ip_connect(s, host_af, h->address, portnum, 1);
+r = s = ip_connectedsocket(SOCK_DGRAM, h->address, portnum, portnum,
+               1, NULL, &errstr);
 if (r < 0) goto defer;
 len = Ustrlen(arg);
 r = send(s, arg, len, 0);
-if (r < 0) goto defer;
+if (r < 0)
+  {
+  errstr = US strerror(errno);
+  close(s);
+  goto defer;
+  }
+close(s);
 if (r < len)
   {
   *log_msgptr =
@@ -2927,7 +2935,7 @@ HDEBUG(D_acl)
 return OK;
 
 defer:
-*log_msgptr = string_sprintf("\"udpsend\" failed: %s", strerror(errno));
+*log_msgptr = string_sprintf("\"udpsend\" failed: %s", errstr);
 return DEFER;
 }
 
@@ -2974,7 +2982,7 @@ uschar *debug_opts = NULL;
 uschar *p = NULL;
 int rc = OK;
 #ifdef WITH_CONTENT_SCAN
-int sep = '/';
+int sep = -'/';
 #endif
 
 for (; cb != NULL; cb = cb->next)
@@ -2987,12 +2995,14 @@ for (; cb != NULL; cb = cb->next)
 
   if (cb->type == ACLC_MESSAGE)
     {
+    HDEBUG(D_acl) debug_printf("  message: %s\n", cb->arg);
     user_message = cb->arg;
     continue;
     }
 
   if (cb->type == ACLC_LOG_MESSAGE)
     {
+    HDEBUG(D_acl) debug_printf("l_message: %s\n", cb->arg);
     log_message = cb->arg;
     continue;
     }
@@ -3099,7 +3109,9 @@ for (; cb != NULL; cb = cb->next)
     /* The true/false parsing here should be kept in sync with that used in
     expand.c when dealing with ECOND_BOOL so that we don't have too many
     different definitions of what can be a boolean. */
-    if (Ustrspn(arg, "0123456789") == Ustrlen(arg))     /* Digits, or empty */
+    if (*arg == '-'
+       ? Ustrspn(arg+1, "0123456789") == Ustrlen(arg+1)    /* Negative number */
+       : Ustrspn(arg,   "0123456789") == Ustrlen(arg))     /* Digits, or empty */
       rc = (Uatoi(arg) == 0)? FAIL : OK;
     else
       rc = (strcmpic(arg, US"no") == 0 ||
@@ -3239,8 +3251,9 @@ for (; cb != NULL; cb = cb->next)
       disable_callout_flush = TRUE;
       break;
 
-      case CONTROL_FAKEDEFER:
       case CONTROL_FAKEREJECT:
+      cancel_cutthrough_connection("fakereject");
+      case CONTROL_FAKEDEFER:
       fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
       if (*p == '/')
         {
@@ -3270,10 +3283,12 @@ for (; cb != NULL; cb = cb->next)
         *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
         return ERROR;
         }
+      cancel_cutthrough_connection("item frozen");
       break;
 
       case CONTROL_QUEUE_ONLY:
       queue_only_policy = TRUE;
+      cancel_cutthrough_connection("queueing forced");
       break;
 
       case CONTROL_SUBMISSION:
@@ -3340,17 +3355,19 @@ for (; cb != NULL; cb = cb->next)
 
       case CONTROL_CUTTHROUGH_DELIVERY:
       if (deliver_freeze)
-        {
-        *log_msgptr = string_sprintf("\"control=%s\" on frozen item", arg);
-        return ERROR;
-        }
-       if (queue_only_policy)
-        {
-        *log_msgptr = string_sprintf("\"control=%s\" on queue-only item", arg);
-        return ERROR;
-        }
-      cutthrough_delivery = TRUE;
-      break;
+        *log_msgptr = US"frozen";
+      else if (queue_only_policy)
+        *log_msgptr = US"queue-only";
+      else if (fake_response == FAIL)
+        *log_msgptr = US"fakereject";
+      else
+       {
+       cutthrough_delivery = TRUE;
+       break;
+       }
+      *log_msgptr = string_sprintf("\"control=%s\" on %s item",
+                                   arg, *log_msgptr);
+      return ERROR;
       }
     break;
 
@@ -3566,21 +3583,28 @@ for (; cb != NULL; cb = cb->next)
     break;
 
     #ifdef WITH_CONTENT_SCAN
-    case ACLC_MALWARE:
+    case ACLC_MALWARE:                 /* Run the malware backend. */
       {
       /* Separate the regular expression and any optional parameters. */
       uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
-      /* Run the malware backend. */
-      rc = malware(&ss);
-      /* Modify return code based upon the existance of options. */
-      while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
-            != NULL) {
-        if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
-          {
-          /* FAIL so that the message is passed to the next ACL */
-          rc = FAIL;
-          }
-        }
+      uschar *opt;
+      BOOL defer_ok = FALSE;
+      int timeout = 0;
+
+      while ((opt = string_nextinlist(&arg, &sep, NULL, 0)))
+        if (strcmpic(opt, US"defer_ok") == 0)
+         defer_ok = TRUE;
+       else if (  strncmpic(opt, US"tmo=", 4) == 0
+               && (timeout = readconf_readtime(opt+4, '\0', FALSE)) < 0
+               )
+         {
+         *log_msgptr = string_sprintf("bad timeout value in '%s'", opt);
+         return ERROR;
+         }
+
+      rc = malware(ss, timeout);
+      if (rc == DEFER && defer_ok)
+       rc = FAIL;      /* FAIL so that the message is passed to the next ACL */
       }
     break;
 
@@ -4115,7 +4139,11 @@ while (acl != NULL)
   switch(acl->verb)
     {
     case ACL_ACCEPT:
-    if (cond == OK || cond == DISCARD) return cond;
+    if (cond == OK || cond == DISCARD)
+      {
+      HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name);
+      return cond;
+      }
     if (endpass_seen)
       {
       HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n");
@@ -4126,17 +4154,26 @@ while (acl != NULL)
     case ACL_DEFER:
     if (cond == OK)
       {
+      HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name);
       acl_temp_details = TRUE;
       return DEFER;
       }
     break;
 
     case ACL_DENY:
-    if (cond == OK) return FAIL;
+    if (cond == OK)
+      {
+      HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name);
+      return FAIL;
+      }
     break;
 
     case ACL_DISCARD:
-    if (cond == OK || cond == DISCARD) return DISCARD;
+    if (cond == OK || cond == DISCARD)
+      {
+      HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name);
+      return DISCARD;
+      }
     if (endpass_seen)
       {
       HDEBUG(D_acl) debug_printf("discard: endpass encountered - denying access\n");
@@ -4145,11 +4182,19 @@ while (acl != NULL)
     break;
 
     case ACL_DROP:
-    if (cond == OK) return FAIL_DROP;
+    if (cond == OK)
+      {
+      HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name);
+      return FAIL_DROP;
+      }
     break;
 
     case ACL_REQUIRE:
-    if (cond != OK) return cond;
+    if (cond != OK)
+      {
+      HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name);
+      return cond;
+      }
     break;
 
     case ACL_WARN:
@@ -4305,7 +4350,7 @@ sender_verified_failed = NULL;
 ratelimiters_cmd = NULL;
 log_reject_target = LOG_MAIN|LOG_REJECT;
 
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
 if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR )
 #else
 if (where == ACL_WHERE_RCPT )
@@ -4349,7 +4394,7 @@ If conn-failure, no action (and keep the spooled copy).
 switch (where)
 {
 case ACL_WHERE_RCPT:
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
 case ACL_WHERE_PRDR:
 #endif
   if( rcpt_count > 1 )
@@ -4469,4 +4514,6 @@ FILE *f = (FILE *)ctx;
 fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of acl.c */