Keep router-variables separate on addrs, to avoid taint contamination
[exim.git] / src / src / route.c
index bbaa5285d37f9decffcfb9e81f77d851602d9b29..0817a4eda62a784ba66229de589339ba68442714 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with routing, and the list of generic router options. */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with routing, and the list of generic router options. */
@@ -116,6 +116,8 @@ optionlist optionlist_routers[] = {
                  (void *)(offsetof(router_instance, self)) },
   { "senders",            opt_stringptr|opt_public,
                  (void *)offsetof(router_instance, senders) },
                  (void *)(offsetof(router_instance, self)) },
   { "senders",            opt_stringptr|opt_public,
                  (void *)offsetof(router_instance, senders) },
+  { "set",                opt_stringptr|opt_public|opt_rep_str,
+                 (void *)offsetof(router_instance, set) },
   #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   { "translate_ip_address", opt_stringptr|opt_public,
                  (void *)offsetof(router_instance, translate_ip_address) },
   #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   { "translate_ip_address", opt_stringptr|opt_public,
                  (void *)offsetof(router_instance, translate_ip_address) },
@@ -140,7 +142,7 @@ optionlist optionlist_routers[] = {
                  (void *)offsetof(router_instance, verify_sender) }
 };
 
                  (void *)offsetof(router_instance, verify_sender) }
 };
 
-int optionlist_routers_size = sizeof(optionlist_routers)/sizeof(optionlist);
+int optionlist_routers_size = nelem(optionlist_routers);
 
 
 #ifdef MACRO_PREDEF
 
 
 #ifdef MACRO_PREDEF
@@ -150,12 +152,11 @@ int optionlist_routers_size = sizeof(optionlist_routers)/sizeof(optionlist);
 void
 options_routers(void)
 {
 void
 options_routers(void)
 {
-struct router_info * ri;
 uschar buf[64];
 
 options_from_list(optionlist_routers, nelem(optionlist_routers), US"ROUTERS", NULL);
 
 uschar buf[64];
 
 options_from_list(optionlist_routers, nelem(optionlist_routers), US"ROUTERS", NULL);
 
-for (ri = routers_available; ri->driver_name[0]; ri++)
+for (router_info * ri = routers_available; ri->driver_name[0]; ri++)
   {
   spf(buf, sizeof(buf), US"_DRIVER_ROUTER_%T", ri->driver_name);
   builtin_macro_create(buf);
   {
   spf(buf, sizeof(buf), US"_DRIVER_ROUTER_%T", ri->driver_name);
   builtin_macro_create(buf);
@@ -222,8 +223,6 @@ function. */
 void
 route_init(void)
 {
 void
 route_init(void)
 {
-router_instance *r;
-
 readconf_driver_init(US"router",
   (driver_instance **)(&routers),     /* chain anchor */
   (driver_info *)routers_available,   /* available drivers */
 readconf_driver_init(US"router",
   (driver_instance **)(&routers),     /* chain anchor */
   (driver_info *)routers_available,   /* available drivers */
@@ -233,7 +232,7 @@ readconf_driver_init(US"router",
   optionlist_routers,                 /* generic options */
   optionlist_routers_size);
 
   optionlist_routers,                 /* generic options */
   optionlist_routers_size);
 
-for (r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->next)
   {
   uschar *s = r->self;
 
   {
   uschar *s = r->self;
 
@@ -244,14 +243,12 @@ for (r = routers; r; r = r->next)
 
   /* Check for transport or no transport on certain routers */
 
 
   /* Check for transport or no transport on certain routers */
 
-  if ((r->info->ri_flags & ri_yestransport) != 0 &&
-      r->transport_name == NULL &&
-      !r->verify_only)
+  if (  (r->info->ri_flags & ri_yestransport)
+     && !r->transport_name && !r->verify_only)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport is required for this router", r->name);
 
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport is required for this router", r->name);
 
-  if ((r->info->ri_flags & ri_notransport) != 0 &&
-       r->transport_name != NULL)
+  if ((r->info->ri_flags & ri_notransport) && r->transport_name)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport must not be defined for this router", r->name);
 
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "%s router:\n  "
       "a transport must not be defined for this router", r->name);
 
@@ -292,14 +289,16 @@ for (r = routers; r; r = r->next)
 
   /* Check redirect_router and pass_router are valid */
 
 
   /* Check redirect_router and pass_router are valid */
 
-  if (r->redirect_router_name != NULL)
+  if (r->redirect_router_name)
     set_router(r, r->redirect_router_name, &(r->redirect_router), FALSE);
 
     set_router(r, r->redirect_router_name, &(r->redirect_router), FALSE);
 
-  if (r->pass_router_name != NULL)
+  if (r->pass_router_name)
     set_router(r, r->pass_router_name, &(r->pass_router), TRUE);
 
     set_router(r, r->pass_router_name, &(r->pass_router), TRUE);
 
+#ifdef notdef
   DEBUG(D_route) debug_printf("DSN: %s %s\n", r->name,
        r->dsn_lasthop ? "lasthop set" : "propagating DSN");
   DEBUG(D_route) debug_printf("DSN: %s %s\n", r->name,
        r->dsn_lasthop ? "lasthop set" : "propagating DSN");
+#endif
   }
 }
 
   }
 }
 
@@ -317,8 +316,7 @@ is finished, via this function. */
 void
 route_tidyup(void)
 {
 void
 route_tidyup(void)
 {
-router_instance *r;
-for (r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->next)
   if (r->info->tidyup) (r->info->tidyup)(r);
 }
 
   if (r->info->tidyup) (r->info->tidyup)(r);
 }
 
@@ -352,9 +350,8 @@ while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))))
   int plen = Ustrlen(prefix);
   if (prefix[0] == '*')
     {
   int plen = Ustrlen(prefix);
   if (prefix[0] == '*')
     {
-    const uschar *p;
     prefix++;
     prefix++;
-    for (p = local_part + Ustrlen(local_part) - (--plen);
+    for (const uschar * p = local_part + Ustrlen(local_part) - (--plen);
          p >= local_part; p--)
       if (strncmpic(prefix, p, plen) == 0) return plen + p - local_part;
     }
          p >= local_part; p--)
       if (strncmpic(prefix, p, plen) == 0) return plen + p - local_part;
     }
@@ -396,9 +393,8 @@ while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))))
   int slen = Ustrlen(suffix);
   if (suffix[slen-1] == '*')
     {
   int slen = Ustrlen(suffix);
   if (suffix[slen-1] == '*')
     {
-    const uschar *p, *pend;
-    pend = local_part + alen - (--slen) + 1;
-    for (p = local_part; p < pend; p++)
+    const uschar *pend = local_part + alen - (--slen) + 1;
+    for (const uschar * p = local_part; p < pend; p++)
       if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part);
     }
   else
       if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part);
     }
   else
@@ -606,7 +602,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
 
   if (!ss)
     {
 
   if (!ss)
     {
-    if (expand_string_forcedfail) continue;
+    if (f.expand_string_forcedfail) continue;
     *perror = string_sprintf("failed to expand \"%s\" for require_files: %s",
       check, expand_string_message);
     goto RETURN_DEFER;
     *perror = string_sprintf("failed to expand \"%s\" for require_files: %s",
       check, expand_string_message);
     goto RETURN_DEFER;
@@ -854,7 +850,7 @@ deliver_localpart_data = NULL;
 sender_data = NULL;
 local_user_gid = (gid_t)(-1);
 local_user_uid = (uid_t)(-1);
 sender_data = NULL;
 local_user_gid = (gid_t)(-1);
 local_user_uid = (uid_t)(-1);
-search_find_defer = FALSE;
+f.search_find_defer = FALSE;
 
 /* Skip this router if not verifying and it has verify_only set */
 
 
 /* Skip this router if not verifying and it has verify_only set */
 
@@ -866,7 +862,7 @@ if ((verify == v_none || verify == v_expn) && r->verify_only)
 
 /* Skip this router if testing an address (-bt) and address_test is not set */
 
 
 /* Skip this router if testing an address (-bt) and address_test is not set */
 
-if (address_test_mode && !r->address_test)
+if (f.address_test_mode && !r->address_test)
   {
   DEBUG(D_route) debug_printf("%s router skipped: address_test is unset\n",
     r->name);
   {
   DEBUG(D_route) debug_printf("%s router skipped: address_test is unset\n",
     r->name);
@@ -958,7 +954,7 @@ if (r->router_home_directory)
   uschar *router_home = expand_string(r->router_home_directory);
   if (!router_home)
     {
   uschar *router_home = expand_string(r->router_home_directory);
   if (!router_home)
     {
-    if (!expand_string_forcedfail)
+    if (!f.expand_string_forcedfail)
       {
       *perror = string_sprintf("failed to expand \"%s\" for "
         "router_home_directory: %s", r->router_home_directory,
       {
       *perror = string_sprintf("failed to expand \"%s\" for "
         "router_home_directory: %s", r->router_home_directory,
@@ -1003,7 +999,7 @@ if (r->condition)
   DEBUG(D_route) debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition);
   if (!expand_check_condition(r->condition, r->name, US"router"))
     {
   DEBUG(D_route) debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition);
   if (!expand_check_condition(r->condition, r->name, US"router"))
     {
-    if (search_find_defer)
+    if (f.search_find_defer)
       {
       *perror = US"condition check lookup defer";
       DEBUG(D_route) debug_printf("%s\n", *perror);
       {
       *perror = US"condition check lookup defer";
       DEBUG(D_route) debug_printf("%s\n", *perror);
@@ -1367,6 +1363,8 @@ new->prop.errors_address = parent->prop.errors_address;
 
 new->prop.ignore_error = addr->prop.ignore_error;
 new->prop.address_data = addr->prop.address_data;
 
 new->prop.ignore_error = addr->prop.ignore_error;
 new->prop.address_data = addr->prop.address_data;
+new->prop.variables = NULL;
+tree_dup((tree_node **)&new->prop.variables, addr->prop.variables);
 new->dsn_flags = addr->dsn_flags;
 new->dsn_orcpt = addr->dsn_orcpt;
 
 new->dsn_flags = addr->dsn_flags;
 new->dsn_orcpt = addr->dsn_orcpt;
 
@@ -1409,6 +1407,79 @@ if (addr->transport && tree_search(tree_nonrecipients, addr->unique))
 
 
 
 
 
 
+/************************************************/
+/* Add router-assigned variables
+Return OK/DEFER/FAIL/PASS */
+
+static int
+set_router_vars(address_item * addr, const router_instance * r)
+{
+const uschar * varlist = r->set;
+tree_node ** root = (tree_node **) &addr->prop.variables;
+int sep = 0;
+
+if (!varlist) return OK;
+
+/* Walk the varlist, creating variables */
+
+for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); )
+  {
+  const uschar * assignment = ele;
+  int esep = '=';
+  uschar * name = string_nextinlist(&assignment, &esep, NULL, 0);
+  uschar * val;
+  tree_node * node;
+
+  /* Variable name must exist and start "r_". */
+
+  if (!name || name[0] != 'r' || name[1] != '_' || !name[2])
+    return FAIL;
+  name += 2;
+
+  if (!(val = expand_string(US assignment)))
+    if (f.expand_string_forcedfail)
+      {
+      int yield;
+      BOOL more;
+      DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
+         "(router variable): decline action taken\n", ele);
+
+      /* Expand "more" if necessary; DEFER => an expansion failed */
+
+      yield = exp_bool(addr, US"router", r->name, D_route,
+                     US"more", r->more, r->expand_more, &more);
+      if (yield != OK) return yield;
+
+      if (!more)
+       {
+       DEBUG(D_route)
+         debug_printf("\"more\"=false: skipping remaining routers\n");
+       router_name = NULL;
+       r = NULL;
+       return FAIL;
+       }
+      return PASS;
+      }
+    else
+      {
+      addr->message = string_sprintf("expansion of \"%s\" failed "
+       "in %s router: %s", ele, r->name, expand_string_message);
+      return DEFER;
+      }
+
+  if (!(node = tree_search(*root, name)))
+    {
+    node = store_get(sizeof(tree_node) + Ustrlen(name));
+    Ustrcpy(node->name, name);
+    (void)tree_insertnode(root, node);
+    }
+  node->data.ptr = US val;
+  DEBUG(D_route) debug_printf("set r_%s = '%s'\n", name, val);
+  }
+return OK;
+}
+
+
 /*************************************************
 *                 Route one address              *
 *************************************************/
 /*************************************************
 *                 Route one address              *
 *************************************************/
@@ -1460,7 +1531,6 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   uschar *error;
   struct passwd *pw = NULL;
   struct passwd pwcopy;
   uschar *error;
   struct passwd *pw = NULL;
   struct passwd pwcopy;
-  address_item *parent;
   BOOL loop_detected = FALSE;
   BOOL more;
   int loopcount = 0;
   BOOL loop_detected = FALSE;
   BOOL more;
   int loopcount = 0;
@@ -1474,7 +1544,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   /* There are some weird cases where logging is disabled */
 
 
   /* There are some weird cases where logging is disabled */
 
-  disable_logging = r->disable_logging;
+  f.disable_logging = r->disable_logging;
 
   /* Record the last router to handle the address, and set the default
   next router. */
 
   /* Record the last router to handle the address, and set the default
   next router. */
@@ -1494,7 +1564,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   continually adding to an address, for example), put a long stop counter on
   the number of parents. */
 
   continually adding to an address, for example), put a long stop counter on
   the number of parents. */
 
-  for (parent = addr->parent; parent; parent = parent->parent)
+  for (address_item * parent = addr->parent; parent; parent = parent->parent)
     {
     if (parent->router == r)
       {
     {
     if (parent->router == r)
       {
@@ -1609,6 +1679,61 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   search_error_message = NULL;
 
 
   search_error_message = NULL;
 
+  /* Add any variable-settings that are on the router, to the set on the
+  addr. Expansion is done here and not later when the addr is used.  There may
+  be multiple settings, gathered during readconf; this code gathers them during
+  router traversal.  On the addr string they are held as a variable tree, so
+  as to maintain the post-expansion taints separate. */
+
+  if ((yield = set_router_vars(addr, r)) != OK)
+    if (yield == PASS)
+      continue;                /* with next router */
+    else
+      goto ROUTE_EXIT;
+
+#ifdef notdef
+  if (r->set)
+    {
+    const uschar * list = r->set;
+    int sep = 0;
+    for (uschar * ele; (ele = string_nextinlist(&list, &sep, NULL, 0)); )
+      {
+      uschar * ee;
+      if (!(ee = expand_string(ele)))
+       if (f.expand_string_forcedfail)
+         {
+         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
+             "(router variable): decline action taken\n", ele);
+
+         /* Expand "more" if necessary; DEFER => an expansion failed */
+
+         yield = exp_bool(addr, US"router", r->name, D_route,
+                         US"more", r->more, r->expand_more, &more);
+         if (yield != OK) goto ROUTE_EXIT;
+
+         if (!more)
+           {
+           DEBUG(D_route)
+             debug_printf("\"more\"=false: skipping remaining routers\n");
+           router_name = NULL;
+           r = NULL;
+           break;
+           }
+         else continue;    /* With next router */
+         }
+       else
+         {
+         addr->message = string_sprintf("expansion of \"%s\" failed "
+           "in %s router: %s", ele, r->name, expand_string_message);
+         yield = DEFER;
+         goto ROUTE_EXIT;
+         }
+
+      addr->prop.set = string_append_listele(addr->prop.set, ':', ee);
+      }
+    }
+#endif
+
   /* Finally, expand the address_data field in the router. Forced failure
   behaves as if the router declined. Any other failure is more serious. On
   success, the string is attached to the address for all subsequent processing.
   /* Finally, expand the address_data field in the router. Forced failure
   behaves as if the router declined. Any other failure is more serious. On
   success, the string is attached to the address for all subsequent processing.
@@ -1617,10 +1742,9 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   if (r->address_data)
     {
     DEBUG(D_route) debug_printf("processing address_data\n");
   if (r->address_data)
     {
     DEBUG(D_route) debug_printf("processing address_data\n");
-    deliver_address_data = expand_string(r->address_data);
-    if (!deliver_address_data)
+    if (!(deliver_address_data = expand_string(r->address_data)))
       {
       {
-      if (expand_string_forcedfail)
+      if (f.expand_string_forcedfail)
         {
         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
             "(address_data): decline action taken\n", r->address_data);
         {
         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
             "(address_data): decline action taken\n", r->address_data);
@@ -1672,10 +1796,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
     pw = &pwcopy;
     }
 
     pw = &pwcopy;
     }
 
-  /* Run the router, and handle the consequences. */
-
-  /* ... but let us check on DSN before. If this should be the last hop for DSN
-  set flag. */
+  /* If this should be the last hop for DSN flag the addr. */
 
   if (r->dsn_lasthop && !(addr->dsn_flags & rf_dsnlasthop))
     {
 
   if (r->dsn_lasthop && !(addr->dsn_flags & rf_dsnlasthop))
     {
@@ -1683,6 +1804,8 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
     HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address);
     }
 
     HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address);
     }
 
+  /* Run the router, and handle the consequences. */
+
   HDEBUG(D_route) debug_printf("calling %s router\n", r->name);
 
   yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote,
   HDEBUG(D_route) debug_printf("calling %s router\n", r->name);
 
   yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote,
@@ -1766,7 +1889,7 @@ if (!r)
       uschar *expmessage = expand_string(addr->router->cannot_route_message);
       if (!expmessage)
         {
       uschar *expmessage = expand_string(addr->router->cannot_route_message);
       if (!expmessage)
         {
-        if (!expand_string_forcedfail)
+        if (!f.expand_string_forcedfail)
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
             "cannot_route_message in %s router: %s", addr->router->name,
             expand_string_message);
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
             "cannot_route_message in %s router: %s", addr->router->name,
             expand_string_message);
@@ -1821,8 +1944,7 @@ if (r->translate_ip_address)
   {
   int rc;
   int old_pool = store_pool;
   {
   int rc;
   int old_pool = store_pool;
-  host_item *h;
-  for (h = addr->host_list; h; h = h->next)
+  for (host_item * h = addr->host_list; h; h = h->next)
     {
     uschar *newaddress;
     uschar *oldaddress, *oldname;
     {
     uschar *newaddress;
     uschar *oldaddress, *oldname;
@@ -1835,7 +1957,7 @@ if (r->translate_ip_address)
 
     if (!newaddress)
       {
 
     if (!newaddress)
       {
-      if (expand_string_forcedfail) continue;
+      if (f.expand_string_forcedfail) continue;
       addr->basic_errno = ERRNO_EXPANDFAIL;
       addr->message = string_sprintf("translate_ip_address expansion "
         "failed: %s", expand_string_message);
       addr->basic_errno = ERRNO_EXPANDFAIL;
       addr->message = string_sprintf("translate_ip_address expansion "
         "failed: %s", expand_string_message);
@@ -1887,16 +2009,14 @@ HDEBUG(D_route) debug_printf("routed by %s router%s\n", r->name,
 
 DEBUG(D_route)
   {
 
 DEBUG(D_route)
   {
-  host_item *h;
-
   debug_printf("  envelope to: %s\n", addr->address);
   debug_printf("  envelope to: %s\n", addr->address);
-  debug_printf("  transport: %s\n", (addr->transport == NULL)?
-    US"<none>" : addr->transport->name);
+  debug_printf("  transport: %s\n", addr->transport
+    ? addr->transport->name : US"<none>");
 
   if (addr->prop.errors_address)
     debug_printf("  errors to %s\n", addr->prop.errors_address);
 
 
   if (addr->prop.errors_address)
     debug_printf("  errors to %s\n", addr->prop.errors_address);
 
-  for (h = addr->host_list; h; h = h->next)
+  for (host_item * h = addr->host_list; h; h = h->next)
     {
     debug_printf("  host %s", h->name);
     if (h->address) debug_printf(" [%s]", h->address);
     {
     debug_printf("  host %s", h->name);
     if (h->address) debug_printf(" [%s]", h->address);
@@ -1923,7 +2043,7 @@ if (yield == DEFER && addr->message)
 
 deliver_set_expansions(NULL);
 router_name = NULL;
 
 deliver_set_expansions(NULL);
 router_name = NULL;
-disable_logging = FALSE;
+f.disable_logging = FALSE;
 return yield;
 }
 
 return yield;
 }