SPDX: Mass-update to GPL-2.0-or-later
[exim.git] / src / src / acl.c
index 0078aca7dc6cbdac91445ced0646b32c8692c10c..143890668fa6cd1c350bc85ba8f8d0d628e3bd07 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;
@@ -3116,12 +3126,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 +3644,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 +3659,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 +3812,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 +3863,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 +3892,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 +3976,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 +4901,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
 */