$local_part_verified
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 11 Jan 2020 18:07:10 +0000 (18:07 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 11 Jan 2020 20:45:29 +0000 (20:45 +0000)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/acl.c
src/src/configure.default
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/route.c
src/src/routers/rf_get_errors_address.c
test/confs/0005

index 241540c..254ed69 100644 (file)
@@ -6362,7 +6362,7 @@ All other options are defaulted.
 .code
 local_delivery:
   driver = appendfile
-  file = /var/mail/$home
+  file = /var/mail/$local_part_verified
   delivery_date_add
   envelope_to_add
   return_path_add
@@ -6370,7 +6370,17 @@ local_delivery:
 # mode = 0660
 .endd
 This &(appendfile)& transport is used for local delivery to user mailboxes in
-traditional BSD mailbox format. By default it runs under the uid and gid of the
+traditional BSD mailbox format.
+
+.new
+We prefer to avoid using &$local_part$& directly to define the mailbox filename,
+as it is provided by a potential bad actor.
+Instead we use &$local_part_verified$&,
+the result of looking up &$local_part$& in the user database
+(done by using &%check_local_user%& in the the router).
+.wen
+
+By default &(appendfile)& runs under the uid and gid of the
 local user, which requires the sticky bit to be set on the &_/var/mail_&
 directory. Some systems use the alternative approach of running mail deliveries
 under a particular group instead of using the sticky bit. The commented options
@@ -12202,6 +12212,7 @@ the complete argument of the ETRN command (see section &<<SECTETRN>>&).
 .cindex "tainted data"
 If the origin of the data is an incoming message,
 the result of expanding this variable is tainted.
+See also &$domain_verified$&.
 .wen
 
 
@@ -12402,15 +12413,18 @@ once.
 If the origin of the data is an incoming message,
 the result of expanding this variable is tainted.
 
-&*Warning*&: the content of this variable is usually provided by a potential attacker.
+&*Warning*&: the content of this variable is usually provided by a potential
+attacker.
 Consider carefully the implications of using it unvalidated as a name
 for file access.
 This presents issues for users' &_.forward_& and filter files.
-For traditional full user accounts, use &%check_local_users%& and the &$home$&
-variable rather than this one.
+For traditional full user accounts, use &%check_local_users%& and the
+&$local_part_verified$& variable rather than this one.
 For virtual users, store a suitable pathname component in the database
 which is used for account name validation, and use that retrieved value
 rather than this variable.
+If needed, use a router &%address_data%& or &%set%& option for
+the retrieved data.
 .wen
 
 .vindex "&$local_part_prefix$&"
@@ -12479,6 +12493,14 @@ When an address is being routed or delivered, and a
 specific suffix for the local part was recognized, it is available in this
 variable, having been removed from &$local_part$&.
 
+.new
+.vitem &$local_part_verified$&
+.vindex "&$local_part_verified$&"
+If the router generic option &%check_local_part%& has run successfully,
+this variable has the user database version of &$local_part$&.
+Such values are not tainted and hence usable for building file names.
+.wen
+
 .vitem &$local_scan_data$&
 .vindex "&$local_scan_data$&"
 This variable contains the text returned by the &[local_scan()]& function when
index 6b163b8..8a00bfc 100644 (file)
@@ -21,6 +21,9 @@ Version 4.94
     driver for PLAIN; only against itself for SCRAM-SHA-1 and SCRAM-SHA-1-PLUS
     methods.
 
+ 5. Variable $local_part_verified, set by the router check_local_part condition
+    with untainted data.
+
 
 Version 4.93
 ------------
index 799af39..7284831 100644 (file)
@@ -866,11 +866,10 @@ while ((s = (*func)()) != NULL)
     {
     uschar *endptr;
 
-    if (Ustrncmp(s, "acl_c", 5) != 0 &&
-        Ustrncmp(s, "acl_m", 5) != 0)
+    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);
+       "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s);
       return NULL;
       }
 
@@ -878,19 +877,19 @@ while ((s = (*func)()) != NULL)
     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);
+       "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)",
+       s);
       return NULL;
       }
 
-    while (*endptr != 0 && *endptr != '=' && !isspace(*endptr))
+    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;
-        }
+       {
+       *error = string_sprintf("invalid character \"%c\" in variable name "
+         "in ACL modifier \"set %s\"", *endptr, s);
+       return NULL;
+       }
       endptr++;
       }
 
@@ -3634,15 +3633,12 @@ for (; cb; cb = cb->next)
       sender_address_cache, -1, 0, CUSS &sender_data);
     break;
 
-    /* Connection variables must persist forever */
+    /* Connection variables must persist forever; message variables not */
 
     case ACLC_SET:
       {
       int old_pool = store_pool;
-      if (  cb->u.varname[0] == 'c'
-#ifndef DISABLE_DKIM
-         || cb->u.varname[0] == 'd'
-#endif
+      if (  cb->u.varname[0] != 'm'
 #ifndef DISABLE_EVENT
         || event_name          /* An event is being delivered */
 #endif
index 08f5a9d..93aa167 100644 (file)
@@ -863,7 +863,7 @@ smarthost_smtp:
 
 local_delivery:
   driver = appendfile
-  file = /var/mail/$home
+  file = /var/mail/$local_part_verified
   delivery_date_add
   envelope_to_add
   return_path_add
index 366cd73..be6cd61 100644 (file)
@@ -587,6 +587,7 @@ static var_entry var_table[] = {
   { "local_part_data",     vtype_stringptr,   &deliver_localpart_data },
   { "local_part_prefix",   vtype_stringptr,   &deliver_localpart_prefix },
   { "local_part_suffix",   vtype_stringptr,   &deliver_localpart_suffix },
+  { "local_part_verified", vtype_stringptr,   &deliver_localpart_verified },
 #ifdef HAVE_LOCAL_SCAN
   { "local_scan_data",     vtype_stringptr,   &local_scan_data },
 #endif
index 15ad4e9..edd1edb 100644 (file)
@@ -817,6 +817,7 @@ uschar *deliver_localpart_orig = NULL;
 uschar *deliver_localpart_parent = NULL;
 uschar *deliver_localpart_prefix = NULL;
 uschar *deliver_localpart_suffix = NULL;
+uschar *deliver_localpart_verified = NULL;
 uschar *deliver_out_buffer     = NULL;
 int     deliver_queue_load_max = -1;
 address_item  *deliver_recipients = NULL;
index bb66cb2..9dfa4a7 100644 (file)
@@ -488,6 +488,7 @@ extern uschar *deliver_localpart_orig; /* The original local part for delivery *
 extern uschar *deliver_localpart_parent; /* The parent local part for delivery */
 extern uschar *deliver_localpart_prefix; /* The stripped prefix, if any */
 extern uschar *deliver_localpart_suffix; /* The stripped suffix, if any */
+extern uschar *deliver_localpart_verified; /* de-tainted by check_local_part */
 extern uschar *deliver_out_buffer;     /* Buffer for copying file */
 extern int     deliver_queue_load_max; /* Different value for queue running */
 extern address_item *deliver_recipients; /* Current set of addresses */
index c6119ee..16ce72c 100644 (file)
@@ -927,7 +927,7 @@ if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts,
 login of a local user. Note: the third argument to route_finduser() must be
 NULL here, to prevent a numeric string being taken as a numeric uid. If the
 user is found, set deliver_home to the home directory, and also set
-local_user_{uid,gid}.  */
+local_user_{uid,gid} and local_part_verified.  */
 
 if (r->check_local_user)
   {
@@ -938,6 +938,7 @@ if (r->check_local_user)
       r->name, addr->local_part);
     return SKIP;
     }
+  deliver_localpart_verified = string_copy(US (*pw)->pw_name);
   deliver_home = string_copy(US (*pw)->pw_dir);
   local_user_gid = (*pw)->pw_gid;
   local_user_uid = (*pw)->pw_uid;
@@ -1670,6 +1671,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   the local part sorted. */
 
   router_name = r->name;
+  deliver_localpart_verified = NULL;
   deliver_set_expansions(addr);
 
   /* For convenience, the pre-router checks are in a separate function, which
index 858c806..9ef4680 100644 (file)
@@ -88,13 +88,13 @@ else
   const uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT];
   address_item *snew = deliver_make_addr(s, FALSE);
 
-  if (sender_address != NULL)
+  if (sender_address)
     {
     save1 = sender_address[0];
     sender_address[0] = 0;
     }
 
-  for (i = 0, p = address_expansions; *p != NULL;)
+  for (i = 0, p = address_expansions; *p;)
     address_expansions_save[i++] = **p++;
   f.address_test_mode = FALSE;
 
@@ -119,10 +119,10 @@ else
     debug_printf("------ End verifying errors address %s ------\n", s);
 
   f.address_test_mode = save_address_test_mode;
-  for (i = 0, p = address_expansions; *p != NULL;)
+  for (i = 0, p = address_expansions; *p)
     **p++ = address_expansions_save[i++];
 
-  if (sender_address != NULL) sender_address[0] = save1;
+  if (sender_address) sender_address[0] = save1;
   }
 
 return OK;
index 7c0689c..77b7910 100644 (file)
@@ -52,7 +52,7 @@ local_delivery:
   driver = appendfile
   delivery_date_add
   envelope_to_add
-  file = DIR/test-mail/$local_part
+  file = DIR/test-mail/$local_part_verified
   headers_add = "X-body-linecount: $body_linecount\n\
                  X-message-linecount: $message_linecount\n\
                  X-received-count: $received_count"