Add acl call as an expansion condition
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 14 Jun 2012 22:24:16 +0000 (23:24 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Thu, 14 Jun 2012 22:24:16 +0000 (23:24 +0100)
src/src/acl.c
src/src/expand.c
src/src/functions.h

index a721665d445abb26bbc2520cea4240228ef836e0..84b0609cff4e7ff51d57ee386856b4f64958febc 100644 (file)
@@ -3863,6 +3863,43 @@ return FAIL;
 }
 
 
+
+
+/* Same args as acl_check_internal() above, but the string s is
+the name of an ACL followed optionally by up to 9 space-separated arguments.
+The name and args are separately expanded.  Args go into $acl_arg globals. */
+int
+acl_check_args(int where, address_item *addr, uschar *s, int level,
+  uschar **user_msgptr, uschar **log_msgptr)
+{
+uschar * tmp;
+uschar * name;
+
+if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp)))
+  goto bad;
+
+for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++)
+  {
+  while (*s && isspace(*s)) s++;
+  if (!*s) break;
+  if (!(tmp = string_dequote(&s)) || !(acl_arg[acl_narg] = expand_string(tmp)))
+    {
+    tmp = name;
+    goto bad;
+    }
+  }
+
+return acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr);
+
+bad:
+if (expand_string_forcedfail) return ERROR;
+*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s",
+  tmp, expand_string_message);
+return search_find_defer?DEFER:ERROR;
+}
+
+
+
 /*************************************************
 *        Check access using an ACL               *
 *************************************************/
index 913a808b88d52be279c96ee0a9592d4a2dcf5f39..7529ad1ca458b5f4c6c64e67bb638cf6e2fcfc16 100644 (file)
@@ -249,6 +249,7 @@ static uschar *cond_table[] = {
   US"==",     /* Backward compatibility */
   US">",
   US">=",
+  US"acl",
   US"and",
   US"bool",
   US"bool_lax",
@@ -294,6 +295,7 @@ enum {
   ECOND_NUM_EE,
   ECOND_NUM_G,
   ECOND_NUM_GE,
+  ECOND_ACL,
   ECOND_AND,
   ECOND_BOOL,
   ECOND_BOOL_LAX,
@@ -2066,12 +2068,50 @@ switch(cond_type)
   return s;
 
 
+  /* call ACL (in a conditional context).  Accept true, deny false.
+  Defer is a forced-fail.  Anything set by message= goes to $value.
+  See also the expansion-item version EITEM_ACL. */
+
+  case ECOND_ACL:
+    /* ${if acl {name arg1 arg2...}  {yes}{no}}
+    {
+    uschar *nameargs;
+    uschar *user_msg;
+    uschar *log_msg;
+    BOOL cond = FALSE;
+    int size = 0;
+
+    while (isspace(*s)) s++;
+    if (*s++ != '{') goto COND_FAILED_CURLY_START;
+    if (!(nameargs = expand_string_internal(s, TRUE, &s, FALSE, FALSE)) return NULL;
+    if (*s++ != '}') goto COND_FAILED_CURLY_END;
+
+    switch(acl_check_args(ACL_WHERE_EXPANSION, NULL, nameargs, &user_msg, &log_msg))
+      {
+      case OK:
+       cond = TRUE;
+      case FAIL:
+       if (user_msg)
+          lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg));
+        if (yield != NULL) *yield = cond;
+       return s;
+
+      case DEFER:
+        expand_string_forcedfail = TRUE;
+      default:
+        expand_string_message = string_sprintf("error from acl \"%s\"", nameargs);
+       return NULL;
+      }
+    }
+  return s;
+
+
   /* saslauthd: does Cyrus saslauthd authentication. Four parameters are used:
 
      ${if saslauthd {{username}{password}{service}{realm}}  {yes}[no}}
 
   However, the last two are optional. That is why the whole set is enclosed
-  in their own set or braces. */
+  in their own set of braces. */
 
   case ECOND_SASLAUTHD:
   #ifndef CYRUS_SASLAUTHD_SOCKET
@@ -3654,18 +3694,19 @@ while (*s != 0)
   switch(item_type)
     {
     /* Call an ACL from an expansion.  We feed data in via $acl_arg1 - $acl_arg9.
-    If the ACL returns acceptance we return content set by "message ="
+    If the ACL returns accept or reject we return content set by "message ="
     There is currently no limit on recursion; this would have us call
     acl_check_internal() directly and get a current level from somewhere.
     */
 
     case EITEM_ACL:
+      /* ${acl {name} {arg1}{arg2}...} */
       {
-      int rc;
-      uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */
-      uschar *new_yield;
+      int i;
+      uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */
       uschar *user_msg;
       uschar *log_msg;
+
       switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl"))
         {
         case 1: goto EXPAND_FAILED_CURLY;
@@ -3674,11 +3715,11 @@ while (*s != 0)
         }
       if (skipping) continue;
 
-      for (rc = 1; rc < sizeof(sub)/sizeof(*sub) && sub[rc]; rc++)
-        acl_arg[rc-1] = sub[rc];
-      acl_narg = rc-1;
-      while (rc < sizeof(sub)/sizeof(*sub))
-        acl_arg[rc++ - 1] = NULL;
+      for (i = 1; i < sizeof(sub)/sizeof(*sub) && sub[i]; i++)
+        acl_arg[i-1] = sub[i];
+      acl_narg = i-1;
+      while (i < sizeof(sub)/sizeof(*sub))
+        acl_arg[i++ - 1] = NULL;
 
       DEBUG(D_expand)
         debug_printf("expanding: acl: %s  arg: %s%s\n",
@@ -3686,16 +3727,18 @@ while (*s != 0)
          acl_narg>0 ? sub[1]   : US"<none>",
          acl_narg>1 ? " +more" : "");
 
-      switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg))
+      switch(acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg))
        {
        case OK:
+       case FAIL:
          if (user_msg)
             yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg));
          continue;
+
        case DEFER:
-         continue;
+          expand_string_forcedfail = TRUE;
        default:
-          expand_string_message = string_sprintf("acl \"%s\" did not accept", sub[0]);
+          expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
          goto EXPAND_FAILED;
        }
       }
index 09f7ab95c86b71cf88a8a2293e6af4107bff4818..2257a3d7cec9536060b096f57a991d91179c7a78 100644 (file)
@@ -49,6 +49,8 @@ extern BOOL    tls_openssl_options_parse(uschar *, long *);
 
 extern acl_block *acl_read(uschar *(*)(void), uschar **);
 extern int     acl_check(int, uschar *, uschar *, uschar **, uschar **);
+extern int     acl_check_args(int, address_item *, uschar *, int, uschar **, uschar **);
+
 extern tree_node *acl_var_create(uschar *);
 extern void    acl_var_write(uschar *, uschar *, void *);
 extern uschar *auth_b64encode(uschar *, int);