Add variable $sender_helo_verified
[exim.git] / src / src / acl.c
index 0078aca7dc6cbdac91445ced0646b32c8692c10c..8e1d924575284d0b3787e258084376f6d8b9e8bc 100644 (file)
@@ -5,6 +5,7 @@
 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Code for handling Access Control Lists (ACLs) */
 
@@ -734,6 +735,78 @@ return -1;
 }
 
 
+static BOOL
+acl_varname_to_cond(const uschar ** sp, acl_condition_block * cond, uschar ** error)
+{
+const uschar * s = *sp, * endptr;
+
+#ifndef DISABLE_DKIM
+if (  Ustrncmp(s, "dkim_verify_status", 18) == 0
+   || Ustrncmp(s, "dkim_verify_reason", 18) == 0)
+  {
+  endptr = s+18;
+  if (isalnum(*endptr))
+    {
+    *error = string_sprintf("invalid variable name after \"set\" in ACL "
+      "modifier \"set %s\" "
+      "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)",
+      s);
+    return FALSE;
+    }
+  cond->u.varname = string_copyn(s, 18);
+  }
+else
+#endif
+  {
+  if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0)
+    {
+    *error = string_sprintf("invalid variable name after \"set\" in ACL "
+      "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
+    return FALSE;
+    }
+
+  endptr = s + 5;
+  if (!isdigit(*endptr) && *endptr != '_')
+    {
+    *error = string_sprintf("invalid variable name after \"set\" in ACL "
+      "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
+      s);
+    return FALSE;
+    }
+
+  for ( ; *endptr && *endptr != '=' && !isspace(*endptr); endptr++)
+    if (!isalnum(*endptr) && *endptr != '_')
+      {
+      *error = string_sprintf("invalid character \"%c\" in variable name "
+       "in ACL modifier \"set %s\"", *endptr, s);
+      return FALSE;
+      }
+
+  cond->u.varname = string_copyn(s + 4, endptr - s - 4);
+  }
+s = endptr;
+Uskip_whitespace(&s);
+*sp = s;
+return TRUE;
+}
+
+
+static BOOL
+acl_data_to_cond(const uschar * s, acl_condition_block * cond,
+  const uschar * name, uschar ** error)
+{
+if (*s++ != '=')
+  {
+  *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name,
+    conditions[cond->type].is_modifier ? US"modifier" : US"condition");
+  return FALSE;;
+  }
+Uskip_whitespace(&s);
+cond->arg = string_copy(s);
+return TRUE;
+}
+
+
 /*************************************************
 *            Read and parse one ACL              *
 *************************************************/
@@ -760,7 +833,7 @@ acl_block **lastp = &yield;
 acl_block *this = NULL;
 acl_condition_block *cond;
 acl_condition_block **condp = NULL;
-uschar * s;
+const uschar * s;
 
 *error = NULL;
 
@@ -768,7 +841,7 @@ while ((s = (*func)()))
   {
   int v, c;
   BOOL negated = FALSE;
-  uschar *saveline = s;
+  const uschar * saveline = s;
   uschar name[EXIM_DRIVERNAME_MAX];
 
   /* Conditions (but not verbs) are allowed to be negated by an initial
@@ -808,16 +881,15 @@ while ((s = (*func)()))
       *error = string_sprintf("malformed ACL line \"%s\"", saveline);
       return NULL;
       }
-    this = store_get(sizeof(acl_block), GET_UNTAINTED);
-    *lastp = this;
-    lastp = &(this->next);
+    *lastp = this = store_get(sizeof(acl_block), GET_UNTAINTED);
+    lastp = &this->next;
     this->next = NULL;
     this->condition = NULL;
     this->verb = v;
     this->srcline = config_lineno;     /* for debug output */
     this->srcfile = config_filename;   /**/
-    condp = &(this->condition);
-    if (*s == 0) continue;               /* No condition on this line */
+    condp = &this->condition;
+    if (!*s) continue;               /* No condition on this line */
     if (*s == '!')
       {
       negated = TRUE;
@@ -861,7 +933,7 @@ while ((s = (*func)()))
   cond->u.negated = negated;
 
   *condp = cond;
-  condp = &(cond->next);
+  condp = &cond->next;
 
   /* The "set" modifier is different in that its argument is "name=value"
   rather than just a value, and we can check the validity of the name, which
@@ -874,75 +946,13 @@ while ((s = (*func)()))
   compatibility. */
 
   if (c == ACLC_SET)
-#ifndef DISABLE_DKIM
-    if (  Ustrncmp(s, "dkim_verify_status", 18) == 0
-       || Ustrncmp(s, "dkim_verify_reason", 18) == 0)
-      {
-      uschar * endptr = s+18;
-
-      if (isalnum(*endptr))
-       {
-       *error = string_sprintf("invalid variable name after \"set\" in ACL "
-         "modifier \"set %s\" "
-         "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)",
-         s);
-       return NULL;
-       }
-      cond->u.varname = string_copyn(s, 18);
-      s = endptr;
-      Uskip_whitespace(&s);
-      }
-    else
-#endif
-    {
-    uschar *endptr;
-
-    if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0)
-      {
-      *error = string_sprintf("invalid variable name after \"set\" in ACL "
-       "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
-      return NULL;
-      }
-
-    endptr = s + 5;
-    if (!isdigit(*endptr) && *endptr != '_')
-      {
-      *error = string_sprintf("invalid variable name after \"set\" in ACL "
-       "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
-       s);
-      return NULL;
-      }
-
-    while (*endptr && *endptr != '=' && !isspace(*endptr))
-      {
-      if (!isalnum(*endptr) && *endptr != '_')
-       {
-       *error = string_sprintf("invalid character \"%c\" in variable name "
-         "in ACL modifier \"set %s\"", *endptr, s);
-       return NULL;
-       }
-      endptr++;
-      }
-
-    cond->u.varname = string_copyn(s + 4, endptr - s - 4);
-    s = endptr;
-    Uskip_whitespace(&s);
-    }
+    if (!acl_varname_to_cond(&s, cond, error)) return NULL;
 
   /* For "set", we are now positioned for the data. For the others, only
   "endpass" has no data */
 
   if (c != ACLC_ENDPASS)
-    {
-    if (*s++ != '=')
-      {
-      *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name,
-        conditions[c].is_modifier ? US"modifier" : US"condition");
-      return NULL;
-      }
-    Uskip_whitespace(&s);
-    cond->arg = string_copy(s);
-    }
+    if (!acl_data_to_cond(s, cond, name, error)) return NULL;
   }
 
 return yield;
@@ -1633,6 +1643,30 @@ return period;
 
 
 
+static BOOL
+sender_helo_verified_internal(void)
+{
+/* We can test the result of optional HELO verification that might have
+occurred earlier. If not, we can attempt the verification now. */
+
+if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo();
+return f.helo_verified;
+}
+
+static int
+sender_helo_verified_cond(void)
+{
+return sender_helo_verified_internal() ? OK : FAIL;
+}
+
+uschar *
+sender_helo_verified_boolstr(void)
+{
+return sender_helo_verified_internal() ? US"yes" : US"no";
+}
+
+
+
 /* 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
@@ -1729,11 +1763,7 @@ switch(vp->value)
     return FAIL;
 
   case VERIFY_HELO:
-    /* We can test the result of optional HELO verification that might have
-    occurred earlier. If not, we can attempt the verification now. */
-
-    if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo();
-    return f.helo_verified ? OK : FAIL;
+    return sender_helo_verified_cond();
 
   case VERIFY_CSA:
     /* Do Client SMTP Authorization checks in a separate function, and turn the
@@ -3116,12 +3146,9 @@ acl_check_condition(int verb, acl_condition_block *cb, int where,
   address_item *addr, int level, BOOL *epp, uschar **user_msgptr,
   uschar **log_msgptr, int *basic_errno)
 {
-uschar *user_message = NULL;
-uschar *log_message = NULL;
+uschar * user_message = NULL;
+uschar * log_message = NULL;
 int rc = OK;
-#ifdef WITH_CONTENT_SCAN
-int sep = -'/';
-#endif
 
 for (; cb; cb = cb->next)
   {
@@ -3637,12 +3664,13 @@ for (; cb; cb = cb->next)
       break;
       }
 
-    #ifdef EXPERIMENTAL_DCC
+#ifdef EXPERIMENTAL_DCC
     case ACLC_DCC:
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar *ss = string_nextinlist(&list, &sep, NULL, 0);
+      int sep = -'/';
+      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. */
@@ -3651,13 +3679,13 @@ for (; cb; cb = cb->next)
           rc = FAIL;   /* FAIL so that the message is passed to the next ACL */
       break;
       }
-    #endif
+#endif
 
-    #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
     case ACLC_DECODE:
       rc = mime_decode(&arg);
       break;
-    #endif
+#endif
 
     case ACLC_DELAY:
       {
@@ -3804,11 +3832,10 @@ for (; cb; cb = cb->next)
 
     case ACLC_LOG_REJECT_TARGET:
       {
-      int logbits = 0;
-      int sep = 0;
-      const uschar *s = arg;
-      uschar * ss;
-      while ((ss = string_nextinlist(&s, &sep, NULL, 0)))
+      int logbits = 0, sep = 0;
+      const uschar * s = arg;
+
+      for (uschar * ss; ss = string_nextinlist(&s, &sep, NULL, 0); )
         {
         if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
         else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC;
@@ -3856,17 +3883,16 @@ for (; cb; cb = cb->next)
       break;
       }
 
-    #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
     case ACLC_MALWARE:                 /* Run the malware backend. */
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar * ss = string_nextinlist(&list, &sep, NULL, 0);
-      uschar * opt;
       BOOL defer_ok = FALSE;
-      int timeout = 0;
+      int timeout = 0, sep = -'/';
+      uschar * ss = string_nextinlist(&list, &sep, NULL, 0);
 
-      while ((opt = string_nextinlist(&list, &sep, NULL, 0)))
+      for (uschar * opt; opt = string_nextinlist(&list, &sep, NULL, 0); )
         if (strcmpic(opt, US"defer_ok") == 0)
          defer_ok = TRUE;
        else if (  strncmpic(opt, US"tmo=", 4) == 0
@@ -3886,7 +3912,7 @@ for (; cb; cb = cb->next)
     case ACLC_MIME_REGEX:
       rc = mime_regex(&arg, textonly);
       break;
-    #endif
+#endif
 
     case ACLC_QUEUE:
       if (is_tainted(arg))
@@ -3970,7 +3996,8 @@ for (; cb; cb = cb->next)
       {
       /* Separate the regular expression and any optional parameters. */
       const uschar * list = arg;
-      uschar *ss = string_nextinlist(&list, &sep, NULL, 0);
+      int sep = -'/';
+      uschar * ss = string_nextinlist(&list, &sep, NULL, 0);
 
       rc = spam(CUSS &ss);
       /* Modify return code based upon the existence of options. */
@@ -4894,6 +4921,29 @@ if (is_tainted(value))
 fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
 }
 
+
+
+
+uschar *
+acl_standalone_setvar(const uschar * s)
+{
+acl_condition_block * cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED);
+uschar * errstr = NULL, * log_msg = NULL;
+BOOL endpass_seen;
+int e;
+
+cond->next = NULL;
+cond->type = ACLC_SET;
+if (!acl_varname_to_cond(&s, cond, &errstr)) return errstr;
+if (!acl_data_to_cond(s, cond, US"'-be'", &errstr)) return errstr;
+
+if (acl_check_condition(ACL_WARN, cond, ACL_WHERE_UNKNOWN,
+                           NULL, 0, &endpass_seen, &errstr, &log_msg, &e) != OK)
+  return string_sprintf("oops: %s", errstr);
+return string_sprintf("variable %s set", cond->u.varname);
+}
+
+
 #endif /* !MACRO_PREDEF */
 /* vi: aw ai sw=2
 */