options for which string expansion is performed are marked with † after
the data type. ACL rules always expand strings. A couple of expansion
conditions do not expand some of the brace-delimited branches, for security
-reasons.
+reasons,
+.new
+.cindex "tainted data" expansion
+.cindex expansion "tainted data"
+and expansion of data deriving from the sender (&"tainted data"&)
+is not permitted.
+.wen
certificate.
.endlist
+.new
+Any of the above may have an extra hyphen prepended, to indicate the the
+corresponding data is untrusted.
+.wen
+
Following the options there is a list of those addresses to which the message
is not to be delivered. This set of addresses is initialized from the command
line when the &%-t%& option is used and &%extract_addresses_remove_arguments%&
JH/31 Avoid re-expansion in ${sort } expansion. (CVE-2019-13917)
+JH/32 Introduce a general tainting mechanism for values read from the input
+ channel, and values derived from them. Refuse to expand any tainted
+ values, to catch one form of exploit.
+
Exim version 4.92
-----------------
log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s",
strerror(errno));
-buf = store_get(needed);
+buf = store_get(needed, FALSE);
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s",
log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s",
strerror(errno));
-buf = store_get(needed);
+buf = store_get(needed, FALSE);
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s",
log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s",
strerror(errno));
-buf = store_get(needed);
+buf = store_get(needed, FALSE);
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s",
log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s",
strerror(errno));
-buf = store_get(needed);
+buf = store_get(needed, FALSE);
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s",
cygwin_debug = TRUE;
fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv);
if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0)
- && ((win32_path = malloc(size)) != NULL)
+ && ((win32_path = store_malloc(size)) != NULL)
&& (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) {
fprintf(stderr, " Root / mapped to %ls.\n", win32_path);
- free(win32_path);
+ store_free(win32_path);
}
}
else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
{
uschar *id;
uschar *p = buffer;
- void *reset_point;
+ rmark reset_point;
int length = Ustrlen(buffer);
int i;
it for various regular expression matches and take appropriate
action. Get the current store point so we can reset to it. */
- reset_point = store_get(0);
+ reset_point = store_mark();
/* First, update any stripchart data values, noting that the zeroth
stripchart is the queue length, which is handled elsewhere, and the
if (log_datestamping)
{
uschar log_file_wanted[256];
- /* Do *not* use "%s" here, we need the %D datestamp in the log_file to
- * be expanded! */
- string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file);
+ /* Do *not* use "%s" here, we need the %D datestamp in the log_file string to
+ be expanded. The trailing NULL arg is to quieten preprocessors that need at
+ least one arg for a variadic set in a macro. */
+ string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file, NULL);
if (Ustrcmp(log_file_wanted, log_file_open) != 0)
{
if (LOG != NULL)
constructing file names and things. This call will initialize
the store_get() function. */
-big_buffer = store_get(big_buffer_size);
+big_buffer = store_get(big_buffer_size, FALSE);
/* Set up the version string and date and output them */
{
/* Do *not* use "%s" here, we need the %D datestamp in the log_file to
be expanded! */
- (void)string_format(log_file_open, sizeof(log_file_open), CS log_file);
+ (void)string_format(log_file_open, sizeof(log_file_open), CS log_file, NULL);
log_datestamping = string_datestamp_offset >= 0;
LOG = fopen(CS log_file_open, "r");
uschar buffer[256];
header_line *h, *next;
Widget text = text_create(US client_data, text_depth);
-void *reset_point;
+rmark reset_point;
w = w; /* Keep picky compilers happy */
call_data = call_data;
/* Remember the point in the dynamic store so we can recover to it afterwards.
Then use Exim's function to read the header. */
-reset_point = store_get(0);
+reset_point = store_mark();
sprintf(CS buffer, "%s-H", US client_data);
if (spool_read_header(buffer, TRUE, FALSE) != spool_read_OK)
{
tree_node *node, **root;
root = (name[0] == 'c')? &acl_var_c : &acl_var_m;
-node = store_get(sizeof(tree_node) + Ustrlen(name));
+node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE);
Ustrcpy(node->name, name);
node->data.ptr = NULL;
(void)tree_insertnode(root, node);
{
int i, rc, save_errno;
struct stat statdata;
-void *reset_point;
+rmark reset_point;
uschar *p;
queue_item *q = (queue_item *)store_malloc(sizeof(queue_item));
uschar buffer[256];
we can recover the store into which the header is read. All data read by
spool_read_header that is to be preserved is copied into malloc store. */
-reset_point = store_get(0);
+reset_point = store_mark();
message_size = 0;
message_subdir[0] = dir_char;
sprintf(CS buffer, "%s-H", name);
if (Ustat(big_buffer, &statbuf) == 0)
msg = string_sprintf("*** Format error in spool file: size = %d ***",
statbuf.st_size);
- else msg = string_sprintf("*** Format error in spool file ***");
+ else msg = US"*** Format error in spool file ***";
}
- else msg = string_sprintf("*** Cannot read spool file ***");
+ else msg = US"*** Cannot read spool file ***";
if (rc == spool_read_hdrerror)
{
{
int i;
FILE *jread;
-void *reset_point;
+rmark reset_point;
struct stat statdata;
uschar buffer[1024];
/* Get the contents of the header file; if any problem, just give up.
Arrange to recover the dynamic store afterwards. */
-reset_point = store_get(0);
+reset_point = store_mark();
sprintf(CS buffer, "%s-H", p->name);
if (spool_read_header(buffer, FALSE, TRUE) != spool_read_OK)
{
thresh : stripchart_midmax[num];
if (newmax == 10) sprintf(CS buffer, "%s", stripchart_name[num]);
else sprintf(CS buffer, "%s x%d", stripchart_name[num], newmax/10);
- if (size_stripchart != NULL && num == 1) Ustrcat(buffer, "%");
+ if (size_stripchart != NULL && num == 1) Ustrcat(buffer, US"%");
xs_SetValues(stripchart_label[num], 1, "label", buffer);
oldmax = stripchart_max[num];
stripchart_max[num] = newmax;
Ustrncat(version_date, EXIM_BUILD_DATE_OVERRIDE, 31);
#else
-Ustrcpy(today, __DATE__);
+Ustrcpy(today, US __DATE__);
if (today[4] == ' ') i = 1;
today[3] = today[6] = '-';
Ustrncat(version_date, today+4+i, 3-i);
Ustrncat(version_date, today, 4);
Ustrncat(version_date, today+7, 4);
-Ustrcat(version_date, " ");
-Ustrcat(version_date, __TIME__);
+Ustrcat(version_date, US" ");
+Ustrcat(version_date, US __TIME__);
#endif
}
{
int i;
va_list ap;
-Arg *aa = (num_args > 15)? (Arg *)malloc(num_args*sizeof(Arg)) : xs_temparg;
+Arg *aa = (num_args > 15)? store_malloc(num_args*sizeof(Arg)) : xs_temparg;
va_start(ap, num_args);
for (i = 0; i < num_args; i++)
{
}
va_end(ap);
XtSetValues(w, aa, num_args);
-if (num_args > 15) free(aa);
+if (num_args > 15) store_free(aa);
}
/* End of em_xs.c */
*error = string_sprintf("malformed ACL line \"%s\"", saveline);
return NULL;
}
- this = store_get(sizeof(acl_block));
+ this = store_get(sizeof(acl_block), FALSE);
*lastp = this;
lastp = &(this->next);
this->next = NULL;
return NULL;
}
- cond = store_get(sizeof(acl_condition_block));
+ cond = store_get(sizeof(acl_condition_block), FALSE);
cond->next = NULL;
cond->type = c;
cond->u.negated = negated;
if (!*hptr)
{
- header_line *h = store_get(sizeof(header_line));
+ /* The header_line struct itself is not tainted, though it points to
+ tainted data. */
+ header_line *h = store_get(sizeof(header_line), FALSE);
h->text = hdr;
h->next = NULL;
h->type = newtype;
t = tree_search(csa_cache, domain);
if (t != NULL) return t->data.val;
-t = store_get_perm(sizeof(tree_node) + Ustrlen(domain));
+t = store_get_perm(sizeof(tree_node) + Ustrlen(domain), is_tainted(domain));
Ustrcpy(t->name, domain);
(void)tree_insertnode(&csa_cache, t);
if ((rc = verify_check_notblind(case_sensitive)) != OK)
{
- *log_msgptr = string_sprintf("bcc recipient detected");
+ *log_msgptr = US"bcc recipient detected";
if (smtp_return_error_details)
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
}
string_cat(NULL, US"error in arguments to \"ratelimit\" condition: ");
va_start(ap, format);
-g = string_vformat(g, TRUE, format, ap);
+g = string_vformat(g, SVFMT_EXTEND|SVFMT_REBUFFER, format, ap);
va_end(ap);
gstring_release_unused(g);
/* No Bloom filter. This basic ratelimit block is initialized below. */
HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n");
dbdb_size = sizeof(*dbd);
- dbdb = store_get(dbdb_size);
+ dbdb = store_get(dbdb_size, FALSE); /* not tainted */
}
else
{
extra = (int)limit * 2 - sizeof(dbdb->bloom);
if (extra < 0) extra = 0;
dbdb_size = sizeof(*dbdb) + extra;
- dbdb = store_get(dbdb_size);
+ dbdb = store_get(dbdb_size, FALSE); /* not tainted */
dbdb->bloom_epoch = tv.tv_sec;
dbdb->bloom_size = sizeof(dbdb->bloom) + extra;
memset(dbdb->bloom, 0, dbdb->bloom_size);
dbfn_close(dbm);
-/* Store the result in the tree for future reference. */
+/* Store the result in the tree for future reference. Take the taint status
+from the key for consistency even though it's unlikely we'll ever expand this. */
-t = store_get(sizeof(tree_node) + Ustrlen(key));
+t = store_get(sizeof(tree_node) + Ustrlen(key), is_tainted(key));
t->data.ptr = dbd;
Ustrcpy(t->name, key);
(void)tree_insertnode(anchor, t);
}
/* Make a single-item host list. */
-h = store_get(sizeof(host_item));
+h = store_get(sizeof(host_item), FALSE);
memset(h, 0, sizeof(host_item));
h->name = hostname;
h->port = portnum;
(sender_host_address == NULL)? US"" : sender_host_address,
CUSS &host_data);
if (rc == DEFER) *log_msgptr = search_error_message;
- if (host_data) host_data = string_copy_malloc(host_data);
+ if (host_data) host_data = string_copy_perm(host_data, TRUE);
break;
case ACLC_LOCAL_PARTS:
"Directory separator not permitted in queue name: '%s'", arg);
return ERROR;
}
- queue_name = string_copy_malloc(arg);
+ queue_name = string_copy_perm(arg, FALSE);
break;
case ACLC_RATELIMIT:
else if (*ss == '/')
{
struct stat statbuf;
+ if (is_tainted(ss))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to open tainted ACL file name \"%s\"", ss);
+ /* Avoid leaking info to an attacker */
+ *log_msgptr = US"internal configuration error";
+ return ERROR;
+ }
if ((fd = Uopen(ss, O_RDONLY, 0)) < 0)
{
*log_msgptr = string_sprintf("failed to open ACL file \"%s\": %s", ss,
strerror(errno));
return ERROR;
}
-
if (fstat(fd, &statbuf) != 0)
{
*log_msgptr = string_sprintf("failed to fstat ACL file \"%s\": %s", ss,
return ERROR;
}
- acl_text = store_get(statbuf.st_size + 1);
+ /* If the string being used as a filename is tainted, so is the file content */
+ acl_text = store_get(statbuf.st_size + 1, is_tainted(ss));
acl_text_end = acl_text + statbuf.st_size + 1;
if (read(fd, acl_text, statbuf.st_size) != statbuf.st_size)
if (!acl && *log_msgptr) return ERROR;
if (fd >= 0)
{
- tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss));
+ tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss), is_tainted(ss));
Ustrcpy(t->name, ss);
t->data.ptr = acl;
(void)tree_insertnode(&acl_anchor, t);
tree_node * node, ** root = name[0] == 'c' ? &acl_var_c : &acl_var_m;
if (!(node = tree_search(*root, name)))
{
- node = store_get(sizeof(tree_node) + Ustrlen(name));
+ node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
Ustrcpy(node->name, name);
(void)tree_insertnode(root, node);
}
acl_var_write(uschar *name, uschar *value, void *ctx)
{
FILE *f = (FILE *)ctx;
+if (is_tainted(value)) putc('-', f);
fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
}
}
DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i);
-*pas = as = store_get(sizeof(arc_set));
+*pas = as = store_get(sizeof(arc_set), FALSE);
memset(as, 0, sizeof(arc_set));
as->next = next;
as->prev = prev;
if (!instance_only)
{
- al->rawsig_no_b_val.data = store_get(h->slen + 1);
+ al->rawsig_no_b_val.data = store_get(h->slen + 1, TRUE); /* tainted */
memcpy(al->rawsig_no_b_val.data, h->text, off); /* copy the header name blind */
r = al->rawsig_no_b_val.data + off;
al->rawsig_no_b_val.len = off;
{
unsigned i;
arc_set * as;
-arc_line * al = store_get(sizeof(arc_line)), ** alp;
+arc_line * al = store_get(sizeof(arc_line), FALSE), ** alp;
uschar * e;
memset(al, 0, sizeof(arc_line));
DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
for (h = header_list; h; h = h->next)
{
- r = store_get(sizeof(hdr_rlist));
+ r = store_get(sizeof(hdr_rlist), FALSE);
r->prev = rprev;
r->used = FALSE;
r->h = h;
static hdr_rlist *
arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
{
-hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line));
+hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), FALSE);
header_line * h = r->h = (header_line *)(r+1);
r->prev = list;
const uschar * identity, int instance, blob * ar)
{
int aar_off = g ? g->ptr : 0;
-arc_set * as = store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line));
+arc_set * as =
+ store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), FALSE);
arc_line * al = (arc_line *)(as+1);
header_line * h = (header_line *)(al+1);
int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
blob sig;
int ams_off;
-arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line));
+arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE);
header_line * h = (header_line *)(al+1);
/* debug_printf("%s\n", __FUNCTION__); */
gstring * arcset;
arc_set * as;
uschar * status = arc_ar_cv_status(ar);
-arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line));
+arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE);
header_line * h = (header_line *)(al+1);
gstring * hdata = NULL;
if (pam_arg_ended) return PAM_CONV_ERR;
-reply = malloc(sizeof(struct pam_response) * num_msg);
-
-if (reply == NULL) return PAM_CONV_ERR;
+reply = store_get(sizeof(struct pam_response) * num_msg, FALSE);
for (int i = 0; i < num_msg; i++)
{
arg = US"";
pam_arg_ended = TRUE;
}
- reply[i].resp = CS string_copy_malloc(arg); /* PAM frees resp */
+ reply[i].resp = CS string_copy_perm(arg, FALSE); /* PAM frees resp */
reply[i].resp_retcode = PAM_SUCCESS;
break;
break;
default: /* Must be an error of some sort... */
- free (reply);
pam_conv_had_error = TRUE;
return PAM_CONV_ERR;
}
debug_printf("CRAM-MD5: user name = %s\n", auth_vars[0]);
debug_printf(" challenge = %s\n", challenge);
debug_printf(" received = %s\n", clear);
- Ustrcpy(buff," digest = ");
+ Ustrcpy(buff, US" digest = ");
for (i = 0; i < 16; i++) sprintf(CS buff+22+2*i, "%02x", digest[i]);
debug_printf("%.54s\n", buff);
}
const uschar *list, *listptr, *buffer;
int rc, i;
unsigned int len;
-uschar *rs_point, *expanded_hostname;
+rmark rs_point;
+uschar *expanded_hostname;
char *realm_expanded;
sasl_conn_t *conn;
* the hierarchy is stored for us behind our back. This point
* creates a hierarchy point for this function.
*/
-rs_point = store_get(0);
+rs_point = store_mark();
/* loop until either we get to the end of the list, or we match the
* public name of this authenticator
static void
exim_heimdal_error_debug(const char *, krb5_context, krb5_error_code);
static int
- exim_gssapi_error_defer(uschar *, OM_uint32, OM_uint32, const char *, ...)
+ exim_gssapi_error_defer(rmark, OM_uint32, OM_uint32, const char *, ...)
PRINTF_FUNCTION(4, 5);
#define EmptyBuf(buf) do { buf.value = NULL; buf.length = 0; } while (0)
auth_heimdal_gssapi_options_block *ob =
(auth_heimdal_gssapi_options_block *)(ablock->options_block);
BOOL handled_empty_ir;
-uschar *store_reset_point;
+rmark store_reset_point;
uschar *keytab;
uschar sasl_config[4];
uschar requested_qop;
-store_reset_point = store_get(0);
+store_reset_point = store_mark();
HDEBUG(D_auth)
debug_printf("heimdal: initialising auth context for %s\n", ablock->name);
static int
-exim_gssapi_error_defer(uschar *store_reset_point,
+exim_gssapi_error_defer(rmark store_reset_point,
OM_uint32 major, OM_uint32 minor,
const char *format, ...)
{
HDEBUG(D_auth)
{
va_start(ap, format);
- g = string_vformat(NULL, TRUE, format, ap);
+ g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, format, ap);
va_end(ap);
}
if (count > MAX_REQ_LEN) {
return -1;
} else {
- *retval = store_get(count + 1);
+ /* Assume the file is trusted, so no tainting */
+ *retval = store_get(count + 1, FALSE);
rc = (retry_read(fd, *retval, count) < (int) count);
(*retval)[count] = '\0';
return count;
auth_xtextdecode(uschar *code, uschar **ptr)
{
register int x;
-uschar *result = store_get(Ustrlen(code) + 1);
+uschar *result = store_get(Ustrlen(code) + 1, is_tainted(code));
*ptr = result;
while ((x = (*code++)) != 0)
while (c -- > 0)
count += ((x = *p++) < 33 || x > 127 || x == '+' || x == '=')? 3 : 1;
-pp = code = store_get(count);
+pp = code = store_get(count, is_tainted(clear));
p = US clear;
c = len;
{
int l = Ustrlen(code);
- *ptr = result = store_get(1 + l/4 * 3 + l%4);
+ *ptr = result = store_get(1 + l/4 * 3 + l%4, is_tainted(code));
}
/* Each cycle of the loop handles a quantum of 4 input bytes. For the last
uschar *
b64encode(const uschar * clear, int len)
{
-uschar *code = store_get(4*((len+2)/3) + 1);
+uschar *code = store_get(4*((len+2)/3) + 1, is_tainted(clear));
uschar *p = code;
while (len-- >0)
return NULL;
};
- /* get store for the verdict string */
- verdicts = store_get(1);
+ /* Get store for the verdict string. Since we are processing message data, assume that
+ the verdict is tainted. XXX this should use a growable-string */
+
+ verdicts = store_get(1, TRUE);
*verdicts = '\0';
for ( err = bmiAccessFirstVerdict(message, &verdict);
char *verdict_str;
err = bmiCreateStrFromVerdict(verdict,&verdict_str);
- if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
+ if (!store_extend(verdicts, TRUE,
+ Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
/* can't allocate more store */
return NULL;
};
}
else {
/* deliver to alternate location */
- rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1);
+ rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, TRUE);
Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
};
return NULL;
/* allocate room for the b64 verdict string */
- verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1);
+ verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, TRUE);
/* loop through verdicts */
verdict_ptr = bmi_verdicts;
int extra = pcount ? *pcount : 0;
uschar **argv;
-argv = store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *));
+argv = store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *), FALSE);
/* In all case, the list starts out with the path, any macros, and a changed
config file. */
int save_log_selector = *log_selector;
gstring * whofrom;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
/* Make the address available in ASCII representation, and also fish out
the remote port. */
"please try again later.\r\n", FALSE);
mac_smtp_fflush();
search_tidyup();
- _exit(EXIT_FAILURE);
+ exim_underbar_exit(EXIT_FAILURE);
}
}
else if (*nah) smtp_active_hostname = nah;
{
mac_smtp_fflush();
search_tidyup();
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
for (;;)
{
int rc;
message_id[0] = 0; /* Clear out any previous message_id */
- reset_point = store_get(0); /* Save current store high water point */
+ reset_point = store_mark(); /* Save current store high water point */
DEBUG(D_any)
debug_printf("Process %d is ready for new message\n", (int)getpid());
cancel_cutthrough_connection(TRUE, US"receive dropped");
mac_smtp_fflush();
smtp_log_no_mail(); /* Log no mail if configured */
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (message_id[0] == 0) continue; /* No message was accepted */
}
/*XXX should we pause briefly, hoping that the client will be the
active TCP closer hence get the TCP_WAIT endpoint? */
DEBUG(D_receive) debug_printf("SMTP>>(close on process exit)\n");
- _exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
+ exim_underbar_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* Show the recipients when debugging */
int r = receive_messagecount;
BOOL q = f.queue_only_policy;
smtp_reset(reset_point);
+ reset_point = NULL;
f.queue_only_policy = q;
receive_messagecount = r;
}
(void) deliver_message(message_id, FALSE, FALSE);
search_tidyup();
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (dpid > 0)
if (smtp_slots[i].pid <= 0)
{
smtp_slots[i].pid = pid;
- if (smtp_accept_max_per_host != NULL)
+ /* Connection closes come asyncronously, so we cannot stack this store */
+ if (smtp_accept_max_per_host)
smtp_slots[i].host_address = string_copy_malloc(sender_host_address);
smtp_accept_count++;
break;
}
DEBUG(D_any) debug_printf("%d SMTP accept process%s running\n",
- smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
+ smtp_accept_count, smtp_accept_count == 1 ? "" : "es");
}
/* Get here via goto in error cases */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
- int i;
DEBUG(D_any)
{
debug_printf("child %d ended: status=0x%x\n", (int)pid, status);
if (smtp_slots)
{
+ int i;
for (i = 0; i < smtp_accept_max; i++)
if (smtp_slots[i].pid == pid)
{
if (queue_pid_slots)
{
int max = atoi(CS expand_string(queue_run_max));
- for (i = 0; i < max; i++)
+ for (int i = 0; i < max; i++)
if (queue_pid_slots[i] == pid)
{
queue_pid_slots[i] = 0;
if (f.inetd_wait_mode)
{
listen_socket_count = 1;
- listen_sockets = store_get(sizeof(int));
+ listen_sockets = store_get(sizeof(int), FALSE);
(void) close(3);
if (dup2(0, 3) == -1)
log_write(0, LOG_MAIN|LOG_PANIC_DIE,
sep = 0;
while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
pct++;
- default_smtp_port = store_get((pct+1) * sizeof(int));
+ default_smtp_port = store_get((pct+1) * sizeof(int), FALSE);
list = daemon_smtp_port;
sep = 0;
for (pct = 0;
ipa->port = default_smtp_port[0];
for (int i = 1; default_smtp_port[i] > 0; i++)
{
- ip_address_item *new = store_get(sizeof(ip_address_item));
+ ip_address_item *new = store_get(sizeof(ip_address_item), FALSE);
memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1);
new->port = default_smtp_port[i];
for (ipa = addresses; ipa; ipa = ipa->next)
listen_socket_count++;
- listen_sockets = store_get(sizeof(int) * listen_socket_count);
+ listen_sockets = store_get(sizeof(int) * listen_socket_count, FALSE);
} /* daemon_listen but not inetd_wait_mode */
if (smtp_accept_max > 0)
{
- smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot));
+ smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot), FALSE);
for (int i = 0; i < smtp_accept_max; i++) smtp_slots[i] = empty_smtp_slot;
}
}
originator_uid = exim_uid;
originator_gid = exim_gid;
-originator_login = ((pw = getpwuid(exim_uid)) != NULL)?
- string_copy_malloc(US pw->pw_name) : US"exim";
+originator_login = (pw = getpwuid(exim_uid))
+ ? string_copy_perm(US pw->pw_name, FALSE) : US"exim";
/* Get somewhere to keep the list of queue-runner pids if we are keeping track
of them (and also if we are doing queue runs). */
if (queue_interval > 0 && local_queue_run_max > 0)
{
- queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t));
+ queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t), FALSE);
for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0;
}
/* No need to re-exec; SIGALRM remains set to the default handler */
queue_run(NULL, NULL, FALSE);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
if (pid < 0)
};
#endif
-#define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FILE__, __LINE__)
+#define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FUNCTION__, __LINE__)
static int err_lib_dane = -1;
static int dane_idx = -1;
if (Ustrncmp(ent->d_name, name, namelen) == 0)
{
struct stat statbuf;
- Ustrcpy(lastname, ent->d_name);
+ Ustrcpy(lastname, US ent->d_name);
if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
{
DEBUG(D_hints_lookup) debug_printf_indent("ensuring %s is owned by exim\n", filename);
void *yield;
EXIM_DATUM key_datum, result_datum;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
-yield = store_get(EXIM_DATUM_SIZE(result_datum));
+/* Assume the data store could have been tainted. Properly, we should
+store the taint status with the data. */
+
+yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
EXIM_DATUM key_datum, value_datum;
dbdata_generic *gptr = (dbdata_generic *)ptr;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
gptr->time_stamp = time(NULL);
dbfn_delete(open_db *dbblock, const uschar *key)
{
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
debug_printf("DCC: Client IP (default): %s\n", client_ip);
}
/* strncat(opts, my_request, strlen(my_request)); */
- Ustrcat(opts, "\n");
+ Ustrcat(opts, US"\n");
Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1);
- Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1);
+ Ustrncat(opts, US"\nHELO ", sizeof(opts)-Ustrlen(opts)-1);
Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2);
- Ustrcat(opts, "\n");
+ Ustrcat(opts, US"\n");
/* initialize the other variables */
dcchdr = header_list;
if (Ustrlen(sender_address) > 0)
Ustrncpy(from, sender_address, sizeof(from));
else
- Ustrncpy(from, "<>", sizeof(from));
- Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1);
+ Ustrncpy(from, US"<>", sizeof(from));
+ Ustrncat(from, US"\n", sizeof(from)-Ustrlen(from)-1);
/**************************************
* Now creating the socket connection *
/* connecting to the dccifd UNIX socket */
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
- Ustrncpy(serv_addr.sun_path, sockpath, sizeof(serv_addr.sun_path));
+ Ustrncpy(US serv_addr.sun_path, sockpath, sizeof(serv_addr.sun_path));
if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){
DEBUG(D_acl)
debug_printf("DCC: Creating UNIX socket connection failed: %s\n", strerror(errno));
bzero(sendbuf, sizeof(sendbuf));
}
Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1);
- Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
+ Ustrncat(sendbuf, US"\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
}
/* send a blank line between options and message */
- Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
+ Ustrncat(sendbuf, US"\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
/* Now we send the input buffer */
DEBUG(D_acl)
debug_printf("DCC: %s\nDCC: ****************************\n", sendbuf);
}
/* a blank line separates header from body */
- Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
+ Ustrncat(sendbuf, US"\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
flushbuffer(sockfd, sendbuf);
DEBUG(D_acl)
debug_printf("\nDCC: ****************************\n%s", sendbuf);
if(recvbuf[i] == 'A') {
DEBUG(D_acl)
debug_printf("DCC: Overall result = A\treturning OK\n");
- Ustrcpy(dcc_return_text, "Mail accepted by DCC");
+ Ustrcpy(dcc_return_text, US"Mail accepted by DCC");
dcc_result = US"A";
retval = OK;
}
else if(recvbuf[i] == 'S') {
DEBUG(D_acl)
debug_printf("DCC: Overall result = S\treturning OK\n");
- Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC");
+ Ustrcpy(dcc_return_text, US"Not all recipients accepted by DCC");
/* Since we're in an ACL we want a global result
* so we accept for all */
dcc_result = US"A";
else if(recvbuf[i] == 'G') {
DEBUG(D_acl)
debug_printf("DCC: Overall result = G\treturning FAIL\n");
- Ustrcpy(dcc_return_text, "Greylisted by DCC");
+ Ustrcpy(dcc_return_text, US"Greylisted by DCC");
dcc_result = US"G";
retval = FAIL;
}
debug_printf("DCC: Overall result = T\treturning DEFER\n");
retval = DEFER;
log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf);
- Ustrcpy(dcc_return_text, "Temporary error with DCC");
+ Ustrcpy(dcc_return_text, US"Temporary error with DCC");
dcc_result = US"T";
}
else {
debug_printf("DCC: Overall result = something else\treturning DEFER\n");
retval = DEFER;
log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf);
- Ustrcpy(dcc_return_text, "Unknown DCC response");
+ Ustrcpy(dcc_return_text, US"Unknown DCC response");
dcc_result = US"T";
}
}
if (((xtra_hdrs = expand_string(US"$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) {
Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2);
if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n')
- Ustrcat(dcc_xtra_hdrs, "\n");
+ Ustrcat(dcc_xtra_hdrs, US"\n");
header_add(' ', "%s", dcc_xtra_hdrs);
DEBUG(D_acl)
debug_printf("DCC: adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs);
if (host_checking && debug_selector == 0)
{
- Ustrcpy(debug_ptr, ">>> ");
+ Ustrcpy(debug_ptr, US">>> ");
debug_ptr += 4;
}
for (int i = indent >> 2; i > 0; i--)
DEBUG(D_noutf8)
{
- Ustrcpy(debug_ptr, " !");
+ Ustrcpy(debug_ptr, US" !");
debug_ptr += 4; /* 3 spaces + shriek */
debug_prefix_length += 4;
}
else
{
- Ustrcpy(debug_ptr, " " UTF8_VERT_2DASH);
+ Ustrcpy(debug_ptr, US" " UTF8_VERT_2DASH);
debug_ptr += 6; /* 3 spaces + 3 UTF-8 octets */
debug_prefix_length += 6;
}
- Ustrncpy(debug_ptr, " ", indent &= 3);
+ Ustrncpy(debug_ptr, US" ", indent &= 3);
debug_ptr += indent;
debug_prefix_length += indent;
}
-/* Use the checked formatting routine to ensure that the buffer
-does not overflow. Ensure there's space for a newline at the end. */
+/* Use the lengthchecked formatting routine to ensure that the buffer
+does not overflow. Ensure there's space for a newline at the end.
+However, use taint-unchecked routines for writing into the buffer
+so that we can write tainted info into the static debug_buffer -
+we trust that we will never expand the results. */
{
gstring gs = { .size = (int)sizeof(debug_buffer) - 1,
.ptr = debug_ptr - debug_buffer,
.s = debug_buffer };
- if (!string_vformat(&gs, FALSE, format, ap))
+ if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap))
{
uschar * s = US"**** debug string too long - truncated ****\n";
uschar * p = gs.s + gs.ptr;
address_item *
deliver_make_addr(uschar *address, BOOL copy)
{
-address_item *addr = store_get(sizeof(address_item));
+address_item *addr = store_get(sizeof(address_item), FALSE);
*addr = address_defaults;
if (copy) address = string_copy(address);
addr->address = address;
else
{
- uschar * cmp = g->s + g->ptr;
+ uschar * cmp;
+ int off = g->ptr; /* start of the "full address" */
if (addr->local_part)
{
of all, do a caseless comparison; if this succeeds, do a caseful comparison
on the local parts. */
+ cmp = g->s + off; /* only now, as rebuffer likely done */
string_from_gstring(g); /* ensure nul-terminated */
if ( strcmpic(cmp, topaddr->address) == 0
&& Ustrncmp(cmp, topaddr->address, Ustrchr(cmp, '@') - cmp) == 0
delivery_log(int flags, address_item * addr, int logchar, uschar * msg)
{
gstring * g; /* Used for a temporary, expanding buffer, for building log lines */
-void * reset_point; /* released afterwards. */
+rmark reset_point;
/* Log the delivery on the main log. We use an extensible string to build up
the log line, and reset the store afterwards. Remote deliveries should always
lookup_dnssec_authenticated = NULL;
#endif
-g = reset_point = string_get(256);
+reset_point = store_mark();
+g = string_get_tainted(256, TRUE); /* addrs will be tainted, so avoid copy */
if (msg)
g = string_append(g, 2, host_and_ident(TRUE), US" ");
deferral_log(address_item * addr, uschar * now,
int logflags, uschar * driver_name, uschar * driver_kind)
{
-gstring * g;
-void * reset_point;
+rmark reset_point = store_mark();
+gstring * g = string_get(256);
/* Build up the line that is used for both the message log and the main
log. */
-g = reset_point = string_get(256);
-
/* Create the address string for logging. Must not do this earlier, because
an OK result may be changed to FAIL when a pipe returns text. */
static void
failure_log(address_item * addr, uschar * driver_kind, uschar * now)
{
-void * reset_point;
-gstring * g = reset_point = string_get(256);
+rmark reset_point = store_mark();
+gstring * g = string_get(256);
#ifndef DISABLE_EVENT
/* Message failures for which we will send a DSN get their event raised
gstring * g;
va_start(ap, format);
- g = string_vformat(NULL, TRUE, CS format, ap);
+ g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS format, ap);
va_end(ap);
addr->message = string_from_gstring(g);
}
static BOOL
previously_transported(address_item *addr, BOOL testing)
{
-(void)string_format(big_buffer, big_buffer_size, "%s/%s",
+uschar * s = string_sprintf("%s/%s",
addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name);
-if (tree_search(tree_nonrecipients, big_buffer) != 0)
+if (tree_search(tree_nonrecipients, s) != 0)
{
DEBUG(D_deliver|D_route|D_transport)
debug_printf("%s was previously delivered (%s transport): discarded\n",
f.disable_logging = FALSE; /* Jic */
addr->message = addr->router
? string_sprintf("No transport set by %s router", addr->router->name)
- : string_sprintf("No transport set by system filter");
+ : US"No transport set by system filter";
post_process_one(addr, DEFER, logflags, EXIM_DTYPE_TRANSPORT, 0);
continue;
}
else for (addr2 = addr; addr2; addr2 = addr2->next)
if (addr2->transport_return == OK)
{
- addr3 = store_get(sizeof(address_item));
+ addr3 = store_get(sizeof(address_item), FALSE);
*addr3 = *addr2;
addr3->next = NULL;
addr3->shadow_message = US &addr2->shadow_message;
if (!r || !(*ptr & rf_delete))
{
- r = store_get(sizeof(retry_item));
+ r = store_get(sizeof(retry_item), FALSE);
r->next = addr->retries;
addr->retries = r;
r->flags = *ptr++;
if (*ptr)
{
- h = store_get(sizeof(host_item));
+ h = store_get(sizeof(host_item), FALSE);
h->name = string_copy(ptr);
while (*ptr++);
h->address = string_copy(ptr);
if (!parlist)
{
- parlist = store_get(remote_max_parallel * sizeof(pardata));
+ parlist = store_get(remote_max_parallel * sizeof(pardata), FALSE);
for (poffset = 0; poffset < remote_max_parallel; poffset++)
parlist[poffset].pid = 0;
}
this, Jan 1999.] We know the syntax is valid, so this can be done by simply
removing quoting backslashes and any unquoted doublequotes. */
-t = addr->cc_local_part = store_get(len+1);
+t = addr->cc_local_part = store_get(len+1, is_tainted(address));
while(len-- > 0)
{
int c = *address++;
if (new_address)
{
- address_item *new_parent = store_get(sizeof(address_item));
+ address_item *new_parent = store_get(sizeof(address_item), FALSE);
*new_parent = *addr;
addr->parent = new_parent;
new_parent->child_count = 1;
if (addr_new)
{
- int uid = (system_filter_uid_set)? system_filter_uid : geteuid();
- int gid = (system_filter_gid_set)? system_filter_gid : getegid();
+ int uid = system_filter_uid_set ? system_filter_uid : geteuid();
+ int gid = system_filter_gid_set ? system_filter_gid : getegid();
/* The text "system-filter" is tested in transport_set_up_command() and in
set_up_shell_command() in the pipe transport, to enable them to permit
if (!tmp)
p->message = string_sprintf("failed to expand \"%s\" as a "
"system filter transport name", tpname);
+ if (is_tainted(tmp))
+ p->message = string_sprintf("attempt to used tainted value '%s' for"
+ "transport '%s' as a system filter", tmp, tpname);
tpname = tmp;
}
else
keep piling '>' characters on the front. */
if (addr->address[0] == '>')
- {
while (tree_search(tree_duplicates, addr->unique))
addr->unique = string_sprintf(">%s", addr->unique);
- }
else if ((tnode = tree_search(tree_duplicates, addr->unique)))
{
&addr_succeed, v_none)) == DEFER)
retry_add_item(addr,
addr->router->retry_use_local_part
- ? string_sprintf("R:%s@%s", addr->local_part, addr->domain)
- : string_sprintf("R:%s", addr->domain),
+ ? string_sprintf("R:%s@%s", addr->local_part, addr->domain)
+ : string_sprintf("R:%s", addr->domain),
0);
/* Otherwise, if there is an existing retry record in the database, add
{
/* copy and relink address_item and send report with all of them at once later */
address_item * addr_next = addr_senddsn;
- addr_senddsn = store_get(sizeof(address_item));
+ addr_senddsn = store_get(sizeof(address_item), FALSE);
*addr_senddsn = *a;
addr_senddsn->next = addr_next;
}
uschar *
dkim_exim_query_dns_txt(uschar * name)
{
+/*XXX need to always alloc the dnsa, from tainted mem.
+Then, we hope, the answers will be tainted */
+
dns_answer dnsa;
dns_scan dnss;
+rmark reset_point = store_mark();
gstring * g = NULL;
lookup_dnssec_authenticated = NULL;
}
bad:
-if (g) store_reset(g);
+store_reset(reset_point);
return NULL; /*XXX better error detail? logging? */
}
/* Can't use exim's string manipulation functions so allocate memory
for libopendmarc using its max hostname length definition. */
- dmarc_domain = US calloc(DMARC_MAXHOSTNAMELEN, sizeof(uschar));
+ dmarc_domain = store_get(DMARC_MAXHOSTNAMELEN, TRUE);
libdm_status = opendmarc_policy_fetch_utilized_domain(dmarc_pctx,
dmarc_domain, DMARC_MAXHOSTNAMELEN-1);
- dmarc_used_domain = string_copy(dmarc_domain);
- free(dmarc_domain);
+ store_release_above(dmarc_domain + Ustrlen(dmarc_domain)+1);
+ dmarc_used_domain = dmarc_domain;
if (libdm_status != DMARC_PARSE_OKAY)
log_write(0, LOG_MAIN|LOG_PANIC,
{
int len = Ustrlen(domain);
int asize = size; /* Locally modified */
-uschar name[256];
+uschar * name;
uschar utilname[256];
uschar *aptr = answerptr; /* Locally modified */
struct stat statbuf;
/* Remove terminating dot. */
if (domain[len - 1] == '.') len--;
-Ustrncpy(name, domain, len);
-name[len] = 0;
+name = string_copyn(domain, len);
/* Look for the fakens utility, and if it exists, call it. */
*pp++ = '.';
p = ppp - 1;
}
- Ustrcpy(pp, "in-addr.arpa");
+ Ustrcpy(pp, US"in-addr.arpa");
}
/* Handle IPv6 address; convert to binary so as to fill out any
for (int i = 3; i >= 0; i--)
for (int j = 0; j < 32; j += 4)
pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
- Ustrcpy(pp, "ip6.arpa.");
+ Ustrcpy(pp, US"ip6.arpa.");
/* Another way of doing IPv6 reverse lookups was proposed in conjunction
with A6 records. However, it fell out of favour when they did. The
sprintf(pp, "%08X", v6[i]);
pp += 8;
}
- Ustrcpy(pp, "].ip6.arpa.");
+ Ustrcpy(pp, US"].ip6.arpa.");
**************************************************/
}
static int
dns_return(const uschar * name, int type, int rc)
{
-tree_node *node = store_get_perm(sizeof(tree_node) + 290);
+tree_node *node = store_get_perm(sizeof(tree_node) + 290, TRUE);
dns_fail_tag(node->name, name, type);
node->data.val = rc;
(void)tree_insertnode(&tree_dns_fails, node);
if (!cname_rr.data)
return DNS_FAIL;
- data = store_get(256);
+ /* DNS data comes from the outside, hence tainted */
+ data = store_get(256, TRUE);
if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0)
return DNS_FAIL;
uschar *p = US rr->data;
if (p + 4 <= dnsa_lim)
{
- yield = store_get(sizeof(dns_address) + 20);
+ /* the IP is not regarded as tainted */
+ yield = store_get(sizeof(dns_address) + 20, FALSE);
(void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
yield->next = NULL;
}
{
struct in6_addr in6;
for (int i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i];
- yield = store_get(sizeof(dns_address) + 50);
+ yield = store_get(sizeof(dns_address) + 50, FALSE);
inet_ntop(AF_INET6, &in6, CS yield->address, 50);
yield->next = NULL;
}
static void
addlookupmodule(void *dl, struct lookup_module_info *info)
{
-struct lookupmodulestr *p = store_malloc(sizeof(struct lookupmodulestr));
+struct lookupmodulestr *p = store_get(sizeof(struct lookupmodulestr), FALSE);
p->dl = dl;
p->info = info;
int countmodules = 0;
int moduleerrors = 0;
#endif
- struct lookupmodulestr *p;
static BOOL lookup_list_init_done = FALSE;
-
+ rmark reset_point;
if (lookup_list_init_done)
return;
+ reset_point = store_mark();
lookup_list_init_done = TRUE;
#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
memset(lookup_list, 0, sizeof(lookup_info *) * lookup_list_count);
/* now add all lookups to the real list */
- p = lookupmodules;
- while (p) {
- struct lookupmodulestr *pnext;
-
+ for (struct lookupmodulestr * p = lookupmodules; p; p = p->next)
for (int j = 0; j < p->info->lookupcount; j++)
add_lookup_to_list(p->info->lookups[j]);
-
- pnext = p->next;
- store_free(p);
- p = pnext;
- }
+ store_reset(reset_point);
/* just to be sure */
lookupmodules = NULL;
}
/* We don't have the full Exim headers dragged in, but this function
is used for debugging output. */
-extern gstring * string_vformat(gstring *, BOOL, const char *, va_list);
+extern gstring * string_vformat(gstring *, unsigned, const char *, va_list);
/*************************************************
* Handle calls to print debug output *
*************************************************/
-/* The message just gets written to stderr
+/* The message just gets written to stderr.
+We use tainted memory to format into just so that we can handle
+tainted arguments.
Arguments:
format a printf() format
debug_printf(char *format, ...)
{
va_list ap;
-gstring * g = string_get(1024);
-void * reset_point = g;
+rmark reset_point = store_mark();
+gstring * g = string_get_tainted(1024, TRUE);
va_start(ap, format);
-if (!string_vformat(g, FALSE, format, ap))
+if (!string_vformat(g, 0, format, ap))
{
char * s = "**** debug string overflowed buffer ****\n";
char * p = CS g->s + g->ptr;
}
else if (Ustrcmp(keep_environment, "*") != 0)
+ {
+ rmark reset_point = store_mark();
if (environ) for (uschar ** p = USS environ; *p; /* see below */)
{
/* It's considered broken if we do not find the '=', according to
if (os_unsetenv(name) < 0) return FALSE;
else p = USS environ; /* RESTART from the beginning */
else p++;
- store_reset(name);
}
}
+ store_reset(reset_point);
+ }
if (add_environment)
{
- uschar * p;
- int sep = 0;
- const uschar * envlist = add_environment;
+ uschar * p;
+ int sep = 0;
+ const uschar * envlist = add_environment;
- while ((p = string_nextinlist(&envlist, &sep, NULL, 0))) putenv(CS p);
+ while ((p = string_nextinlist(&envlist, &sep, NULL, 0))) putenv(CS p);
}
- return TRUE;
+return TRUE;
}
static void *
function_store_get(size_t size)
{
-return store_get((int)size);
+/* For now, regard all RE results as potentially tainted. We might need
+more intelligence on this point. */
+return store_get((int)size, TRUE);
}
static void
g = string_fmt_append(&gs, "%5d ", (int)getpid());
len = g->ptr;
va_start(ap, format);
-if (!string_vformat(g, FALSE, format, ap))
+if (!string_vformat(g, 0, format, ap))
{
gs.ptr = len;
g = string_cat(&gs, US"**** string overflowed buffer ****");
{
if (devnull < 0) devnull = open("/dev/null", O_RDWR);
if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- string_open_failed(errno, "/dev/null"));
+ string_open_failed(errno, "/dev/null", NULL));
if (devnull != i) (void)dup2(devnull, i);
}
}
exim_exit(int rc, const uschar * process)
{
search_tidyup();
+store_exit();
DEBUG(D_any)
debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
">>>>>>>>>>>>>>>>\n", (int)getpid(),
}
+void
+exim_underbar_exit(int rc)
+{
+store_exit();
+_exit(rc);
+}
+
+
/* Print error string, then die */
static void
#ifdef USE_READLINE
char *readline_line = NULL;
- if (fn_readline != NULL)
+ if (fn_readline)
{
- if ((readline_line = fn_readline((i > 0)? "":"> ")) == NULL) break;
- if (*readline_line != 0 && fn_addhist != NULL) fn_addhist(readline_line);
+ if (!(readline_line = fn_readline((i > 0)? "":"> "))) break;
+ if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line);
p = US readline_line;
}
else
while (ss > p && isspace(ss[-1])) ss--;
if (i > 0)
- {
while (p < ss && isspace(*p)) p++; /* leading space after cont */
- }
g = string_catn(g, p, ss - p);
}
/* Get a list of macros which are whitelisted */
-whitelisted = string_copy_malloc(US WHITELIST_D_MACROS);
+whitelisted = string_copy_perm(US WHITELIST_D_MACROS, FALSE);
prev_char_item = FALSE;
white_count = 0;
for (p = whitelisted; *p != '\0'; ++p)
uschar *real_sender_address;
uschar *originator_home = US"/";
size_t sz;
-void *reset_point;
+rmark reset_point;
struct passwd *pw;
struct stat statbuf;
/* Set up the handler for the data request signal, and set the initial
descriptive text. */
+process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */
set_process_info("initializing");
os_restarting_signal(SIGUSR1, usr1_handler);
else
{
/* Well, the trust list at least is up to scratch... */
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
uschar *trusted_configs[32];
int nr_configs = 0;
int i = 0;
&sep, big_buffer, big_buffer_size)) != NULL)
{
for (i=0; i < nr_configs; i++)
- {
if (Ustrcmp(filename, trusted_configs[i]) == 0)
break;
- }
if (i == nr_configs)
{
f.trusted_config = FALSE;
break;
}
}
- store_reset(reset_point);
}
- else
- {
- /* No valid prefixes found in trust_list file. */
+ else /* No valid prefixes found in trust_list file. */
f.trusted_config = FALSE;
- }
+ store_reset(reset_point);
}
}
- else
- {
- /* Could not open trust_list file. */
+ else /* Could not open trust_list file. */
f.trusted_config = FALSE;
- }
}
#else
/* Not root; don't trust config */
if (clmacro_count >= MAX_CLMACROS)
exim_fail("exim: too many -D options on command line\n");
- clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name,
- m->replacement);
+ clmacros[clmacro_count++] =
+ string_sprintf("-D%s=%s", m->name, m->replacement);
}
#endif
break;
{ badarg = TRUE; break; }
}
if (*argrest == 0)
- sender_address = string_sprintf(""); /* Ensure writeable memory */
+ *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */
else
{
uschar *temp = argrest + Ustrlen(argrest) - 1;
#endif
sender_address = parse_extract_address(argrest, &errmess,
&dummy_start, &dummy_end, &sender_address_domain, TRUE);
+ sender_address = string_copy_taint(sender_address, TRUE);
#ifdef SUPPORT_I18N
message_smtputf8 = string_is_utf8(sender_address);
allow_utf8_domains = FALSE;
/* -oMas: setting authenticated sender */
- else if (Ustrcmp(argrest, "Mas") == 0) authenticated_sender = argv[++i];
+ else if (Ustrcmp(argrest, "Mas") == 0)
+ authenticated_sender = string_copy_taint(argv[++i], TRUE);
/* -oMai: setting authenticated id */
- else if (Ustrcmp(argrest, "Mai") == 0) authenticated_id = argv[++i];
+ else if (Ustrcmp(argrest, "Mai") == 0)
+ authenticated_id = string_copy_taint(argv[++i], TRUE);
/* -oMi: Set incoming interface address */
/* -oMs: Set sender host name */
- else if (Ustrcmp(argrest, "Ms") == 0) sender_host_name = argv[++i];
+ else if (Ustrcmp(argrest, "Ms") == 0)
+ sender_host_name = string_copy_taint(argv[++i], TRUE);
/* -oMt: Set sender ident */
&& f.really_exim && !list_options && !checking)
{
uschar *p = big_buffer;
- Ustrcpy(p, "cwd= (failed)");
+ Ustrcpy(p, US"cwd= (failed)");
if (!initial_cwd)
p += 13;
uschar *quote;
if (p + len + 8 >= big_buffer + big_buffer_size)
{
- Ustrcpy(p, " ...");
+ Ustrcpy(p, US" ...");
log_write(0, LOG_MAIN, "%s", big_buffer);
- Ustrcpy(big_buffer, "...");
+ Ustrcpy(big_buffer, US"...");
p = big_buffer + 3;
}
printing = string_printing(argv[i]);
readconf_rest();
-/* The configuration data will have been read into POOL_PERM because we won't
-ever want to reset back past it. Change the current pool to POOL_MAIN. In fact,
-this is just a bit of pedantic tidiness. It wouldn't really matter if the
-configuration were read into POOL_MAIN, because we don't do any resets till
-later on. However, it seems right, and it does ensure that both pools get used.
-*/
-
-store_pool = POOL_MAIN;
-
/* Handle the -brt option. This is for checking out retry configurations.
The next three arguments are a domain name or a complete address, and
optionally two error numbers. All it does is to call the function that
else if ((pid = fork()) == 0)
{
(void)deliver_message(argv[i], forced_delivery, deliver_give_up);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
else if (pid < 0)
{
the caller. This will get overwritten below for an inetd call. If a trusted
caller has set it empty, unset it. */
-if (sender_ident == NULL) sender_ident = originator_login;
- else if (sender_ident[0] == 0) sender_ident = NULL;
+if (!sender_ident) sender_ident = originator_login;
+else if (!*sender_ident) sender_ident = NULL;
/* Handle the -brw option, which is for checking out rewriting rules. Cause log
writes (on errors) to go to stderr instead. Can't do this earlier, as want the
unless a trusted caller supplies a sender address with -f, or is passing in the
message via SMTP (inetd invocation or otherwise). */
-if ((sender_address == NULL && !smtp_input) ||
- (!f.trusted_caller && filter_test == FTEST_NONE))
+if ( !sender_address && !smtp_input
+ || !f.trusted_caller && filter_test == FTEST_NONE)
{
f.sender_local = TRUE;
via -oMas and -oMai and if so, they will already be set. Otherwise, force
defaults except when host checking. */
- if (authenticated_sender == NULL && !host_checking)
+ if (!authenticated_sender && !host_checking)
authenticated_sender = string_sprintf("%s@%s", originator_login,
qualify_domain_sender);
- if (authenticated_id == NULL && !host_checking)
+ if (!authenticated_id && !host_checking)
authenticated_id = originator_login;
}
specify a sender address for SMTP input, we leave sender_address unset. This
causes the MAIL commands to be honoured. */
-if ((!smtp_input && sender_address == NULL) ||
- !receive_check_set_sender(sender_address))
+if ( !smtp_input && !sender_address
+ || !receive_check_set_sender(sender_address))
{
/* Either the caller is not permitted to set a general sender, or this is
non-SMTP input and the trusted caller has not set a sender. If there is no
address, which indicates an error message, or doesn't exist (root caller, smtp
interface, no -f argument). */
-if (sender_address != NULL && sender_address[0] != 0 &&
- sender_address_domain == 0)
+if (sender_address && *sender_address && sender_address_domain == 0)
sender_address = string_sprintf("%s@%s", local_part_quote(sender_address),
qualify_domain_sender);
it. The code works for both IPv4 and IPv6, as it happens. */
size = host_aton(sender_host_address, x);
- sender_host_address = store_get(48); /* large enough for full IPv6 */
+ sender_host_address = store_get(48, FALSE); /* large enough for full IPv6 */
(void)host_nmtoa(size, x, -1, sender_host_address, ':');
/* Now set up for testing */
if (smtp_start_session())
{
- for (reset_point = store_get(0); ; store_reset(reset_point))
+ for (; (reset_point = store_mark()); store_reset(reset_point))
{
if (smtp_setup_msg() <= 0) break;
if (!receive_msg(FALSE)) break;
/* Save the current store pool point, for resetting at the start of
each message, and save the real sender address, if any. */
-reset_point = store_get(0);
real_sender_address = sender_address;
/* Loop to receive messages; receive_msg() returns TRUE if there are more
while (more)
{
+ reset_point = store_mark();
message_id[0] = 0;
/* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
}
}
- receive_add_recipient(recipient, -1);
+ receive_add_recipient(string_copy_taint(recipient, TRUE), -1);
s = ss;
if (!finished)
while (*(++s) != 0 && (*s == ',' || isspace(*s)));
rc = deliver_message(message_id, FALSE, FALSE);
search_tidyup();
- _exit((!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED)?
- EXIT_SUCCESS : EXIT_FAILURE);
+ exim_underbar_exit(!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED
+ ? EXIT_SUCCESS : EXIT_FAILURE);
}
if (pid < 0)
/* dummies needed by Solaris build */
void *
-store_get_3(int size, const char *filename, int linenumber)
+store_get_3(int size, BOOL tainted, const char *filename, int linenumber)
{ return NULL; }
-void
-store_reset_3(void *ptr, const char *filename, int linenumber)
+void **
+store_reset_3(void **ptr, int pool, const char *filename, int linenumber)
{ }
exit(1);
}
-Ustrcpy(temp_dbmname, argv[arg+1]);
-Ustrcat(temp_dbmname, ".dbmbuild_temp");
+Ustrcpy(temp_dbmname, US argv[arg+1]);
+Ustrcat(temp_dbmname, US".dbmbuild_temp");
Ustrcpy(dirname, temp_dbmname);
if ((bptr = Ustrrchr(dirname, '/')))
*bptr = '\0';
else
- Ustrcpy(dirname, ".");
+ Ustrcpy(dirname, US".");
/* It is apparently necessary to open with O_RDWR for this to work
with gdbm-1.7.3, though no reading is actually going to be done. */
#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
Ustrcpy(real_dbmname, temp_dbmname);
- Ustrcpy(buffer, argv[arg+1]);
+ Ustrcpy(buffer, US argv[arg+1]);
if (Urename(real_dbmname, buffer) != 0)
{
printf("Unable to rename %s as %s\n", real_dbmname, buffer);
void *yield;
EXIM_DATUM key_datum, result_datum;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
-yield = store_get(EXIM_DATUM_SIZE(result_datum));
+/* Assume for now that anything stored could have been tainted. Properly
+we should store the taint status along with the data. */
+
+yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
EXIM_DATUM key_datum, value_datum;
dbdata_generic *gptr = (dbdata_generic *)ptr;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
gptr->time_stamp = time(NULL);
dbfn_delete(open_db *dbblock, const uschar *key)
{
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
EXIM_DATUM key_datum;
uschar *t;
uschar name[MESSAGE_ID_LENGTH + 1];
void *value;
+ rmark reset_point = store_mark();
/* Keep a copy of the key separate, as in some DBM's the pointer is into data
which might change. */
printf(" %s %.*s\n", keybuffer, length, session->session);
break;
}
- store_reset(value);
}
+ store_reset(reset_point);
}
dbfn_close(dbm);
uschar **argv = USS cargv;
uschar buffer[256];
uschar name[256];
-void *reset_point = store_get(0);
+rmark reset_point;
name[0] = 0; /* No name set */
dbdata_type = check_args(argc, argv, US"fixdb", US"");
printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]);
-for(;;)
+for(; (reset_point = store_mark()); store_reset(reset_point))
{
open_db dbblock;
open_db *dbm;
uschar *t;
uschar field[256], value[256];
- store_reset(reset_point);
-
printf("> ");
if (Ufgets(buffer, 256, stdin) == NULL) break;
int maxkeep = 30 * 24 * 60 * 60;
int dbdata_type, i, oldest, path_len;
key_item *keychain = NULL;
-void *reset_point;
+rmark reset_point;
open_db dbblock;
open_db *dbm;
EXIM_CURSOR *cursor;
key;
key = dbfn_scan(dbm, FALSE, &cursor))
{
- key_item *k = store_get(sizeof(key_item) + Ustrlen(key));
+ key_item *k = store_get(sizeof(key_item) + Ustrlen(key), is_tainted(key));
k->next = keychain;
keychain = k;
Ustrcpy(k->key, key);
/* Now scan the collected keys and operate on the records, resetting
the store each time round. */
-reset_point = store_get(0);
-
-while (keychain)
+for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
{
dbdata_generic *value;
- store_reset(reset_point);
key = keychain->key;
keychain = keychain->next;
value = dbfn_read_with_length(dbm, key, NULL);
return sender_host_name ? sender_host_name : US"";
case vtype_localpart: /* Get local part from address */
- s = *((uschar **)(val));
- if (s == NULL) return US"";
- domain = Ustrrchr(s, '@');
- if (domain == NULL) return s;
+ if (!(s = *((uschar **)(val)))) return US"";
+ if (!(domain = Ustrrchr(s, '@'))) return s;
if (domain - s > sizeof(var_buffer) - 1)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
" in string expansion", sizeof(var_buffer));
- Ustrncpy(var_buffer, s, domain - s);
- var_buffer[domain - s] = 0;
- return var_buffer;
+ return string_copyn(s, domain - s);
case vtype_domain: /* Get domain from address */
- s = *((uschar **)(val));
- if (s == NULL) return US"";
+ if (!(s = *((uschar **)(val)))) return US"";
domain = Ustrrchr(s, '@');
- return (domain == NULL)? US"" : domain + 1;
+ return domain ? domain + 1 : US"";
case vtype_msgheaders:
return find_header(NULL, newsize, exists_only ? FH_EXISTS_ONLY : 0, NULL);
return NULL;
}
- s = read_name(name, 256, s+1, US"_");
+ s = read_name(name, sizeof(name), s+1, US"_");
/* Test for a header's existence. If the name contains a closing brace
character, this may be a user error where the terminating colon has been
&& (*++t == '_' || Ustrncmp(t, "eader_", 6) == 0)
)
{
- s = read_header_name(name, 256, s);
+ s = read_header_name(name, sizeof(name), s);
/* {-for-text-editors */
if (Ustrchr(name, '}') != NULL) malformed_header = TRUE;
if (yield) *yield =
{
if (!(t = find_variable(name, TRUE, yield == NULL, NULL)))
{
- expand_string_message = (name[0] == 0)?
- string_sprintf("variable name omitted after \"def:\"") :
- string_sprintf("unknown variable \"%s\" after \"def:\"", name);
+ expand_string_message = name[0]
+ ? string_sprintf("unknown variable \"%s\" after \"def:\"", name)
+ : US"variable name omitted after \"def:\"";
check_variable_error_message(name);
return NULL;
}
return NULL;
}
- DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item);
+ DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", opname, iterate_item);
if (!eval_condition(sub[1], resetok, &tempcond))
{
expand_string_message = string_sprintf("%s inside \"%s\" condition",
static uschar *
prvs_daystamp(int day_offset)
{
-uschar *days = store_get(32); /* Need at least 24 for cases */
+uschar *days = store_get(32, FALSE); /* Need at least 24 for cases */
(void)string_format(days, 32, TIME_T_FMT, /* where TIME_T_FMT is %lld */
(time(NULL) + day_offset*86400)/86400);
return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : US"100";
uschar finalhash[20];
uschar innerkey[64];
uschar outerkey[64];
-uschar *finalhash_hex = store_get(40);
+uschar *finalhash_hex;
if (key_num == NULL)
key_num = US"0";
chash_mid(HMAC_SHA1, &h, outerkey);
chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash);
-p = finalhash_hex;
+/* Hashing is deemed sufficient to de-taint any input data */
+
+p = finalhash_hex = store_get(40, FALSE);
for (int i = 0; i < 3; i++)
{
*p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left,
BOOL skipping, BOOL honour_dollar, BOOL *resetok_p)
{
+rmark reset_point = store_mark();
gstring * yield = string_get(Ustrlen(string) + 64);
int item_type;
const uschar *s = string;
f.expand_string_forcedfail = FALSE;
expand_string_message = US"";
+if (is_tainted(string))
+ {
+ expand_string_message =
+ string_sprintf("attempt to expand tainted string '%s'", s);
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message);
+ goto EXPAND_FAILED;
+ }
+
while (*s != 0)
{
uschar *value;
buffer. */
if (!yield)
- g = store_get(sizeof(gstring));
+ g = store_get(sizeof(gstring), FALSE);
else if (yield->ptr == 0)
{
- if (resetok) store_reset(yield);
+ if (resetok) reset_point = store_reset(reset_point);
yield = NULL;
- g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */
+ reset_point = store_mark();
+ g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */
}
/* Header */
{
if (sigalrm_seen || runrc == -256)
{
- expand_string_message = string_sprintf("command timed out");
+ expand_string_message = US"command timed out";
killpg(pid, SIGKILL); /* Kill the whole process group */
}
while (isspace(*s)) s++;
if (*s != ':')
{
- expand_string_message = string_sprintf(
- "missing object value-separator for extract json");
+ expand_string_message =
+ US"missing object value-separator for extract json";
goto EXPAND_FAILED_CURLY;
}
s++;
}
xtract = s;
- tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
- if (!tmp) goto EXPAND_FAILED;
+ if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok)))
+ goto EXPAND_FAILED;
xtract = string_copyn(xtract, s - xtract);
if (*s++ != '}')
newlist = string_append_listele(newlist, sep, dstitem);
newkeylist = string_append_listele(newkeylist, sep, dstfield);
+/*XXX why field-at-a-time copy? Why not just dup the rest of the list? */
while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
{
if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message);
goto EXPAND_FAILED;
}
- t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]));
+ t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), is_tainted(argv[0]));
Ustrcpy(t->name, argv[0]);
t->data.ptr = handle;
(void)tree_insertnode(&dlobj_anchor, t);
case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break;
case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_l"; break;
default:
- expand_string_message = string_sprintf("bad suffix on \"list\" operator");
+ expand_string_message = US"bad suffix on \"list\" operator";
goto EXPAND_FAILED;
}
gstring * g = NULL;
if (!yield)
- g = store_get(sizeof(gstring));
+ g = store_get(sizeof(gstring), FALSE);
else if (yield->ptr == 0)
{
- if (resetok) store_reset(yield);
+ if (resetok) reset_point = store_reset(reset_point);
yield = NULL;
- g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */
+ reset_point = store_mark();
+ g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */
}
if (!(value = find_variable(name, FALSE, skipping, &newsize)))
{
In many cases the final string will be the first one that was got and so there
will be optimal store usage. */
-if (resetok) store_reset(yield->s + (yield->size = yield->ptr + 1));
+if (resetok) gstring_release_unused(yield);
else if (resetok_p) *resetok_p = FALSE;
DEBUG(D_expand)
+ {
+ BOOL tainted = is_tainted(yield->s);
DEBUG(D_noutf8)
{
debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string);
debug_printf_indent("%sresult: %s\n",
skipping ? "|-----" : "\\_____", yield->s);
+ if (tainted)
+ debug_printf_indent("%s \\__(tainted)\n",
+ skipping ? "| " : " ");
if (skipping)
debug_printf_indent("\\___skipping: result is not used\n");
}
debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ
"expanding: %.*s\n",
(int)(s - string), string);
- debug_printf_indent("%s"
- UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
+ debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
"result: %s\n",
skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT,
yield->s);
+ if (tainted)
+ debug_printf_indent("%s(tainted)\n",
+ skipping
+ ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ);
if (skipping)
debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ
"skipping: result is not used\n");
}
+ }
expand_level--;
return yield->s;
}
#endif /* EXIM_PERL */
+/* Thie deliberately regards the input as untainted, so that it can be
+expanded; only reasonable since this is a test for string-expansions. */
+
while (fgets(buffer, sizeof(buffer), stdin) != NULL)
{
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
uschar *yield = expand_string(buffer);
- if (yield != NULL)
- {
+ if (yield)
printf("%s\n", yield);
- store_reset(reset_point);
- }
else
{
if (f.search_find_defer) printf("search_find deferred\n");
if (f.expand_string_forcedfail) printf("Forced failure\n");
printf("\n");
}
+ store_reset(reset_point);
}
search_tidyup();
if (*ptr == 0)
{
- *error_pointer = string_sprintf("\"then\" missing at end of filter file");
+ *error_pointer = US"\"then\" missing at end of filter file";
break;
}
/* Build a condition block from the specific word. */
- c = store_get(sizeof(condition_block));
+ c = store_get(sizeof(condition_block), FALSE);
c->left.u = c->right.u = NULL;
c->testfor = testfor;
testfor = TRUE;
}
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
if (*error_pointer) break;
- aa = store_get(sizeof(string_item));
+ aa = store_get(sizeof(string_item), FALSE);
aa->text = string_copy(buffer);
aa->next = c->left.a;
c->left.a = aa;
else if (Ustrcmp(buffer, "and") == 0)
{
- condition_block *andc = store_get(sizeof(condition_block));
+ condition_block *andc = store_get(sizeof(condition_block), FALSE);
andc->parent = current_parent;
andc->type = cond_and;
andc->testfor = TRUE;
else if (Ustrcmp(buffer, "or") == 0)
{
- condition_block *orc = store_get(sizeof(condition_block));
+ condition_block *orc = store_get(sizeof(condition_block), FALSE);
condition_block *or_parent = NULL;
if (current_parent)
if (Ustrncmp(ptr, "if(", 3) == 0)
{
- Ustrcpy(buffer, "if");
+ Ustrcpy(buffer, US"if");
ptr += 2;
}
else if (Ustrncmp(ptr, "elif(", 5) == 0)
{
- Ustrcpy(buffer, "elif");
+ Ustrcpy(buffer, US"elif");
ptr += 4;
}
else
if (command == logwrite_command)
{
int len = Ustrlen(buffer);
- if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, "\n");
+ if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, US"\n");
}
argument.u = string_copy(buffer);
if (*error_pointer != NULL) yield = FALSE; else
{
- new = store_get(sizeof(filter_cmd) + sizeof(union argtypes));
+ new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), FALSE);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &(new->next);
/* Finish has no arguments; fmsg defaults to NULL */
case finish_command:
- new = store_get(sizeof(filter_cmd));
+ new = store_get(sizeof(filter_cmd), FALSE);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &(new->next);
/* Set up the command block for if */
- new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes));
+ new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &(new->next);
while (had_else_endif == had_elif)
{
filter_cmd *newnew =
- store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes));
+ store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE);
new->args[2].f = newnew;
new = newnew;
new->next = NULL;
case mail_command:
case vacation_command:
- new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes));
+ new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), FALSE);
new->next = NULL;
new->command = command;
new->seen = seen_force? seen_value : FALSE;
(debug_selector & D_filter) != 0)
{
indent();
- debug_printf("Extracted address %s\n", filter_thisaddress);
+ debug_printf_indent("Extracted address %s\n", filter_thisaddress);
}
yield = test_condition(c->right.c, FALSE);
}
if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
- debug_printf("Match expanded arguments:\n");
- debug_printf(" Subject = %s\n", exp[0]);
- debug_printf(" Pattern = %s\n", exp[1]);
+ debug_printf_indent("Match expanded arguments:\n");
+ debug_printf_indent(" Subject = %s\n", exp[0]);
+ debug_printf_indent(" Pattern = %s\n", exp[1]);
}
re = pcre_compile(CS exp[1],
(debug_selector & D_filter) != 0)
{
indent();
- debug_printf("%sondition is %s: ",
+ debug_printf_indent("%sondition is %s: ",
toplevel? "C" : "Sub-c",
(yield == c->testfor)? "true" : "false");
print_condition(c, TRUE);
- debug_printf("\n");
+ debug_printf_indent("\n");
}
return yield == c->testfor;
address_item *addr;
BOOL condition_value;
-while (commands != NULL)
+while (commands)
{
int ff_ret;
uschar *fmsg, *ff_name;
for (i = 0; i < (command_exparg_count[commands->command] & 15); i++)
{
uschar *ss = commands->args[i].u;
- if (ss == NULL)
- {
+ if (!ss)
expargs[i] = NULL;
- }
else
- {
- expargs[i] = expand_string(ss);
- if (expargs[i] == NULL)
+ if (!(expargs[i] = expand_string(ss)))
{
*error_pointer = string_sprintf("failed to expand \"%s\" in "
"%s command: %s", ss, command_list[commands->command],
expand_string_message);
return FF_ERROR;
}
- }
}
/* Now switch for each command, setting the "delivered" flag if any of them
switch(commands->command)
{
case add_command:
- for (i = 0; i < 2; i++)
- {
- uschar *ss = expargs[i];
- uschar *end;
+ for (i = 0; i < 2; i++)
+ {
+ uschar *ss = expargs[i];
+ uschar *end;
- if (i == 1 && (*ss++ != 'n' || ss[1] != 0))
- {
- *error_pointer = string_sprintf("unknown variable \"%s\" in \"add\" "
- "command", expargs[i]);
- return FF_ERROR;
- }
+ if (i == 1 && (*ss++ != 'n' || ss[1] != 0))
+ {
+ *error_pointer = string_sprintf("unknown variable \"%s\" in \"add\" "
+ "command", expargs[i]);
+ return FF_ERROR;
+ }
- /* Allow for "--" at the start of the value (from -$n0) for example */
- if (i == 0) while (ss[0] == '-' && ss[1] == '-') ss += 2;
+ /* Allow for "--" at the start of the value (from -$n0) for example */
+ if (i == 0) while (ss[0] == '-' && ss[1] == '-') ss += 2;
- n[i] = (int)Ustrtol(ss, &end, 0);
- if (*end != 0)
- {
- *error_pointer = string_sprintf("malformed number \"%s\" in \"add\" "
- "command", ss);
- return FF_ERROR;
- }
- }
+ n[i] = (int)Ustrtol(ss, &end, 0);
+ if (*end != 0)
+ {
+ *error_pointer = string_sprintf("malformed number \"%s\" in \"add\" "
+ "command", ss);
+ return FF_ERROR;
+ }
+ }
- filter_n[n[1]] += n[0];
- if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]);
- break;
+ filter_n[n[1]] += n[0];
+ if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]);
+ break;
- /* A deliver command's argument must be a valid address. Its optional
- second argument (system filter only) must also be a valid address. */
+ /* A deliver command's argument must be a valid address. Its optional
+ second argument (system filter only) must also be a valid address. */
case deliver_command:
- for (i = 0; i < 2; i++)
- {
- s = expargs[i];
- if (s != NULL)
- {
- int start, end, domain;
- uschar *error;
- uschar *ss = parse_extract_address(s, &error, &start, &end, &domain,
- FALSE);
- if (ss != NULL)
- expargs[i] = ((filter_options & RDO_REWRITE) != 0)?
- rewrite_address(ss, TRUE, FALSE, global_rewrite_rules,
- rewrite_existflags) :
- rewrite_address_qualify(ss, TRUE);
- else
- {
- *error_pointer = string_sprintf("malformed address \"%s\" in "
- "filter file: %s", s, error);
- return FF_ERROR;
- }
- }
- }
+ for (i = 0; i < 2; i++)
+ {
+ s = expargs[i];
+ if (s != NULL)
+ {
+ int start, end, domain;
+ uschar *error;
+ uschar *ss = parse_extract_address(s, &error, &start, &end, &domain,
+ FALSE);
+ if (ss != NULL)
+ expargs[i] = ((filter_options & RDO_REWRITE) != 0)?
+ rewrite_address(ss, TRUE, FALSE, global_rewrite_rules,
+ rewrite_existflags) :
+ rewrite_address_qualify(ss, TRUE);
+ else
+ {
+ *error_pointer = string_sprintf("malformed address \"%s\" in "
+ "filter file: %s", s, error);
+ return FF_ERROR;
+ }
+ }
+ }
- /* Stick the errors address into a simple variable, as it will
- be referenced a few times. Check that the caller is permitted to
- specify it. */
+ /* Stick the errors address into a simple variable, as it will
+ be referenced a few times. Check that the caller is permitted to
+ specify it. */
- s = expargs[1];
+ s = expargs[1];
- if (s != NULL && !f.system_filtering)
- {
- uschar *ownaddress = expand_string(US"$local_part@$domain");
- if (strcmpic(ownaddress, s) != 0)
- {
- *error_pointer = US"errors_to must point to the caller's address";
- return FF_ERROR;
- }
- }
+ if (s != NULL && !f.system_filtering)
+ {
+ uschar *ownaddress = expand_string(US"$local_part@$domain");
+ if (strcmpic(ownaddress, s) != 0)
+ {
+ *error_pointer = US"errors_to must point to the caller's address";
+ return FF_ERROR;
+ }
+ }
- /* Test case: report what would happen */
+ /* Test case: report what would happen */
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%seliver message to: %s%s%s%s\n",
- (commands->seen)? "D" : "Unseen d",
- expargs[0],
- commands->noerror? " (noerror)" : "",
- (s != NULL)? " errors_to " : "",
- (s != NULL)? s : US"");
- }
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%seliver message to: %s%s%s%s\n",
+ (commands->seen)? "D" : "Unseen d",
+ expargs[0],
+ commands->noerror? " (noerror)" : "",
+ (s != NULL)? " errors_to " : "",
+ (s != NULL)? s : US"");
+ }
- /* Real case. */
+ /* Real case. */
- else
- {
- DEBUG(D_filter) debug_printf("Filter: %sdeliver message to: %s%s%s%s\n",
- (commands->seen)? "" : "unseen ",
- expargs[0],
- commands->noerror? " (noerror)" : "",
- (s != NULL)? " errors_to " : "",
- (s != NULL)? s : US"");
-
- /* Create the new address and add it to the chain, setting the
- af_ignore_error flag if necessary, and the errors address, which can be
- set in a system filter and to the local address in user filters. */
-
- addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */
- addr->prop.errors_address = (s == NULL)?
- s : string_copy(s); /* Default is NULL */
- if (commands->noerror) addr->prop.ignore_error = TRUE;
- addr->next = *generated;
- *generated = addr;
- }
- break;
+ else
+ {
+ DEBUG(D_filter) debug_printf_indent("Filter: %sdeliver message to: %s%s%s%s\n",
+ (commands->seen)? "" : "unseen ",
+ expargs[0],
+ commands->noerror? " (noerror)" : "",
+ (s != NULL)? " errors_to " : "",
+ (s != NULL)? s : US"");
+
+ /* Create the new address and add it to the chain, setting the
+ af_ignore_error flag if necessary, and the errors address, which can be
+ set in a system filter and to the local address in user filters. */
+
+ addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */
+ addr->prop.errors_address = (s == NULL)?
+ s : string_copy(s); /* Default is NULL */
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
+ addr->next = *generated;
+ *generated = addr;
+ }
+ break;
case save_command:
- s = expargs[0];
- mode = commands->args[1].i;
+ s = expargs[0];
+ mode = commands->args[1].i;
- /* Test case: report what would happen */
+ /* Test case: report what would happen */
- if (filter_test != FTEST_NONE)
- {
- indent();
- if (mode < 0)
- printf("%save message to: %s%s\n", (commands->seen)?
- "S" : "Unseen s", s, commands->noerror? " (noerror)" : "");
- else
- printf("%save message to: %s %04o%s\n", (commands->seen)?
- "S" : "Unseen s", s, mode, commands->noerror? " (noerror)" : "");
- }
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ if (mode < 0)
+ printf("%save message to: %s%s\n", (commands->seen)?
+ "S" : "Unseen s", s, commands->noerror? " (noerror)" : "");
+ else
+ printf("%save message to: %s %04o%s\n", (commands->seen)?
+ "S" : "Unseen s", s, mode, commands->noerror? " (noerror)" : "");
+ }
- /* Real case: Ensure save argument starts with / if there is a home
- directory to prepend. */
+ /* Real case: Ensure save argument starts with / if there is a home
+ directory to prepend. */
- else
- {
- if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 &&
- deliver_home != NULL && deliver_home[0] != 0)
- s = string_sprintf("%s/%s", deliver_home, s);
- DEBUG(D_filter) debug_printf("Filter: %ssave message to: %s%s\n",
- (commands->seen)? "" : "unseen ", s,
- commands->noerror? " (noerror)" : "");
-
- /* Create the new address and add it to the chain, setting the
- af_pfr and af_file flags, the af_ignore_error flag if necessary, and the
- mode value. */
-
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
- setflag(addr, af_pfr);
- setflag(addr, af_file);
- if (commands->noerror) addr->prop.ignore_error = TRUE;
- addr->mode = mode;
- addr->next = *generated;
- *generated = addr;
- }
- break;
+ else
+ {
+ if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 &&
+ deliver_home != NULL && deliver_home[0] != 0)
+ s = string_sprintf("%s/%s", deliver_home, s);
+ DEBUG(D_filter) debug_printf_indent("Filter: %ssave message to: %s%s\n",
+ (commands->seen)? "" : "unseen ", s,
+ commands->noerror? " (noerror)" : "");
+
+ /* Create the new address and add it to the chain, setting the
+ af_pfr and af_file flags, the af_ignore_error flag if necessary, and the
+ mode value. */
+
+ addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ setflag(addr, af_pfr);
+ setflag(addr, af_file);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
+ addr->mode = mode;
+ addr->next = *generated;
+ *generated = addr;
+ }
+ break;
case pipe_command:
- s = string_copy(commands->args[0].u);
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%sipe message to: %s%s\n", (commands->seen)?
- "P" : "Unseen p", s, commands->noerror? " (noerror)" : "");
- }
- else /* Ensure pipe command starts with | */
- {
- DEBUG(D_filter) debug_printf("Filter: %spipe message to: %s%s\n",
- (commands->seen)? "" : "unseen ", s,
- commands->noerror? " (noerror)" : "");
- if (s[0] != '|') s = string_sprintf("|%s", s);
-
- /* Create the new address and add it to the chain, setting the
- af_ignore_error flag if necessary. Set the af_expand_pipe flag so that
- each command argument is expanded in the transport after the command
- has been split up into separate arguments. */
-
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
- setflag(addr, af_pfr);
- setflag(addr, af_expand_pipe);
- if (commands->noerror) addr->prop.ignore_error = TRUE;
- addr->next = *generated;
- *generated = addr;
-
- /* If there are any numeric variables in existence (e.g. after a regex
- condition), or if $thisaddress is set, take a copy for use in the
- expansion. Note that we can't pass NULL for filter_thisaddress, because
- NULL terminates the list. */
-
- if (expand_nmax >= 0 || filter_thisaddress != NULL)
- {
- int i;
- int ecount = (expand_nmax >= 0)? expand_nmax : -1;
- uschar **ss = store_get(sizeof(uschar *) * (ecount + 3));
- addr->pipe_expandn = ss;
- if (filter_thisaddress == NULL) filter_thisaddress = US"";
- *ss++ = string_copy(filter_thisaddress);
- for (i = 0; i <= expand_nmax; i++)
- *ss++ = string_copyn(expand_nstring[i], expand_nlength[i]);
- *ss = NULL;
- }
- }
- break;
+ s = string_copy(commands->args[0].u);
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%sipe message to: %s%s\n", (commands->seen)?
+ "P" : "Unseen p", s, commands->noerror? " (noerror)" : "");
+ }
+ else /* Ensure pipe command starts with | */
+ {
+ DEBUG(D_filter) debug_printf_indent("Filter: %spipe message to: %s%s\n",
+ commands->seen ? "" : "unseen ", s,
+ commands->noerror ? " (noerror)" : "");
+ if (s[0] != '|') s = string_sprintf("|%s", s);
- /* Set up the file name and mode, and close any previously open
- file. */
+ /* Create the new address and add it to the chain, setting the
+ af_ignore_error flag if necessary. Set the af_expand_pipe flag so that
+ each command argument is expanded in the transport after the command
+ has been split up into separate arguments. */
+
+ addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ setflag(addr, af_pfr);
+ setflag(addr, af_expand_pipe);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
+ addr->next = *generated;
+ *generated = addr;
+
+ /* If there are any numeric variables in existence (e.g. after a regex
+ condition), or if $thisaddress is set, take a copy for use in the
+ expansion. Note that we can't pass NULL for filter_thisaddress, because
+ NULL terminates the list. */
+
+ if (expand_nmax >= 0 || filter_thisaddress != NULL)
+ {
+ int ecount = expand_nmax >= 0 ? expand_nmax : -1;
+ uschar **ss = store_get(sizeof(uschar *) * (ecount + 3), FALSE);
+
+ addr->pipe_expandn = ss;
+ if (!filter_thisaddress) filter_thisaddress = US"";
+ *ss++ = string_copy(filter_thisaddress);
+ for (int i = 0; i <= expand_nmax; i++)
+ *ss++ = string_copyn(expand_nstring[i], expand_nlength[i]);
+ *ss = NULL;
+ }
+ }
+ break;
+
+ /* Set up the file name and mode, and close any previously open
+ file. */
case logfile_command:
- log_mode = commands->args[1].i;
- if (log_mode == -1) log_mode = 0600;
- if (log_fd >= 0)
- {
- (void)close(log_fd);
- log_fd = -1;
- }
- log_filename = expargs[0];
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename);
- }
- break;
+ log_mode = commands->args[1].i;
+ if (log_mode == -1) log_mode = 0600;
+ if (log_fd >= 0)
+ {
+ (void)close(log_fd);
+ log_fd = -1;
+ }
+ log_filename = expargs[0];
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename);
+ }
+ break;
case logwrite_command:
- s = expargs[0];
+ s = expargs[0];
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L",
- string_printing(s));
- }
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L",
+ string_printing(s));
+ }
- /* Attempt to write to a log file only if configured as permissible.
- Logging may be forcibly skipped for verifying or testing. */
+ /* Attempt to write to a log file only if configured as permissible.
+ Logging may be forcibly skipped for verifying or testing. */
- else if ((filter_options & RDO_LOG) != 0) /* Locked out */
- {
- DEBUG(D_filter)
- debug_printf("filter log command aborted: euid=%ld\n",
- (long int)geteuid());
- *error_pointer = US"logwrite command forbidden";
- return FF_ERROR;
- }
- else if ((filter_options & RDO_REALLOG) != 0)
- {
- int len;
- DEBUG(D_filter) debug_printf("writing filter log as euid %ld\n",
- (long int)geteuid());
- if (log_fd < 0)
- {
- if (log_filename == NULL)
- {
- *error_pointer = US"attempt to obey \"logwrite\" command "
- "without a previous \"logfile\"";
- return FF_ERROR;
- }
- log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode);
- if (log_fd < 0)
- {
- *error_pointer = string_open_failed(errno, "filter log file \"%s\"",
- log_filename);
- return FF_ERROR;
- }
- }
- len = Ustrlen(s);
- if (write(log_fd, s, len) != len)
- {
- *error_pointer = string_sprintf("write error on file \"%s\": %s",
- log_filename, strerror(errno));
- return FF_ERROR;
- }
- }
- else
- {
- DEBUG(D_filter) debug_printf("skipping logwrite (verifying or testing)\n");
- }
- break;
+ else if ((filter_options & RDO_LOG) != 0) /* Locked out */
+ {
+ DEBUG(D_filter)
+ debug_printf_indent("filter log command aborted: euid=%ld\n",
+ (long int)geteuid());
+ *error_pointer = US"logwrite command forbidden";
+ return FF_ERROR;
+ }
+ else if ((filter_options & RDO_REALLOG) != 0)
+ {
+ int len;
+ DEBUG(D_filter) debug_printf_indent("writing filter log as euid %ld\n",
+ (long int)geteuid());
+ if (log_fd < 0)
+ {
+ if (log_filename == NULL)
+ {
+ *error_pointer = US"attempt to obey \"logwrite\" command "
+ "without a previous \"logfile\"";
+ return FF_ERROR;
+ }
+ log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode);
+ if (log_fd < 0)
+ {
+ *error_pointer = string_open_failed(errno, "filter log file \"%s\"",
+ log_filename);
+ return FF_ERROR;
+ }
+ }
+ len = Ustrlen(s);
+ if (write(log_fd, s, len) != len)
+ {
+ *error_pointer = string_sprintf("write error on file \"%s\": %s",
+ log_filename, strerror(errno));
+ return FF_ERROR;
+ }
+ }
+ else
+ {
+ DEBUG(D_filter) debug_printf_indent("skipping logwrite (verifying or testing)\n");
+ }
+ break;
- /* Header addition and removal is available only in the system filter. The
- command is rejected at parse time otherwise. However "headers charset" is
- always permitted. */
+ /* Header addition and removal is available only in the system filter. The
+ command is rejected at parse time otherwise. However "headers charset" is
+ always permitted. */
case headers_command:
- {
- int subtype = commands->args[1].i;
- s = expargs[0];
+ {
+ int subtype = commands->args[1].i;
+ s = expargs[0];
- if (filter_test != FTEST_NONE)
- printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" :
- (subtype == FALSE)? "remove" : "charset", string_printing(s));
+ if (filter_test != FTEST_NONE)
+ printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" :
+ (subtype == FALSE)? "remove" : "charset", string_printing(s));
- if (subtype == TRUE)
- {
- while (isspace(*s)) s++;
- if (s[0] != 0)
- {
- header_add(htype_other, "%s%s", s, (s[Ustrlen(s)-1] == '\n')?
- "" : "\n");
- header_last->type = header_checkname(header_last, FALSE);
- if (header_last->type >= 'a') header_last->type = htype_other;
- }
- }
+ if (subtype == TRUE)
+ {
+ while (isspace(*s)) s++;
+ if (s[0] != 0)
+ {
+ header_add(htype_other, "%s%s", s, (s[Ustrlen(s)-1] == '\n')?
+ "" : "\n");
+ header_last->type = header_checkname(header_last, FALSE);
+ if (header_last->type >= 'a') header_last->type = htype_other;
+ }
+ }
- else if (subtype == FALSE)
- {
- int sep = 0;
- uschar *ss;
- const uschar *list = s;
- uschar buffer[128];
- while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
- != NULL)
- header_remove(0, ss);
- }
+ else if (subtype == FALSE)
+ {
+ int sep = 0;
+ uschar *ss;
+ const uschar *list = s;
+ uschar buffer[128];
+ while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
+ != NULL)
+ header_remove(0, ss);
+ }
- /* This setting lasts only while the filter is running; on exit, the
- variable is reset to the previous value. */
+ /* This setting lasts only while the filter is running; on exit, the
+ variable is reset to the previous value. */
- else headers_charset = s;
- }
- break;
+ else headers_charset = s;
+ }
+ break;
- /* Defer, freeze, and fail are available only when explicitly permitted.
- These commands are rejected at parse time otherwise. The message can get
- very long by the inclusion of message headers; truncate if it is, and also
- ensure printing characters so as not to mess up log files. */
+ /* Defer, freeze, and fail are available only when explicitly permitted.
+ These commands are rejected at parse time otherwise. The message can get
+ very long by the inclusion of message headers; truncate if it is, and also
+ ensure printing characters so as not to mess up log files. */
case defer_command:
- ff_name = US"defer";
- ff_ret = FF_DEFER;
- goto DEFERFREEZEFAIL;
+ ff_name = US"defer";
+ ff_ret = FF_DEFER;
+ goto DEFERFREEZEFAIL;
case fail_command:
- ff_name = US"fail";
- ff_ret = FF_FAIL;
- goto DEFERFREEZEFAIL;
+ ff_name = US"fail";
+ ff_ret = FF_FAIL;
+ goto DEFERFREEZEFAIL;
case freeze_command:
- ff_name = US"freeze";
- ff_ret = FF_FREEZE;
+ ff_name = US"freeze";
+ ff_ret = FF_FREEZE;
- DEFERFREEZEFAIL:
- fmsg = expargs[0];
- if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, " ... (truncated)");
- fmsg = US string_printing(fmsg);
- *error_pointer = fmsg;
+ DEFERFREEZEFAIL:
+ fmsg = expargs[0];
+ if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, US" ... (truncated)");
+ fmsg = US string_printing(fmsg);
+ *error_pointer = fmsg;
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg);
- }
- else DEBUG(D_filter) debug_printf("Filter: %s \"%s\"\n", ff_name, fmsg);
- return ff_ret;
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg);
+ }
+ else DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg);
+ return ff_ret;
case finish_command:
- if (filter_test != FTEST_NONE)
- {
- indent();
- printf("%sinish\n", (commands->seen)? "Seen f" : "F");
- }
- else
- {
- DEBUG(D_filter) debug_printf("Filter: %sfinish\n",
- (commands->seen)? " Seen " : "");
- }
- finish_obeyed = TRUE;
- return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+ if (filter_test != FTEST_NONE)
+ {
+ indent();
+ printf("%sinish\n", (commands->seen)? "Seen f" : "F");
+ }
+ else
+ {
+ DEBUG(D_filter) debug_printf_indent("Filter: %sfinish\n",
+ (commands->seen)? " Seen " : "");
+ }
+ finish_obeyed = TRUE;
+ return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
case if_command:
- {
- uschar *save_address = filter_thisaddress;
- int ok = FF_DELIVERED;
- condition_value = test_condition(commands->args[0].c, TRUE);
- if (*error_pointer != NULL) ok = FF_ERROR; else
- {
- output_indent += 2;
- ok = interpret_commands(commands->args[condition_value? 1:2].f,
- generated);
- output_indent -= 2;
- }
- filter_thisaddress = save_address;
- if (finish_obeyed || (ok != FF_DELIVERED && ok != FF_NOTDELIVERED))
- return ok;
- }
- break;
+ {
+ uschar *save_address = filter_thisaddress;
+ int ok = FF_DELIVERED;
+ condition_value = test_condition(commands->args[0].c, TRUE);
+ if (*error_pointer != NULL) ok = FF_ERROR; else
+ {
+ output_indent += 2;
+ ok = interpret_commands(commands->args[condition_value? 1:2].f,
+ generated);
+ output_indent -= 2;
+ }
+ filter_thisaddress = save_address;
+ if (finish_obeyed || (ok != FF_DELIVERED && ok != FF_NOTDELIVERED))
+ return ok;
+ }
+ break;
- /* To try to catch runaway loops, do not generate mail if the
- return path is unset or if a non-trusted user supplied -f <>
- as the return path. */
+ /* To try to catch runaway loops, do not generate mail if the
+ return path is unset or if a non-trusted user supplied -f <>
+ as the return path. */
case mail_command:
case vacation_command:
- if (return_path == NULL || return_path[0] == 0)
- {
- if (filter_test != FTEST_NONE)
- printf("%s command ignored because return_path is empty\n",
- command_list[commands->command]);
- else DEBUG(D_filter) debug_printf("%s command ignored because return_path "
- "is empty\n", command_list[commands->command]);
- break;
- }
+ if (return_path == NULL || return_path[0] == 0)
+ {
+ if (filter_test != FTEST_NONE)
+ printf("%s command ignored because return_path is empty\n",
+ command_list[commands->command]);
+ else DEBUG(D_filter) debug_printf_indent("%s command ignored because return_path "
+ "is empty\n", command_list[commands->command]);
+ break;
+ }
- /* Check the contents of the strings. The type of string can be deduced
- from the value of i.
+ /* Check the contents of the strings. The type of string can be deduced
+ from the value of i.
- . If i is equal to mailarg_index_text it's a text string for the body,
- where anything goes.
+ . If i is equal to mailarg_index_text it's a text string for the body,
+ where anything goes.
- . If i is > mailarg_index_text, we are dealing with a file name, which
- cannot contain non-printing characters.
+ . If i is > mailarg_index_text, we are dealing with a file name, which
+ cannot contain non-printing characters.
- . If i is less than mailarg_index_headers we are dealing with something
- that will go in a single message header line, where newlines must be
- followed by white space.
+ . If i is less than mailarg_index_headers we are dealing with something
+ that will go in a single message header line, where newlines must be
+ followed by white space.
- . If i is equal to mailarg_index_headers, we have a string that contains
- one or more headers. Newlines that are not followed by white space must
- be followed by a header name.
- */
+ . If i is equal to mailarg_index_headers, we have a string that contains
+ one or more headers. Newlines that are not followed by white space must
+ be followed by a header name.
+ */
- for (i = 0; i < MAILARGS_STRING_COUNT; i++)
- {
- uschar *p;
- uschar *s = expargs[i];
+ for (i = 0; i < MAILARGS_STRING_COUNT; i++)
+ {
+ uschar *p;
+ uschar *s = expargs[i];
- if (s == NULL) continue;
+ if (s == NULL) continue;
- if (i != mailarg_index_text) for (p = s; *p != 0; p++)
- {
- int c = *p;
- if (i > mailarg_index_text)
+ if (i != mailarg_index_text) for (p = s; *p != 0; p++)
{
- if (!mac_isprint(c))
+ int c = *p;
+ if (i > mailarg_index_text)
{
- *error_pointer = string_sprintf("non-printing character in \"%s\" "
- "in %s command", string_printing(s),
- command_list[commands->command]);
- return FF_ERROR;
+ if (!mac_isprint(c))
+ {
+ *error_pointer = string_sprintf("non-printing character in \"%s\" "
+ "in %s command", string_printing(s),
+ command_list[commands->command]);
+ return FF_ERROR;
+ }
}
- }
- /* i < mailarg_index_text */
+ /* i < mailarg_index_text */
- else if (c == '\n' && !isspace(p[1]))
- {
- if (i < mailarg_index_headers)
+ else if (c == '\n' && !isspace(p[1]))
{
- *error_pointer = string_sprintf("\\n not followed by space in "
- "\"%.1024s\" in %s command", string_printing(s),
- command_list[commands->command]);
- return FF_ERROR;
- }
+ if (i < mailarg_index_headers)
+ {
+ *error_pointer = string_sprintf("\\n not followed by space in "
+ "\"%.1024s\" in %s command", string_printing(s),
+ command_list[commands->command]);
+ return FF_ERROR;
+ }
- /* Check for the start of a new header line within the string */
+ /* Check for the start of a new header line within the string */
- else
- {
- uschar *pp;
- for (pp = p + 1;; pp++)
+ else
{
- c = *pp;
- if (c == ':' && pp != p + 1) break;
- if (c == 0 || c == ':' || isspace(*pp))
+ uschar *pp;
+ for (pp = p + 1;; pp++)
{
- *error_pointer = string_sprintf("\\n not followed by space or "
- "valid header name in \"%.1024s\" in %s command",
- string_printing(s), command_list[commands->command]);
- return FF_ERROR;
+ c = *pp;
+ if (c == ':' && pp != p + 1) break;
+ if (c == 0 || c == ':' || isspace(*pp))
+ {
+ *error_pointer = string_sprintf("\\n not followed by space or "
+ "valid header name in \"%.1024s\" in %s command",
+ string_printing(s), command_list[commands->command]);
+ return FF_ERROR;
+ }
}
+ p = pp;
}
- p = pp;
}
- }
- } /* Loop to scan the string */
-
- /* The string is OK */
+ } /* Loop to scan the string */
- commands->args[i].u = s;
- }
+ /* The string is OK */
- /* Proceed with mail or vacation command */
-
- if (filter_test != FTEST_NONE)
- {
- uschar *to = commands->args[mailarg_index_to].u;
- indent();
- printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M",
- to ? to : US"<default>",
- commands->command == vacation_command ? " (vacation)" : "",
- commands->noerror ? " (noerror)" : "");
- for (i = 1; i < MAILARGS_STRING_COUNT; i++)
- {
- uschar *arg = commands->args[i].u;
- if (arg)
- {
- int len = Ustrlen(mailargs[i]);
- int indent = (debug_selector != 0)? output_indent : 0;
- while (len++ < 7 + indent) printf(" ");
- printf("%s: %s%s\n", mailargs[i], string_printing(arg),
- (commands->args[mailarg_index_expand].u != NULL &&
- Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
- }
+ commands->args[i].u = s;
}
- if (commands->args[mailarg_index_return].u)
- printf("Return original message\n");
- }
- else
- {
- uschar *tt;
- uschar *to = commands->args[mailarg_index_to].u;
- gstring * log_addr = NULL;
-
- if (!to) to = expand_string(US"$reply_address");
- while (isspace(*to)) to++;
- for (tt = to; *tt != 0; tt++) /* Get rid of newlines */
- if (*tt == '\n') *tt = ' ';
+ /* Proceed with mail or vacation command */
- DEBUG(D_filter)
+ if (filter_test != FTEST_NONE)
{
- debug_printf("Filter: %smail to: %s%s%s\n",
- commands->seen ? "seen " : "",
- to,
+ uschar *to = commands->args[mailarg_index_to].u;
+ indent();
+ printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M",
+ to ? to : US"<default>",
commands->command == vacation_command ? " (vacation)" : "",
commands->noerror ? " (noerror)" : "");
for (i = 1; i < MAILARGS_STRING_COUNT; i++)
{
uschar *arg = commands->args[i].u;
- if (arg != NULL)
+ if (arg)
{
int len = Ustrlen(mailargs[i]);
- while (len++ < 15) debug_printf(" ");
- debug_printf("%s: %s%s\n", mailargs[i], string_printing(arg),
+ int indent = (debug_selector != 0)? output_indent : 0;
+ while (len++ < 7 + indent) printf(" ");
+ printf("%s: %s%s\n", mailargs[i], string_printing(arg),
(commands->args[mailarg_index_expand].u != NULL &&
Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
}
}
+ if (commands->args[mailarg_index_return].u)
+ printf("Return original message\n");
}
-
- /* Create the "address" for the autoreply. This is used only for logging,
- as the actual recipients are extracted from the To: line by -t. We use the
- same logic here to extract the working addresses (there may be more than
- one). Just in case there are a vast number of addresses, stop when the
- string gets too long. */
-
- tt = to;
- while (*tt != 0)
+ else
{
- uschar *ss = parse_find_address_end(tt, FALSE);
- uschar *recipient, *errmess;
- int start, end, domain;
- int temp = *ss;
+ uschar *tt;
+ uschar *to = commands->args[mailarg_index_to].u;
+ gstring * log_addr = NULL;
- *ss = 0;
- recipient = parse_extract_address(tt, &errmess, &start, &end, &domain,
- FALSE);
- *ss = temp;
+ if (!to) to = expand_string(US"$reply_address");
+ while (isspace(*to)) to++;
- /* Ignore empty addresses and errors; an error will occur later if
- there's something really bad. */
+ for (tt = to; *tt != 0; tt++) /* Get rid of newlines */
+ if (*tt == '\n') *tt = ' ';
- if (recipient)
+ DEBUG(D_filter)
{
- log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1);
- log_addr = string_cat (log_addr, recipient);
+ debug_printf_indent("Filter: %smail to: %s%s%s\n",
+ commands->seen ? "seen " : "",
+ to,
+ commands->command == vacation_command ? " (vacation)" : "",
+ commands->noerror ? " (noerror)" : "");
+ for (i = 1; i < MAILARGS_STRING_COUNT; i++)
+ {
+ uschar *arg = commands->args[i].u;
+ if (arg != NULL)
+ {
+ int len = Ustrlen(mailargs[i]);
+ while (len++ < 15) debug_printf_indent(" ");
+ debug_printf_indent("%s: %s%s\n", mailargs[i], string_printing(arg),
+ (commands->args[mailarg_index_expand].u != NULL &&
+ Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
+ }
+ }
}
- /* Check size */
+ /* Create the "address" for the autoreply. This is used only for logging,
+ as the actual recipients are extracted from the To: line by -t. We use the
+ same logic here to extract the working addresses (there may be more than
+ one). Just in case there are a vast number of addresses, stop when the
+ string gets too long. */
- if (log_addr && log_addr->ptr > 256)
+ tt = to;
+ while (*tt != 0)
{
- log_addr = string_catn(log_addr, US", ...", 5);
- break;
- }
+ uschar *ss = parse_find_address_end(tt, FALSE);
+ uschar *recipient, *errmess;
+ int start, end, domain;
+ int temp = *ss;
- /* Move on past this address */
+ *ss = 0;
+ recipient = parse_extract_address(tt, &errmess, &start, &end, &domain,
+ FALSE);
+ *ss = temp;
- tt = ss + (*ss ? 1 : 0);
- while (isspace(*tt)) tt++;
- }
+ /* Ignore empty addresses and errors; an error will occur later if
+ there's something really bad. */
- if (log_addr)
- addr = deliver_make_addr(string_from_gstring(log_addr), FALSE);
- else
- {
- addr = deliver_make_addr(US ">**bad-reply**", FALSE);
- setflag(addr, af_bad_reply);
- }
+ if (recipient)
+ {
+ log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1);
+ log_addr = string_cat (log_addr, recipient);
+ }
- setflag(addr, af_pfr);
- if (commands->noerror) addr->prop.ignore_error = TRUE;
- addr->next = *generated;
- *generated = addr;
+ /* Check size */
- addr->reply = store_get(sizeof(reply_item));
- addr->reply->from = NULL;
- addr->reply->to = string_copy(to);
- addr->reply->file_expand =
- commands->args[mailarg_index_expand].u != NULL;
- addr->reply->expand_forbid = expand_forbid;
- addr->reply->return_message =
- commands->args[mailarg_index_return].u != NULL;
- addr->reply->once_repeat = 0;
-
- if (commands->args[mailarg_index_once_repeat].u != NULL)
- {
- addr->reply->once_repeat =
- readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0,
- FALSE);
- if (addr->reply->once_repeat < 0)
+ if (log_addr && log_addr->ptr > 256)
+ {
+ log_addr = string_catn(log_addr, US", ...", 5);
+ break;
+ }
+
+ /* Move on past this address */
+
+ tt = ss + (*ss ? 1 : 0);
+ while (isspace(*tt)) tt++;
+ }
+
+ if (log_addr)
+ addr = deliver_make_addr(string_from_gstring(log_addr), FALSE);
+ else
{
- *error_pointer = string_sprintf("Bad time value for \"once_repeat\" "
- "in mail or vacation command: %s",
- commands->args[mailarg_index_once_repeat].u);
- return FF_ERROR;
+ addr = deliver_make_addr(US ">**bad-reply**", FALSE);
+ setflag(addr, af_bad_reply);
}
- }
- /* Set up all the remaining string arguments (those other than "to") */
+ setflag(addr, af_pfr);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
+ addr->next = *generated;
+ *generated = addr;
+
+ addr->reply = store_get(sizeof(reply_item), FALSE);
+ addr->reply->from = NULL;
+ addr->reply->to = string_copy(to);
+ addr->reply->file_expand =
+ commands->args[mailarg_index_expand].u != NULL;
+ addr->reply->expand_forbid = expand_forbid;
+ addr->reply->return_message =
+ commands->args[mailarg_index_return].u != NULL;
+ addr->reply->once_repeat = 0;
+
+ if (commands->args[mailarg_index_once_repeat].u != NULL)
+ {
+ addr->reply->once_repeat =
+ readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0,
+ FALSE);
+ if (addr->reply->once_repeat < 0)
+ {
+ *error_pointer = string_sprintf("Bad time value for \"once_repeat\" "
+ "in mail or vacation command: %s",
+ commands->args[mailarg_index_once_repeat].u);
+ return FF_ERROR;
+ }
+ }
- for (i = 1; i < mailargs_string_passed; i++)
- {
- uschar *ss = commands->args[i].u;
- *(USS((US addr->reply) + reply_offsets[i])) =
- ss ? string_copy(ss) : NULL;
+ /* Set up all the remaining string arguments (those other than "to") */
+
+ for (i = 1; i < mailargs_string_passed; i++)
+ {
+ uschar *ss = commands->args[i].u;
+ *(USS((US addr->reply) + reply_offsets[i])) =
+ ss ? string_copy(ss) : NULL;
+ }
}
- }
- break;
+ break;
case testprint_command:
- if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
- {
- const uschar *s = string_printing(expargs[0]);
- if (filter_test == FTEST_NONE)
- debug_printf("Filter: testprint: %s\n", s);
- else
- printf("Testprint: %s\n", s);
- }
+ if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
+ {
+ const uschar *s = string_printing(expargs[0]);
+ if (filter_test == FTEST_NONE)
+ debug_printf_indent("Filter: testprint: %s\n", s);
+ else
+ printf("Testprint: %s\n", s);
+ }
}
commands = commands->next;
{
uschar *self, *self_from, *self_to;
uschar *psself = NULL, *psself_from = NULL, *psself_to = NULL;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
BOOL yield;
header_line *h;
int to_count = 2;
contain any value other than "no", the message is not personal (RFC 3834).
Previously the test was for "auto-". */
-for (h = header_list; h != NULL; h = h->next)
+for (h = header_list; h; h = h->next)
{
uschar *s;
if (h->type == htype_old) continue;
filter_cmd **lastcmdptr = &commands;
DEBUG(D_route) debug_printf("Filter: start of processing\n");
+acl_level++;
/* Initialize "not in an if command", set the global flag that is always TRUE
while filtering, and zero the variables. */
switch(yield)
{
case FF_DEFER:
- s = US"Filtering ended by \"defer\".";
- break;
+ s = US"Filtering ended by \"defer\".";
+ break;
case FF_FREEZE:
- s = US"Filtering ended by \"freeze\".";
- break;
+ s = US"Filtering ended by \"freeze\".";
+ break;
case FF_FAIL:
- s = US"Filtering ended by \"fail\".";
- break;
+ s = US"Filtering ended by \"fail\".";
+ break;
case FF_DELIVERED:
- s = US"Filtering set up at least one significant delivery "
- "or other action.\n"
- "No other deliveries will occur.";
- break;
+ s = US"Filtering set up at least one significant delivery "
+ "or other action.\n"
+ "No other deliveries will occur.";
+ break;
case FF_NOTDELIVERED:
- s = US"Filtering did not set up a significant delivery.\n"
- "Normal delivery will occur.";
- break;
+ s = US"Filtering did not set up a significant delivery.\n"
+ "Normal delivery will occur.";
+ break;
case FF_ERROR:
- s = string_sprintf("Filter error: %s", *error);
- break;
+ s = string_sprintf("Filter error: %s", *error);
+ break;
}
if (filter_test != FTEST_NONE) printf("%s\n", CS s);
- else debug_printf("%s\n", s);
+ else debug_printf_indent("%s\n", s);
}
/* Close the log file if it was opened, and kill off any numerical variables
f.filter_running = FALSE;
headers_charset = save_headers_charset;
+acl_level--;
DEBUG(D_route) debug_printf("Filter: end of processing\n");
return yield;
}
int above = message_body_visible - below;
if (above > 0)
{
- uschar *temp = store_get(below);
+ uschar *temp = store_get(below, TRUE);
memcpy(temp, message_body_end, below);
memmove(message_body_end, s+1, above);
memcpy(message_body_end + above, temp, below);
return FALSE;
}
-filebuf = store_get(statbuf.st_size + 1);
+filebuf = store_get(statbuf.st_size + 1, is_tainted(filename));
rc = read(fd, filebuf, statbuf.st_size);
(void)close(fd);
extern void exim_exit(int, const uschar *) NORETURN;
extern void exim_nullstd(void);
extern void exim_setugid(uid_t, gid_t, BOOL, uschar *);
+extern void exim_underbar_exit(int);
extern void exim_wait_tick(struct timeval *, int);
extern int exp_bool(address_item *addr,
uschar *mtype, uschar *mname, unsigned dgb_opt, uschar *oname, BOOL bvalue,
extern void smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
extern void smtp_proxy_tls(void *, uschar *, size_t, int *, int) NORETURN;
extern BOOL smtp_read_response(void *, uschar *, int, int, int);
-extern void smtp_reset(void *);
+extern void *smtp_reset(void *);
extern void smtp_respond(uschar *, int, BOOL, uschar *);
extern void smtp_notquit_exit(uschar *, uschar *, uschar *, ...);
extern void smtp_port_for_connect(host_item *, int);
extern int stdin_feof(void);
extern int stdin_ferror(void);
extern int stdin_ungetc(int);
+
+extern void store_exit(void);
extern gstring *string_append(gstring *, int, ...) WARN_UNUSED_RESULT;
extern gstring *string_append_listele(gstring *, uschar, const uschar *) WARN_UNUSED_RESULT;
extern gstring *string_append_listele_n(gstring *, uschar, const uschar *, unsigned) WARN_UNUSED_RESULT;
extern uschar *string_copy_dnsdomain(uschar *);
extern uschar *string_copy_malloc(const uschar *);
extern uschar *string_dequote(const uschar **);
-extern gstring *string_fmt_append(gstring *, const char *, ...) ALMOST_PRINTF(2,3);
-extern BOOL string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4);
extern uschar *string_format_size(int, uschar *);
extern int string_interpret_escape(const uschar **);
extern int string_is_ip_address(const uschar *, int *);
extern BOOL string_is_utf8(const uschar *);
#endif
extern uschar *string_nextinlist(const uschar **, int *, uschar *, int);
-extern uschar *string_open_failed(int, const char *, ...) PRINTF_FUNCTION(2,3);
extern const uschar *string_printing2(const uschar *, BOOL);
extern uschar *string_split_message(uschar *);
extern uschar *string_timediff(struct timeval *);
extern uschar *string_localpart_alabel_to_utf8(const uschar *, uschar **);
extern uschar *string_localpart_utf8_to_alabel(const uschar *, uschar **);
#endif
-extern gstring *string_vformat(gstring *, BOOL, const char *, va_list);
+
+#define string_format(buf, siz, fmt, ...) \
+ string_format_trc(buf, siz, US __FUNCTION__, __LINE__, fmt, __VA_ARGS__)
+extern BOOL string_format_trc(uschar *, int, const uschar *, unsigned,
+ const char *, ...) ALMOST_PRINTF(5,6);
+
+#define string_vformat(g, flgs, fmt, ap) \
+ string_vformat_trc(g, US __FUNCTION__, __LINE__, \
+ STRING_SPRINTF_BUFFER_SIZE, flgs, fmt, ap)
+extern gstring *string_vformat_trc(gstring *, const uschar *, unsigned,
+ unsigned, unsigned, const char *, va_list);
+
+#define string_open_failed(eno, fmt, ...) \
+ string_open_failed_trc(eno, US __FUNCTION__, __LINE__, fmt, __VA_ARGS__)
+extern uschar *string_open_failed_trc(int, const uschar *, unsigned,
+ const char *, ...) PRINTF_FUNCTION(4,5);
+
extern int strcmpic(const uschar *, const uschar *);
extern int strncmpic(const uschar *, const uschar *, int);
extern uschar *strstric(uschar *, uschar *, BOOL);
/******************************************************************************/
/* String functions */
+#if !defined(MACRO_PREDEF)
/*************************************************
* Copy and save string *
*************************************************/
/* This function assumes that memcpy() is faster than strcpy().
*/
-#if !defined(MACRO_PREDEF)
static inline uschar *
-string_copy(const uschar *s)
+string_copy_taint_trc(const uschar *s, BOOL tainted, const char * func, int line)
{
int len = Ustrlen(s) + 1;
-uschar *ss = store_get(len);
+uschar *ss = store_get_3(len, tainted, func, line);
memcpy(ss, s, len);
return ss;
}
+#define string_copy_taint(s, tainted) \
+ string_copy_taint_trc((s), tainted, __FUNCTION__, __LINE__)
+
+static inline uschar *
+string_copy(const uschar * s)
+{
+return string_copy_taint((s), is_tainted(s));
+}
+
/*************************************************
* Copy, lowercase and save string *
static inline uschar *
string_copylc(const uschar *s)
{
-uschar *ss = store_get(Ustrlen(s) + 1);
+uschar *ss = store_get(Ustrlen(s) + 1, is_tainted(s));
uschar *p = ss;
while (*s != 0) *p++ = tolower(*s++);
*p = 0;
static inline uschar *
string_copyn(const uschar *s, int n)
{
-uschar *ss = store_get(n + 1);
+uschar *ss = store_get(n + 1, is_tainted(s));
Ustrncpy(ss, s, n);
ss[n] = 0;
return ss;
static inline uschar *
string_copynlc(uschar *s, int n)
{
-uschar *ss = store_get(n + 1);
+uschar *ss = store_get(n + 1, is_tainted(s));
uschar *p = ss;
while (n-- > 0) *p++ = tolower(*s++);
*p = 0;
}
+/*************************************************
+* Copy and save string in longterm store *
+*************************************************/
+
+/* This function assumes that memcpy() is faster than strcpy().
+
+Argument: string to copy
+Returns: copy of string in new store
+*/
+
+static inline uschar *
+string_copy_perm(const uschar *s, BOOL force_taint)
+{
+int old_pool = store_pool;
+int len = Ustrlen(s) + 1;
+uschar *ss;
+
+store_pool = POOL_PERM;
+ss = store_get(len, force_taint || is_tainted(s));
+memcpy(ss, s, len);
+store_pool = old_pool;
+return ss;
+}
+
+
+
+/* sprintf into a buffer, taint-unchecked */
+
+static inline void
+string_format_nt(uschar * buf, int siz, const char * fmt, ...)
+{
+gstring gs = { .size = siz, .ptr = 0, .s = buf };
+va_list ap;
+va_start(ap, fmt);
+(void) string_vformat(&gs, SVFMT_TAINT_NOCHK, fmt, ap);
+va_end(ap);
+}
+
+
+
/******************************************************************************/
/* Growable-string functions */
-/* Create a growable-string with some preassigned space, in untainted memory */
+/* Create a growable-string with some preassigned space */
+
+#define string_get_tainted(size, tainted) \
+ string_get_tainted_trc((size), (tainted), __FUNCTION__, __LINE__)
static inline gstring *
-string_get(unsigned size)
+string_get_tainted_trc(unsigned size, BOOL tainted, const char * func, unsigned line)
{
-gstring * g = store_get(sizeof(gstring) + size);
+gstring * g = store_get_3(sizeof(gstring) + size, tainted, func, line);
g->size = size;
g->ptr = 0;
g->s = US(g + 1);
return g;
}
+#define string_get(size) \
+ string_get_trc((size), __FUNCTION__, __LINE__)
+
+static inline gstring *
+string_get_trc(unsigned size, const char * func, unsigned line)
+{
+return string_get_tainted_trc(size, FALSE, func, line);
+}
+
/* NUL-terminate the C string in the growable-string, and return it. */
static inline uschar *
return g->s;
}
+
+#define gstring_release_unused(g) \
+ gstring_release_unused_trc(g, __FUNCTION__, __LINE__)
+
static inline void
-gstring_release_unused(gstring * g)
+gstring_release_unused_trc(gstring * g, const char * file, unsigned line)
{
-if (g) store_reset(g->s + (g->size = g->ptr + 1));
+if (g) store_release_above_3(g->s + (g->size = g->ptr + 1), file, line);
+}
+
+
+/* sprintf-append to a growable-string */
+
+#define string_fmt_append(g, fmt, ...) \
+ string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \
+ SVFMT_EXTEND|SVFMT_REBUFFER, fmt, __VA_ARGS__)
+
+#define string_fmt_append_f(g, flgs, fmt, ...) \
+ string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \
+ flgs, fmt, __VA_ARGS__)
+
+static inline gstring *
+string_fmt_append_f_trc(gstring * g, const uschar * func, unsigned line,
+ unsigned flags, const char *format, ...)
+{
+va_list ap;
+va_start(ap, format);
+g = string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE,
+ flags, format, ap);
+va_end(ap);
+return g;
}
/******************************************************************************/
#endif
uschar *pipelining_advertise_hosts = US"*";
uschar *primary_hostname = NULL;
-uschar process_info[PROCESS_INFO_SIZE];
+uschar *process_info;
int process_info_len = 0;
uschar *process_log_path = NULL;
extern BOOL preserve_message_logs; /* Save msglog files */
extern uschar *primary_hostname; /* Primary name of this computer */
extern BOOL print_topbitchars; /* Topbit chars are printing chars */
-extern uschar process_info[]; /* For SIGUSR1 output */
+extern uschar *process_info; /* For SIGUSR1 output */
extern int process_info_len;
extern uschar *process_log_path; /* Alternate path */
extern BOOL prod_requires_admin; /* TRUE if prodding requires admin */
void
exim_sha_finish(hctx * h, blob * b)
{
-b->data = store_get(b->len = h->hashlen);
+/* Hashing is sufficient to purify any tainted input */
+b->data = store_get(b->len = h->hashlen, FALSE);
switch (h->method)
{
case HASH_SHA1: SHA1_Final (b->data, &h->u.sha1); break;
void
exim_sha_finish(hctx * h, blob * b)
{
-b->data = store_get(b->len = h->hashlen);
+b->data = store_get(b->len = h->hashlen, FALSE);
gnutls_hash_output(h->sha, b->data);
}
void
exim_sha_finish(hctx * h, blob * b)
{
-b->data = store_get(b->len = h->hashlen);
+b->data = store_get(b->len = h->hashlen, FALSE);
memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen);
}
void
exim_sha_finish(hctx * h, blob * b)
{
-b->data = store_get(b->len = h->hashlen);
+b->data = store_get(b->len = h->hashlen, FALSE);
switch (h->method)
{
case HASH_SHA1: sha1_finish(h->u.sha1, b->data); break;
void
exim_sha_finish(hctx * h, blob * b)
{
-b->data = store_get(b->len = h->hashlen);
+b->data = store_get(b->len = h->hashlen, FALSE);
native_sha1_end(&h->sha1, NULL, 0, b->data);
}
header_line **hptr;
uschar *p, *q;
-uschar buffer[HEADER_ADD_BUFFER_SIZE];
-gstring gs = { .size = HEADER_ADD_BUFFER_SIZE, .ptr = 0, .s = buffer };
+uschar * buf = store_get(HEADER_ADD_BUFFER_SIZE, FALSE);
+gstring gs = { .size = HEADER_ADD_BUFFER_SIZE, .ptr = 0, .s = buf };
if (!header_last) return NULL;
-if (!string_vformat(&gs, FALSE, format, ap))
+if (!string_vformat(&gs, SVFMT_REBUFFER, format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string too long in header_add: "
"%.100s ...", string_from_gstring(&gs));
+
+if (gs.s != buf) store_release_above(buf);
+gstring_release_unused(&gs);
string_from_gstring(&gs);
/* Find where to insert this header */
point, we have hptr pointing to the link field that will point to the new
header, and h containing the following header, or NULL. */
-for (p = q = buffer; *p; p = q)
+for (p = q = gs.s; *p; p = q)
{
for (;;)
{
if (*(++q) != ' ' && *q != '\t') break;
}
- new = store_get(sizeof(header_line));
+ new = store_get(sizeof(header_line), FALSE);
new->text = string_copyn(p, q - p);
new->slen = q - p;
new->type = type;
(ipa == 6 && af == AF_INET6))
{
int x[4];
- yield = store_get(sizeof(struct hostent));
- alist = store_get(2 * sizeof(char *));
- adds = store_get(alen);
+ yield = store_get(sizeof(struct hostent), FALSE);
+ alist = store_get(2 * sizeof(char *), FALSE);
+ adds = store_get(alen, FALSE);
yield->h_name = CS name;
yield->h_aliases = NULL;
yield->h_addrtype = af;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
count++;
- yield = store_get(sizeof(struct hostent));
- alist = store_get((count + 1) * sizeof(char *));
- adds = store_get(count *alen);
+ yield = store_get(sizeof(struct hostent), FALSE);
+ alist = store_get((count + 1) * sizeof(char *), FALSE);
+ adds = store_get(count *alen, FALSE);
yield->h_name = CS name;
yield->h_aliases = NULL;
continue;
}
- h = store_get(sizeof(host_item));
+ h = store_get(sizeof(host_item), FALSE);
h->name = name;
h->address = NULL;
h->port = PORT_NONE;
host_build_sender_fullhost(void)
{
BOOL show_helo = TRUE;
-uschar * address, * fullhost, * rcvhost, * reset_point;
+uschar * address, * fullhost, * rcvhost;
+rmark reset_point;
int len;
if (!sender_host_address) return;
-reset_point = store_get(0);
+reset_point = store_mark();
/* Set up address, with or without the port. After discussion, it seems that
the only format that doesn't cause trouble is [aaaa]:pppp. However, we can't
}
}
-if (sender_fullhost) store_free(sender_fullhost);
-sender_fullhost = string_copy_malloc(fullhost);
-if (sender_rcvhost) store_free(sender_rcvhost);
-sender_rcvhost = string_copy_malloc(rcvhost);
+sender_fullhost = string_copy_perm(fullhost, TRUE);
+sender_rcvhost = string_copy_perm(rcvhost, TRUE);
store_reset(reset_point);
ident set, no host => U=ident
ident set, host set => H=sender_fullhost U=ident
+Use taint-unchecked routines on the assumption we'll never expand the results.
+
Arguments:
useflag TRUE if first item to be flagged (H= or U=); if there are two
items, the second is always flagged
host_and_ident(BOOL useflag)
{
if (!sender_fullhost)
- (void)string_format(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
sender_ident ? sender_ident : US"unknown");
else
{
if (LOGGING(incoming_interface) && interface_address)
iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
if (sender_ident)
- (void)string_format(big_buffer, big_buffer_size, "%s%s%s U=%s",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s",
flag, sender_fullhost, iface, sender_ident);
else
- (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
+ string_format_nt(big_buffer, big_buffer_size, "%s%s%s",
flag, sender_fullhost, iface);
}
return big_buffer;
address above. The field in the ip_address_item is large enough to hold an
IPv6 address. */
- next = store_get(sizeof(ip_address_item));
+ next = store_get(sizeof(ip_address_item), FALSE);
next->next = NULL;
Ustrcpy(next->address, s);
next->port = port;
ip_address_item *ipa2;
for (ipa2 = list; ipa2; ipa2 = ipa2->next)
if (Ustrcmp(ipa2->address, ipa->address) == 0) return list;
-ipa2 = store_get_perm(sizeof(ip_address_item));
+ipa2 = store_get_perm(sizeof(ip_address_item), FALSE);
*ipa2 = *ipa;
ipa2->next = list;
return ipa2;
if (local_interface_data == NULL)
{
- void *reset_item = store_get(0);
+ void *reset_item = store_mark();
ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces,
US"local_interfaces");
ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces,
/* Failed to look up the host. */
-if (hosts == NULL)
+if (!hosts)
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup failed: h_errno=%d\n",
h_errno);
treat this as non-existent. In some operating systems, this is returned as an
empty string; in others as a single dot. */
-if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
+if (!hosts->h_name || !hosts->h_name[0] || hosts->h_name[0] == '.')
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an empty name: "
"treated as non-existent host name\n");
/* Copy and lowercase the name, which is in static storage in many systems.
Put it in permanent memory. */
-s = US hosts->h_name;
-len = Ustrlen(s) + 1;
-t = sender_host_name = store_get_perm(len);
-while (*s != 0) *t++ = tolower(*s++);
-*t = 0;
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_TAINT_PERM; /* names are tainted */
+
+ sender_host_name = string_copylc(US hosts->h_name);
-/* If the host has aliases, build a copy of the alias list */
+ /* If the host has aliases, build a copy of the alias list */
-if (hosts->h_aliases)
- {
- int count = 1;
- uschar **ptr;
- for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
- ptr = sender_host_aliases = store_get_perm(count * sizeof(uschar *));
- for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
- {
- uschar *s = *aliases;
- int len = Ustrlen(s) + 1;
- uschar *t = *ptr++ = store_get_perm(len);
- while (*s != 0) *t++ = tolower(*s++);
- *t = 0;
- }
- *ptr = NULL;
+ if (hosts->h_aliases)
+ {
+ int count = 1;
+ uschar **ptr;
+
+ for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
+ store_pool = POOL_PERM;
+ ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
+ store_pool = POOL_TAINT_PERM;
+
+ for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
+ *ptr++ = string_copylc(*aliases);
+ *ptr = NULL;
+ }
+ store_pool = old_pool;
}
return OK;
/* Get store for the list of aliases. For compatibility with
gethostbyaddr, we make an empty list if there are none. */
- aptr = sender_host_aliases = store_get(count * sizeof(uschar *));
+ aptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
/* Re-scan and extract the names */
rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
{
- uschar * s = store_get(ssize);
+ uschar * s = store_get(ssize, TRUE); /* names are tainted */
/* If an overlong response was received, the data will have been
truncated and dn_expand may fail. */
break;
}
- store_reset(s + Ustrlen(s) + 1);
- if (s[0] == 0)
+ store_release_above(s + Ustrlen(s) + 1);
+ if (!s[0])
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
"empty name: treated as non-existent host name\n");
}
if (!sender_host_name) sender_host_name = s;
else *aptr++ = s;
- while (*s != 0) { *s = tolower(*s); s++; }
+ while (*s) { *s = tolower(*s); s++; }
}
*aptr = NULL; /* End of alias list */
store_pool = old_pool; /* Reset store pool */
- /* If we've found a names, break out of the "order" loop */
+ /* If we've found a name, break out of the "order" loop */
- if (sender_host_name != NULL) break;
+ if (sender_host_name) break;
}
/* If the DNS lookup deferred, we must also defer. */
else
{
- host_item *next = store_get(sizeof(host_item));
+ host_item *next = store_get(sizeof(host_item), FALSE);
next->name = host->name;
next->mx = host->mx;
next->address = text_address;
/* Not a duplicate */
new_sort_key = host->mx * 1000 + random_number(500) + randoffset;
- next = store_get(sizeof(host_item));
+ next = store_get(sizeof(host_item), FALSE);
/* New address goes first: insert the new block after the first one
(so as not to disturb the original pointer) but put the new address
/* Make a new host item and seek the correct insertion place */
{
int sort_key = precedence * 1000 + weight;
- host_item *next = store_get(sizeof(host_item));
+ host_item *next = store_get(sizeof(host_item), FALSE);
next->name = string_copy_dnsdomain(data);
next->address = NULL;
next->port = port;
callout_address = string_copy(path);
server.sun_family = AF_UNIX;
-Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
+Ustrncpy(US server.sun_path, path, sizeof(server.sun_path)-1);
server.sun_path[sizeof(server.sun_path)-1] = '\0';
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
{
extern int smtp_fflush(void);
extern void smtp_printf(const char *, BOOL, ...) PRINTF_FUNCTION(1,3);
extern void smtp_vprintf(const char *, BOOL, va_list);
-extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2);
+
+#define string_sprintf(fmt, ...) \
+ string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__)
+extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4);
#ifdef LOCAL_SCAN
/* When compiling a local_scan() file we want to rename a published API, so that
if (flags & LOG_CONFIG) g = log_config_info(g, flags);
+ /* We want to be able to log tainted info, but log_buffer is directly
+ malloc'd. So use deliberately taint-nonchecking routines to build into
+ it, trusting that we will never expand the results. */
+
va_start(ap, format);
i = g->ptr;
- if (!string_vformat(g, FALSE, format, ap))
+ if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
{
g->ptr = i;
g = string_cat(g, US"**** log string overflowed log buffer ****");
va_start(ap, format);
{
int i = g->ptr;
- if (!string_vformat(g, FALSE, format, ap))
+
+ /* We want to be able to log tainted info, but log_buffer is directly
+ malloc'd. So use deliberately taint-nonchecking routines to build into
+ it, trusting that we will never expand the results. */
+
+ if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
{
g->ptr = i;
g = string_cat(g, US"**** log string overflowed log buffer ****\n");
if ( flags & LOG_SENDER
&& g->ptr < LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender))
- g = string_fmt_append(g, " from <%s>", raw_sender);
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " from <%s>", raw_sender);
/* Add list of recipients to the message if required; the raw list,
before rewriting, was saved in raw_recipients. There may be none, if an ACL
&& raw_recipients_count > 0)
{
int i;
- g = string_fmt_append(g, " for");
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL);
for (i = 0; i < raw_recipients_count; i++)
{
uschar * s = raw_recipients[i];
if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break;
- g = string_fmt_append(g, " %s", s);
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s);
}
}
{
if (header_list && LOGGING(rejected_header))
{
- uschar * p = g->s + g->ptr;
+ gstring * g2;
int i;
if (recipients_count > 0)
{
/* List the sender */
- string_format(p, LOG_BUFFER_SIZE - g->ptr,
- "Envelope-from: <%s>\n", sender_address);
- while (*p) p++;
- g->ptr = p - g->s;
+ g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+ "Envelope-from: <%s>\n", sender_address);
+ if (g2) g = g2;
/* List up to 5 recipients */
- string_format(p, LOG_BUFFER_SIZE - g->ptr,
- "Envelope-to: <%s>\n", recipients_list[0].address);
- while (*p) p++;
- g->ptr = p - g->s;
+ g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+ "Envelope-to: <%s>\n", recipients_list[0].address);
+ if (g2) g = g2;
for (i = 1; i < recipients_count && i < 5; i++)
{
- string_format(p, LOG_BUFFER_SIZE - g->ptr, " <%s>\n",
- recipients_list[i].address);
- while (*p) p++;
- g->ptr = p - g->s;
+ g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+ " <%s>\n", recipients_list[i].address);
+ if (g2) g = g2;
}
if (i < recipients_count)
{
- string_format(p, LOG_BUFFER_SIZE - g->ptr,
- " ...\n");
- while (*p) p++;
- g->ptr = p - g->s;
+ g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " ...\n", NULL);
+ if (g2) g = g2;
}
}
for (header_line * h = header_list; h; h = h->next) if (h->text)
{
- BOOL fitted = string_format(p, LOG_BUFFER_SIZE - g->ptr,
- "%c %s", h->type, h->text);
- while (*p) p++;
- g->ptr = p - g->s;
- if (!fitted) /* Buffer is full; truncate */
+ g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+ "%c %s", h->type, h->text);
+ if (g2)
+ g = g2;
+ else /* Buffer is full; truncate */
{
g->ptr -= 100; /* For message and separator */
if (g->s[g->ptr-1] == '\n') g->ptr--;
}
/* Having got a file open we need the structure to put things in */
- cdbp = store_get(sizeof(struct cdb_state));
+ cdbp = store_get(sizeof(struct cdb_state), FALSE);
/* store_get() does not return if memory was not available... */
/* preload the structure.... */
cdbp->fileno = fileno;
/* get a buffer to stash the basic offsets in - this should speed
* things up a lot - especially on multiple lookups */
- cdbp->cdb_offsets = store_get(CDB_HASH_TABLE);
+ cdbp->cdb_offsets = store_get(CDB_HASH_TABLE, FALSE);
/* now fill the buffer up... */
if (cdb_bread(fileno, cdbp->cdb_offsets, CDB_HASH_TABLE) == -1) {
item_ptr += item_key_len;
- /* ... and the returned result */
+ /* ... and the returned result. Assume it is not
+ tainted, lacking any way of telling. */
- *result = store_get(item_dat_len + 1);
+ *result = store_get(item_dat_len + 1, FALSE);
memcpy(*result, item_ptr, item_dat_len);
(*result)[item_dat_len] = 0;
return OK;
if (item_key_len == key_len)
{ /* finally check if key matches */
- uschar * item_key = store_get(key_len);
+ rmark reset_point = store_mark();
+ uschar * item_key = store_get(key_len, TRUE); /* keys liable to be tainted */
if (cdb_bread(cdbp->fileno, item_key, key_len) == -1) return DEFER;
- if (Ustrncmp(keystring, item_key, key_len) == 0) {
-
- /* Reclaim some store */
- store_reset(item_key);
-
- /* matches - get data length */
- item_dat_len = cdb_unpack(packbuf + 4);
-
- /* then we build a new result string. We know we have enough
- memory so disable Coverity errors about the tainted item_dat_ken */
-
- *result = store_get(item_dat_len + 1);
- /* coverity[tainted_data] */
- if (cdb_bread(cdbp->fileno, *result, item_dat_len) == -1)
- return DEFER;
-
- /* coverity[tainted_data] */
- (*result)[item_dat_len] = 0;
- return OK;
- }
+ if (Ustrncmp(keystring, item_key, key_len) == 0)
+ {
+ /* Reclaim some store */
+ store_reset(reset_point);
+
+ /* matches - get data length */
+ item_dat_len = cdb_unpack(packbuf + 4);
+
+ /* then we build a new result string. We know we have enough
+ memory so disable Coverity errors about the tainted item_dat_ken */
+
+ *result = store_get(item_dat_len + 1, FALSE);
+ /* coverity[tainted_data] */
+ if (cdb_bread(cdbp->fileno, *result, item_dat_len) == -1)
+ return DEFER;
+
+ /* coverity[tainted_data] */
+ (*result)[item_dat_len] = 0;
+ return OK;
+ }
/* Reclaim some store */
- store_reset(item_key);
+ store_reset(reset_point);
}
}
cur_offset += 8;
or less than, the length of the delimited list passed in + 1. */
buflen = length + 3;
-key_buffer = store_get(buflen);
+key_buffer = store_get(buflen, is_tainted(keystring));
key_buffer[0] = '\0';
/* Reclaim unused memory */
-store_reset(yield->s + yield->ptr + 1);
+gstring_release_unused(yield);
/* If yield NULL we have not found anything. Otherwise, insert the terminating
zero and return the result. */
XSQLDA *out_sqlda;
XSQLVAR *var;
int i;
+rmark reset_point;
char buffer[256];
ISC_STATUS status[20], *statusp = status;
}
else
{
- cn = store_get(sizeof(ibase_connection));
+ cn = store_get(sizeof(ibase_connection), FALSE);
cn->server = server_copy;
cn->dbh = NULL;
cn->transh = NULL;
goto IBASE_EXIT;
}
-out_sqlda = store_get(XSQLDA_LENGTH(1));
+/* Lacking any information, assume that the data is untainted */
+reset_point = store_mark();
+out_sqlda = store_get(XSQLDA_LENGTH(1), FALSE);
out_sqlda->version = SQLDA_VERSION1;
out_sqlda->sqln = 1;
(status, &cn->transh, &stmth, 0, query, 1, out_sqlda))
{
isc_interprete(buffer, &statusp);
- store_reset(out_sqlda);
+ reset_point = store_reset(reset_point);
out_sqlda = NULL;
*errmsg =
string_sprintf("Interbase prepare_statement() failed: %s",
/* re-allocate the output structure if there's more than one field */
if (out_sqlda->sqln < out_sqlda->sqld)
{
- XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld));
+ XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld), FALSE);
if (isc_dsql_describe
(status, &stmth, out_sqlda->version, new_sqlda))
{
isc_interprete(buffer, &statusp);
isc_dsql_free_statement(status, &stmth, DSQL_drop);
- store_reset(out_sqlda);
+ reset_point = store_reset(reset_point);
out_sqlda = NULL;
*errmsg = string_sprintf("Interbase describe_statement() failed: %s",
buffer);
switch (var->sqltype & ~1)
{
case SQL_VARYING:
- var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2);
+ var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2, FALSE);
break;
case SQL_TEXT:
- var->sqldata = CS store_get(sizeof(char) * var->sqllen);
+ var->sqldata = CS store_get(sizeof(char) * var->sqllen, FALSE);
break;
case SQL_SHORT:
- var->sqldata = CS store_get(sizeof(short));
+ var->sqldata = CS store_get(sizeof(short), FALSE);
break;
case SQL_LONG:
- var->sqldata = CS store_get(sizeof(ISC_LONG));
+ var->sqldata = CS store_get(sizeof(ISC_LONG), FALSE);
break;
#ifdef SQL_INT64
case SQL_INT64:
- var->sqldata = CS store_get(sizeof(ISC_INT64));
+ var->sqldata = CS store_get(sizeof(ISC_INT64), FALSE);
break;
#endif
case SQL_FLOAT:
- var->sqldata = CS store_get(sizeof(float));
+ var->sqldata = CS store_get(sizeof(float), FALSE);
break;
case SQL_DOUBLE:
- var->sqldata = CS store_get(sizeof(double));
+ var->sqldata = CS store_get(sizeof(double), FALSE);
break;
#ifdef SQL_TIMESTAMP
case SQL_DATE:
- var->sqldata = CS store_get(sizeof(ISC_QUAD));
+ var->sqldata = CS store_get(sizeof(ISC_QUAD), FALSE);
break;
#else
case SQL_TIMESTAMP:
- var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP));
+ var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP), FALSE);
break;
case SQL_TYPE_DATE:
- var->sqldata = CS store_get(sizeof(ISC_DATE));
+ var->sqldata = CS store_get(sizeof(ISC_DATE), FALSE);
break;
case SQL_TYPE_TIME:
- var->sqldata = CS store_get(sizeof(ISC_TIME));
+ var->sqldata = CS store_get(sizeof(ISC_TIME), FALSE);
break;
#endif
}
if (var->sqltype & 1)
- var->sqlind = (short *) store_get(sizeof(short));
+ var->sqlind = (short *) store_get(sizeof(short), FALSE);
}
/* finally, we're ready to execute the statement */
*errmsg = US "Interbase: no data found";
}
else
- store_reset(result->s + result->ptr + 1);
+ gstring_release_unused(result);
/* Get here by goto from various error checks. */
if (count == 0)
return s;
- t = quoted = store_get(Ustrlen(s) + count + 1);
+ t = quoted = store_get(Ustrlen(s) + count + 1, FALSE);
while ((c = *s++) != 0) {
if (Ustrchr("'", c) != NULL) {
This burns some 300kB in handling a 37kB JSON file, for the benefit of
a fast free. The alternative of staying with malloc is nearly as bad,
eyeballing the activity there are 20% the number of free vs. alloc
-calls (before the big bunch at the end). */
+calls (before the big bunch at the end).
+
+Assume that the file is trusted, so no tainting */
static void *
json_malloc(size_t nbytes)
{
-void * p = store_get((int)nbytes);
+void * p = store_get((int)nbytes, FALSE);
/* debug_printf("%s %d: %p\n", __FUNCTION__, (int)nbytes, p); */
return p;
}
than the host name + "ldaps:///" plus : and a port number, say 20 + the
length of the host name. What we get should accommodate both, easily. */
- uschar *shost = (host == NULL)? US"" : host;
- uschar *init_url = store_get(20 + 3 * Ustrlen(shost));
- uschar *init_ptr;
+ uschar * shost = host ? host : US"";
+ rmark reset_point = store_mark();
+ gstring * g;
/* Handle connection via Unix socket ("ldapi"). We build a basic LDAP URI to
contain the path name, with slashes escaped as %2F. */
if (ldapi)
{
- int ch;
- init_ptr = init_url + 8;
- Ustrcpy(init_url, "ldapi://");
- while ((ch = *shost++))
- if (ch == '/')
- { Ustrncpy(init_ptr, "%2F", 3); init_ptr += 3; }
- else
- *init_ptr++ = ch;
- *init_ptr = 0;
+ g = string_catn(NULL, US"ldapi://", 8);
+ for (uschar ch; (ch = *shost); shost++)
+ g = ch == '/' ? string_catn(g, US"%2F", 3) : string_catn(g, shost, 1);
}
/* This is not an ldapi call. Just build a URI with the protocol type, host
else
{
- init_ptr = Ustrchr(ldap_url, '/');
- Ustrncpy(init_url, ldap_url, init_ptr - ldap_url);
- init_ptr = init_url + (init_ptr - ldap_url);
- sprintf(CS init_ptr, "//%s:%d/", shost, port);
+ uschar * init_ptr = Ustrchr(ldap_url, '/');
+ g = string_catn(NULL, ldap_url, init_ptr - ldap_url);
+ g = string_fmt_append(g, "//%s:%d/", shost, port);
}
+ string_from_gstring(g);
/* Call ldap_initialize() and check the result */
- DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", init_url);
- if ((rc = ldap_initialize(&ld, CS init_url)) != LDAP_SUCCESS)
+ DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", g->s);
+ if ((rc = ldap_initialize(&ld, CS g->s)) != LDAP_SUCCESS)
{
*errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n",
- rc, init_url);
+ rc, g->s);
goto RETURN_ERROR;
}
- store_reset(init_url); /* Might as well save memory when we can */
+ store_reset(reset_point); /* Might as well save memory when we can */
/* ------------------------- Not OpenLDAP ---------------------- */
/* Now add this connection to the chain of cached connections */
- lcp = store_get(sizeof(LDAP_CONNECTION));
- lcp->host = (host == NULL)? NULL : string_copy(host);
+ lcp = store_get(sizeof(LDAP_CONNECTION), FALSE);
+ lcp->host = host ? string_copy(host) : NULL;
lcp->bound = FALSE;
lcp->user = NULL;
lcp->password = NULL;
if (rescount < 1)
{
- *errmsg = string_sprintf("LDAP search: no results");
+ *errmsg = US"LDAP search: no results";
error_yield = FAIL;
goto RETURN_ERROR_BREAK;
}
else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF;
else
{
+ *errmsg = US"LDAP option REFERRALS is not \"follow\" or \"nofollow\"";
DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
return DEFER;
}
/* Get sufficient store to hold the quoted string */
-t = quoted = store_get(len + count + 1);
+t = quoted = store_get(len + count + 1, is_tainted(s));
/* Handle plain quote_ldap */
{
if (Ustrchr(LDAP_DN_QUOTE, c) != NULL)
{
- Ustrncpy(t, "%5C", 3); /* insert \ where needed */
+ Ustrncpy(t, US"%5C", 3); /* insert \ where needed */
t += 3; /* fall through to check URL */
}
if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */
while (*ss++ != 0)
{
- Ustrncpy(t, "%5C%20", 6);
+ Ustrncpy(t, US"%5C%20", 6);
t += 6;
}
}
int ret, save_errno;
const uschar * errstr;
-lmdb_p = store_get(sizeof(Lmdbstrct));
+lmdb_p = store_get(sizeof(Lmdbstrct), FALSE);
lmdb_p->txn = NULL;
if ((ret = mdb_env_create(&db_env)))
BOOL last_was_eol = TRUE;
BOOL this_is_eol = TRUE;
int old_pool = store_pool;
-void *reset_point = NULL;
+rmark reset_point = NULL;
uschar buffer[4096];
/* Wildcard searches may use up some store, because of expansions. We don't
if(type == LSEARCH_WILD || type == LSEARCH_NWILD)
{
store_pool = POOL_MAIN;
- reset_point = store_get(0);
+ reset_point = store_mark();
}
filename = filename; /* Keep picky compilers happy */
if (reset_point)
{
- store_reset(reset_point);
+ reset_point = store_reset(reset_point);
store_pool = old_pool;
}
yield = string_cat(yield, s);
}
- store_reset(yield->s + yield->ptr + 1);
+ gstring_release_unused(yield);
*result = string_from_gstring(yield);
return OK;
}
/* Get store for a new handle, initialize it, and connect to the server */
- mysql_handle = store_get(sizeof(MYSQL));
+ mysql_handle = store_get(sizeof(MYSQL), FALSE);
mysql_init(mysql_handle);
mysql_options(mysql_handle, MYSQL_READ_DEFAULT_GROUP, CS group);
if (mysql_real_connect(mysql_handle,
/* Add the connection to the cache */
- cn = store_get(sizeof(mysql_connection));
+ cn = store_get(sizeof(mysql_connection), FALSE);
cn->server = server_copy;
cn->handle = mysql_handle;
cn->next = mysql_connections;
if (result)
{
*resultptr = string_from_gstring(result);
- store_reset(result->s + (result->size = result->ptr + 1));
+ gstring_release_unused(result);
return OK;
}
else
if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++;
if (count == 0) return s;
-t = quoted = store_get(Ustrlen(s) + count + 1);
+t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s));
while ((c = *s++) != 0)
{
char *nis_domain;
if (yp_get_default_domain(&nis_domain) != 0)
{
- *errmsg = string_sprintf("failed to get default NIS domain");
+ *errmsg = US"failed to get default NIS domain";
return NULL;
}
return nis_domain;
*errmsg = string_sprintf("NIS+ field %s not found for %s", field_name,
query);
else
- store_reset(yield->s + yield->ptr + 1);
+ gstring_release_unused(yield);
/* Free result store before finishing. */
while (*t != 0) if (*t++ == '\"') count++;
if (count == 0) return s;
-t = quoted = store_get(Ustrlen(s) + count + 1);
+t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s));
while (*s != 0)
{
/* Get store for a new connection, initialize it, and connect to the server */
- oracle_handle = store_get(sizeof(struct cda_def));
- hda = store_get(HDA_SIZE);
+ oracle_handle = store_get(sizeof(struct cda_def), FALSE);
+ hda = store_get(HDA_SIZE, FALSE);
memset(hda,'\0',HDA_SIZE);
/*
/* Add the connection to the cache */
- cn = store_get(sizeof(oracle_connection));
+ cn = store_get(sizeof(oracle_connection), FALSE);
cn->server = server_copy;
cn->handle = oracle_handle;
cn->next = oracle_connections;
/* We have a connection. Open a cursor and run the query */
-cda = store_get(sizeof(Cda_Def));
+cda = store_get(sizeof(Cda_Def), FALSE);
if (oopen(cda, oracle_handle, (text *)0, -1, -1, (text *)0, -1) != 0)
{
/* Find the number of fields returned and sort out their types. If the number
is one, we don't add field names to the data. Otherwise we do. */
-def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE);
-desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE);
+def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE, FALSE);
+desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE, FALSE);
if ((num_fields = describe_define(cda,def,desc)) == -1)
{
*errmsg = "ORACLE: no data found";
}
else
- store_reset(result->s + result->ptr + 1);
+ gstring_release_unused(result);
/* Get here by goto from various error checks. */
if (strchr("\n\t\r\b\'\"\\", c) != NULL) count++;
if (count == 0) return s;
-t = quoted = store_get((int)strlen(s) + count + 1);
+t = quoted = store_get((int)strlen(s) + count + 1, is_tainted(s));
while ((c = *s++) != 0)
{
int yield = DEFER;
unsigned int num_fields, num_tuples;
pgsql_connection *cn;
+rmark reset_point = store_mark();
uschar *server_copy = NULL;
uschar *sdata[3];
if(PQstatus(pg_conn) == CONNECTION_BAD)
{
- store_reset(server_copy);
+ reset_point = store_reset(reset_point);
*errmsg = string_sprintf("PGSQL connection failed: %s",
PQerrorMessage(pg_conn));
PQfinish(pg_conn);
/* Add the connection to the cache */
- cn = store_get(sizeof(pgsql_connection));
+ cn = store_get(sizeof(pgsql_connection), FALSE);
cn->server = server_copy;
cn->handle = pg_conn;
cn->next = pgsql_connections;
if (result)
{
- store_reset(result->s + result->ptr + 1);
+ gstring_release_unused(result);
*resultptr = string_from_gstring(result);
return OK;
}
if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++;
if (count == 0) return s;
-t = quoted = store_get(Ustrlen(s) + count + 1);
+t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s));
while ((c = *s++) != 0)
{
socket ? redisConnectUnix(CCS socket) : redisConnect(CCS server, port);
if (!redis_handle)
{
- *errmsg = string_sprintf("REDIS connection failed");
+ *errmsg = US"REDIS connection failed";
*defer_break = FALSE;
goto REDIS_EXIT;
}
/* Add the connection to the cache */
- cn = store_get(sizeof(redis_connection));
+ cn = store_get(sizeof(redis_connection), FALSE);
cn->server = server_copy;
cn->handle = redis_handle;
cn->next = redis_connections;
if (result)
- store_reset(result->s + result->ptr + 1);
+ gstring_release_unused(result);
else
{
yield = FAIL;
if (isspace(c) || c == '\\') count++;
if (count == 0) return s;
-t = quoted = store_get(Ustrlen(s) + count + 1);
+t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s));
while ((c = *s++) != 0)
{
while ((c = *t++) != 0) if (c == '\'') count++;
if (count == 0) return s;
-t = quoted = store_get(Ustrlen(s) + count + 1);
+t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s));
while ((c = *s++) != 0)
{
#define DEBUG(x) if (debug_selector & (x))
#define HDEBUG(x) if (host_checking || (debug_selector & (x)))
-#define PTR_CHK(ptr) \
-do { \
-if ((void *)ptr > (void *)store_get(0)) \
- debug_printf("BUG: ptr '%s' beyond arena at %s:%d\n", \
- mac_expanded_string(ptr), __FUNCTION__, __LINE__); \
-} while(0)
-
/* The default From: text for DSNs */
#define DEFAULT_DSN_FROM "Mail Delivery System <Mailer-Daemon@$qualify_domain>"
": 0x18 :session resumed unasked: 0x1A :session resumed unasked" \
": 0x1C :session resumed: 0x1E :session resumed, also new ticket"
+/* Flags for string_vformat */
+#define SVFMT_EXTEND BIT(0)
+#define SVFMT_REBUFFER BIT(1)
+#define SVFMT_TAINT_NOCHK BIT(2)
+
+
/* End of macros.h */
malware_daemon_ctx.sock);
}
- if (!(drweb_fbuf = US malloc(fsize_uint)))
+ if (!(drweb_fbuf = store_malloc(fsize_uint)))
{
(void)close(drweb_fd);
return m_panic_defer_3(scanent, NULL,
{
int err = errno;
(void)close(drweb_fd);
- free(drweb_fbuf);
+ store_free(drweb_fbuf);
return m_panic_defer_3(scanent, NULL,
string_sprintf("can't read spool file %s: %s",
eml_filename, strerror(err)),
/* send file body to socket */
if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
{
- free(drweb_fbuf);
+ store_free(drweb_fbuf);
return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
"unable to send file body to socket (%s)", scanner_options),
malware_daemon_ctx.sock);
}
+ store_free(drweb_fbuf);
}
else
{
return m_panic_defer_3(scanent, CUS callout_address,
US"cannot read report size", malware_daemon_ctx.sock);
drweb_slen = ntohl(drweb_slen);
- tmpbuf = store_get(drweb_slen);
+
+ /* assume tainted, since it is external input */
+ tmpbuf = store_get(drweb_slen, TRUE);
/* read report body */
if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
/* Local file; so we def want to use_scan_command and don't want to try
* passing IP/port combinations */
use_scan_command = TRUE;
- cd = (clamd_address *) store_get(sizeof(clamd_address));
+ cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
/* extract socket-path part */
sublist = scanner_options;
continue;
}
- cd = (clamd_address *) store_get(sizeof(clamd_address));
+ cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
/* extract host and port part */
sublist = scanner_options;
if (lseek(clam_fd, 0, SEEK_SET) < 0)
goto b_seek;
- if (!(clamav_fbuf = US malloc(fsize_uint)))
+ if (!(clamav_fbuf = store_malloc(fsize_uint)))
{
(void)close(clam_fd);
return m_panic_defer_3(scanent, NULL,
if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
{
int err = errno;
- free(clamav_fbuf); (void)close(clam_fd);
+ store_free(clamav_fbuf); (void)close(clam_fd);
return m_panic_defer_3(scanent, NULL,
string_sprintf("can't read spool file %s: %s",
eml_filename, strerror(err)),
(send(malware_daemon_ctx.sock, clamav_fbuf, fsize_uint, 0) < 0) ||
(send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
{
- free(clamav_fbuf);
+ store_free(clamav_fbuf);
return m_panic_defer_3(scanent, NULL,
string_sprintf("unable to send file body to socket (%s)", hostname),
malware_daemon_ctx.sock);
}
-
- free(clamav_fbuf);
+ store_free(clamav_fbuf);
}
else
{ /* use scan command */
if (Ustrcmp(ss, "+caseful") == 0)
{
check_string_block *cb = (check_string_block *)arg;
- Ustrcpy(cb->subject, cb->origsubject);
+ Ustrcpy(US cb->subject, cb->origsubject);
cb->caseless = FALSE;
continue;
}
so we use the permanent store pool */
store_pool = POOL_PERM;
- p = store_get(sizeof(namedlist_cacheblock));
+ p = store_get(sizeof(namedlist_cacheblock), FALSE);
p->key = string_copy(get_check_key(arg, type));
{
check_address_block ab;
unsigned int *local_cache_bits = cache_bits;
+int len;
/* RFC 2505 recommends that for spam checking, local parts should be caselessly
compared. Therefore, Exim now forces the entire address into lower case here,
the list can be used to restore a caseful copy of the local part from the
original address. */
-sprintf(CS big_buffer, "%.*s", big_buffer_size - 1, address);
-for (uschar * p = big_buffer + Ustrlen(big_buffer) - 1; p >= big_buffer; p--)
+if ((len = Ustrlen(address)) > 255) len = 255;
+ab.address = string_copyn(address, len);
+
+for (uschar * p = ab.address + len - 1; p >= ab.address; p--)
{
if (!caseless && *p == '@') break;
*p = tolower(*p);
/* Set up the data to be passed ultimately to check_address. */
ab.origaddress = address;
-ab.address = big_buffer;
+/* ab.address is above */
ab.expand_setup = expand_setup;
ab.caseless = caseless;
uschar * header = NULL;
struct mime_boundary_context nested_context;
-/* reserve a line buffer to work in */
-header = store_get(MIME_MAX_HEADER_SIZE+1);
+/* reserve a line buffer to work in. Assume tainted data. */
+header = store_get(MIME_MAX_HEADER_SIZE+1, TRUE);
/* Not actually used at the moment, but will be vital to fixing
* some RFC 2046 nonconformance later... */
if (bounce_return_body && message_file)
{
BOOL enddot = f.dot_ends && message_file == stdin;
- uschar * buf = store_get(bounce_return_linesize_limit+2);
+ uschar * buf = store_get(bounce_return_linesize_limit+2, TRUE);
if (firstline) fprintf(fp, "%s", CS firstline);
#ifndef MYTYPES_H
#define MYTYPES_H
+# include <string.h>
+
#ifndef FALSE
# define FALSE 0
#endif
#define Uread(f,b,l) read(f,CS(b),l)
#define Urename(s,t) rename(CCS(s),CCS(t))
#define Ustat(s,t) stat(CCS(s),t)
-#define Ustrcat(s,t) strcat(CS(s),CCS(t))
+#define Ustrcat(s,t) __Ustrcat(s,t, __FUNCTION__, __LINE__)
#define Ustrchr(s,n) US strchr(CCS(s),n)
#define CUstrchr(s,n) CUS strchr(CCS(s),n)
#define CUstrerror(n) CUS strerror(n)
#define Ustrcmp(s,t) strcmp(CCS(s),CCS(t))
-#define Ustrcpy(s,t) strcpy(CS(s),CCS(t))
+#define Ustrcpy(s,t) __Ustrcpy(s,t, __FUNCTION__, __LINE__)
+#define Ustrcpy_nt(s,t) strcpy(CS s, CCS t) /* no taint check */
#define Ustrcspn(s,t) strcspn(CCS(s),CCS(t))
#define Ustrftime(s,m,f,t) strftime(CS(s),m,f,t)
#define Ustrlen(s) (int)strlen(CCS(s))
-#define Ustrncat(s,t,n) strncat(CS(s),CCS(t),n)
+#define Ustrncat(s,t,n) __Ustrncat(s,t,n, __FUNCTION__, __LINE__)
#define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n)
-#define Ustrncpy(s,t,n) strncpy(CS(s),CCS(t),n)
+#define Ustrncpy(s,t,n) __Ustrncpy(s,t,n, __FUNCTION__, __LINE__)
+#define Ustrncpy_nt(s,t,n) strncpy(CS s, CCS t, n) /* no taint check */
#define Ustrpbrk(s,t) strpbrk(CCS(s),CCS(t))
#define Ustrrchr(s,n) US strrchr(CCS(s),n)
#define CUstrrchr(s,n) CUS strrchr(CCS(s),n)
#define Ustrtoul(s,t,b) strtoul(CCS(s),CSS(t),b)
#define Uunlink(s) unlink(CCS(s))
+extern BOOL is_tainted(const void *);
+extern void die_tainted(const uschar *, const uschar *, int);
+
+static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line)
+{
+#ifndef COMPILE_UTILITY
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line);
+#endif
+return US strcat(CS dst, CCS src);
+}
+static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line)
+{
+#ifndef COMPILE_UTILITY
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line);
#endif
+return US strcpy(CS dst, CCS src);
+}
+static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line)
+{
+#ifndef COMPILE_UTILITY
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line);
+#endif
+return US strncat(CS dst, CCS src, n);
+}
+static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line)
+{
+#ifndef COMPILE_UTILITY
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line);
+#endif
+return US strncpy(CS dst, CCS src, n);
+}
+/*XXX will likely need unchecked copy also */
+#endif
/* End of mytypes.h */
/* Create a data block for the address, fill in the data, and put it on the
chain. */
- next = store_get(sizeof(ip_address_item));
+ next = store_get(sizeof(ip_address_item), FALSE);
next->next = NULL;
next->port = 0;
(void)host_ntoa(-1, ifa->ifa_addr, next->address, NULL);
/* Create a data block for the address, fill in the data, and put it on the
chain. */
- next = store_get(sizeof(ip_address_item));
+ next = store_get(sizeof(ip_address_item), FALSE);
next->next = NULL;
next->port = 0;
(void)host_ntoa(-1, addrp, next->address, NULL);
ip_address_item *
os_common_find_running_interfaces(void)
{
-ip_address_item *yield = store_get(sizeof(address_item));
+ip_address_item *yield = store_get(sizeof(address_item), FALSE);
yield->address = US"127.0.0.1";
yield->port = 0;
yield->next = NULL;
#if HAVE_IPV6
-yield->next = store_get(sizeof(address_item));
+yield->next = store_get(sizeof(address_item), FALSE);
yield->next->address = US"::1";
yield->next->port = 0;
yield->next->next = NULL;
address_item *deliver_make_addr(uschar *address, BOOL copy)
{
-address_item *addr = store_get(sizeof(address_item));
+address_item *addr = store_get(sizeof(address_item), FALSE);
addr->next = NULL;
addr->parent = NULL;
addr->address = address;
parse_extract_address(uschar *mailbox, uschar **errorptr, int *start, int *end,
int *domain, BOOL allow_null)
{
-uschar *yield = store_get(Ustrlen(mailbox) + 1);
+uschar *yield = store_get(Ustrlen(mailbox) + 1, is_tainted(mailbox));
uschar *startptr, *endptr;
uschar *s = US mailbox;
uschar *t = US yield;
if (flen <= 0)
{
- *error = string_sprintf("file name missing after :include:");
+ *error = US"file name missing after :include:";
return FF_ERROR;
}
return FF_ERROR;
}
- filebuf = store_get(statbuf.st_size + 1);
+ filebuf = store_get(statbuf.st_size + 1, is_tainted(filename));
if (fread(filebuf, 1, statbuf.st_size, f) != statbuf.st_size)
{
*error = string_sprintf("error while reading included file %s: %s",
if ((*s == '|' || *s == '/') && (recipient == NULL || domain == 0))
{
- uschar *t = store_get(Ustrlen(s) + 1);
+ uschar *t = store_get(Ustrlen(s) + 1, is_tainted(s));
uschar *p = t;
uschar *q = s;
while (*q != 0)
if (syntax_errors != NULL)
{
- error_block *e = store_get(sizeof(error_block));
+ error_block *e = store_get(sizeof(error_block), FALSE);
error_block *last = *syntax_errors;
if (last == NULL) *syntax_errors = e; else
{
{
uschar *domain = NULL;
uschar *id;
+rmark reset_point;
str = skip_comment(str);
if (*str != '<')
for the answer, but it may also be very long if we are processing a header
line. Therefore, take care to release unwanted store afterwards. */
-id = *yield = store_get(Ustrlen(str) + 1);
+reset_point = store_mark();
+id = *yield = store_get(Ustrlen(str) + 1, is_tainted(str));
*id++ = *str++;
str = read_addr_spec(str, id, '>', error, &domain);
-if (*error == NULL)
+if (!*error)
{
if (*str != '>') *error = US"Missing '>' after message-id";
else if (domain == NULL) *error = US"domain missing in message-id";
}
-if (*error != NULL)
+if (*error)
{
- store_reset(*yield);
+ store_reset(reset_point);
return NULL;
}
-while (*id != 0) id++;
+while (*id) id++;
*id++ = *str++;
*id++ = 0;
-store_reset(id);
+store_release_above(id);
str = skip_comment(str);
return str;
static pdkim_stringlist *
pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
{
-pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist));
+pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist), FALSE);
memset(new_entry, 0, sizeof(pdkim_stringlist));
new_entry->value = string_copy(str);
{
BOOL past_field_name = FALSE;
BOOL seen_wsp = FALSE;
-uschar * relaxed = store_get(len+3);
+uschar * relaxed = store_get(len+3, TRUE); /* tainted */
uschar * q = relaxed;
for (const uschar * p = header; p - header < len; p++)
int nchar = 0;
uschar * q;
const uschar * p = str;
-uschar * n = store_get(Ustrlen(str)+1);
+uschar * n = store_get(Ustrlen(str)+1, TRUE);
*n = '\0';
q = n;
BOOL in_b_val = FALSE;
int where = PDKIM_HDR_LIMBO;
-sig = store_get(sizeof(pdkim_signature));
+sig = store_get(sizeof(pdkim_signature), FALSE);
memset(sig, 0, sizeof(pdkim_signature));
sig->bodylength = -1;
sig->keytype = -1;
sig->hashtype = -1;
-q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
+q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, TRUE); /* tainted */
for (uschar * p = raw_hdr; ; p++)
{
int sep = ';';
pdkim_pubkey * pub;
-pub = store_get(sizeof(pdkim_pubkey));
+pub = store_get(sizeof(pdkim_pubkey), TRUE); /* tainted */
memset(pub, 0, sizeof(pdkim_pubkey));
while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0)))
{
pdkim_ctx * ctx;
-ctx = store_get(sizeof(pdkim_ctx));
+ctx = store_get(sizeof(pdkim_ctx), FALSE);
memset(ctx, 0, sizeof(pdkim_ctx));
if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
-ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
+/* The line-buffer is for message data, hence tainted */
+ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE);
ctx->dns_txt_callback = dns_txt_callback;
return ctx;
/* Allocate & init one signature struct */
-sig = store_get(sizeof(pdkim_signature));
+sig = store_get(sizeof(pdkim_signature), FALSE);
memset(sig, 0, sizeof(pdkim_signature));
sig->bodylength = -1;
DEBUG(D_receive) debug_printf("PDKIM: new bodyhash %d/%d/%ld\n",
hashtype, canon_method, bodylength);
-b = store_get(sizeof(pdkim_bodyhash));
+b = store_get(sizeof(pdkim_bodyhash), FALSE);
b->next = ctx->bodyhash;
b->hashtype = hashtype;
b->canon_method = canon_method;
{
memset(ctx, 0, sizeof(pdkim_ctx));
ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
-ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
+/* The line buffer is for message data, hence tainted */
+ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE);
DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
}
}
#define SIGSPACE 128
-sig->data = store_get(SIGSPACE);
+sig->data = store_get(SIGSPACE, FALSE);
if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0)
{
if ( (ctx = EVP_MD_CTX_new())
&& EVP_DigestSignInit(ctx, NULL, md, NULL, sign_ctx->key) > 0
&& EVP_DigestSign(ctx, NULL, &siglen, NULL, 0) > 0
- && (sig->data = store_get(siglen))
+ && (sig->data = store_get(siglen, FALSE))
/* Obtain the signature (slen could change here!) */
&& EVP_DigestSign(ctx, sig->data, &siglen, data->data, data->len) > 0
&& EVP_DigestSignInit(ctx, NULL, md, NULL, sign_ctx->key) > 0
&& EVP_DigestSignUpdate(ctx, data->data, data->len) > 0
&& EVP_DigestSignFinal(ctx, NULL, &siglen) > 0
- && (sig->data = store_get(siglen))
+ && (sig->data = store_get(siglen, FALSE))
/* Obtain the signature (slen could change here!) */
&& EVP_DigestSignFinal(ctx, sig->data, &siglen) > 0
Ustrcmp(name + SPOOL_NAME_LENGTH - 2, "-H") == 0)
{
queue_filename *next =
- store_get(sizeof(queue_filename) + Ustrlen(name));
+ store_get(sizeof(queue_filename) + Ustrlen(name), is_tainted(name));
Ustrcpy(next->text, name);
next->dir_uschar = subdirchar;
i <= (queue_run_in_order ? -1 : subcount);
i++)
{
- void *reset_point1 = store_get(0);
+ rmark reset_point1 = store_mark();
DEBUG(D_queue_run)
{
{
BOOL wanted = TRUE;
BOOL orig_dont_deliver = f.dont_deliver;
- void *reset_point2 = store_get(0);
+ rmark reset_point2 = store_mark();
/* Restore the original setting of dont_deliver after reading the header,
so that a setting for a particular message doesn't force it for any that
if (f.running_in_test_harness) millisleep(100);
(void)close(pfd[pipe_read]);
rc = deliver_message(fq->text, force_delivery, FALSE);
- _exit(rc == DELIVER_NOT_ATTEMPTED);
+ exim_underbar_exit(rc == DELIVER_NOT_ATTEMPTED);
}
if (pid < 0)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork of delivery process from "
{
int subcount;
int now = (int)time(NULL);
-void *reset_point;
+rmark reset_point;
queue_filename * qf = NULL;
uschar subdirs[64];
for (int i = 0; i < count; i++)
{
queue_filename *next =
- store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2);
+ store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2, is_tainted(list[i]));
sprintf(CS next->text, "%s-H", list[i]);
next->dir_uschar = '*';
next->next = NULL;
/* Now scan the chain and print information, resetting store used
each time. */
-for (reset_point = store_get(0);
- qf;
+for (;
+ qf && (reset_point = store_mark());
spool_clear_header_globals(), store_reset(reset_point), qf = qf->next
)
{
rda_exists(uschar *filename, uschar **error)
{
int rc, saved_errno;
-uschar *slash;
struct stat statbuf;
+uschar * s;
if ((rc = Ustat(filename, &statbuf)) >= 0) return FILE_EXIST;
saved_errno = errno;
-Ustrncpy(big_buffer, filename, big_buffer_size - 3);
+s = string_copy(filename);
sigalrm_seen = FALSE;
if (saved_errno == ENOENT)
{
- slash = Ustrrchr(big_buffer, '/');
- Ustrcpy(slash+1, ".");
+ uschar * slash = Ustrrchr(s, '/');
+ Ustrcpy(slash+1, US".");
ALARM(30);
- rc = Ustat(big_buffer, &statbuf);
+ rc = Ustat(s, &statbuf);
if (rc != 0 && errno == EACCES && !sigalrm_seen)
{
*slash = 0;
- rc = Ustat(big_buffer, &statbuf);
+ rc = Ustat(s, &statbuf);
}
saved_errno = errno;
ALARM_CLR(0);
- DEBUG(D_route) debug_printf("stat(%s)=%d\n", big_buffer, rc);
+ DEBUG(D_route) debug_printf("stat(%s)=%d\n", s, rc);
}
if (sigalrm_seen || rc != 0)
{
- *error = string_sprintf("failed to stat %s (%s)", big_buffer,
- sigalrm_seen? "timeout" : strerror(saved_errno));
+ *error = string_sprintf("failed to stat %s (%s)", s,
+ sigalrm_seen? "timeout" : strerror(saved_errno));
return FILE_EXIST_UNCLEAR;
}
/* Read the file in one go in order to minimize the time we have it open. */
-filebuf = store_get(statbuf.st_size + 1);
+filebuf = store_get(statbuf.st_size + 1, is_tainted(filename));
if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
{
int old_expand_forbid = expand_forbid;
DEBUG(D_route) debug_printf("data is %s filter program\n",
- (*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve");
+ *filtertype == FILTER_EXIM ? "an Exim" : "a Sieve");
/* RDO_FILTER is an "allow" bit */
}
expand_forbid =
- (expand_forbid & ~RDO_FILTER_EXPANSIONS) |
- (options & RDO_FILTER_EXPANSIONS);
+ expand_forbid & ~RDO_FILTER_EXPANSIONS | options & RDO_FILTER_EXPANSIONS;
/* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
else
/* We know we have enough memory so disable the error on "len" */
/* coverity[tainted_data] */
- if (read(fd, *sp = store_get(len), len) != len) return FALSE;
+ /* We trust the data source, so untainted */
+ if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
return TRUE;
}
uschar *readerror = US"";
void (*oldsignal)(int);
-DEBUG(D_route) debug_printf("rda_interpret (%s): %s\n",
- (rdata->isfile)? "file" : "string", rdata->string);
+DEBUG(D_route) debug_printf("rda_interpret (%s): '%s'\n",
+ rdata->isfile ? "file" : "string", string_printing(rdata->string));
/* Do the expansions of the file name or data first, while still privileged. */
-data = expand_string(rdata->string);
-if (data == NULL)
+if (!(data = expand_string(rdata->string)))
{
if (f.expand_string_forcedfail) return FF_NOTDELIVERED;
*error = string_sprintf("failed to expand \"%s\": %s", rdata->string,
}
rdata->string = data;
-DEBUG(D_route) debug_printf("expanded: %s\n", data);
+DEBUG(D_route) debug_printf("expanded: '%s'\n", data);
if (rdata->isfile && data[0] != '/')
{
out:
(void)close(fd);
search_tidyup();
- _exit(0);
+ exim_underbar_exit(0);
bad:
DEBUG(D_rewrite) debug_printf("rda_interpret: failed write to pipe\n");
uschar *s;
if (!rda_read_string(fd, &s)) goto DISASTER;
if (!s) break;
- e = store_get(sizeof(error_block));
+ e = store_get(sizeof(error_block), FALSE);
e->next = NULL;
e->text1 = s;
if (!rda_read_string(fd, &s)) goto DISASTER;
/* First string is the address; NULL => end of addresses */
if (!rda_read_string(fd, &recipient)) goto DISASTER;
- if (recipient == NULL) break;
+ if (!recipient) break;
/* Hang on the end of the chain */
if (i > 0)
{
- addr->pipe_expandn = store_get((i+1) * sizeof(uschar *));
+ addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), FALSE);
addr->pipe_expandn[i] = NULL;
while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
}
if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
if ((reply_options & REPLY_EXISTS) != 0)
{
- addr->reply = store_get(sizeof(reply_item));
+ addr->reply = store_get(sizeof(reply_item), FALSE);
addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;
macro_item *
macro_create(const uschar * name, const uschar * val, BOOL command_line)
{
-macro_item * m = store_get(sizeof(macro_item));
+macro_item * m = store_get(sizeof(macro_item), FALSE);
READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val);
m->next = NULL;
if (config_lines)
save_config_position(config_filename, config_lineno);
- save = store_get(sizeof(config_file_item));
+ save = store_get(sizeof(config_file_item), FALSE);
save->next = config_file_stack;
config_file_stack = save;
save->file = config_file;
static rewrite_rule *
readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal)
{
-rewrite_rule *next = store_get(sizeof(rewrite_rule));
+rewrite_rule *next = store_get(sizeof(rewrite_rule), FALSE);
next->next = NULL;
next->key = string_dequote(&p);
BOOL freesptr = TRUE;
optionlist *ol, *ol2;
struct passwd *pw;
-void *reset_point;
+rmark reset_point;
int intbase = 0;
uschar *inttype = US"";
uschar *sptr;
case opt_gidlist:
case opt_rewrite:
- reset_point = sptr = read_string(s, name);
+ reset_point = store_mark();
+ sptr = read_string(s, name);
/* Having read a string, we now have several different ways of using it,
depending on the data type, so do another switch. If keeping the actual
/* We already have a condition, we're conducting a crude hack to let
multiple condition rules be chained together, despite storing them in
text form. */
- *str_target = string_copy_malloc( (saved_condition = *str_target)
+ *str_target = string_copy_perm( (saved_condition = *str_target)
? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
saved_condition, sptr)
- : sptr);
+ : sptr,
+ FALSE);
/* TODO(pdp): there is a memory leak here and just below
when we set 3 or more conditions; I still don't
understand the store mechanism enough to know
list_o = string_append_listele(list_o, sep_o, s);
if (list_o)
- *str_target = string_copy_malloc(string_from_gstring(list_o));
+ *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
}
else
{
ignore. Also ignore if the value is already set. */
if (pw == NULL) break;
- Ustrcpy(name+Ustrlen(name)-4, "group");
+ Ustrcpy(name+Ustrlen(name)-4, US"group");
ol2 = find_option(name, oltop, last);
if (ol2 != NULL && ((ol2->type & opt_mask) == opt_gid ||
(ol2->type & opt_mask) == opt_expand_gid))
/* Release store if the value of the string doesn't need to be kept. */
- if (freesptr) store_reset(reset_point);
+ if (freesptr) reset_point = store_reset(reset_point);
break;
/* Expanded boolean: if no characters follow, or if there are no dollar
if (*s != 0 && Ustrchr(s, '$') != 0)
{
sprintf(CS name2, "*expand_%.50s", name);
- ol2 = find_option(name2, oltop, last);
- if (ol2 != NULL)
+ if ((ol2 = find_option(name2, oltop, last)))
{
- reset_point = sptr = read_string(s, name);
+ reset_point = store_mark();
+ sptr = read_string(s, name);
if (data_block == NULL)
*((uschar **)(ol2->value)) = sptr;
else
BOOL forcecache = FALSE;
uschar *ss;
tree_node *t;
-namedlist_block *nb = store_get(sizeof(namedlist_block));
+namedlist_block *nb = store_get(sizeof(namedlist_block), FALSE);
if (Ustrncmp(s, "_cache", 6) == 0)
{
while (isspace(*s)) s++;
ss = s;
while (isalnum(*s) || *s == '_') s++;
-t = store_get(sizeof(tree_node) + s-ss);
+t = store_get(sizeof(tree_node) + s-ss, is_tainted(ss));
Ustrncpy(t->name, ss, s-ss);
t->name[s-ss] = 0;
while (isspace(*s)) s++;
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"tls_require_ciphers invalid: %s", errmsg);
fflush(NULL);
- _exit(0);
+ exim_underbar_exit(0);
}
do {
"wrong owner, group, or mode", big_buffer);
}
+/* Do a dummy store-allocation of a size related to the (toplevel) file size.
+This assumes we will need this much storage to handle all the allocations
+during startup; it won't help when .include is being used. When it does, it
+will cut down on the number of store blocks (and malloc calls, and sbrk
+syscalls). It also assume we're on the relevant pool. */
+
+if (statbuf.st_size > 8192)
+ {
+ rmark r = store_mark();
+ store_get((int)statbuf.st_size, FALSE);
+ store_reset(r);
+ }
+
/* Process the main configuration settings. They all begin with a lower case
letter. If we see something starting with an upper case letter, it is taken as
a macro definition. */
{
int len = dd->options_len;
d->info = dd;
- d->options_block = store_get(len);
+ d->options_block = store_get(len, FALSE);
memcpy(d->options_block, dd->options_block, len);
for (int i = 0; i < *(dd->options_count); i++)
dd->options[i].type &= ~opt_set;
/* Set up a new driver instance data block on the chain, with
its default values installed. */
- d = store_get(instance_size);
+ d = store_get(instance_size, FALSE);
memcpy(d, instance_default, instance_size);
*p = d;
p = &d->next;
const uschar *pp;
uschar *error;
- next = store_get(sizeof(retry_config));
+ next = store_get(sizeof(retry_config), FALSE);
next->next = NULL;
*chain = next;
chain = &(next->next);
while (*p != 0)
{
- retry_rule *rule = store_get(sizeof(retry_rule));
+ retry_rule *rule = store_get(sizeof(retry_rule), FALSE);
*rchain = rule;
rchain = &(rule->next);
rule->next = NULL;
if (*p != ':' || name[0] == 0)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
- node = store_get(sizeof(tree_node) + Ustrlen(name));
+ node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
Ustrcpy(node->name, name);
if (!tree_insertnode(&acl_anchor, node))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
int mid = last/2;
int n = Ustrlen(next_section);
- if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, "s");
+ if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, US"s");
for (;;)
{
static config_line_item *current;
config_line_item *next;
-next = (config_line_item*) store_get(sizeof(config_line_item));
+next = (config_line_item*) store_get(sizeof(config_line_item), FALSE);
next->line = string_copy(line);
next->next = NULL;
recipient_item *oldlist = recipients_list;
int oldmax = recipients_list_max;
recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50;
- recipients_list = store_get(recipients_list_max * sizeof(recipient_item));
+ recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE);
if (oldlist != NULL)
memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item));
}
uschar *queued_by = NULL;
uschar *errmsg;
+rmark rcvd_log_reset_point;
gstring * g;
struct stat statbuf;
/* Working header pointers */
+rmark reset_point;
header_line *next;
/* Flags for noting the existence of certain headers (only one left) */
header. Temporarily mark it as "old", i.e. not to be used. We keep header_last
pointing to the end of the chain to make adding headers simple. */
-received_header = header_list = header_last = store_get(sizeof(header_line));
+received_header = header_list = header_last = store_get(sizeof(header_line), FALSE);
header_list->next = NULL;
header_list->type = htype_old;
header_list->text = NULL;
/* Control block for the next header to be read. */
-next = store_get(sizeof(header_line));
-next->text = store_get(header_size);
+reset_point = store_mark();
+next = store_get(sizeof(header_line), FALSE); /* not tainted */
+next->text = store_get(header_size, TRUE); /* tainted */
/* Initialize message id to be null (indicating no message read), and the
header names list to be the normal list. Indicate there is no data file open
goto OVERSIZE;
header_size *= 2;
- if (!store_extend(next->text, oldsize, header_size))
- next->text = store_newblock(next->text, header_size, ptr);
+ /* The data came from the message, so is tainted. */
+
+ if (!store_extend(next->text, TRUE, oldsize, header_size))
+ next->text = store_newblock(next->text, TRUE, header_size, ptr);
}
/* Cope with receiving a binary zero. There is dispute about whether
if (ch == '\n')
{
message_ended = END_DOT;
- store_reset(next);
+ reset_point = store_reset(reset_point);
next = NULL;
break; /* End character-reading loop */
}
if (ptr == 1)
{
- store_reset(next);
+ reset_point = store_reset(reset_point);
next = NULL;
break;
}
next->text[ptr] = 0;
next->slen = ptr;
- store_reset(next->text + ptr + 1);
+ store_release_above(next->text + ptr + 1);
/* Check the running total size against the overall message size limit. We
don't expect to fail here, but if the overall limit is set less than MESSAGE_
/* Set up for the next header */
+ reset_point = store_mark();
header_size = 256;
- next = store_get(sizeof(header_line));
- next->text = store_get(header_size);
+ next = store_get(sizeof(header_line), FALSE);
+ next->text = store_get(header_size, TRUE);
ptr = 0;
had_zero = 0;
prevlines_length = 0;
white space that follows the newline must not be removed - it is part
of the header. */
- pp = recipient = store_get(ss - s + 1);
+ pp = recipient = store_get(ss - s + 1, is_tainted(s));
for (uschar * p = s; p < ss; p++) if (*p != '\n') *pp++ = *p;
*pp = 0;
if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0)
{
int len = Ustrlen(s);
- error_block *b = store_get(sizeof(error_block));
+ error_block *b = store_get(sizeof(error_block), FALSE);
while (len > 0 && isspace(s[len-1])) len--;
b->next = NULL;
b->text1 = string_printing(string_copyn(s, len));
if (LOGGING(received_recipients))
{
- raw_recipients = store_get(recipients_count * sizeof(uschar *));
+ raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
for (int i = 0; i < recipients_count; i++)
raw_recipients[i] = string_copy(recipients_list[i].address);
raw_recipients_count = recipients_count;
message id is actually an addr-spec, we can use the parse routine to canonicalize
it. */
+rcvd_log_reset_point = store_mark();
g = string_get(256);
g = string_append(g, 2,
case '4': /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode.
... for which, pass back the exact error */
- if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg);
+ if (cutthrough.defer_pass) smtp_reply = string_copy_perm(msg, TRUE);
cutthrough_done = TMP_REJ; /* Avoid the usual immediate delivery attempt */
break; /* message_id needed for SMTP accept below */
break; /* message_id needed for SMTP accept below */
case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */
- smtp_reply = string_copy_malloc(msg); /* Pass on the exact error */
+ smtp_reply = string_copy_perm(msg, TRUE); /* Pass on the exact error */
cutthrough_done = PERM_REJ;
break;
}
}
f.receive_call_bombout = FALSE;
-store_reset(g); /* The store for the main log message can be reused */
+/* The store for the main log message can be reused */
+rcvd_log_reset_point = store_reset(rcvd_log_reset_point);
/* If the message is frozen, and freeze_tell is set, do the telling. */
continue;
}
- ri = store_get(sizeof(pcre_list));
+ ri = store_get(sizeof(pcre_list), FALSE);
ri->re = re;
ri->pcre_text = regex_string;
ri->next = re_list_head;
return FAIL; /* no regexes -> nothing to do */
/* match each line against all regexes */
-linebuffer = store_get(32767);
+linebuffer = store_get(32767, TRUE); /* tainted */
while (fgets(CS linebuffer, 32767, mbox_file))
{
if ( mime_stream && mime_current_boundary /* check boundary */
return DEFER;
}
-/* get 32k memory */
-mime_subject = store_get(32767);
+/* get 32k memory, tainted */
+mime_subject = store_get(32767, TRUE);
mime_subject_len = fread(mime_subject, 1, 32766, f);
void
retry_add_item(address_item *addr, uschar *key, int flags)
{
-retry_item *rti = store_get(sizeof(retry_item));
+retry_item *rti = store_get(sizeof(retry_item), FALSE);
host_item * host = addr->host_used;
rti->next = addr->retries;
if (!retry_record)
{
- retry_record = store_get(sizeof(dbdata_retry) + message_length);
+ retry_record = store_get(sizeof(dbdata_retry) + message_length,
+ is_tainted(message));
message_space = message_length;
retry_record->first_failed = now;
retry_record->last_try = now;
if (message_length > message_space)
{
- dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length);
+ dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length, FALSE);
memcpy(newr, retry_record, sizeof(dbdata_retry));
retry_record = newr;
}
{
int lastnewline = 0;
header_line *newh = NULL;
-void *function_reset_point = store_get(0);
+rmark function_reset_point = store_mark();
uschar *s = Ustrchr(h->text, ':') + 1;
while (isspace(*s)) s++;
uschar *sprev;
uschar *ss = parse_find_address_end(s, FALSE);
uschar *recipient, *new, *errmess;
- void *loop_reset_point = store_get(0);
+ rmark loop_reset_point = store_mark();
BOOL changed = FALSE;
int terminator = *ss;
int start, end, domain;
if (!recipient)
{
- store_reset(loop_reset_point);
+ loop_reset_point = store_reset(loop_reset_point);
continue;
}
if (changed && ((is_recipient && !f.allow_unqualified_recipient) ||
(!is_recipient && !f.allow_unqualified_sender)))
{
- store_reset(loop_reset_point);
+ loop_reset_point = store_reset(loop_reset_point);
continue;
}
}
point, because we may have a rewritten line from a previous time round the
loop. */
- if (!changed) store_reset(loop_reset_point);
+ if (!changed) loop_reset_point = store_reset(loop_reset_point);
/* If the address has changed, create a new header containing the
rewritten address. We do not need to set the chain pointers at this
int newlen = Ustrlen(new);
int oldlen = end - start;
- header_line *prev = (newh == NULL)? h : newh;
- uschar *newt = store_malloc(prev->slen - oldlen + newlen + 4);
- uschar *newtstart = newt;
+ header_line * prev = newh ? newh : h;
+ uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, TRUE);
+ uschar * newtstart = newt;
int type = prev->type;
int slen = prev->slen - oldlen + newlen;
if (*p != '\n')
{
lastnewline = newt - newtstart;
- Ustrcat(newt, "\n\t");
+ Ustrcat(newt, US"\n\t");
slen += 2;
}
}
rewritten copy from a previous time round this loop. */
store_reset(function_reset_point);
- newh = store_get(sizeof(header_line));
+ function_reset_point = store_mark();
+ newh = store_get(sizeof(header_line), FALSE);
newh->type = type;
newh->slen = slen;
newh->text = string_copyn(newtstart, slen);
- store_free(newtstart);
/* Set up for scanning the rest of the header */
int len = 0;
uschar *ptr;
-ptr = *ptrptr = store_get(Ustrlen(string) + 1); /* No longer than this */
+ptr = *ptrptr = store_get(Ustrlen(string) + 1, is_tainted(string)); /* No longer than this */
while (*string != 0)
{
translated into a multibyte code such as UTF-8. That's why we use the dynamic
string building code. */
-yield = store_get(sizeof(gstring) + ++size);
+yield = store_get(sizeof(gstring) + ++size, is_tainted(string));
yield->size = size;
yield->ptr = 0;
yield->s = US(yield + 1);
if (mimeword != string)
yield = string_catn(yield, string, mimeword - string);
+/*XXX that might have to convert an untainted string to a tainted one */
/* Do a charset translation if required. This is supported only on hosts
that have the iconv() function. Translation errors set error, but carry on,
{
exim_setugid(uid, gid, TRUE,
string_sprintf("require_files check, file=%s", ss));
- if (route_check_access(ss, uid, gid, 4)) _exit(0);
+ if (route_check_access(ss, uid, gid, 4))
+ exim_underbar_exit(0);
DEBUG(D_route) debug_printf("route_check_access() failed\n");
- _exit(1);
+ exim_underbar_exit(1);
}
/* In the parent, wait for the child to finish */
while (waitpid(pid, &status, 0) < 0)
- {
if (errno != EINTR) /* unexpected error, interpret as failure */
{
status = 1;
break;
}
- }
signal(SIGCHLD, oldsignal); /* restore */
if ((status == 0) == invert) return SKIP;
BOOL cache_set = (Ustrcmp(lastname, s) == 0);
DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s,
- cache_set? "using cached result" : "cache not available");
+ cache_set ? "using cached result" : "cache not available");
if (!cache_set)
{
return TRUE;
}
- (void)string_format(lastname, sizeof(lastname), "%s", s);
+ string_format_nt(lastname, sizeof(lastname), "%s", s);
/* Force failure if string length is greater than given maximum */
}
if (!(node = tree_search(*root, name)))
- {
- node = store_get(sizeof(tree_node) + Ustrlen(name));
+ { /* name should never be tainted */
+ node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE);
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);
+ DEBUG(D_route) debug_printf("set r_%s%s = '%s'%s\n",
+ name, is_tainted(name)?" (tainted)":"",
+ val, is_tainted(val)?" (tainted)":"");
/* All expansions after this point need visibility of that variable */
router_var = *root;
/* If succeeded while verifying but fail_verify is set, convert into
a failure, and take it off the local or remote delivery list. */
- if (((verify == v_sender && r->fail_verify_sender) ||
- (verify == v_recipient && r->fail_verify_recipient)) &&
- (yield == OK || yield == PASS))
+ if ( ( verify == v_sender && r->fail_verify_sender
+ || verify == v_recipient && r->fail_verify_recipient
+ )
+ && (yield == OK || yield == PASS))
{
addr->message = string_sprintf("%s router forced verify failure", r->name);
if (*paddr_remote == addr) *paddr_remote = addr->next;
HDEBUG(D_route)
{
debug_printf("%s router %s for %s\n", r->name,
- (yield == PASS)? "passed" : "declined", addr->address);
+ yield == PASS ? "passed" : "declined", addr->address);
if (Ustrcmp(old_domain, addr->domain) != 0)
debug_printf("domain %s rewritten\n", old_domain);
}
if (yield == DEFER)
{
- HDEBUG(D_route)
- {
- debug_printf("%s router: defer for %s\n", r->name, addr->address);
- debug_printf(" message: %s\n", (addr->message == NULL)?
- US"<none>" : addr->message);
- }
+ HDEBUG(D_route) debug_printf("%s router: defer for %s\n message: %s\n",
+ r->name, addr->address, addr->message ? addr->message : US"<none>");
goto ROUTE_EXIT;
}
/* Get store in which to preserve the original host item, chained on
to the address. */
-addr->host_list = store_get(sizeof(host_item));
+addr->host_list = store_get(sizeof(host_item), FALSE);
addr->host_list[0] = h;
/* Fill in the transport and queue the address for delivery. */
/* Set up a host item */
-h = store_get(sizeof(host_item));
+h = store_get(sizeof(host_item), FALSE);
h->next = NULL;
h->address = string_copy(ip);
uschar *hostname, *reroute, *domain;
const uschar *listptr;
uschar host_buffer[256];
-host_item *host = store_get(sizeof(host_item));
+host_item *host = store_get(sizeof(host_item), FALSE);
address_item *new_addr;
iplookup_router_options_block *ob =
(iplookup_router_options_block *)(rblock->options_block);
DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
rblock->name, addr->address, addr->domain);
-reply = store_get(256);
+reply = store_get(256, TRUE); /* tainted data */
/* Build the query string to send. If not explicitly given, a default of
"user@domain user@domain" is used. */
if (hostlist[0])
{
host_item *h;
- addr->host_list = h = store_get(sizeof(host_item));
+ addr->host_list = h = store_get(sizeof(host_item), FALSE);
h->name = string_copy(hostlist);
h->address = NULL;
h->port = PORT_NONE;
if (ob->qualify_preserve_domain)
qualify_domain_recipient = addr->domain;
-else if (ob->qualify_domain != NULL)
+else if (ob->qualify_domain)
{
uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc);
- if (new_qdr == NULL) return xrc;
+ if (!new_qdr) return xrc;
qualify_domain_recipient = new_qdr;
}
redirect.check_group = ob->check_group;
redirect.pw = pw;
-if (ob->file != NULL)
- {
- redirect.string = ob->file;
- redirect.isfile = TRUE;
- }
-else
- {
- redirect.string = ob->data;
- redirect.isfile = FALSE;
- }
+redirect.string = (redirect.isfile = (ob->file != NULL))
+ ? ob->file : ob->data;
frc = rda_interpret(&redirect, options, ob->include_directory,
ob->sieve_vacation_directory, ob->sieve_enotify_mailto_owner,
switch (frc)
{
case FF_NONEXIST:
- addr->message = addr->user_message = NULL;
- return DECLINE;
+ addr->message = addr->user_message = NULL;
+ return DECLINE;
case FF_BLACKHOLE:
- DEBUG(D_route) debug_printf("address :blackhole:d\n");
- generated = NULL;
- discarded = US":blackhole:";
- frc = FF_DELIVERED;
- break;
+ DEBUG(D_route) debug_printf("address :blackhole:d\n");
+ generated = NULL;
+ discarded = US":blackhole:";
+ frc = FF_DELIVERED;
+ break;
- /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands
- (:defer: or :fail: in an alias file or "fail" in a filter). If a configured
- message was supplied, allow it to be included in an SMTP response after
- verifying. Remove any SMTP code if it is not allowed. */
+ /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands
+ (:defer: or :fail: in an alias file or "fail" in a filter). If a configured
+ message was supplied, allow it to be included in an SMTP response after
+ verifying. Remove any SMTP code if it is not allowed. */
case FF_DEFER:
- yield = DEFER;
- goto SORT_MESSAGE;
+ yield = DEFER;
+ goto SORT_MESSAGE;
case FF_FAIL:
- if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
- return xrc;
- add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
- yield = FAIL;
+ if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
+ return xrc;
+ add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
+ yield = FAIL;
- SORT_MESSAGE:
- if (addr->message == NULL)
- addr->message = (yield == FAIL)? US"forced rejection" : US"forced defer";
- else
- {
- int ovector[3];
- if (ob->forbid_smtp_code &&
- pcre_exec(regex_smtp_code, NULL, CS addr->message,
- Ustrlen(addr->message), 0, PCRE_EOPT,
- ovector, sizeof(ovector)/sizeof(int)) >= 0)
+ SORT_MESSAGE:
+ if (!addr->message)
+ addr->message = yield == FAIL ? US"forced rejection" : US"forced defer";
+ else
{
- DEBUG(D_route) debug_printf("SMTP code at start of error message "
- "is ignored because forbid_smtp_code is set\n");
- addr->message += ovector[1];
+ int ovector[3];
+ if (ob->forbid_smtp_code &&
+ pcre_exec(regex_smtp_code, NULL, CS addr->message,
+ Ustrlen(addr->message), 0, PCRE_EOPT,
+ ovector, sizeof(ovector)/sizeof(int)) >= 0)
+ {
+ DEBUG(D_route) debug_printf("SMTP code at start of error message "
+ "is ignored because forbid_smtp_code is set\n");
+ addr->message += ovector[1];
+ }
+ addr->user_message = addr->message;
+ setflag(addr, af_pass_message);
}
- addr->user_message = addr->message;
- setflag(addr, af_pass_message);
- }
- return yield;
+ return yield;
- /* As in the case of a system filter, a freeze does not happen after a manual
- thaw. In case deliveries were set up by the filter, we set the child count
- high so that their completion does not mark the original address done. */
+ /* As in the case of a system filter, a freeze does not happen after a manual
+ thaw. In case deliveries were set up by the filter, we set the child count
+ high so that their completion does not mark the original address done. */
case FF_FREEZE:
- if (!f.deliver_manual_thaw)
- {
- if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop))
- != OK) return xrc;
- add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
- if (addr->message == NULL) addr->message = US"frozen by filter";
- addr->special_action = SPECIAL_FREEZE;
- addr->child_count = 9999;
- return DEFER;
- }
- frc = FF_NOTDELIVERED;
- break;
+ if (!f.deliver_manual_thaw)
+ {
+ if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop))
+ != OK) return xrc;
+ add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
+ if (addr->message == NULL) addr->message = US"frozen by filter";
+ addr->special_action = SPECIAL_FREEZE;
+ addr->child_count = 9999;
+ return DEFER;
+ }
+ frc = FF_NOTDELIVERED;
+ break;
- /* Handle syntax errors and :include: failures and lookup defers */
+ /* Handle syntax errors and :include: failures and lookup defers */
case FF_ERROR:
case FF_INCLUDEFAIL:
- /* If filtertype is still FILTER_UNSET, it means that the redirection data
- was never inspected, so the error was an expansion failure or failure to open
- the file, or whatever. In these cases, the existing error message is probably
- sufficient. */
+ /* If filtertype is still FILTER_UNSET, it means that the redirection data
+ was never inspected, so the error was an expansion failure or failure to open
+ the file, or whatever. In these cases, the existing error message is probably
+ sufficient. */
- if (filtertype == FILTER_UNSET) return DEFER;
+ if (filtertype == FILTER_UNSET) return DEFER;
- /* If it was a filter and skip_syntax_errors is set, we want to set up
- the error message so that it can be logged and mailed to somebody. */
+ /* If it was a filter and skip_syntax_errors is set, we want to set up
+ the error message so that it can be logged and mailed to somebody. */
- if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors)
- {
- eblock = store_get(sizeof(error_block));
- eblock->next = NULL;
- eblock->text1 = addr->message;
- eblock->text2 = NULL;
- addr->message = addr->user_message = NULL;
- }
+ if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors)
+ {
+ eblock = store_get(sizeof(error_block), FALSE);
+ eblock->next = NULL;
+ eblock->text1 = addr->message;
+ eblock->text2 = NULL;
+ addr->message = addr->user_message = NULL;
+ }
- /* Otherwise set up the error for the address and defer. */
+ /* Otherwise set up the error for the address and defer. */
- else
- {
- addr->basic_errno = ERRNO_BADREDIRECT;
- addr->message = string_sprintf("error in %s %s: %s",
- (filtertype != FILTER_FORWARD)? "filter" : "redirect",
- (ob->data == NULL)? "file" : "data",
- addr->message);
- return DEFER;
- }
+ else
+ {
+ addr->basic_errno = ERRNO_BADREDIRECT;
+ addr->message = string_sprintf("error in %s %s: %s",
+ filtertype == FILTER_FORWARD ? "redirect" : "filter",
+ ob->data ? "data" : "file",
+ addr->message);
+ return DEFER;
+ }
}
rf_change_domain(address_item *addr, const uschar *domain, BOOL rewrite,
address_item **addr_new)
{
-address_item *parent = store_get(sizeof(address_item));
+address_item *parent = store_get(sizeof(address_item), FALSE);
uschar *at = Ustrrchr(addr->address, '@');
uschar *address = string_sprintf("%.*s@%s",
(int)(at - addr->address), addr->address, domain);
shared with other addresses. The output function outputs them in reverse
order. */
- header_line * h = store_get(sizeof(header_line));
+ header_line * h = store_get(sizeof(header_line), FALSE);
/* We used to use string_sprintf() to add the newline if needed, but that
causes problems if the header line is exceedingly long (e.g. adding
h->text = s;
else
{
- h->text = store_get(slen+2);
+ h->text = store_get(slen+2, is_tainted(s));
memcpy(h->text, s, slen);
h->text[slen++] = '\n';
h->text[slen] = 0;
uschar *ss;
BOOL expandable;
-if (tpname == NULL)
+if (!tpname)
{
- if (require_name == NULL) return TRUE;
+ if (!require_name) return TRUE;
addr->basic_errno = ERRNO_BADTRANSPORT;
addr->message = string_sprintf("%s unset in %s router", require_name,
router_name);
if (expandable)
{
- ss = expand_string(tpname);
- if (ss == NULL)
+ if (!(ss = expand_string(tpname)))
{
addr->basic_errno = ERRNO_BADTRANSPORT;
addr->message = string_sprintf("failed to expand transport "
"\"%s\" in %s router: %s", tpname, router_name, expand_string_message);
return FALSE;
}
+ if (is_tainted(ss))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted value '%s' from '%s' for transport", ss, tpname);
+ addr->basic_errno = ERRNO_BADTRANSPORT;
+ /* Avoid leaking info to an attacker */
+ addr->message = US"internal configuration error";
+ return FALSE;
+ }
}
-else ss = tpname;
+else
+ ss = tpname;
for (transport_instance * tp = transports; tp; tp = tp->next)
if (Ustrcmp(tp->name, ss) == 0)
{
debug_printf("queued for %s transport: local_part = %s\ndomain = %s\n"
" errors_to=%s\n",
- (addr->transport == NULL)? US"<unset>" : addr->transport->name,
+ addr->transport ? addr->transport->name : US"<unset>",
addr->local_part, addr->domain, addr->prop.errors_address);
debug_printf(" domain_data=%s localpart_data=%s\n", addr->prop.domain_data,
addr->prop.localpart_data);
/* Allow us to reset store used for lookups and lookup caching */
-static void *search_reset_point = NULL;
+static rmark search_reset_point = NULL;
for (int i = 0; i < lookup_list_count; i++) if (lookup_list[i]->tidy)
(lookup_list[i]->tidy)();
-if (search_reset_point) store_reset(search_reset_point);
-search_reset_point = NULL;
+if (search_reset_point) search_reset_point = store_reset(search_reset_point);
store_pool = old_pool;
}
/* Change to the search store pool and remember our reset point */
store_pool = POOL_SEARCH;
-if (search_reset_point == NULL) search_reset_point = store_get(0);
+if (!search_reset_point) search_reset_point = store_mark();
DEBUG(D_lookup) debug_printf_indent("search_open: %s \"%s\"\n", lk->name,
filename ? filename : US"NULL");
if (!t)
{
- t = store_get(sizeof(tree_node) + Ustrlen(keybuffer));
- t->data.ptr = c = store_get(sizeof(search_cache));
+ t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), FALSE);
+ t->data.ptr = c = store_get(sizeof(search_cache), FALSE);
c->item_cache = NULL;
Ustrcpy(t->name, keybuffer);
tree_insertnode(&search_tree, t);
}
else
{
- e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len);
+ e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring));
e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
e->ptr = data;
t = (tree_node *)(e+1);
if (affixlen == 0) keystring2 = keystring; else
{
- keystring2 = store_get(len + affixlen + 1);
+ keystring2 = store_get(len + affixlen + 1,
+ is_tainted(keystring) || is_tainted(affix));
Ustrncpy(keystring2, affix, affixlen);
Ustrcpy(keystring2 + affixlen, keystring);
DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2);
uschar * s;
if (Ustrchr(name, '=')) return -1;
if (overwrite || !getenv(name))
- putenv(CS string_copy_malloc(string_sprintf("%s=%s", name, val)));
+ putenv(CS string_copy_perm(string_sprintf("%s=%s", name, val)), FALSE);
return 0;
}
dst->length=0;
else
{
- dst->character=store_get(dst->length+1); /* plus one for \0 */
+ dst->character = store_get(dst->length+1, is_tainted(src->character)); /* plus one for \0 */
new=dst->character;
}
for (const uschar * start = src->character, * end = start + src->length;
filter->errmsg=US"Invalid URI encoding";
return -1;
}
- new=store_get(sizeof(string_item));
- new->text=store_get(to.length+1);
- if (to.length) memcpy(new->text,to.character,to.length);
+ new=store_get(sizeof(string_item), FALSE);
+ new->text = store_get(to.length+1, is_tainted(to.character));
+ if (to.length) memcpy(new->text, to.character, to.length);
new->text[to.length]='\0';
new->next=*recipient;
*recipient=new;
}
if (hname.length==2 && strcmpic(hname.character, US"to")==0)
{
- new=store_get(sizeof(string_item));
- new->text=store_get(hvalue.length+1);
- if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length);
+ new=store_get(sizeof(string_item), FALSE);
+ new->text = store_get(hvalue.length+1, is_tainted(hvalue.character));
+ if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length);
new->text[hvalue.length]='\0';
new->next=*recipient;
*recipient=new;
value->length=0;
value->character=(uschar*)0;
-t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header)));
+t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header)));
+if (!t) return;
while (*r==' ' || *r=='\t') ++r;
while (*r)
{
struct String *new;
dataCapacity = dataCapacity ? dataCapacity * 2 : 4;
- new = store_get(sizeof(struct String) * dataCapacity);
+ new = store_get(sizeof(struct String) * dataCapacity, FALSE);
if (d) memcpy(new,d,sizeof(struct String)*dataLength);
d = new;
}
else /* single string */
{
- if ((d=store_get(sizeof(struct String)*2))==(struct String*)0)
- {
+ if (!(d=store_get(sizeof(struct String)*2, FALSE)))
return -1;
- }
+
m=parse_string(filter,&d[0]);
if (m==-1)
- {
return -1;
- }
+
else if (m==0)
{
filter->pc=orig;
if (exec)
{
/* We are only interested in addresses below, so no MIME decoding */
- header_value=expand_string(string_sprintf("$rheader_%s",quote(h)));
- if (header_value == NULL)
+ if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h)))))
{
filter->errmsg=CUS "header string expansion failed";
return -1;
{
uschar *header_def;
- header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
- if (header_def == NULL)
+ header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
+ if (!header_def)
{
filter->errmsg=CUS "header string expansion failed";
return -1;
uschar *header_def;
expand_header(&header_value,h);
- header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
- if (header_value.character == NULL || header_def == NULL)
+ header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h)));
+ if (!header_value.character || !header_def)
{
filter->errmsg=CUS "header string expansion failed";
return -1;
}
if (exec && envelopeExpr)
{
- if ((envelope=expand_string(US envelopeExpr)) == NULL)
+ if (!(envelope=expand_string(US envelopeExpr)))
{
filter->errmsg=CUS "header string expansion failed";
return -1;
subject.character=(uschar*)0;
body.length=-1;
body.character=(uschar*)0;
- envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
+ envelope_from = sender_address && sender_address[0]
+ ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
+ if (!envelope_from)
+ {
+ filter->errmsg=CUS "expansion failure for envelope from";
+ return -1;
+ }
for (;;)
{
if (parse_white(filter)==-1) return -1;
if (message.length==-1) message=subject;
if (message.length==-1) expand_header(&message,&str_subject);
expand_header(&auto_submitted_value,&str_auto_submitted);
- auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
- if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
+ auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}");
+ if (!auto_submitted_value.character || !auto_submitted_def)
{
filter->errmsg=CUS "header string expansion failed";
return -1;
if (!already)
/* New notification, process it */
{
- struct Notification *sent;
- sent=store_get(sizeof(struct Notification));
+ struct Notification * sent = store_get(sizeof(struct Notification), FALSE);
sent->method=method;
sent->importance=importance;
sent->message=message;
int buffer_capacity;
f = fdopen(fd, "wb");
- fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
+ fprintf(f,"From: %s\n", from.length == -1
+ ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
for (string_item * p = recipient; p; p=p->next)
fprintf(f,"To: %s\n",p->text);
fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
}
/* Allocation is larger than necessary, but enough even for split MIME words */
buffer_capacity=32+4*message.length;
- buffer=store_get(buffer_capacity);
+ buffer=store_get(buffer_capacity, TRUE);
if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
fprintf(f,"\n");
if (body.length>0) fprintf(f,"%s\n",body.character);
}
for (struct String * a = addresses; a->length != -1; ++a)
{
- string_item * new = store_get(sizeof(string_item));
+ string_item * new = store_get(sizeof(string_item), FALSE);
- new->text=store_get(a->length+1);
+ new->text = store_get(a->length+1, is_tainted(a->character));
if (a->length) memcpy(new->text,a->character,a->length);
new->text[a->length]='\0';
new->next=aliases;
{
uschar *subject_def;
- subject_def=expand_string(US"${if def:header_subject {true}{false}}");
- if (Ustrcmp(subject_def,"true")==0)
+ subject_def = expand_string(US"${if def:header_subject {true}{false}}");
+ if (subject_def && Ustrcmp(subject_def,"true")==0)
{
gstring * g = string_catn(NULL, US"Auto: ", 6);
addr->prop.ignore_error = TRUE;
addr->next = *generated;
*generated = addr;
- addr->reply = store_get(sizeof(reply_item));
+ addr->reply = store_get(sizeof(reply_item), FALSE);
memset(addr->reply,0,sizeof(reply_item)); /* XXX */
addr->reply->to = string_copy(sender_address);
if (from.length==-1)
addr->reply->from = from.character;
/* Allocation is larger than necessary, but enough even for split MIME words */
buffer_capacity=32+4*subject.length;
- buffer=store_get(buffer_capacity);
+ buffer = store_get(buffer_capacity, is_tainted(subject.character));
/* deconst cast safe as we pass in a non-const item */
addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE);
addr->reply->oncelog = string_from_gstring(once);
error = error;
DEBUG(D_route) debug_printf("Sieve: start of processing\n");
-sieve.filter=filter;
+sieve.filter = filter;
-if (vacation_directory == NULL)
+if (!vacation_directory)
sieve.vacation_directory = NULL;
else
{
- sieve.vacation_directory=expand_string(vacation_directory);
- if (sieve.vacation_directory == NULL)
+ if (!(sieve.vacation_directory = expand_string(vacation_directory)))
{
*error = string_sprintf("failed to expand \"%s\" "
"(sieve_vacation_directory): %s", vacation_directory,
}
}
-if (enotify_mailto_owner == NULL)
+if (!enotify_mailto_owner)
sieve.enotify_mailto_owner = NULL;
else
{
- sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner);
- if (sieve.enotify_mailto_owner == NULL)
+ if (!(sieve.enotify_mailto_owner = expand_string(enotify_mailto_owner)))
{
*error = string_sprintf("failed to expand \"%s\" "
"(sieve_enotify_mailto_owner): %s", enotify_mailto_owner,
}
}
-sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
+sieve.useraddress = useraddress
+ ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix";
sieve.subaddress = subaddress;
#ifdef COMPILE_SYNTAX_CHECKER
if (sieve.keep)
{
add_addr(generated,US"inbox",1,0,0,0);
- msg = string_sprintf("Implicit keep");
+ msg = US"Implicit keep";
r = FF_DELIVERED;
}
else
{
- msg = string_sprintf("No implicit keep");
+ msg = US"No implicit keep";
r = FF_DELIVERED;
}
}
if (recipients_count > 0)
{
- raw_recipients = store_get(recipients_count * sizeof(uschar *));
+ raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
for (int i = 0; i < recipients_count; i++)
raw_recipients[i] = recipients_list[i].address;
raw_recipients_count = recipients_count;
smtp_data_sigint_exit();
smtp_had_error = save_errno;
- smtp_read_error = string_copy_malloc(
- string_sprintf(" (error: %s)", strerror(save_errno)));
+ smtp_read_error = string_copy_perm(
+ string_sprintf(" (error: %s)", strerror(save_errno)), FALSE);
}
else
smtp_had_eof = 1;
/* This is split off so that verify.c:respond_printf() can, in effect, call
smtp_printf(), bearing in mind that in C a vararg function can't directly
call another vararg function, only a function which accepts a va_list. */
+/*XXX consider passing caller-info in, for string_vformat-onward */
void
smtp_vprintf(const char *format, BOOL more, va_list ap)
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
BOOL yield;
-yield = !! string_vformat(&gs, FALSE, format, ap);
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that we'll never expand it. */
+
+yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap);
string_from_gstring(&gs);
DEBUG(D_receive)
{
- void *reset_point = store_get(0);
uschar *msg_copy, *cr, *end;
msg_copy = string_copy(gs.s);
end = msg_copy + gs.ptr;
while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */
memmove(cr, cr + 1, (end--) - cr);
debug_printf("SMTP>> %s", msg_copy);
- store_reset(reset_point);
}
if (!yield)
/* Discard any previous helo name */
-if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+sender_helo_name = NULL;
/* Skip tests if junk is permitted. */
/* Save argument if OK */
-if (yield) sender_helo_name = string_copy_malloc(start);
+if (yield) sender_helo_name = string_copy_perm(start, TRUE);
return yield;
}
Returns: nothing
*/
-void
+void *
smtp_reset(void *reset_point)
{
recipients_list = NULL;
store_free(this);
}
store_reset(reset_point);
+return store_mark();
}
smtp_setup_batch_msg(void)
{
int done = 0;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
/* Save the line count at the start of each transaction - single commands
like HELO and RSET count as whole transactions. */
if ((receive_feof)()) return 0; /* Treat EOF as QUIT */
cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg");
-smtp_reset(reset_point); /* Reset for start of message */
+reset_point = smtp_reset(reset_point); /* Reset for start of message */
/* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
value. The values are 2 larger than the required yield of the function. */
case RSET_CMD:
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
bsmtp_transaction_linecount = receive_linecount;
break;
/* Reset to start of message */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
/* Apply SMTP rewrite */
acl_var_c = NULL;
-/* Allow for trailing 0 in the command and data buffers. */
+/* Allow for trailing 0 in the command and data buffers. Tainted. */
-if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "malloc() failed for SMTP command buffer");
+smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE);
smtp_cmd_buffer[0] = 0;
smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1;
{
#if OPTSTYLE == 1
EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
- struct ip_options *ipopt = store_get(optlen);
+ struct ip_options *ipopt = store_get(optlen, FALSE);
#elif OPTSTYLE == 2
struct ip_opts ipoptblock;
struct ip_opts *ipopt = &ipoptblock;
va_list ap;
va_start(ap, defaultrespond);
- g = string_vformat(NULL, TRUE, CS defaultrespond, ap);
+ g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
va_end(ap);
smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
}
case OK:
if (!au->set_id || set_id) /* Complete success */
{
- if (set_id) authenticated_id = string_copy_malloc(set_id);
+ if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
sender_host_authenticated = au->name;
sender_host_auth_pubname = au->public_name;
authentication_failed = FALSE;
/* Fall through */
case DEFER:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
*s = string_sprintf("435 Unable to authenticate at present%s",
auth_defer_user_msg);
*ss = string_sprintf("435 Unable to authenticate at present%s: %s",
break;
case FAIL:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
*s = US"535 Incorrect authentication data";
*ss = string_sprintf("535 Incorrect authentication data%s", set_id);
break;
default:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
*s = US"435 Internal error";
*ss = string_sprintf("435 Internal error%s: return %d from authentication "
"check", set_id, rc);
BOOL discarded = FALSE;
BOOL last_was_rej_mail = FALSE;
BOOL last_was_rcpt = FALSE;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
TLS between messages (an Exim client may do this if it has messages queued up
for the host). Note: we do NOT reset AUTH at this point. */
-smtp_reset(reset_point);
+reset_point = smtp_reset(reset_point);
message_ended = END_NOTSTARTED;
chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
&user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+ sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
break;
}
smtp_code = US"250 "; /* Default response code plus space*/
if (!user_msg)
{
- g = string_fmt_append(NULL, "%.3s %s Hello %s%s%s",
+ /* sender_host_name below will be tainted, so save on copy when we hit it */
+ g = string_get_tainted(24, TRUE);
+ g = string_fmt_append(g, "%.3s %s Hello %s%s%s",
smtp_code,
smtp_active_hostname,
sender_ident ? sender_ident : US"",
+ (tls_in.active.sock >= 0 ? pcrpted : 0)
];
cancel_cutthrough_connection(TRUE, US"sent EHLO response");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break; /* HELO/EHLO */
obviously need to throw away any previous data. */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
sender_data = recipient_data = NULL;
incomplete_transaction_log(US"STARTTLS");
cancel_cutthrough_connection(TRUE, US"STARTTLS received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
if (sender_helo_name)
{
- store_free(sender_helo_name);
sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
set_process_info("handling incoming TLS connection from %s",
case RSET_CMD:
smtp_rset_handler();
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break;
{
uschar buffer[256];
buffer[0] = 0;
- Ustrcat(buffer, " AUTH");
+ Ustrcat(buffer, US" AUTH");
#ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
- Ustrcat(buffer, " STARTTLS");
+ Ustrcat(buffer, US" STARTTLS");
#endif
- Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT");
- Ustrcat(buffer, " NOOP QUIT RSET HELP");
- if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
- if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
- if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
+ Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT");
+ Ustrcat(buffer, US" NOOP QUIT RSET HELP");
+ if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN");
+ if (acl_smtp_expn) Ustrcat(buffer, US" EXPN");
+ if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY");
smtp_printf("214%s\r\n", FALSE, buffer);
}
break;
}
enq_end(etrn_serialize_key);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
/* Back in the top level SMTP process. Check that we started a subprocess
if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
}
else
- {
- if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
- else smtp_user_msg(US"250", user_msg);
- }
+ if (!user_msg)
+ smtp_printf("250 OK\r\n", FALSE);
+ else
+ smtp_user_msg(US"250", user_msg);
signal(SIGCHLD, oldsignal);
break;
return FALSE;
}
+if (is_tainted(expint))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted value '%s' from '%s' for interface",
+ expint, istring);
+ addr->transport_return = PANIC;
+ addr->message = string_sprintf("failed to expand \"interface\" "
+ "option for %s: configuration error", msg);
+ return FALSE;
+ }
+
while (isspace(*expint)) expint++;
if (*expint == 0) return TRUE;
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
va_list ap;
+ /* Use taint-unchecked routines for writing into big_buffer, trusting that
+ we'll never expand the results. Actually, the error-message use - leaving
+ the results in big_buffer for potential later use - is uncomfortably distant.
+ XXX Would be better to assume all smtp commands are short, use normal pool
+ alloc rather than big_buffer, and another global for the data-for-error. */
+
va_start(ap, format);
- if (!string_vformat(&gs, FALSE, CS format, ap))
+ if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
"SMTP");
va_end(ap);
if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
}
- Ustrncpy(CS outblock->ptr, gs.s, gs.ptr);
+ Ustrncpy(outblock->ptr, gs.s, gs.ptr);
outblock->ptr += gs.ptr;
outblock->cmd_count++;
gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
uschar * s;
DEBUG(D_acl) debug_printf_indent("spamd: addr entry '%s'\n", address);
- sd = (spamd_address_container *)store_get(sizeof(spamd_address_container));
+ sd = store_get(sizeof(spamd_address_container), FALSE);
for (sublist = address, args = 0, spamd_param_init(sd);
(s = string_nextinlist(&sublist, &sublist_sep, NULL, 0));
}
Ustrcpy(spam_action_buffer,
- spamd_score >= spamd_threshold ? "reject" : "no action");
+ spamd_score >= spamd_threshold ? US"reject" : US"no action");
}
/* Create report. Since this is a multiline string,
if (n < 5) return FALSE; /* malformed line */
buffer[n-1] = 0; /* Remove \n */
-node = store_get(sizeof(tree_node) + n - 3);
+node = store_get(sizeof(tree_node) + n - 3, is_tainted(buffer));
*connect = node;
Ustrcpy(node->name, buffer + 3);
node->data.ptr = NULL;
if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>')
goto SPOOL_FORMAT_ERROR;
-sender_address = store_get(n-2);
+sender_address = store_get(n-2, TRUE); /* tainted */
Ustrncpy(sender_address, big_buffer+1, n-3);
sender_address[n-3] = 0;
To allow new versions of Exim that add additional flags to interwork with older
versions that do not understand them, just ignore any lines starting with "-"
that we don't recognize. Otherwise it wouldn't be possible to back off a new
-version that left new-style flags written on the spool. */
+version that left new-style flags written on the spool.
+
+If the line starts with "--" the content of the variable is tainted. */
-p = big_buffer + 2;
for (;;)
{
int len;
+ BOOL tainted;
+ uschar * var;
+
if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
if (big_buffer[0] != '-') break;
while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1
{ /* buffer not big enough for line; certs make this possible */
uschar * buf;
if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
- buf = store_get_perm(big_buffer_size *= 2);
+ buf = store_get_perm(big_buffer_size *= 2, FALSE);
memcpy(buf, big_buffer, --len);
big_buffer = buf;
if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL)
}
big_buffer[len-1] = 0;
- switch(big_buffer[1])
+ tainted = big_buffer[1] == '-';
+ var = big_buffer + (tainted ? 2 : 1);
+ p = var + 1;
+
+ switch(*var)
{
case 'a':
uschar *name, *endptr;
int count;
tree_node *node;
- endptr = Ustrchr(big_buffer + 6, ' ');
+ endptr = Ustrchr(var + 5, ' ');
if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
- name = string_sprintf("%c%.*s", big_buffer[4],
- (int)(endptr - big_buffer - 6), big_buffer + 6);
+ name = string_sprintf("%c%.*s", var[3],
+ (int)(endptr - var - 5), var + 5);
if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
node = acl_var_create(name);
- node->data.ptr = store_get(count + 1);
+ node->data.ptr = store_get(count + 1, tainted);
if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR;
((uschar*)node->data.ptr)[count] = 0;
}
f.allow_unqualified_sender = TRUE;
else if (Ustrncmp(p, "uth_id", 6) == 0)
- authenticated_id = string_copy(big_buffer + 9);
+ authenticated_id = string_copy_taint(var + 8, tainted);
else if (Ustrncmp(p, "uth_sender", 10) == 0)
- authenticated_sender = string_copy(big_buffer + 13);
+ authenticated_sender = string_copy_taint(var + 12, tainted);
else if (Ustrncmp(p, "ctive_hostname", 14) == 0)
- smtp_active_hostname = string_copy(big_buffer + 17);
+ smtp_active_hostname = string_copy_taint(var + 16, tainted);
/* For long-term backward compatibility, we recognize "-acl", which was
used before the number of ACL variables changed from 10 to 20. This was
unsigned index, count;
uschar name[20]; /* Need plenty of space for %u format */
tree_node * node;
- if ( sscanf(CS big_buffer + 5, "%u %u", &index, &count) != 2
+ if ( sscanf(CS var + 4, "%u %u", &index, &count) != 2
|| index >= 20
|| count > 16384 /* arbitrary limit on variable size */
)
else
(void) string_format(name, sizeof(name), "%c%u", 'm', index - 10);
node = acl_var_create(name);
- node->data.ptr = store_get(count + 1);
+ node->data.ptr = store_get(count + 1, tainted);
/* We sanity-checked the count, so disable the Coverity error */
/* coverity[tainted_data] */
if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR;
case 'b':
if (Ustrncmp(p, "ody_linecount", 13) == 0)
- body_linecount = Uatoi(big_buffer + 15);
+ body_linecount = Uatoi(var + 14);
else if (Ustrncmp(p, "ody_zerocount", 13) == 0)
- body_zerocount = Uatoi(big_buffer + 15);
+ body_zerocount = Uatoi(var + 14);
#ifdef EXPERIMENTAL_BRIGHTMAIL
else if (Ustrncmp(p, "mi_verdicts ", 12) == 0)
- bmi_verdicts = string_copy(big_buffer + 14);
+ bmi_verdicts = string_copy_taint(var + 13, tainted);
#endif
break;
f.deliver_firsttime = TRUE;
/* Check if the dsn flags have been set in the header file */
else if (Ustrncmp(p, "sn_ret", 6) == 0)
- dsn_ret= atoi(CS big_buffer + 8);
+ dsn_ret= atoi(CS var + 7);
else if (Ustrncmp(p, "sn_envid", 8) == 0)
- dsn_envid = string_copy(big_buffer + 11);
+ dsn_envid = string_copy_taint(var + 10, tainted);
break;
case 'f':
if (Ustrncmp(p, "rozen", 5) == 0)
{
f.deliver_freeze = TRUE;
- if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1)
+ if (sscanf(CS var+6, TIME_T_FMT, &deliver_frozen_at) != 1)
goto SPOOL_READ_ERROR;
}
break;
else if (Ustrcmp(p, "ost_lookup_failed") == 0)
host_lookup_failed = TRUE;
else if (Ustrncmp(p, "ost_auth", 8) == 0)
- sender_host_authenticated = string_copy(big_buffer + 11);
+ sender_host_authenticated = string_copy_taint(var + 10, tainted);
else if (Ustrncmp(p, "ost_name", 8) == 0)
- sender_host_name = string_copy(big_buffer + 11);
+ sender_host_name = string_copy_taint(var + 10, tainted);
else if (Ustrncmp(p, "elo_name", 8) == 0)
- sender_helo_name = string_copy(big_buffer + 11);
+ sender_helo_name = string_copy_taint(var + 10, tainted);
/* We now record the port number after the address, separated by a
dot. For compatibility during upgrading, do nothing if there
else if (Ustrncmp(p, "ost_address", 11) == 0)
{
- sender_host_port = host_address_extract_port(big_buffer + 14);
- sender_host_address = string_copy(big_buffer + 14);
+ sender_host_port = host_address_extract_port(var + 13);
+ sender_host_address = string_copy_taint(var + 13, tainted);
}
break;
case 'i':
if (Ustrncmp(p, "nterface_address", 16) == 0)
{
- interface_port = host_address_extract_port(big_buffer + 19);
- interface_address = string_copy(big_buffer + 19);
+ interface_port = host_address_extract_port(var + 18);
+ interface_address = string_copy_taint(var + 18, tainted);
}
else if (Ustrncmp(p, "dent", 4) == 0)
- sender_ident = string_copy(big_buffer + 7);
+ sender_ident = string_copy_taint(var + 6, tainted);
break;
case 'l':
if (Ustrcmp(p, "ocal") == 0)
f.sender_local = TRUE;
- else if (Ustrcmp(big_buffer, "-localerror") == 0)
+ else if (Ustrcmp(var, "localerror") == 0)
f.local_error_message = TRUE;
#ifdef HAVE_LOCAL_SCAN
else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
- local_scan_data = string_copy(big_buffer + 12);
+ local_scan_data = string_copy_taint(var + 11, tainted);
#endif
break;
case 'm':
- if (Ustrcmp(p, "anual_thaw") == 0) f.deliver_manual_thaw = TRUE;
+ if (Ustrcmp(p, "anual_thaw") == 0)
+ f.deliver_manual_thaw = TRUE;
else if (Ustrncmp(p, "ax_received_linelength", 22) == 0)
- max_received_linelength = Uatoi(big_buffer + 24);
+ max_received_linelength = Uatoi(var + 23);
break;
case 'N':
case 'r':
if (Ustrncmp(p, "eceived_protocol", 16) == 0)
- received_protocol = string_copy(big_buffer + 19);
+ received_protocol = string_copy_taint(var + 18, tainted);
else if (Ustrncmp(p, "eceived_time_usec", 17) == 0)
{
unsigned usec;
- if (sscanf(CS big_buffer + 21, "%u", &usec) == 1)
+ if (sscanf(CS var + 20, "%u", &usec) == 1)
received_time.tv_usec = usec;
}
break;
f.sender_set_untrusted = TRUE;
#ifdef WITH_CONTENT_SCAN
else if (Ustrncmp(p, "pam_bar ", 8) == 0)
- spam_bar = string_copy(big_buffer + 10);
+ spam_bar = string_copy_taint(var + 9, tainted);
else if (Ustrncmp(p, "pam_score ", 10) == 0)
- spam_score = string_copy(big_buffer + 12);
+ spam_score = string_copy_taint(var + 11, tainted);
else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
- spam_score_int = string_copy(big_buffer + 16);
+ spam_score_int = string_copy_taint(var + 15, tainted);
#endif
#ifndef COMPILE_UTILITY
else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0)
if (Ustrncmp(q, "certificate_verified", 20) == 0)
tls_in.certificate_verified = TRUE;
else if (Ustrncmp(q, "cipher", 6) == 0)
- tls_in.cipher = string_copy(big_buffer + 12);
+ tls_in.cipher = string_copy_taint(var + 11, tainted);
# ifndef COMPILE_UTILITY /* tls support fns not built in */
else if (Ustrncmp(q, "ourcert", 7) == 0)
- (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
+ (void) tls_import_cert(var + 12, &tls_in.ourcert);
else if (Ustrncmp(q, "peercert", 8) == 0)
- (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
+ (void) tls_import_cert(var + 13, &tls_in.peercert);
# endif
else if (Ustrncmp(q, "peerdn", 6) == 0)
- tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
+ tls_in.peerdn = string_unprinting(string_copy_taint(var + 11, tainted));
else if (Ustrncmp(q, "sni", 3) == 0)
- tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
+ tls_in.sni = string_unprinting(string_copy_taint(var + 8, tainted));
else if (Ustrncmp(q, "ocsp", 4) == 0)
- tls_in.ocsp = big_buffer[10] - '0';
+ tls_in.ocsp = var[9] - '0';
# ifdef EXPERIMENTAL_TLS_RESUME
else if (Ustrncmp(q, "resumption", 10) == 0)
- tls_in.resumption = big_buffer[16] - 'A';
+ tls_in.resumption = var[15] - 'A';
# endif
}
#endif /* COMPILE_UTILITY */
recipients_list_max = rcount;
-recipients_list = store_get(rcount * sizeof(recipient_item));
+recipients_list = store_get(rcount * sizeof(recipient_item), FALSE);
/* We sanitised the count and know we have enough memory, so disable
the Coverity error on recipients_count */
if (*p == ',')
{
int dummy;
+#if !defined (COMPILE_UTILITY)
+ DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 3 spool file\n");
+#endif
while (isdigit(*(--p)) || *p == ',');
if (*p == ' ')
{
else if (*p == ' ')
{
+#if !defined (COMPILE_UTILITY)
+ DEBUG(D_deliver) debug_printf("**** SPOOL_IN - early Exim 4 spool file\n");
+#endif
*p++ = 0;
(void)sscanf(CS p, "%d", &pno);
}
if (len > 0)
{
p -= len;
- errors_to = string_copy(p);
+ errors_to = string_copy_taint(p, TRUE);
}
}
if (len > 0)
{
p -= len;
- orcpt = string_copy(p);
+ orcpt = string_copy_taint(p, TRUE);
}
}
big_buffer, errors_to);
#endif
- recipients_list[recipients_count].address = string_copy(big_buffer);
+ recipients_list[recipients_count].address = string_copy_taint(big_buffer, TRUE);
recipients_list[recipients_count].pno = pno;
recipients_list[recipients_count].errors_to = errors_to;
recipients_list[recipients_count].orcpt = orcpt;
if (read_headers)
{
- h = store_get(sizeof(header_line));
+ h = store_get(sizeof(header_line), FALSE);
h->next = NULL;
h->type = flag[0];
h->slen = n;
- h->text = store_get(n+1);
+ h->text = store_get(n+1, TRUE); /* tainted */
if (h->type == htype_received) received_count++;
FILE *mbox_file = NULL, *l_data_file = NULL, *yield = NULL;
struct stat statbuf;
int j;
-void *reset_point;
+rmark reset_point;
mbox_path = string_sprintf("%s/scan/%s/%s.eml",
spool_directory, message_id, message_id);
if (mbox_fname) *mbox_fname = mbox_path;
-reset_point = store_get(0);
+reset_point = store_mark();
/* Skip creation if already spooled out as mbox file */
if (!spool_mbox_ok)
if (spool_mbox_ok && !f.no_mbox_unspool)
{
- uschar *mbox_path;
uschar *file_path;
struct dirent *entry;
DIR *tempdir;
-
- mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
+ rmark reset_point = store_mark();
+ uschar * mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id);
if (!(tempdir = opendir(CS mbox_path)))
{
/* remove directory */
rmdir(CS mbox_path);
- store_reset(mbox_path);
+ store_reset(reset_point);
}
spool_mbox_ok = 0;
}
+static void
+spool_var_write(FILE * fp, const uschar * name, const uschar * val)
+{
+if (is_tainted(val)) putc('-', fp);
+fprintf(fp, "-%s %s\n", name, val);
+}
+
/*************************************************
* Write the header spool file *
*************************************************/
/* If there is information about a sending host, remember it. The HELO
data can be set for local SMTP as well as remote. */
-if (sender_helo_name)
- fprintf(fp, "-helo_name %s\n", sender_helo_name);
+if (sender_helo_name) spool_var_write(fp, US"helo_name", sender_helo_name);
if (sender_host_address)
{
+ if (is_tainted(sender_host_address)) putc('-', fp);
fprintf(fp, "-host_address %s.%d\n", sender_host_address, sender_host_port);
if (sender_host_name)
- fprintf(fp, "-host_name %s\n", sender_host_name);
+ spool_var_write(fp, US"host_name", sender_host_name);
if (sender_host_authenticated)
- fprintf(fp, "-host_auth %s\n", sender_host_authenticated);
+ spool_var_write(fp, US"host_auth", sender_host_authenticated);
}
/* Also about the interface a message came in on */
if (interface_address)
+ {
+ if (is_tainted(interface_address)) putc('-', fp);
fprintf(fp, "-interface_address %s.%d\n", interface_address, interface_port);
+ }
if (smtp_active_hostname != primary_hostname)
- fprintf(fp, "-active_hostname %s\n", smtp_active_hostname);
+ spool_var_write(fp, US"active_hostname", smtp_active_hostname);
/* Likewise for any ident information; for local messages this is
likely to be the same as originator_login, but will be different if
the originator was root, forcing a different ident. */
-if (sender_ident) fprintf(fp, "-ident %s\n", sender_ident);
+if (sender_ident)
+ spool_var_write(fp, US"ident", sender_ident);
/* Ditto for the received protocol */
if (received_protocol)
- fprintf(fp, "-received_protocol %s\n", received_protocol);
+ spool_var_write(fp, US"received_protocol", received_protocol);
/* Preserve any ACL variables that are set. */
if (body_zerocount > 0) fprintf(fp, "-body_zerocount %d\n", body_zerocount);
if (authenticated_id)
- fprintf(fp, "-auth_id %s\n", authenticated_id);
+ spool_var_write(fp, US"auth_id", authenticated_id);
if (authenticated_sender)
- fprintf(fp, "-auth_sender %s\n", authenticated_sender);
+ spool_var_write(fp, US"auth_sender", authenticated_sender);
if (f.allow_unqualified_recipient) fprintf(fp, "-allow_unqualified_recipient\n");
if (f.allow_unqualified_sender) fprintf(fp, "-allow_unqualified_sender\n");
if (f.sender_local) fprintf(fp, "-local\n");
if (f.local_error_message) fprintf(fp, "-localerror\n");
#ifdef HAVE_LOCAL_SCAN
-if (local_scan_data) fprintf(fp, "-local_scan %s\n", local_scan_data);
+if (local_scan_data) spool_var_write(fp, US"local_scan", local_scan_data);
#endif
#ifdef WITH_CONTENT_SCAN
-if (spam_bar) fprintf(fp,"-spam_bar %s\n", spam_bar);
-if (spam_score) fprintf(fp,"-spam_score %s\n", spam_score);
-if (spam_score_int) fprintf(fp,"-spam_score_int %s\n", spam_score_int);
+if (spam_bar) spool_var_write(fp, US"spam_bar", spam_bar);
+if (spam_score) spool_var_write(fp, US"spam_score", spam_score);
+if (spam_score_int) spool_var_write(fp, US"spam_score_int", spam_score_int);
#endif
if (f.deliver_manual_thaw) fprintf(fp, "-manual_thaw\n");
if (f.sender_set_untrusted) fprintf(fp, "-sender_set_untrusted\n");
#ifdef EXPERIMENTAL_BRIGHTMAIL
-if (bmi_verdicts) fprintf(fp, "-bmi_verdicts %s\n", bmi_verdicts);
+if (bmi_verdicts) spool_var_write(fp, US"bmi_verdicts", bmi_verdicts);
#endif
#ifndef DISABLE_TLS
if (tls_in.certificate_verified) fprintf(fp, "-tls_certificate_verified\n");
-if (tls_in.cipher) fprintf(fp, "-tls_cipher %s\n", tls_in.cipher);
+if (tls_in.cipher) spool_var_write(fp, US"tls_cipher", tls_in.cipher);
if (tls_in.peercert)
{
(void) tls_export_cert(big_buffer, big_buffer_size, tls_in.peercert);
- fprintf(fp, "-tls_peercert %s\n", CS big_buffer);
+ fprintf(fp, "--tls_peercert %s\n", CS big_buffer);
}
-if (tls_in.peerdn) fprintf(fp, "-tls_peerdn %s\n", string_printing(tls_in.peerdn));
-if (tls_in.sni) fprintf(fp, "-tls_sni %s\n", string_printing(tls_in.sni));
+if (tls_in.peerdn) spool_var_write(fp, US"tls_peerdn", string_printing(tls_in.peerdn));
+if (tls_in.sni) spool_var_write(fp, US"tls_sni", string_printing(tls_in.sni));
if (tls_in.ourcert)
{
(void) tls_export_cert(big_buffer, big_buffer_size, tls_in.ourcert);
*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim maintainers 2019 */
/* See the file NOTICE for conditions of use and distribution. */
/* Exim gets and frees all its store through these functions. In the original
. There is a separate pool (POOL_SEARCH) that is used only for lookup storage.
This means it can be freed when search_tidyup() is called to close down all
the lookup caching.
+
+. Orthogonal to the three pool types, there are two classes of memory: untainted
+ and tainted. The latter is used for values derived from untrusted input, and
+ the string-expansion mechanism refuses to operate on such values (obviously,
+ it can expand an untainted value to return a tainted result). The classes
+ are implemented by duplicating the three pool types. Pool resets are requested
+ against the nontainted sibling and apply to both siblings.
*/
/* keep config.h before memcheck.h, for NVALGRIND */
#include "config.h"
+#include <sys/mman.h>
#include "memcheck.h"
appears (I checked that gcc does do this). */
#define alignment \
- ((sizeof(void *) > sizeof(double))? sizeof(void *) : sizeof(double))
-
-/* Size of block to get from malloc to carve up into smaller ones. This
-must be a multiple of the alignment. We assume that 8192 is going to be
-suitably aligned. */
-
-#define STORE_BLOCK_SIZE 8192
+ (sizeof(void *) > sizeof(double) ? sizeof(void *) : sizeof(double))
/* store_reset() will not free the following block if the last used block has
less than this much left in it. */
#define ALIGNED_SIZEOF_STOREBLOCK \
(((sizeof(storeblock) + alignment - 1) / alignment) * alignment)
+/* Size of block to get from malloc to carve up into smaller ones. This
+must be a multiple of the alignment. We assume that 8192 is going to be
+suitably aligned. */
+
+#define STORE_BLOCK_SIZE (8192 - ALIGNED_SIZEOF_STOREBLOCK)
+
/* Variables holding data for the local pools of store. The current pool number
is held in store_pool, which is global so that it can be changed from outside.
Setting the initial length values to -1 forces a malloc for the first call,
even if the length is zero (which is used for getting a point to reset to). */
-int store_pool = POOL_PERM;
+int store_pool = POOL_MAIN;
-static storeblock *chainbase[3] = { NULL, NULL, NULL };
-static storeblock *current_block[3] = { NULL, NULL, NULL };
-static void *next_yield[3] = { NULL, NULL, NULL };
-static int yield_length[3] = { -1, -1, -1 };
+#define NPOOLS 6
+static storeblock *chainbase[NPOOLS];
+static storeblock *current_block[NPOOLS];
+static void *next_yield[NPOOLS];
+static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 };
+
+/* The limits of the tainted pools. Tracking these on new allocations enables
+a fast is_tainted implementation. We assume the kernel only allocates mmaps using
+one side or the other of data+heap, not both. */
+
+static void * tainted_base = (void *)-1;
+static void * tainted_top = (void *)0;
/* pool_malloc holds the amount of memory used by the store pools; this goes up
and down as store is reset or released. nonpool_malloc is the total got by
malloc from other calls; this doesn't go down because it is just freed by
pointer. */
-static int pool_malloc = 0;
-static int nonpool_malloc = 0;
+static int pool_malloc;
+static int nonpool_malloc;
/* This variable is set by store_get() to its yield, and by store_reset() to
NULL. This enables string_cat() to optimize its store handling for very long
strings. That's why the variable is global. */
-void *store_last_get[3] = { NULL, NULL, NULL };
+void *store_last_get[NPOOLS];
+
+/* These are purely for stats-gathering */
+
+static int nbytes[NPOOLS]; /* current bytes allocated */
+static int maxbytes[NPOOLS]; /* max number reached */
+static int nblocks[NPOOLS]; /* current number of blocks allocated */
+static int maxblocks[NPOOLS];
+static int n_nonpool_blocks; /* current number of direct store_malloc() blocks */
+static int max_nonpool_blocks;
+static int max_pool_malloc; /* max value for pool_malloc */
+static int max_nonpool_malloc; /* max value for nonpool_malloc */
+
+
+static const uschar * pooluse[NPOOLS] = {
+[POOL_MAIN] = US"main",
+[POOL_PERM] = US"perm",
+[POOL_SEARCH] = US"search",
+[POOL_TAINT_MAIN] = US"main",
+[POOL_TAINT_PERM] = US"perm",
+[POOL_TAINT_SEARCH] = US"search",
+};
+static const uschar * poolclass[NPOOLS] = {
+[POOL_MAIN] = US"untainted",
+[POOL_PERM] = US"untainted",
+[POOL_SEARCH] = US"untainted",
+[POOL_TAINT_MAIN] = US"tainted",
+[POOL_TAINT_PERM] = US"tainted",
+[POOL_TAINT_SEARCH] = US"tainted",
+};
+
+
+static void * store_mmap(int, const char *, int);
+static void * internal_store_malloc(int, const char *, int);
+static void internal_store_free(void *, const char *, int linenumber);
+
+/******************************************************************************/
+
+/* Predicate: if an address is in a tainted pool.
+By extension, a variable pointing to this address is tainted.
+*/
+
+BOOL
+is_tainted(const void * p)
+{
+BOOL rc = p >= tainted_base && p < tainted_top;
+#ifndef COMPILE_UTILITY
+DEBUG(D_memory) if (rc) debug_printf_indent("is_tainted: YES\n");
+#endif
+return rc;
+}
+
+void
+die_tainted(const uschar * msg, const uschar * func, int line)
+{
+log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n",
+ msg, func, line);
+}
/*************************************************
Arguments:
size amount wanted
- filename source file from which called
- linenumber line number in source file.
+ func function from which called
+ linenumber line number in source file
Returns: pointer to store (panic on malloc failure)
*/
void *
-store_get_3(int size, const char *filename, int linenumber)
+store_get_3(int size, BOOL tainted, const char *func, int linenumber)
{
+int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
+
/* Round up the size to a multiple of the alignment. Although this looks a
messy statement, because "alignment" is a constant expression, the compiler can
do a reasonable job of optimizing, especially if the value of "alignment" is a
size is STORE_BLOCK_SIZE, and we would expect this to be the norm, since
these functions are mostly called for small amounts of store. */
-if (size > yield_length[store_pool])
+if (size > yield_length[pool])
{
- int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size;
+ int length = size <= STORE_BLOCK_SIZE ? STORE_BLOCK_SIZE : size;
int mlength = length + ALIGNED_SIZEOF_STOREBLOCK;
- storeblock * newblock = NULL;
+ storeblock * newblock;
/* Sometimes store_reset() may leave a block for us; check if we can use it */
- if ( (newblock = current_block[store_pool])
+ if ( (newblock = current_block[pool])
&& (newblock = newblock->next)
&& newblock->length < length
)
{
/* Give up on this block, because it's too small */
- store_free(newblock);
+ nblocks[pool]--;
+ if (pool < POOL_TAINT_BASE)
+ internal_store_free(newblock, func, linenumber);
+ else
+ {
+#ifndef COMPILE_UTILITY
+ DEBUG(D_memory)
+ debug_printf("---Unmap %6p %-20s %4d\n", newblock, func, linenumber);
+#endif
+ munmap(newblock, newblock->length + ALIGNED_SIZEOF_STOREBLOCK);
+ }
newblock = NULL;
}
if (!newblock)
{
- pool_malloc += mlength; /* Used in pools */
- nonpool_malloc -= mlength; /* Exclude from overall total */
- newblock = store_malloc(mlength);
+ if ((nbytes[pool] += mlength) > maxbytes[pool])
+ maxbytes[pool] = nbytes[pool];
+ if ((pool_malloc += mlength) > max_pool_malloc) /* Used in pools */
+ max_pool_malloc = pool_malloc;
+ nonpool_malloc -= mlength; /* Exclude from overall total */
+ if (++nblocks[pool] > maxblocks[pool])
+ maxblocks[pool] = nblocks[pool];
+
+ newblock = tainted
+ ? store_mmap(mlength, func, linenumber)
+ : internal_store_malloc(mlength, func, linenumber);
newblock->next = NULL;
newblock->length = length;
- if (!chainbase[store_pool])
- chainbase[store_pool] = newblock;
+
+ if (!chainbase[pool])
+ chainbase[pool] = newblock;
else
- current_block[store_pool]->next = newblock;
+ current_block[pool]->next = newblock;
}
- current_block[store_pool] = newblock;
- yield_length[store_pool] = newblock->length;
- next_yield[store_pool] =
- (void *)(CS current_block[store_pool] + ALIGNED_SIZEOF_STOREBLOCK);
- (void) VALGRIND_MAKE_MEM_NOACCESS(next_yield[store_pool], yield_length[store_pool]);
+ current_block[pool] = newblock;
+ yield_length[pool] = newblock->length;
+ next_yield[pool] =
+ (void *)(CS current_block[pool] + ALIGNED_SIZEOF_STOREBLOCK);
+ (void) VALGRIND_MAKE_MEM_NOACCESS(next_yield[pool], yield_length[pool]);
}
/* There's (now) enough room in the current block; the yield is the next
pointer. */
-store_last_get[store_pool] = next_yield[store_pool];
+store_last_get[pool] = next_yield[pool];
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
#ifdef COMPILE_UTILITY
-filename = filename;
+func = func;
linenumber = linenumber;
#else
DEBUG(D_memory)
- {
- if (f.running_in_test_harness)
- debug_printf("---%d Get %5d\n", store_pool, size);
- else
- debug_printf("---%d Get %6p %5d %-14s %4d\n", store_pool,
- store_last_get[store_pool], size, filename, linenumber);
- }
+ debug_printf("---%d Get %6p %5d %-14s %4d\n", pool,
+ store_last_get[pool], size, func, linenumber);
#endif /* COMPILE_UTILITY */
-(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[store_pool], size);
+(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[pool], size);
/* Update next pointer and number of bytes left in the current block. */
-next_yield[store_pool] = (void *)(CS next_yield[store_pool] + size);
-yield_length[store_pool] -= size;
-
-return store_last_get[store_pool];
+next_yield[pool] = (void *)(CS next_yield[pool] + size);
+yield_length[pool] -= size;
+return store_last_get[pool];
}
Arguments:
size amount wanted
- filename source file from which called
- linenumber line number in source file.
+ func function from which called
+ linenumber line number in source file
Returns: pointer to store (panic on malloc failure)
*/
void *
-store_get_perm_3(int size, const char *filename, int linenumber)
+store_get_perm_3(int size, BOOL tainted, const char *func, int linenumber)
{
void *yield;
int old_pool = store_pool;
store_pool = POOL_PERM;
-yield = store_get_3(size, filename, linenumber);
+yield = store_get_3(size, tainted, func, linenumber);
store_pool = old_pool;
return yield;
}
/* While reading strings of unknown length, it is often the case that the
string is being read into the block at the top of the stack. If it needs to be
-extended, it is more efficient just to extend the top block rather than
+extended, it is more efficient just to extend within the top block rather than
allocate a new block and then have to copy the data. This function is provided
for the use of string_cat(), but of course can be used elsewhere too.
+The block itself is not expanded; only the top allocation from it.
Arguments:
ptr pointer to store block
oldsize current size of the block, as requested by user
newsize new size required
- filename source file from which called
+ func function from which called
linenumber line number in source file
Returns: TRUE if the block is at the top of the stack and has been
*/
BOOL
-store_extend_3(void *ptr, int oldsize, int newsize, const char *filename,
- int linenumber)
+store_extend_3(void *ptr, BOOL tainted, int oldsize, int newsize,
+ const char *func, int linenumber)
{
+int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
int inc = newsize - oldsize;
int rounded_oldsize = oldsize;
+/* Check that the block being extended was already of the required taint status;
+refuse to extend if not. */
+
+if (is_tainted(ptr) != tainted)
+ return FALSE;
+
if (rounded_oldsize % alignment != 0)
rounded_oldsize += alignment - (rounded_oldsize % alignment);
-if (CS ptr + rounded_oldsize != CS (next_yield[store_pool]) ||
- inc > yield_length[store_pool] + rounded_oldsize - oldsize)
+if (CS ptr + rounded_oldsize != CS (next_yield[pool]) ||
+ inc > yield_length[pool] + rounded_oldsize - oldsize)
return FALSE;
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
#ifdef COMPILE_UTILITY
-filename = filename;
+func = func;
linenumber = linenumber;
#else
DEBUG(D_memory)
- {
- if (f.running_in_test_harness)
- debug_printf("---%d Ext %5d\n", store_pool, newsize);
- else
- debug_printf("---%d Ext %6p %5d %-14s %4d\n", store_pool, ptr, newsize,
- filename, linenumber);
- }
+ debug_printf("---%d Ext %6p %5d %-14s %4d\n", pool, ptr, newsize,
+ func, linenumber);
#endif /* COMPILE_UTILITY */
if (newsize % alignment != 0) newsize += alignment - (newsize % alignment);
-next_yield[store_pool] = CS ptr + newsize;
-yield_length[store_pool] -= newsize - rounded_oldsize;
+next_yield[pool] = CS ptr + newsize;
+yield_length[pool] -= newsize - rounded_oldsize;
(void) VALGRIND_MAKE_MEM_UNDEFINED(ptr + oldsize, inc);
return TRUE;
}
*************************************************/
/* This function resets the next pointer, freeing any subsequent whole blocks
-that are now unused. Normally it is given a pointer that was the yield of a
-call to store_get, and is therefore aligned, but it may be given an offset
-after such a pointer in order to release the end of a block and anything that
-follows.
+that are now unused. Call with a cookie obtained from store_mark() only; do
+not call with a pointer returned by store_get(). Both the untainted and tainted
+pools corresposding to store_pool are reset.
Arguments:
- ptr place to back up to
- filename source file from which called
+ r place to back up to
+ func function from which called
linenumber line number in source file
Returns: nothing
*/
-void
-store_reset_3(void *ptr, const char *filename, int linenumber)
+static void
+internal_store_reset(void * ptr, int pool, const char *func, int linenumber)
{
storeblock * bb;
-storeblock * b = current_block[store_pool];
+storeblock * b = current_block[pool];
char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK;
-int newlength;
+int newlength, count;
+#ifndef COMPILE_UTILITY
+int oldmalloc = pool_malloc;
+#endif
/* Last store operation was not a get */
-store_last_get[store_pool] = NULL;
+store_last_get[pool] = NULL;
/* See if the place is in the current block - as it often will be. Otherwise,
search for the block in which it lies. */
if (CS ptr < bc || CS ptr > bc + b->length)
{
- for (b = chainbase[store_pool]; b; b = b->next)
+ for (b = chainbase[pool]; b; b = b->next)
{
bc = CS b + ALIGNED_SIZEOF_STOREBLOCK;
if (CS ptr >= bc && CS ptr <= bc + b->length) break;
}
if (!b)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal error: store_reset(%p) "
- "failed: pool=%d %-14s %4d", ptr, store_pool, filename, linenumber);
+ "failed: pool=%d %-14s %4d", ptr, pool, func, linenumber);
}
/* Back up, rounding to the alignment if necessary. When testing, flatten
#ifndef COMPILE_UTILITY
if (debug_store)
{
- assert_no_variables(ptr, newlength, filename, linenumber);
+ assert_no_variables(ptr, newlength, func, linenumber);
if (f.running_in_test_harness)
{
(void) VALGRIND_MAKE_MEM_DEFINED(ptr, newlength);
}
#endif
(void) VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength);
-yield_length[store_pool] = newlength - (newlength % alignment);
-next_yield[store_pool] = CS ptr + (newlength % alignment);
-current_block[store_pool] = b;
-
-/* Free any subsequent block. Do NOT free the first successor, if our
-current block has less than 256 bytes left. This should prevent us from
-flapping memory. However, keep this block only when it has the default size. */
-
-if (yield_length[store_pool] < STOREPOOL_MIN_SIZE &&
- b->next &&
- b->next->length == STORE_BLOCK_SIZE)
+next_yield[pool] = CS ptr + (newlength % alignment);
+count = yield_length[pool];
+count = (yield_length[pool] = newlength - (newlength % alignment)) - count;
+current_block[pool] = b;
+
+/* Free any subsequent block. Do NOT free the first
+successor, if our current block has less than 256 bytes left. This should
+prevent us from flapping memory. However, keep this block only when it has
+the default size. */
+
+if ( yield_length[pool] < STOREPOOL_MIN_SIZE
+ && b->next
+ && b->next->length == STORE_BLOCK_SIZE)
{
b = b->next;
#ifndef COMPILE_UTILITY
if (debug_store)
assert_no_variables(b, b->length + ALIGNED_SIZEOF_STOREBLOCK,
- filename, linenumber);
+ func, linenumber);
#endif
(void) VALGRIND_MAKE_MEM_NOACCESS(CS b + ALIGNED_SIZEOF_STOREBLOCK,
b->length - ALIGNED_SIZEOF_STOREBLOCK);
while ((b = bb))
{
+ int siz = b->length + ALIGNED_SIZEOF_STOREBLOCK;
#ifndef COMPILE_UTILITY
if (debug_store)
assert_no_variables(b, b->length + ALIGNED_SIZEOF_STOREBLOCK,
- filename, linenumber);
+ func, linenumber);
#endif
bb = bb->next;
- pool_malloc -= b->length + ALIGNED_SIZEOF_STOREBLOCK;
- store_free_3(b, filename, linenumber);
+ nbytes[pool] -= siz;
+ pool_malloc -= siz;
+ nblocks[pool]--;
+ if (pool < POOL_TAINT_BASE)
+ internal_store_free(b, func, linenumber);
+ else
+ {
+#ifndef COMPILE_UTILITY
+ DEBUG(D_memory)
+ debug_printf("---Unmap %6p %-20s %4d\n", b, func, linenumber);
+#endif
+ munmap(b, b->length + ALIGNED_SIZEOF_STOREBLOCK);
+ }
}
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
#ifdef COMPILE_UTILITY
-filename = filename;
+func = func;
linenumber = linenumber;
#else
DEBUG(D_memory)
+ debug_printf("---%d Rst %6p %5d %-14s %4d %d\n", pool, ptr,
+ count + oldmalloc - pool_malloc,
+ func, linenumber, pool_malloc);
+#endif /* COMPILE_UTILITY */
+}
+
+
+rmark
+store_reset_3(rmark r, int pool, const char *func, int linenumber)
+{
+void ** ptr = r;
+
+if (pool >= POOL_TAINT_BASE)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "store_reset called for pool %d: %s %d\n", pool, func, linenumber);
+if (!r)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "store_reset called with bad mark: %s %d\n", func, linenumber);
+
+internal_store_reset(*ptr, pool + POOL_TAINT_BASE, func, linenumber);
+internal_store_reset(ptr, pool, func, linenumber);
+return NULL;
+}
+
+
+
+/* Free tail-end unused allocation. This lets us allocate a big chunk
+early, for cases when we only discover later how much was really needed.
+
+Can be called with a value from store_get(), or an offset after such. Only
+the tainted or untainted pool that serviced the store_get() will be affected.
+
+This is mostly a cut-down version of internal_store_reset().
+XXX needs rationalising
+*/
+
+void
+store_release_above_3(void *ptr, const char *func, int linenumber)
+{
+/* Search all pools' "current" blocks. If it isn't one of those,
+ignore it (it usually will be). */
+
+for (int pool = 0; pool < nelem(current_block); pool++)
{
- if (f.running_in_test_harness)
- debug_printf("---%d Rst ** %d\n", store_pool, pool_malloc);
- else
- debug_printf("---%d Rst %6p ** %-14s %4d %d\n", store_pool, ptr,
- filename, linenumber, pool_malloc);
+ storeblock * b = current_block[pool];
+ char * bc;
+ int count, newlength;
+
+ if (!b)
+ continue;
+
+ bc = CS b + ALIGNED_SIZEOF_STOREBLOCK;
+ if (CS ptr < bc || CS ptr > bc + b->length)
+ continue;
+
+ /* Last store operation was not a get */
+
+ store_last_get[pool] = NULL;
+
+ /* Back up, rounding to the alignment if necessary. When testing, flatten
+ the released memory. */
+
+ newlength = bc + b->length - CS ptr;
+#ifndef COMPILE_UTILITY
+ if (debug_store)
+ {
+ assert_no_variables(ptr, newlength, func, linenumber);
+ if (f.running_in_test_harness)
+ {
+ (void) VALGRIND_MAKE_MEM_DEFINED(ptr, newlength);
+ memset(ptr, 0xF0, newlength);
+ }
+ }
+#endif
+ (void) VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength);
+ next_yield[pool] = CS ptr + (newlength % alignment);
+ count = yield_length[pool];
+ count = (yield_length[pool] = newlength - (newlength % alignment)) - count;
+
+ /* Cut out the debugging stuff for utilities, but stop picky compilers from
+ giving warnings. */
+
+#ifdef COMPILE_UTILITY
+ func = func;
+ linenumber = linenumber;
+#else
+ DEBUG(D_memory)
+ debug_printf("---%d Rel %6p %5d %-14s %4d %d\n", pool, ptr, count,
+ func, linenumber, pool_malloc);
+#endif
+ return;
}
-#endif /* COMPILE_UTILITY */
+#ifndef COMPILE_UTILITY
+DEBUG(D_memory)
+ debug_printf("non-last memory release try: %s %d\n", func, linenumber);
+#endif
}
+rmark
+store_mark_3(const char *func, int linenumber)
+{
+void ** p;
+
+if (store_pool >= POOL_TAINT_BASE)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "store_mark called for pool %d: %s %d\n", store_pool, func, linenumber);
+
+/* Stash a mark for the tainted-twin release, in the untainted twin. Return
+a cookie (actually the address in the untainted pool) to the caller.
+Reset uses the cookie to recover the t-mark, winds back the tainted pool with it
+and winds back the untainted pool with the cookie. */
+
+p = store_get_3(sizeof(void *), FALSE, func, linenumber);
+*p = store_get_3(0, TRUE, func, linenumber);
+return p;
+}
+
+
/************************************************
Arguments:
block block of store to consider
- filename source file from which called
+ func function from which called
linenumber line number in source file
Returns: nothing
*/
static void
-store_release_3(void * block, const char * filename, int linenumber)
+store_release_3(void * block, int pool, const char * func, int linenumber)
{
/* It will never be the first block, so no need to check that. */
-for (storeblock * b = chainbase[store_pool]; b; b = b->next)
+for (storeblock * b = chainbase[pool]; b; b = b->next)
{
storeblock * bb = b->next;
if (bb && CS block == CS bb + ALIGNED_SIZEOF_STOREBLOCK)
{
+ int siz = bb->length + ALIGNED_SIZEOF_STOREBLOCK;
b->next = bb->next;
- pool_malloc -= bb->length + ALIGNED_SIZEOF_STOREBLOCK;
+ nbytes[pool] -= siz;
+ pool_malloc -= siz;
+ nblocks[pool]--;
/* Cut out the debugging stuff for utilities, but stop picky compilers
from giving warnings. */
#ifdef COMPILE_UTILITY
- filename = filename;
+ func = func;
linenumber = linenumber;
#else
DEBUG(D_memory)
- if (f.running_in_test_harness)
- debug_printf("-Release %d\n", pool_malloc);
- else
- debug_printf("-Release %6p %-20s %4d %d\n", (void *)bb, filename,
- linenumber, pool_malloc);
+ debug_printf("-Release %6p %-20s %4d %d\n", (void *)bb, func,
+ linenumber, pool_malloc);
if (f.running_in_test_harness)
memset(bb, 0xF0, bb->length+ALIGNED_SIZEOF_STOREBLOCK);
*/
void *
-store_newblock_3(void * block, int newsize, int len,
- const char * filename, int linenumber)
+store_newblock_3(void * block, BOOL tainted, int newsize, int len,
+ const char * func, int linenumber)
{
-BOOL release_ok = store_last_get[store_pool] == block;
-uschar * newtext = store_get(newsize);
+int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
+BOOL release_ok = !tainted && store_last_get[pool] == block;
+uschar * newtext;
+
+if (is_tainted(block) != tainted)
+ die_tainted(US"store_newblock", CUS func, linenumber);
+newtext = store_get(newsize, tainted);
memcpy(newtext, block, len);
-if (release_ok) store_release_3(block, filename, linenumber);
+if (release_ok) store_release_3(block, pool, func, linenumber);
return (void *)newtext;
}
+/******************************************************************************/
+static void *
+store_alloc_tail(void * yield, int size, const char * func, int line,
+ const uschar * type)
+{
+if ((nonpool_malloc += size) > max_nonpool_malloc)
+ max_nonpool_malloc = nonpool_malloc;
+
+/* Cut out the debugging stuff for utilities, but stop picky compilers from
+giving warnings. */
+
+#ifdef COMPILE_UTILITY
+func = func; line = line; type = type;
+#else
+
+/* If running in test harness, spend time making sure all the new store
+is not filled with zeros so as to catch problems. */
+
+if (f.running_in_test_harness)
+ memset(yield, 0xF0, (size_t)size);
+DEBUG(D_memory) debug_printf("--%6s %6p %5d bytes\t%-14s %4d\tpool %5d nonpool %5d\n",
+ type, yield, size, func, line, pool_malloc, nonpool_malloc);
+#endif /* COMPILE_UTILITY */
+
+return yield;
+}
+
+/*************************************************
+* Mmap store *
+*************************************************/
+
+static void *
+store_mmap(int size, const char * func, int line)
+{
+void * yield, * top;
+
+if (size < 16) size = 16;
+
+if (!(yield = mmap(NULL, (size_t)size,
+ PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)))
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to mmap %d bytes of memory: "
+ "called from line %d of %s", size, line, func);
+
+if (yield < tainted_base) tainted_base = yield;
+if ((top = yield + size) > tainted_top) tainted_top = top;
+
+return store_alloc_tail(yield, size, func, line, US"Mmap");
+}
+
/*************************************************
* Malloc store *
*************************************************/
Arguments:
size amount of store wanted
- filename source file from which called
+ func function from which called
linenumber line number in source file
Returns: pointer to gotten store (panic on failure)
*/
-void *
-store_malloc_3(int size, const char *filename, int linenumber)
+static void *
+internal_store_malloc(int size, const char *func, int linenumber)
{
-void *yield;
+void * yield;
if (size < 16) size = 16;
if (!(yield = malloc((size_t)size)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of memory: "
- "called from line %d of %s", size, linenumber, filename);
-
-nonpool_malloc += size;
-
-/* Cut out the debugging stuff for utilities, but stop picky compilers from
-giving warnings. */
-
-#ifdef COMPILE_UTILITY
-filename = filename;
-linenumber = linenumber;
-#else
-
-/* If running in test harness, spend time making sure all the new store
-is not filled with zeros so as to catch problems. */
+ "called from line %d in %s", size, linenumber, func);
-if (f.running_in_test_harness)
- {
- memset(yield, 0xF0, (size_t)size);
- DEBUG(D_memory) debug_printf("--Malloc %5d %d %d\n", size, pool_malloc,
- nonpool_malloc);
- }
-else
- {
- DEBUG(D_memory) debug_printf("--Malloc %6p %5d %-14s %4d %d %d\n", yield,
- size, filename, linenumber, pool_malloc, nonpool_malloc);
- }
-#endif /* COMPILE_UTILITY */
+return store_alloc_tail(yield, size, func, linenumber, US"Malloc");
+}
-return yield;
+void *
+store_malloc_3(int size, const char *func, int linenumber)
+{
+if (n_nonpool_blocks++ > max_nonpool_blocks)
+ max_nonpool_blocks = n_nonpool_blocks;
+return internal_store_malloc(size, func, linenumber);
}
Arguments:
block block of store to free
- filename source file from which called
+ func function from which called
linenumber line number in source file
Returns: nothing
*/
-void
-store_free_3(void *block, const char *filename, int linenumber)
+static void
+internal_store_free(void *block, const char *func, int linenumber)
{
#ifdef COMPILE_UTILITY
-filename = filename;
+func = func;
linenumber = linenumber;
#else
DEBUG(D_memory)
- {
- if (f.running_in_test_harness)
- debug_printf("----Free\n");
- else
- debug_printf("----Free %6p %-20s %4d\n", block, filename, linenumber);
- }
+ debug_printf("----Free %6p %-20s %4d\n", block, func, linenumber);
#endif /* COMPILE_UTILITY */
free(block);
}
+void
+store_free_3(void *block, const char *func, int linenumber)
+{
+n_nonpool_blocks--;
+internal_store_free(block, func, linenumber);
+}
+
+/******************************************************************************/
+/* Stats output on process exit */
+void
+store_exit(void)
+{
+#ifndef COMPILE_UTILITY
+DEBUG(D_memory)
+ {
+ debug_printf("----Exit nonpool max: %3d kB in %d blocks\n",
+ (max_nonpool_malloc+1023)/1024, max_nonpool_blocks);
+ debug_printf("----Exit npools max: %3d kB\n", max_pool_malloc/1024);
+ for (int i = 0; i < NPOOLS; i++)
+ debug_printf("----Exit pool %d max: %3d kB in %d blocks\t%s %s\n",
+ i, maxbytes[i]/1024, maxblocks[i], poolclass[i], pooluse[i]);
+ }
+#endif
+}
+
/* End of store.c */
/* Define symbols for identifying the store pools. */
-enum { POOL_MAIN, POOL_PERM, POOL_SEARCH };
+enum { POOL_MAIN, POOL_PERM, POOL_SEARCH,
+ POOL_TAINT_BASE,
+ POOL_TAINT_MAIN = POOL_TAINT_BASE, POOL_TAINT_PERM, POOL_TAINT_SEARCH };
/* This variable (the one for the current pool) is set by store_get() to its
yield, and by store_reset() to NULL. This allows string_cat() to optimize its
store handling. */
-extern void *store_last_get[3];
+extern void *store_last_get[6];
/* This variable contains the current store pool number. */
/* Macros for calling the memory allocation routines with
tracing information for debugging. */
-#define store_extend(addr,old,new) \
- store_extend_3(addr, old, new, __FILE__, __LINE__)
-
-#define store_free(addr) store_free_3(addr, __FILE__, __LINE__)
-#define store_get(size) store_get_3(size, __FILE__, __LINE__)
-#define store_get_perm(size) store_get_perm_3(size, __FILE__, __LINE__)
-#define store_malloc(size) store_malloc_3(size, __FILE__, __LINE__)
-#define store_newblock(addr,newsize,datalen) \
- store_newblock_3(addr, newsize, datalen, __FILE__, __LINE__)
-#define store_reset(addr) store_reset_3(addr, __FILE__, __LINE__)
+#define store_extend(addr, tainted, old, new) \
+ store_extend_3(addr, tainted, old, new, __FUNCTION__, __LINE__)
+
+#define store_free(addr) \
+ store_free_3(addr, __FUNCTION__, __LINE__)
+#define store_get(size, tainted) \
+ store_get_3(size, tainted, __FUNCTION__, __LINE__)
+#define store_get_perm(size, tainted) \
+ store_get_perm_3(size, tainted, __FUNCTION__, __LINE__)
+#define store_malloc(size) \
+ store_malloc_3(size, __FUNCTION__, __LINE__)
+#define store_mark(void) \
+ store_mark_3(__FUNCTION__, __LINE__)
+#define store_newblock(addr, tainted, newsize, datalen) \
+ store_newblock_3(addr, tainted, newsize, datalen, __FUNCTION__, __LINE__)
+#define store_release_above(addr) \
+ store_release_above_3(addr, __FUNCTION__, __LINE__)
+#define store_reset(mark) \
+ store_reset_3(mark, store_pool, __FUNCTION__, __LINE__)
/* The real functions */
+typedef void ** rmark;
-/* The value of the 2nd arg is __FILE__ in every call, so give its correct type */
-extern BOOL store_extend_3(void *, int, int, const char *, int);
+extern BOOL is_tainted(const void *);
+extern BOOL store_extend_3(void *, BOOL, int, int, const char *, int);
extern void store_free_3(void *, const char *, int);
-extern void *store_get_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
-extern void *store_get_perm_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
-extern void *store_malloc_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
-extern void *store_newblock_3(void *, int, int, const char *, int);
-extern void store_reset_3(void *, const char *, int);
+extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
+extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
+extern void *store_malloc_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT;
+extern rmark store_mark_3(const char *, int);
+extern void *store_newblock_3(void *, BOOL, int, int, const char *, int);
+extern void store_release_above_3(void *, const char *, int);
+extern rmark store_reset_3(rmark, int, const char *, int);
#endif /* STORE_H */
#include "exim.h"
#include <assert.h>
+static void gstring_rebuffer(gstring * g);
#ifndef COMPILE_UTILITY
/*************************************************
uschar *
string_format_size(int size, uschar *buffer)
{
-if (size == 0) Ustrcpy(buffer, " ");
+if (size == 0) Ustrcpy(buffer, US" ");
else if (size < 1024) sprintf(CS buffer, "%5d", size);
else if (size < 10*1024)
sprintf(CS buffer, "%4.1fK", (double)size / 1024.0);
/* Get a new block of store guaranteed big enough to hold the
expanded string. */
-ss = store_get(length + nonprintcount * 3 + 1);
+ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s));
/* Copy everything, escaping non printers. */
if (!p) return s;
len = Ustrlen(s) + 1;
-ss = store_get(len);
+ss = store_get(len, is_tainted(s));
q = ss;
off = p - s;
* Copy and save string *
*************************************************/
-/* This function assumes that memcpy() is faster than strcpy().
-
+/*
Argument: string to copy
-Returns: copy of string in new store
+Returns: copy of string in new store with the same taint status
*/
uschar *
string_copy_function(const uschar *s)
{
+return string_copy_taint(s, is_tainted(s));
+}
+
+/* This function assumes that memcpy() is faster than strcpy().
+As above, but explicitly specifying the result taint status
+*/
+
+uschar *
+string_copy_taint(const uschar * s, BOOL tainted)
+{
int len = Ustrlen(s) + 1;
-uschar *ss = store_get(len);
+uschar *ss = store_get(len, tainted);
memcpy(ss, s, len);
return ss;
}
+
/*************************************************
* Copy and save string, given length *
*************************************************/
uschar *
string_copyn_function(const uschar *s, int n)
{
-uschar *ss = store_get(n + 1);
+uschar *ss = store_get(n + 1, is_tainted(s));
Ustrncpy(ss, s, n);
ss[n] = 0;
return ss;
string_copy_dnsdomain(uschar *s)
{
uschar *yield;
-uschar *ss = yield = store_get(Ustrlen(s) + 1);
+uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s));
while (*s != 0)
{
/* Get enough store to copy into */
-t = yield = store_get(s - *sptr + 1);
+t = yield = store_get(s - *sptr + 1, is_tainted(*sptr));
s = *sptr;
/* Do the copy */
if (*s != '\"')
- {
while (*s != 0 && !isspace(*s)) *t++ = *s++;
- }
else
{
s++;
while (*s != 0 && *s != '\"')
{
- if (*s == '\\') *t++ = string_interpret_escape(&s);
- else *t++ = *s;
+ *t++ = *s == '\\' ? string_interpret_escape(&s) : *s;
s++;
}
- if (*s != 0) s++;
+ if (*s) s++;
}
/* Update the pointer and return the terminated copy */
*/
uschar *
-string_sprintf(const char *format, ...)
+string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...)
{
-#ifdef COMPILE_UTILITY
-uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
-gstring g = { .size = STRING_SPRINTF_BUFFER_SIZE, .ptr = 0, .s = buffer };
-gstring * gp = &g;
-#else
-gstring * gp = string_get(STRING_SPRINTF_BUFFER_SIZE);
-#endif
-gstring * gp2;
+gstring * g;
va_list ap;
-va_start(ap, format);
-gp2 = string_vformat(gp, FALSE, format, ap);
-gp->s[gp->ptr] = '\0';
+va_start(ap, line);
+g = string_vformat_trc(NULL, func, line, STRING_SPRINTF_BUFFER_SIZE,
+ SVFMT_REBUFFER|SVFMT_EXTEND, format, ap);
va_end(ap);
-if (!gp2)
+if (!g)
log_write(0, LOG_MAIN|LOG_PANIC_DIE,
"string_sprintf expansion was longer than %d; format string was (%s)\n"
- "expansion started '%.32s'",
- gp->size, format, gp->s);
+ " called from %s %d\n",
+ STRING_SPRINTF_BUFFER_SIZE, format, func, line);
-#ifdef COMPILE_UTILITY
-return string_copy(gp->s);
-#else
-gstring_release_unused(gp);
-return gp->s;
-#endif
+gstring_release_unused(g);
+return string_from_gstring(g);
}
#ifdef COMPILE_UTILITY
/* Dummy version for this function; it should never be called */
static void
-gstring_grow(gstring * g, int p, int count)
+gstring_grow(gstring * g, int count)
{
assert(FALSE);
}
/************************************************/
-/* Add more space to a growable-string.
+/* Add more space to a growable-string. The caller should check
+first if growth is required. The gstring struct is modified on
+return; specifically, the string-base-pointer may have been changed.
Arguments:
g the growable-string
- p current end of data
- count amount to grow by
+ count amount needed for g->ptr to increase by
*/
static void
-gstring_grow(gstring * g, int p, int count)
+gstring_grow(gstring * g, int count)
{
+int p = g->ptr;
int oldsize = g->size;
+BOOL tainted = is_tainted(g->s);
/* Mostly, string_cat() is used to build small strings of a few hundred
characters at most. There are times, however, when the strings are very much
existing length of the string. */
unsigned inc = oldsize < 4096 ? 127 : 1023;
-g->size = ((p + count + inc) & ~inc) + 1;
+
+if (count <= 0) return;
+g->size = (p + count + inc + 1) & ~inc; /* one for a NUL */
/* Try to extend an existing allocation. If the result of calling
store_extend() is false, either there isn't room in the current memory block,
was the last item on the dynamic memory stack. This is the case if it matches
store_last_get. */
-if (!store_extend(g->s, oldsize, g->size))
- g->s = store_newblock(g->s, g->size, p);
+if (!store_extend(g->s, tainted, oldsize, g->size))
+ g->s = store_newblock(g->s, tainted, g->size, p);
}
string_catn(gstring * g, const uschar *s, int count)
{
int p;
+BOOL srctaint = is_tainted(s);
if (!g)
{
unsigned inc = count < 4096 ? 127 : 1023;
unsigned size = ((count + inc) & ~inc) + 1;
- g = string_get(size);
+ g = string_get_tainted(size, srctaint);
}
+else if (srctaint && !is_tainted(g->s))
+ gstring_rebuffer(g);
p = g->ptr;
if (p + count >= g->size)
- gstring_grow(g, p, count);
+ gstring_grow(g, count);
/* Because we always specify the exact number of characters to copy, we can
use memcpy(), which is likely to be more efficient than strncopy() because the
*/
BOOL
-string_format(uschar * buffer, int buflen, const char * format, ...)
+string_format_trc(uschar * buffer, int buflen,
+ const uschar * func, unsigned line, const char * format, ...)
{
gstring g = { .size = buflen, .ptr = 0, .s = buffer }, *gp;
va_list ap;
va_start(ap, format);
-gp = string_vformat(&g, FALSE, format, ap);
+gp = string_vformat_trc(&g, func, line, STRING_SPRINTF_BUFFER_SIZE,
+ 0, format, ap);
va_end(ap);
g.s[g.ptr] = '\0';
return !!gp;
+/* Copy the content of a string to tainted memory */
+static void
+gstring_rebuffer(gstring * g)
+{
+uschar * s = store_get(g->size, TRUE);
+memcpy(s, g->s, g->ptr);
+g->s = s;
+}
+
-/* Bulid or append to a growing-string, sprintf-style.
+/* Build or append to a growing-string, sprintf-style.
-If the "extend" argument is true, the string passed in can be NULL,
-empty, or non-empty.
+If the "extend" flag is true, the string passed in can be NULL,
+empty, or non-empty. Growing is subject to an overall limit given
+by the size_limit argument.
-If the "extend" argument is false, the string passed in may not be NULL,
+If the "extend" flag is false, the string passed in may not be NULL,
will not be grown, and is usable in the original place after return.
The return value can be NULL to signify overflow.
*/
gstring *
-string_vformat(gstring * g, BOOL extend, const char *format, va_list ap)
+string_vformat_trc(gstring * g, const uschar * func, unsigned line,
+ unsigned size_limit, unsigned flags, const char *format, va_list ap)
{
enum ltypes { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_SIZE=6 };
-int width, precision, off, lim;
+int width, precision, off, lim, need;
const char * fp = format; /* Deliberately not unsigned */
+BOOL dest_tainted = FALSE;
string_datestamp_offset = -1; /* Datestamp not inserted */
string_datestamp_length = 0; /* Datestamp not inserted */
string_datestamp_type = 0; /* Datestamp not inserted */
#ifdef COMPILE_UTILITY
-assert(!extend);
+assert(!(flags & SVFMT_EXTEND));
assert(g);
#else
/* Ensure we have a string, to save on checking later */
if (!g) g = string_get(16);
+else if (!(flags & SVFMT_TAINT_NOCHK)) dest_tainted = is_tainted(g->s);
+
+if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(format))
+ {
+ if (!(flags & SVFMT_REBUFFER))
+ die_tainted(US"string_vformat", func, line);
+ gstring_rebuffer(g);
+ dest_tainted = TRUE;
+ }
#endif /*!COMPILE_UTILITY*/
lim = g->size - 1; /* leave one for a nul */
if (*fp != '%')
{
/* Avoid string_copyn() due to COMPILE_UTILITY */
- if (g->ptr >= lim - 1)
+ if ((need = g->ptr + 1) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, 1);
+ if (!(flags & SVFMT_EXTEND) || need > size_limit) return NULL;
+ gstring_grow(g, 1);
lim = g->size - 1;
}
g->s[g->ptr++] = (uschar) *fp++;
case 'x':
case 'X':
width = length > L_LONG ? 24 : 12;
- if (g->ptr >= lim - width)
+ if ((need = g->ptr + width) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, width);
+ if (!(flags & SVFMT_EXTEND) || need >= size_limit) return NULL;
+ gstring_grow(g, width);
lim = g->size - 1;
gp = CS g->s + g->ptr;
}
case 'p':
{
void * ptr;
- if (g->ptr >= lim - 24)
+ if ((need = g->ptr + 24) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, 24);
+ if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL;
+ gstring_grow(g, 24);
lim = g->size - 1;
gp = CS g->s + g->ptr;
}
case 'g':
case 'G':
if (precision < 0) precision = 6;
- if (g->ptr >= lim - precision - 8)
+ if ((need = g->ptr + precision + 8) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, precision+8);
+ if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL;
+ gstring_grow(g, precision+8);
lim = g->size - 1;
gp = CS g->s + g->ptr;
}
/* String types */
case '%':
- if (g->ptr >= lim - 1)
+ if ((need = g->ptr + 1) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, 1);
+ if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL;
+ gstring_grow(g, 1);
lim = g->size - 1;
}
g->s[g->ptr++] = (uschar) '%';
break;
case 'c':
- if (g->ptr >= lim - 1)
+ if ((need = g->ptr + 1) > lim)
{
- if (!extend) return NULL;
- gstring_grow(g, g->ptr, 1);
+ if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL;
+ gstring_grow(g, 1);
lim = g->size - 1;
}
g->s[g->ptr++] = (uschar) va_arg(ap, int);
if (!s) s = null;
slen = Ustrlen(s);
+ if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(s))
+ if (flags & SVFMT_REBUFFER)
+ {
+ gstring_rebuffer(g);
+ gp = CS g->s + g->ptr;
+ dest_tainted = TRUE;
+ }
+ else
+ die_tainted(US"string_vformat", func, line);
+
INSERT_STRING: /* Come to from %D or %M above */
{
else
width = precision = slen;
- if (!extend)
+ if ((need = g->ptr + width) >= size_limit || !(flags & SVFMT_EXTEND))
{
if (g->ptr == lim) return NULL;
- if (g->ptr >= lim - width)
+ if (need > lim)
{
truncated = TRUE;
width = precision = lim - g->ptr - 1;
if (precision < 0) precision = 0;
}
}
- else if (g->ptr >= lim - width)
+ else if (need > lim)
{
- gstring_grow(g, g->ptr, width - (lim - g->ptr));
+ gstring_grow(g, width);
lim = g->size - 1;
gp = CS g->s + g->ptr;
}
}
}
+if (g->ptr > g->size)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "string_format internal error: caller %s %d", func, line);
return g;
}
#ifndef COMPILE_UTILITY
-
-gstring *
-string_fmt_append(gstring * g, const char *format, ...)
-{
-va_list ap;
-va_start(ap, format);
-g = string_vformat(g, TRUE, format, ap);
-va_end(ap);
-return g;
-}
-
-
-
/*************************************************
* Generate an "open failed" message *
*************************************************/
*/
uschar *
-string_open_failed(int eno, const char *format, ...)
+string_open_failed_trc(int eno, const uschar * func, unsigned line,
+ const char *format, ...)
{
va_list ap;
gstring * g = string_get(1024);
doesn't seem much we can do about that. */
va_start(ap, format);
-(void) string_vformat(g, FALSE, format, ap);
+(void) string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE,
+ 0, format, ap);
string_from_gstring(g);
gstring_release_unused(g);
va_end(ap);
}
m.size = statbuf.st_size;
- if (!(m.data = malloc(m.size)))
+ if (!(m.data = store_malloc(m.size)))
{
fclose(fp);
return tls_error_sys(US"malloc failed", errno, NULL, errstr);
{
saved_errno = errno;
fclose(fp);
- free(m.data);
+ store_free(m.data);
return tls_error_sys(US"fread failed", saved_errno, NULL, errstr);
}
fclose(fp);
rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM);
- free(m.data);
+ store_free(m.data);
if (rc)
return tls_error_gnu(US"gnutls_dh_params_import_pkcs3", rc, host, errstr);
DEBUG(D_tls) debug_printf("read D-H parameters from file \"%s\"\n", filename);
return tls_error_gnu(US"gnutls_dh_params_export_pkcs3(NULL) sizing",
rc, host, errstr);
m.size = sz;
- if (!(m.data = malloc(m.size)))
+ if (!(m.data = store_malloc(m.size)))
return tls_error_sys(US"memory allocation failed", errno, NULL, errstr);
/* this will return a size 1 less than the allocation size above */
if ((rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM,
m.data, &sz)))
{
- free(m.data);
+ store_free(m.data);
return tls_error_gnu(US"gnutls_dh_params_export_pkcs3() real", rc, host, errstr);
}
m.size = sz; /* shrink by 1, probably */
if ((sz = write_to_fd_buf(fd, m.data, (size_t) m.size)) != m.size)
{
- free(m.data);
+ store_free(m.data);
return tls_error_sys(US"TLS cache write D-H params failed",
errno, NULL, errstr);
}
- free(m.data);
+ store_free(m.data);
if ((sz = write_to_fd_buf(fd, US"\n", 1)) != 1)
return tls_error_sys(US"TLS cache write D-H params final newline failed",
errno, NULL, errstr);
several in parallel. */
int old_pool = store_pool;
store_pool = POOL_PERM;
- state = store_get(sizeof(exim_gnutls_state_st));
+ state = store_get(sizeof(exim_gnutls_state_st), FALSE);
store_pool = old_pool;
memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
exim_gnutls_peer_err(US"getting size for cert DN failed");
return FAIL; /* should not happen */
}
-dn_buf = store_get_perm(sz);
+dn_buf = store_get_perm(sz, TRUE); /* tainted */
rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz);
exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]");
for(nrec = 0; state->dane_data_len[nrec]; ) nrec++;
nrec++;
- dd = store_get(nrec * sizeof(uschar *));
- ddl = store_get(nrec * sizeof(int));
+ dd = store_get(nrec * sizeof(uschar *), FALSE);
+ ddl = store_get(nrec * sizeof(int), FALSE);
nrec--;
if ((rc = dane_state_init(&s, 0)))
rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
) if (rr->type == T_TLSA) i++;
-dane_data = store_get(i * sizeof(uschar *));
-dane_data_len = store_get(i * sizeof(int));
+dane_data = store_get(i * sizeof(uschar *), FALSE);
+dane_data_len = store_get(i * sizeof(int), FALSE);
i = 0;
for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
) if (rr->type == T_TLSA && rr->size > 3)
{
const uschar * p = rr->data;
+/*XXX need somehow to mark rr and its data as tainted. Doues this mean copying it? */
uint8_t usage = p[0], sel = p[1], type = p[2];
DEBUG(D_tls)
{
open_db dbblock, * dbm_file;
int dlen = sizeof(dbdata_tls_session) + tkt.size;
- dbdata_tls_session * dt = store_get(dlen);
+ dbdata_tls_session * dt = store_get(dlen, TRUE);
DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size);
memcpy(dt->session, tkt.data, tkt.size);
{
int len = i2d_SSL_SESSION(ss, NULL);
int dlen = sizeof(dbdata_tls_session) + len;
- dbdata_tls_session * dt = store_get(dlen);
+ dbdata_tls_session * dt = store_get(dlen, TRUE);
uschar * s = dt->session;
open_db dbblock, * dbm_file;
rc = store_pool;
store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
exim_client_ctx->corked = NULL;
store_pool = rc;
tls_export_cert(uschar * buf, size_t buflen, void * cert)
{
size_t sz = buflen;
-void * reset_point = store_get(0);
+rmark reset_point = store_mark();
int fail;
const uschar * cp;
int
tls_import_cert(const uschar * buf, void ** cert)
{
-void * reset_point = store_get(0);
+rmark reset_point = store_mark();
gnutls_datum_t datum;
gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert;
int fail = 0;
if (mod && Ustrcmp(mod, "int") == 0)
return string_sprintf("%u", (unsigned)t);
-cp = store_get(len);
+cp = store_get(len, FALSE);
if (f.timestamps_utc)
{
uschar * tz = to_tz(US"GMT0");
!= GNUTLS_E_SHORT_MEMORY_BUFFER)
return g_err("gi0", __FUNCTION__, ret);
-cp = store_get(siz);
+cp = store_get(siz, TRUE);
if ((ret = gnutls_x509_crt_get_issuer_dn(cert, CS cp, &siz)) < 0)
return g_err("gi1", __FUNCTION__, ret);
!= GNUTLS_E_SHORT_MEMORY_BUFFER)
return g_err("gs0", __FUNCTION__, ret);
-cp1 = store_get(len*4+1);
+cp1 = store_get(len*4+1, TRUE);
if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, CS cp1, &len) != 0)
return g_err("gs1", __FUNCTION__, ret);
!= GNUTLS_E_SHORT_MEMORY_BUFFER)
return g_err("gs0", __FUNCTION__, ret);
-cp = store_get(siz);
+cp = store_get(siz, TRUE);
if ((ret = gnutls_x509_crt_get_dn(cert, CS cp, &siz)) < 0)
return g_err("gs1", __FUNCTION__, ret);
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
return g_err("ge0", __FUNCTION__, ret);
-cp1 = store_get(siz*4 + 1);
+cp1 = store_get(siz*4 + 1, TRUE);
ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert,
CS oid, idx, CS cp1, &siz, &crit);
return g_err("gs0", __FUNCTION__, ret);
}
- ele = store_get(siz+1);
+ ele = store_get(siz+1, TRUE);
if ((ret = gnutls_x509_crt_get_subject_alt_name(
(gnutls_x509_crt_t)cert, index, ele, &siz, NULL)) < 0)
return g_err("gs1", __FUNCTION__, ret);
return g_err("gc0", __FUNCTION__, ret);
}
- ele = store_get(siz);
+ ele = store_get(siz, TRUE);
if ((ret = gnutls_x509_crt_get_crl_dist_points(
(gnutls_x509_crt_t)cert, index, ele, &siz, NULL, NULL)) < 0)
return g_err("gc1", __FUNCTION__, ret);
if ( (fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert,
GNUTLS_X509_FMT_DER, cp, &len)) != GNUTLS_E_SHORT_MEMORY_BUFFER
- || !(cp = store_get((int)len))
+ || !(cp = store_get((int)len, TRUE), TRUE) /* tainted */
|| (fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert,
GNUTLS_X509_FMT_DER, cp, &len))
)
!= GNUTLS_E_SHORT_MEMORY_BUFFER)
return g_err("gf0", __FUNCTION__, ret);
-cp = store_get(siz*3+1);
+cp = store_get(siz*3+1, TRUE);
if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, cp, &siz)) < 0)
return g_err("gf1", __FUNCTION__, ret);
int
tls_import_cert(const uschar * buf, void ** cert)
{
-void * reset_point = store_get(0);
+rmark reset_point = store_mark();
const uschar * cp = string_unprinting(US buf);
BIO * bp;
X509 * x = *(X509 **)cert;
/* convert to string in our format */
len = 32;
- s = store_get(len);
+ s = store_get(len, FALSE);
strftime(CS s, (size_t)len, "%b %e %T %Y %z", tm_p);
}
}
/* binary data, DER encoded */
/* just dump for now */
len = BIO_get_mem_data(bp, &cp1);
-cp3 = cp2 = store_get(len*3+1);
+cp3 = cp2 = store_get(len*3+1, TRUE);
while(len)
{
expand_string_message = US"tls_cert_fprt: out of mem\n";
return NULL;
}
-cp = store_get(n*2+1);
+cp = store_get(n*2+1, TRUE);
for (int j = 0; j < (int)n; j++) sprintf(CS cp+2*j, "%02X", md[j]);
return(cp);
}
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
va_list ap;
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that the result will never be expanded. */
+
va_start(ap, format);
-if (!string_vformat(&gs, FALSE, format, ap))
+if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
va_end(ap);
tctx.u.fd = fd;
for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE;
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
ppp->next = *pdlist;
*pdlist = ppp;
ppp->ptr = p;
/* Remember what we have output, and output it. */
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
ppp->next = *pplist;
*pplist = ppp;
ppp->ptr = pp;
{
if (tblock && tblock->rewrite_rules)
{
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
header_line *hh;
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
BOOL first = TRUE;
struct aci *plist = NULL;
struct aci *dlist = NULL;
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad;
!= sizeof(int)
)
rc = FALSE; /* compiler quietening */
- _exit(0);
+ exim_underbar_exit(0);
}
save_errno = errno;
if (!(host_record = dbfn_read(dbm_file, host->name)))
{
- host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH);
+ host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, FALSE);
host_record->count = host_record->sequence = 0;
}
else
{
dbdata_wait *newr =
- store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH);
+ store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE);
memcpy(newr, host_record, sizeof(dbdata_wait) + host_length);
host_record = newr;
}
/* create an array to read entire message queue into memory for processing */
- msgq = store_malloc(sizeof(msgq_t) * host_record->count);
+ msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE);
msgq_count = host_record->count;
msgq_actual = msgq_count;
{
msgq[i].bKeep = TRUE;
- Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
+ Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
MESSAGE_ID_LENGTH);
msgq[i].message_id[MESSAGE_ID_LENGTH] = 0;
}
msgq[i].bKeep = FALSE;
else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data))
{
- Ustrcpy(new_message_id, msgq[i].message_id);
+ Ustrcpy_nt(new_message_id, msgq[i].message_id);
msgq[i].bKeep = FALSE;
bFound = TRUE;
break;
}
if (bFound) /* Usual exit from main loop */
- {
- store_free (msgq);
break;
- }
/* If host_length <= 0 we have emptied a record and not found a good message,
and there are no continuation records. Otherwise there is a continuation
dbfn_close(dbm_file);
return FALSE;
}
-
- store_free(msgq);
} /* we need to process a continuation record */
/* Control gets here when an existing message has been encountered; its
for (address_item * ad = addr; ad; ad = ad->next) address_count++;
max_args = address_count + 60;
-*argvptr = argv = store_get((max_args+1)*sizeof(uschar *));
+*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), FALSE);
/* Split the command up into arguments terminated by white space. Lose
trailing space at the start and end. Double-quoted arguments can contain \\ and
s = cmd;
while (isspace(*s)) s++;
-while (*s != 0 && argcount < max_args)
+for (; *s != 0 && argcount < max_args; argcount++)
{
if (*s == '\'')
{
ss = s + 1;
while (*ss != 0 && *ss != '\'') ss++;
- argv[argcount++] = ss = store_get(ss - s++);
+ argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd));
while (*s != 0 && *s != '\'') *ss++ = *s++;
if (*s != 0) s++;
*ss++ = 0;
}
- else argv[argcount++] = string_copy(string_dequote(CUSS &s));
+ else
+ argv[argcount] = string_dequote(CUSS &s);
while (isspace(*s)) s++;
}
DEBUG(D_transport)
{
debug_printf("direct command:\n");
- for (int i = 0; argv[i] != US 0; i++)
- debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i]));
+ for (int i = 0; argv[i]; i++)
+ debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i]));
}
if (expand_arguments)
int address_pipe_argcount = 0;
int address_pipe_max_args;
uschar **address_pipe_argv;
+ BOOL tainted;
/* We can never have more then the argv we will be loading into */
address_pipe_max_args = max_args - argcount + 1;
debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
/* We allocate an additional for (uschar *)0 */
- address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+ address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), FALSE);
/* +1 because addr->local_part[0] == '|' since af_force_command is set */
s = expand_string(addr->local_part + 1);
+ tainted = is_tainted(s);
if (s == NULL || *s == '\0')
{
{
ss = s + 1;
while (*ss != 0 && *ss != '\'') ss++;
- address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+ address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted);
while (*s != 0 && *s != '\'') *ss++ = *s++;
if (*s != 0) s++;
*ss++ = 0;
expanded_arg = expand_cstring(argv[i]);
f.enable_dollar_recipients = FALSE;
- if (expanded_arg == NULL)
+ if (!expanded_arg)
{
uschar *msg = string_sprintf("Expansion of \"%s\" "
"from command \"%s\" in %s failed: %s",
argv[i], cmd, etext, expand_string_message);
- if (addr != NULL)
+ if (addr)
{
addr->transport_return = expand_failed;
addr->message = msg;
{
struct servent *sp;
host_item host;
-uschar buffer[256];
+uschar * s;
DEBUG(D_transport) debug_printf("notify_comsat called\n");
-sprintf(CS buffer, "%.200s@" OFF_T_FMT "\n", user, offset);
+s = string_sprintf("%.200s@" OFF_T_FMT "\n", user, offset);
if ((sp = getservbyname("biff", "udp")) == NULL)
{
/* Connect never fails for a UDP socket, so don't set a timeout. */
(void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0, NULL);
- rc = send(sock, buffer, Ustrlen(buffer) + 1, 0);
+ rc = send(sock, s, Ustrlen(s) + 1, 0);
(void)close(sock);
if (rc >= 0) break;
*/
off_t
-check_dir_size(uschar *dirname, int *countptr, const pcre *regex)
+check_dir_size(const uschar * dirname, int *countptr, const pcre *regex)
{
DIR *dir;
off_t sum = 0;
while ((ent = readdir(dir)) != NULL)
{
- uschar *name = US ent->d_name;
- uschar buffer[1024];
+ uschar * path, * name = US ent->d_name;
if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
/* If there's a regex, try to find the size using it */
- if (regex != NULL)
+ if (regex)
{
int ovector[6];
if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
/* No regex or no match for the regex, or captured non-digits */
- if (!string_format(buffer, sizeof(buffer), "%s/%s", dirname, name))
- {
- DEBUG(D_transport)
- debug_printf("check_dir_size: name too long: dir=%s name=%s\n", dirname,
- name);
- continue;
- }
+ path = string_sprintf("%s/%s", dirname, name);
- if (Ustat(buffer, &statbuf) < 0)
+ if (Ustat(path, &statbuf) < 0)
{
DEBUG(D_transport)
- debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, buffer,
+ debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, path,
strerror(errno));
continue;
}
if ((statbuf.st_mode & S_IFMT) == S_IFREG)
sum += statbuf.st_size;
else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
- sum += check_dir_size(buffer, &count, regex);
+ sum += check_dir_size(path, &count, regex);
}
closedir(dir);
addr->message = string_sprintf("mailbox is full "
"(quota exceeded while writing to file %s)", filename);
#else
- addr->message = string_sprintf("mailbox is full");
+ addr->message = US"mailbox is full";
#endif /* EDQUOT */
addr->user_message = US"mailbox is full";
DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n",
uschar *iptr = expand_string(nametag);
if (iptr != NULL)
{
- uschar *etag = store_get(Ustrlen(iptr) + 2);
+ uschar *etag = store_get(Ustrlen(iptr) + 2, is_tainted(iptr));
uschar *optr = etag;
while (*iptr != 0)
{
/* Function that is shared with tf_maildir.c */
-extern off_t check_dir_size(uschar *, int *, const pcre *);
+extern off_t check_dir_size(const uschar *, int *, const pcre *);
/* End of transports/appendfile.h */
cache_size = statbuf.st_size;
add_size = sizeof(time_t) + Ustrlen(to) + 1;
- cache_buff = store_get(cache_size + add_size);
+ cache_buff = store_get(cache_size + add_size, is_tainted(oncelog));
if (read(cache_fd, cache_buff, cache_size) != cache_size)
{
int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
if (log_fd >= 0)
{
- uschar *ptr = log_buffer;
+ gstring gs = { .size = LOG_BUFFER_SIZE, .ptr = 0, .s = log_buffer }, *g = &gs;
+
+ /* Use taint-unchecked routines for writing into log_buffer, trusting
+ that we'll never expand it. */
+
DEBUG(D_transport) debug_printf("logging message details\n");
- sprintf(CS ptr, "%s\n", tod_stamp(tod_log));
- while(*ptr) ptr++;
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log));
if (from)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " From: %s\n", from);
- while(*ptr) ptr++;
- }
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " From: %s\n", from);
if (to)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " To: %s\n", to);
- while(*ptr) ptr++;
- }
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " To: %s\n", to);
if (cc)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Cc: %s\n", cc);
- while(*ptr) ptr++;
- }
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Cc: %s\n", cc);
if (bcc)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Bcc: %s\n", bcc);
- while(*ptr) ptr++;
- }
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Bcc: %s\n", bcc);
if (subject)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Subject: %s\n", subject);
- while(*ptr) ptr++;
- }
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Subject: %s\n", subject);
if (headers)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " %s\n", headers);
- while(*ptr) ptr++;
- }
- if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer
- || close(log_fd))
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s\n", headers);
+ if(write(log_fd, g->s, g->ptr) != g->ptr || close(log_fd))
DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
"transport\n", logfile, tblock->name);
}
if (*errno_value == ERRNO_WRITEINCOMPLETE)
{
- *message = string_sprintf("failed to write a data block");
+ *message = US"failed to write a data block";
return FALSE;
}
int rc;
va_list ap;
+/*XXX see comment in smtp_write_command() regarding leaving stuff in
+big_buffer */
+
va_start(ap, format);
-if (!string_vformat(&gs, FALSE, CS format, ap))
+if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
{
va_end(ap);
errno = ERRNO_SMTPFORMAT;
/* First thing is to wait for an initial greeting. */
-Ustrcpy(big_buffer, "initial connection");
+Ustrcpy(big_buffer, US"initial connection");
if (!lmtp_read_response(out, buffer, sizeof(buffer), '2',
timeout)) goto RESPONSE_FAILED;
sigalrm_seen = FALSE;
transport_write_timeout = timeout;
- Ustrcpy(big_buffer, "sending data block"); /* For error messages */
+ Ustrcpy(big_buffer, US"sending data block"); /* For error messages */
DEBUG(D_transport|D_v)
debug_printf(" LMTP>> writing message and terminating \".\"\n");
goto RESPONSE_FAILED;
}
- Ustrcpy(big_buffer, "end of data"); /* For error messages */
+ Ustrcpy(big_buffer, US"end of data"); /* For error messages */
/* We now expect a response for every address that was accepted above,
in the same order. For those that get a response, their status is fixed;
string_sprintf("Failed to expand headers_add or headers_remove: %s",
expand_string_message);
else if (errno == ERRNO_FILTER_FAIL)
- addrlist->message = string_sprintf("Filter process failure");
+ addrlist->message = US"Filter process failure";
else if (errno == ERRNO_WRITEINCOMPLETE)
- addrlist->message = string_sprintf("Failed repeatedly to write data");
+ addrlist->message = US"Failed repeatedly to write data";
else if (errno == ERRNO_SMTPFORMAT)
addrlist->message = US"overlong LMTP command generated";
else
{
const uschar **argv;
-*argvptr = argv = store_get((4)*sizeof(uschar *));
+*argvptr = argv = store_get((4)*sizeof(uschar *), FALSE);
argv[0] = US"/bin/sh";
argv[1] = US"-c";
addr->more_errno,
(addr->more_errno == EX_EXECFAILED)? ": unable to execute command" : "");
else if (errno == ERRNO_WRITEINCOMPLETE)
- addr->message = string_sprintf("Failed repeatedly to write data");
+ addr->message = US"Failed repeatedly to write data";
else
addr->message = string_sprintf("Error %d", errno);
return FALSE;
return FALSE;
case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */
- *message = string_sprintf("failed to write a data block");
+ *message = US"failed to write a data block";
return FALSE;
#ifdef SUPPORT_I18N
else if (errno != 0 || sx->buffer[0] == 0)
{
- string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+ /* Use taint-unchecked routines for writing into big_buffer, trusting
+ that we'll never expand it. */
+
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+ string_from_gstring(g);
return -2;
}
Return True on error, otherwise buffer has (possibly empty) terminated string
*/
-BOOL
+static BOOL
smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
smtp_transport_options_block *ob)
{
-uschar *local_authenticated_sender = authenticated_sender;
+uschar * local_authenticated_sender = authenticated_sender;
#ifdef notdef
debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
#endif
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
{
uschar *new = expand_string(ob->authenticated_sender);
- if (new == NULL)
+ if (!new)
{
if (!f.expand_string_forcedfail)
{
return TRUE;
}
}
- else if (new[0] != 0) local_authenticated_sender = new;
+ else if (*new) local_authenticated_sender = new;
}
/* Add the authenticated sender address if present */
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
- local_authenticated_sender != NULL)
+if ( (f.smtp_authenticated || ob->authenticated_sender_force)
+ && local_authenticated_sender)
{
- string_format(buffer, bufsize, " AUTH=%s",
+ string_format_nt(buffer, bufsize, " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
- Ustrlen(local_authenticated_sender)));
+ Ustrlen(local_authenticated_sender)));
client_authenticated_sender = string_copy(local_authenticated_sender);
}
else
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
- Ustrcpy(p, " SMTPUTF8"), p += 9;
+ Ustrcpy(p, US" SMTPUTF8"), p += 9;
#endif
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
- { Ustrcpy(p, " RET=HDRS"); p += 9; }
+ { Ustrcpy(p, US" RET=HDRS"); p += 9; }
else if (dsn_ret == dsn_ret_full)
- { Ustrcpy(p, " RET=FULL"); p += 9; }
+ { Ustrcpy(p, US" RET=FULL"); p += 9; }
if (dsn_envid)
{
{
BOOL first = TRUE;
- Ustrcpy(p, " NOTIFY=");
+ Ustrcpy(p, US" NOTIFY=");
while (*p) p++;
for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
{
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
+ if (is_tainted(s))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted host list '%s' from '%s' in transport %s",
+ s, ob->hosts, tblock->name);
+ /* Avoid leaking info to an attacker */
+ addrlist->message = US"internal configuration error";
+ addrlist->transport_return = PANIC;
+ return FALSE;
+ }
+
host_build_hostlist(&hostlist, s, ob->hosts_randomize);
/* Check that the expansion yielded something useful. */
if (expanded_hosts)
{
- thost = store_get(sizeof(host_item));
+ thost = store_get(sizeof(host_item), FALSE);
*thost = *host;
thost->name = string_copy(host->name);
thost->address = string_copy(host->address);
-extern BOOL smtp_mail_auth_str(uschar *, unsigned,
- address_item *, smtp_transport_options_block *);
-
#ifdef SUPPORT_SOCKS
extern int socks_sock_connect(host_item *, int, int, uschar *,
transport_instance *, int);
struct dirent *ent;
struct stat statbuf;
-dir = opendir(CS path);
-if (dir == NULL) return 0;
+if (!(dir = opendir(CS path)))
+ return 0;
-while ((ent = readdir(dir)) != NULL)
+while ((ent = readdir(dir)))
{
- uschar *name = US ent->d_name;
- uschar buffer[1024];
+ uschar * s, * name = US ent->d_name;
if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
/* The name is OK; stat it. */
- if (!string_format(buffer, sizeof(buffer), "%s/%s", path, name))
- {
- DEBUG(D_transport)
- debug_printf("maildir_compute_size: name too long: dir=%s name=%s\n",
- path, name);
- continue;
- }
-
- if (Ustat(buffer, &statbuf) < 0)
+ s = string_sprintf("%s/%s", path, name);
+ if (Ustat(s, &statbuf) < 0)
{
DEBUG(D_transport)
debug_printf("maildir_compute_size: stat error %d for %s: %s\n", errno,
- buffer, strerror(errno));
+ s, strerror(errno));
continue;
}
if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
{
DEBUG(D_transport)
- debug_printf("skipping %s/%s: not a directory\n", path, name);
+ debug_printf("skipping %s/%s: not a directory\n", s, name);
continue;
}
/* If this is a maildir folder, call this function recursively. */
if (name[0] == '.')
- {
- sum += maildir_compute_size(buffer, filecount, latest, regex, dir_regex,
+ sum += maildir_compute_size(s, filecount, latest, regex, dir_regex,
timestamp_only);
- }
/* Otherwise it must be a folder that contains messages (e.g. new or cur), so
we need to get its size, unless all we are interested in is the timestamp. */
else if (!timestamp_only)
- {
- sum += check_dir_size(buffer, filecount, regex);
- }
+ sum += check_dir_size(s, filecount, regex);
}
closedir(dir);
filename = string_sprintf("%s/maildirsize", path);
DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path);
-fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600);
-if (fd < 0)
+if ((fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600)) < 0)
{
if (errno != ENOENT) return -1;
DEBUG(D_transport)
still correct, and that the size of the file is still small enough. If so,
compute the maildir size from the file. */
-count = read(fd, buffer, sizeof(buffer));
-if (count >= sizeof(buffer))
+if ((count = read(fd, buffer, sizeof(buffer))) >= sizeof(buffer))
{
DEBUG(D_transport)
debug_printf("maildirsize file too big (%d): recalculating\n", count);
void
tree_add_nonrecipient(uschar *s)
{
-tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
+rmark rpoint = store_mark();
+tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
Ustrcpy(node->name, s);
node->data.ptr = NULL;
-if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(node);
+if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint);
}
void
tree_add_duplicate(uschar *s, address_item *addr)
{
-tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
+rmark rpoint = store_mark();
+tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
Ustrcpy(node->name, s);
node->data.ptr = addr;
-if (!tree_insertnode(&tree_duplicates, node)) store_reset(node);
+if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint);
}
void
tree_add_unusable(host_item *h)
{
+rmark rpoint = store_mark();
tree_node *node;
uschar s[256];
sprintf(CS s, "T:%.200s:%s", h->name, h->address);
-node = store_get(sizeof(tree_node) + Ustrlen(s));
+node = store_get(sizeof(tree_node) + Ustrlen(s),
+ is_tainted(h->name) || is_tainted(h->address));
Ustrcpy(node->name, s);
node->data.val = h->why;
if (h->status == hstatus_unusable_expired) node->data.val += 256;
-if (!tree_insertnode(&tree_unusable, node)) store_reset(node);
+if (!tree_insertnode(&tree_unusable, node)) store_reset(rpoint);
}
tree_add_var(uschar * name, uschar * val, void * ctx)
{
tree_node ** root = ctx;
-tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name));
+tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
Ustrcpy(node->name, name);
node->data.ptr = val;
(void) tree_insertnode(root, node);
p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len);
p_len = ucs4_len*4; /* this multiplier is pure guesswork */
-res = store_get(p_len+5);
+res = store_get(p_len+5, is_tainted(utf8));
res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-';
DEBUG(D_expand) debug_printf("l_a2u: '%s'\n", alabel);
alabel += 4;
p_len = Ustrlen(alabel);
-p = (punycode_uint *) store_get((p_len+1) * sizeof(*p));
+p = store_get((p_len+1) * sizeof(*p), is_tainted(alabel));
if ((rc = punycode_decode(p_len, CCS alabel, &p_len, p, NULL)) != PUNYCODE_SUCCESS)
{
{
if (length == sizeof(dbdata_callout_cache_obs))
{
- dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache));
+ dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache), FALSE);
memcpy(new, cache_record, length);
new->postmaster_stamp = new->random_stamp = new->time_stamp;
cache_record = new;
if (done)
{
- address_item * na = store_get(sizeof(address_item));
+ address_item * na = store_get(sizeof(address_item), FALSE);
*na = cutthrough.addr;
cutthrough.addr = *addr;
cutthrough.addr.host_used = &cutthrough.host;
{
extern int acl_where; /* src/acl.c */
errno = 0;
- addr->message = string_sprintf(
- "response to \"EHLO\" did not include SMTPUTF8");
+ addr->message = US"response to \"EHLO\" did not include SMTPUTF8";
addr->user_message = acl_where == ACL_WHERE_RCPT
? US"533 no support for internationalised mailbox name"
: US"550 mailbox unavailable";
for (address_item * caddr = &cutthrough.addr, * parent = addr->parent;
parent;
caddr = caddr->parent, parent = parent->parent)
- *(caddr->parent = store_get(sizeof(address_item))) = *parent;
+ *(caddr->parent = store_get(sizeof(address_item), FALSE)) = *parent;
ctctx.outblock.buffer = ctbuffer;
ctctx.outblock.buffersize = sizeof(ctbuffer);
else
{ /* Set up a tree entry to cache the lookup */
- t = store_get(sizeof(tree_node) + Ustrlen(query));
+ t = store_get(sizeof(tree_node) + Ustrlen(query), is_tainted(query));
Ustrcpy(t->name, query);
- t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+ t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE);
(void)tree_insertnode(&dnsbl_cache, t);
}
Ustrncat(version_date, EXIM_BUILD_DATE_OVERRIDE, sizeof(date_buffer));
#else
-Ustrcpy(today, __DATE__);
+Ustrcpy(today, US __DATE__);
if (today[4] == ' ') today[4] = '0';
today[3] = today[6] = '-';
Ustrncat(version_date, today+4, 3);
Ustrncat(version_date, today, 4);
Ustrncat(version_date, today+7, 4);
-Ustrcat(version_date, " ");
-Ustrcat(version_date, __TIME__);
+Ustrcat(version_date, US" ");
+Ustrcat(version_date, US __TIME__);
#endif
}
local_part_prefix = bsmtp_ : mmdf_
local_part_prefix_optional
retry_use_local_part
- transport = ${local_part_prefix}local_delivery
+ transport = ${if !def:local_part_prefix {} \
+ {${if eq {bsmtp_}{$local_part_prefix} {bsmtp_}\
+ {${if eq {mmdf_}{$local_part_prefix} {mmdf_} {}}}}}}local_delivery
# ----- Transports -----
log_selector = +subject
domainlist local_domains = test.ex
qualify_domain = test.ex
+untrusted_set_sender = *
# ----- Routers -----
smart:
driver = accept
retry_use_local_part
- transport = $h_transport:
+ transport = ${if eq {t1}{$sender_address_local_part} {local_delivery} \
+ {${if eq {t2}{$sender_address_local_part} {local_delivery_fcntl} \
+ {${if eq {t3}{$sender_address_local_part} {local_delivery_fcntl_blocking} \
+ {} }}}}}
# ----- Transports -----
driver = accept
retry_use_local_part
senders = !
- transport = $local_part
+ transport = ${if eq {reply1}{$local_part} {reply1}{reply2}}
# ----- Transports -----
all:
driver = accept
address_data = ${if match{$local_part}{^(.)}{$1}}
- transport = ${if match{$local_part}{^.*-(.*)\$}{$1}fail}
+ transport = ${if eq {${substr_-1_1:$local_part}}{1} {t1} \
+ {${if eq {${substr_-1_1:$local_part}}{2} {t2} \
+ {${if eq {${substr_-1_1:$local_part}}{3} {t3} {t4}}}}}}
# ----- Transports -----
# ----- Main settings -----
-acl_smtp_rcpt = DIR/aux-fixed/TESTNUM.acl$local_part
+acl_smtp_rcpt = chk_rcpt
qualify_domain = test.ex
trusted_users = CALLER
+# ----- ACL -----
+begin acl
+
+chk_rcpt:
+ accept local_parts = 1
+ endpass
+ acl = DIR/aux-fixed/TESTNUM.acl1
+ accept local_parts = 2
+ endpass
+ acl = DIR/aux-fixed/TESTNUM.acl2
+
# ----- Routers -----
begin routers
user = CALLER
file_transport = t1
-r2:
+r2_8:
+ driver = redirect
+ local_parts = userx8
+ allow_filter
+ data = #Sieve filter\n \
+ require["fileinto","comparator-i;ascii-numeric"]; \
+ if header :comparator "i;ascii-numeric" "X-Sieve" "99" { \
+ fileinto "inbox.JUNK"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2_9:
+ driver = redirect
+ local_parts = userx9
+ allow_filter
+ data = #Sieve filter\n \
+ require["fileinto","comparator-i;ascii-numeric"]; \
+ if header :comparator "i;ascii-numeric" "X-Sieve" "98" { \
+ fileinto "inbox.JUNK"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2_10:
+ driver = redirect
+ local_parts = userx10
+ allow_filter
+ data = #Sieve filter\n \
+ require["fileinto","comparator-i;ascii-numeric"]; \
+ if header :comparator "i;ascii-numeric" "X-Sieve" "99" { \
+ fileinto "inbox.JUNK"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2_11:
driver = redirect
+ local_parts = userx11
+ allow_filter
+ data = #Sieve filter\n \
+ require["fileinto","comparator-i;ascii-numeric"]; \
+ if header :comparator "i;ascii-numeric" "X-Sieve" "-99" { \
+ fileinto "inbox.JUNK"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2_12:
+ driver = redirect
+ local_parts = userx12
+ allow_filter
+ data = #Sieve filter\n \
+ require["fileinto","comparator-i;ascii-numeric"]; \
+ if header :comparator "i;ascii-numeric" "X-Sieve" "-98" { \
+ fileinto "inbox.JUNK"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2_13:
+ driver = redirect
+ local_parts = userx13 : someone13
+ allow_filter
+ data = #Sieve filter\n \
+ require ["vacation"]; \
+ vacation "I am gone. Not here.";
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+ sieve_vacation_directory = DIR/test-vacation-directory
+
+r2_14:
+ driver = redirect
+ local_parts = userx14
local_part_suffix = -*
local_part_suffix_optional
allow_filter
+ data = #Sieve filter\n \
+ require ["envelope","fileinto"]; \
+ if envelope :matches :localpart "to" "*-suffix" { \
+ fileinto "userx-sawsuffix"; \
+ stop; \
+ }
+ user = CALLER
+ file_transport = t1
+ reply_transport = t3
+
+r2:
+ driver = redirect
+ allow_filter
+ skip_syntax_errors
data = "#Sieve filter\n$h_filter:"
user = CALLER
file_transport = t1
reply_transport = t3
- sieve_vacation_directory = DIR/test-vacation-directory
# ----- Transports -----
t1:
driver = smtp
port = PORT_D
- hosts = ${if eq {$sender_host_address}{}{$local_part}{V4NET.0.0.2}}
+ hosts = ${if !eq {$sender_host_address}{} {V4NET.0.0.2} \
+ {${if eq {127.0.0.1}{$local_part} {127.0.0.1} \
+ {${if eq {V4NET.0.0.1}{$local_part} {V4NET.0.0.1}}}}}}
allow_localhost
connect_timeout = 1s
r1:
driver = accept
- transport = $h_transport:
+ transport = OPT
# ----- Transports -----
begin routers
-client:
+r1:
driver = accept
- transport = $sender_address_local_part
+ condition = ${if eq {t1}{$sender_address_local_part}}
+ transport = t1
+
+r2:
+ driver = accept
+ condition = ${if eq {t2}{$sender_address_local_part}}
+ transport = t2
# ----- Transports -----
client:
driver = accept
condition = ${if eq {SERVER}{server}{no}{yes}}
- transport = send_to_server
+ address_data = ${substr_1_1:$domain}
+ transport = send_to_server${if eq {1}{$address_data} {1} \
+ {${if eq {2}{$address_data} {2} \
+ {${if eq {3}{$address_data} {3} \
+ {${if eq {4}{$address_data} {4}{5}}}}}}}}
server:
driver = accept
file = DIR/test-mail/$local_part
user = CALLER
+send_to_server1:
+ driver = smtp
+ allow_localhost
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
+ port = PORT_D
+ interface = <; ::1 ; HOSTIPV4
+
+send_to_server2:
+ driver = smtp
+ allow_localhost
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
+ port = PORT_D
+ interface = <; HOSTIPV6 ; HOSTIPV4
+
+send_to_server3:
+ driver = smtp
+ allow_localhost
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
+ port = PORT_D
+ interface = <; ${if eq{0}{1}{HOSTIPV6}fail}
+
+send_to_server4:
+ driver = smtp
+ allow_localhost
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
+ port = PORT_D
+ interface = <; ${if eq{0}{1}{HOSTIPV6}{ }}
+
+send_to_server5:
+ driver = smtp
+ allow_localhost
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
+ port = PORT_D
+ interface = <; ${if
+
send_to_server:
driver = smtp
allow_localhost
- hosts = $h_hosts
+ hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}}
port = PORT_D
interface = ${expand:$h_interface:}
.include DIR/aux-var/std_conf_prefix
+OPT =
+
primary_hostname = myhost.test.ex
# ----- Main settings -----
localuser:
driver = accept
local_parts = userx
- transport = $h_maildir:appendfile
+ transport = VALUE
# ----- Transports -----
directory = DIR/test-mail
envelope_to_add
maildir_format
- maildir_tag = ${expand:$h_tag:}
+ maildir_tag = OPT
message_prefix =
quota = 20K
quota_size_regex = S=(\d+)$
directory = DIR/test-mail
envelope_to_add
maildir_format
- maildir_tag = ${expand:$h_tag:}
+ maildir_tag = OPT
message_prefix =
quota = 20K
quota_size_regex = ,S=(\d+):
local_part_prefix = bsmtp_ : mmdf_ : mbx_
local_part_prefix_optional
retry_use_local_part
- transport = ${local_part_prefix}local_delivery
+ transport = ${if eq {bsmtp_} {${local_part_prefix}} {bsmtp_} \
+ {${if eq {mmdf_} {${local_part_prefix}} {mmdf_} \
+ {${if eq {mbx_} {${local_part_prefix}} {mbx_}{}}}}}}local_delivery
# ----- Transports -----
r1:
driver = accept
- transport = $h_transport:
+ transport = t1
# ----- Transports -----
-1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="First"
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= t1@foo U=CALLER P=local S=sss T="First"
1999-03-02 09:44:33 10HmaX-0005vi-00 => userx <userx@test.ex> R=smart T=local_delivery
1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Second"
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= t1@foo U=CALLER P=local S=sss T="Second"
1999-03-02 09:44:33 10HmaY-0005vi-00 == userx@test.ex R=smart T=local_delivery defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (lock file)
1999-03-02 09:44:33 Start queue run: pid=pppp -qf
1999-03-02 09:44:33 10HmaY-0005vi-00 => userx <userx@test.ex> R=smart T=local_delivery
1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
1999-03-02 09:44:33 End queue run: pid=pppp -qf
-1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Third"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= t2@foo U=CALLER P=local S=sss T="Third"
1999-03-02 09:44:33 10HmaZ-0005vi-00 == userx@test.ex R=smart T=local_delivery_fcntl defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (fcntl/flock)
1999-03-02 09:44:33 Start queue run: pid=pppp -qf
1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx <userx@test.ex> R=smart T=local_delivery_fcntl
1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
1999-03-02 09:44:33 End queue run: pid=pppp -qf
-1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Fourth"
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= t3@foo U=CALLER P=local S=sss T="Fourth"
1999-03-02 09:44:33 10HmbA-0005vi-00 == userx@test.ex R=smart T=local_delivery_fcntl_blocking defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (fcntl/flock)
1999-03-02 09:44:33 Start queue run: pid=pppp -qf
1999-03-02 09:44:33 10HmbA-0005vi-00 => userx <userx@test.ex> R=smart T=local_delivery_fcntl_blocking
1999-03-02 09:44:33 10HmbF-0005vi-00 => TESTSUITE/test-mail/userx <userx@test.ex> R=r2 T=t1
1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbG-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbG-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx8@test.ex> R=r2_8 T=t1
1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbH-0005vi-00 => TESTSUITE/test-mail/userx <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbH-0005vi-00 => TESTSUITE/test-mail/userx9 <userx9@test.ex> R=r2_9 T=t1
1999-03-02 09:44:33 10HmbH-0005vi-00 Completed
1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbI-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbI-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx10@test.ex> R=r2_10 T=t1
1999-03-02 09:44:33 10HmbI-0005vi-00 Completed
1999-03-02 09:44:33 10HmbJ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbJ-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbJ-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx11@test.ex> R=r2_11 T=t1
1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed
1999-03-02 09:44:33 10HmbK-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbK-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbK-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx12@test.ex> R=r2_12 T=t1
1999-03-02 09:44:33 10HmbK-0005vi-00 Completed
1999-03-02 09:44:33 10HmbL-0005vi-00 <= someone@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbL-0005vi-00 => TESTSUITE/test-mail/userx <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbL-0005vi-00 => TESTSUITE/test-mail/userx13 <userx13@test.ex> R=r2_13 T=t1
1999-03-02 09:44:33 10HmbM-0005vi-00 <= <> R=10HmbL-0005vi-00 U=CALLER P=local S=sss
1999-03-02 09:44:33 10HmbM-0005vi-00 => someone <someone@test.ex> R=rb T=t2
1999-03-02 09:44:33 10HmbM-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbL-0005vi-00 => >someone@test.ex <userx@test.ex> R=r2 T=t3
+1999-03-02 09:44:33 10HmbL-0005vi-00 => >someone@test.ex <userx13@test.ex> R=r2_13 T=t3
1999-03-02 09:44:33 10HmbL-0005vi-00 Completed
1999-03-02 09:44:33 10HmbN-0005vi-00 <= someone@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx <userx-suffix2@test.ex> R=r2 T=t1
-1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix <userx-suffix@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx14 <userx14-suffix2@test.ex> R=r2_14 T=t1
+1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix <userx14-suffix@test.ex> R=r2_14 T=t1
1999-03-02 09:44:33 10HmbN-0005vi-00 Completed
1999-03-02 09:44:33 10HmaY-0005vi-00 <= t2@dustybelt.tld U=CALLER P=local-smtp S=sss
1999-03-02 09:44:33 Start queue run: pid=pppp -qq
1999-03-02 09:44:33 10HmaX-0005vi-00 H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
-1999-03-02 09:44:33 10HmaX-0005vi-00 == fred@anotherone.tld R=client T=t1 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
+1999-03-02 09:44:33 10HmaX-0005vi-00 == fred@anotherone.tld R=r1 T=t1 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
1999-03-02 09:44:33 10HmaY-0005vi-00 H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
-1999-03-02 09:44:33 10HmaY-0005vi-00 == fred@anotherone.tld R=client T=t2 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
+1999-03-02 09:44:33 10HmaY-0005vi-00 == fred@anotherone.tld R=r2 T=t2 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later
1999-03-02 09:44:33 End queue run: pid=pppp -qq
******** SERVER ********
1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
1999-03-02 09:44:33 Start queue run: pid=pppp -qf
-1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => user4@h1.test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmbC-0005vi-00"
1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbD-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => user6@h2.test.ex R=client T=send_to_server2 H=::1 [::1] C="250 OK id=10HmbD-0005vi-00"
1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbA-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbA-0005vi-00 => user6@h3.test.ex R=client T=send_to_server3 H=::1 [::1] C="250 OK id=10HmbE-0005vi-00"
1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbB-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbF-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 => user6@h4.test.ex R=client T=send_to_server4 H=::1 [::1] C="250 OK id=10HmbF-0005vi-00"
1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found ""
+1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found ""
1999-03-02 09:44:33 End queue run: pid=pppp -qf
******** SERVER ********
1999-03-02 09:44:33 10HmbD-0005vi-00 => userx <userx@myhost.test.ex> R=localuser T=maildir_tagged_appendfile
1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected
1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
1999-03-02 09:44:33 10HmbE-0005vi-00 => userx <userx@myhost.test.ex> R=localuser T=maildir_tagged_appendfile
1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
-From CALLER@test.ex Tue Mar 02 09:44:33 1999
+From t1@foo Tue Mar 02 09:44:33 1999
Received: from CALLER by the.local.host.name with local (Exim x.yz)
- (envelope-from <CALLER@test.ex>)
+ (envelope-from <t1@foo>)
id 10HmaX-0005vi-00
for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
Subject: First
-Transport: local_delivery
Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t1@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+local_delivery
First message.
-From CALLER@test.ex Tue Mar 02 09:44:33 1999
+From t1@foo Tue Mar 02 09:44:33 1999
Received: from CALLER by the.local.host.name with local (Exim x.yz)
- (envelope-from <CALLER@test.ex>)
+ (envelope-from <t1@foo>)
id 10HmaY-0005vi-00
for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
Subject: Second
-Transport: local_delivery
Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t1@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+local_delivery
Second message
-From CALLER@test.ex Tue Mar 02 09:44:33 1999
+From t2@foo Tue Mar 02 09:44:33 1999
Received: from CALLER by the.local.host.name with local (Exim x.yz)
- (envelope-from <CALLER@test.ex>)
+ (envelope-from <t2@foo>)
id 10HmaZ-0005vi-00
for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
Subject: Third
-Transport: local_delivery_fcntl
Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t2@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+local_delivery_fcntl
Third message
-From CALLER@test.ex Tue Mar 02 09:44:33 1999
+From t3@foo Tue Mar 02 09:44:33 1999
Received: from CALLER by the.local.host.name with local (Exim x.yz)
- (envelope-from <CALLER@test.ex>)
+ (envelope-from <t3@foo>)
id 10HmbA-0005vi-00
for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
Subject: Fourth
-Transport: local_delivery_fcntl_blocking
Message-Id: <E10HmbA-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t3@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+local_delivery_fcntl_blocking
Fourth message
From CALLER@test.ex Tue Mar 02 09:44:33 1999
Return-path: <CALLER@test.ex>
-Envelope-to: userx@test.ex
+Envelope-to: userx8@test.ex
Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
Received: from CALLER by mail.test.ex with local (Exim x.yz)
(envelope-from <CALLER@test.ex>)
id 10HmbG-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+ for userx8@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
X-Sieve: 99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
- if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
- fileinto "inbox.JUNK";
- stop;
- }
Message-Id: <E10HmbG-0005vi-00@mail.test.ex>
From: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+ require["fileinto","comparator-i;ascii-numeric"];
+ if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
+ fileinto "inbox.JUNK";
+ stop;
+ }
Test 8
From CALLER@test.ex Tue Mar 02 09:44:33 1999
Return-path: <CALLER@test.ex>
-Envelope-to: userx@test.ex
+Envelope-to: userx10@test.ex
Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
Received: from CALLER by mail.test.ex with local (Exim x.yz)
(envelope-from <CALLER@test.ex>)
id 10HmbI-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+ for userx10@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
X-Sieve: 99-
-Filter: require["fileinto","comparator-i;ascii-numeric"];
- if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
- fileinto "inbox.JUNK";
- stop;
- }
Message-Id: <E10HmbI-0005vi-00@mail.test.ex>
From: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+ require["fileinto","comparator-i;ascii-numeric"];
+ if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
+ fileinto "inbox.JUNK";
+ stop;
+ }
Test 10
From CALLER@test.ex Tue Mar 02 09:44:33 1999
Return-path: <CALLER@test.ex>
-Envelope-to: userx@test.ex
+Envelope-to: userx11@test.ex
Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
Received: from CALLER by mail.test.ex with local (Exim x.yz)
(envelope-from <CALLER@test.ex>)
id 10HmbJ-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+ for userx11@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
X-Sieve: -99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
- if header :comparator "i;ascii-numeric" "X-Sieve" "-99" {
- fileinto "inbox.JUNK";
- stop;
- }
Message-Id: <E10HmbJ-0005vi-00@mail.test.ex>
From: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+ require["fileinto","comparator-i;ascii-numeric"];
+ if header :comparator "i;ascii-numeric" "X-Sieve" "-99" {
+ fileinto "inbox.JUNK";
+ stop;
+ }
Test 11
From CALLER@test.ex Tue Mar 02 09:44:33 1999
Return-path: <CALLER@test.ex>
-Envelope-to: userx@test.ex
+Envelope-to: userx12@test.ex
Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
Received: from CALLER by mail.test.ex with local (Exim x.yz)
(envelope-from <CALLER@test.ex>)
id 10HmbK-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+ for userx12@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
X-Sieve: -99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
- if header :comparator "i;ascii-numeric" "X-Sieve" "-98" {
- fileinto "inbox.JUNK";
- stop;
- }
Message-Id: <E10HmbK-0005vi-00@mail.test.ex>
From: CALLER_NAME <CALLER@test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
+ require["fileinto","comparator-i;ascii-numeric"];
+ if header :comparator "i;ascii-numeric" "X-Sieve" "-98" {
+ fileinto "inbox.JUNK";
+ stop;
+ }
Test 12
Received: from CALLER by mail.test.ex with local (Exim x.yz)
id 10HmbM-0005vi-00
for someone@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-From: userx@test.ex
+From: userx13@test.ex
To: someone@test.ex
Subject: Automated reply
In-Reply-To: <E10HmbL-0005vi-00@mail.test.ex>
Test 7
-From CALLER@test.ex Tue Mar 02 09:44:33 1999
-Return-path: <CALLER@test.ex>
-Envelope-to: userx@test.ex
-Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
-Received: from CALLER by mail.test.ex with local (Exim x.yz)
- (envelope-from <CALLER@test.ex>)
- id 10HmbH-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-X-Sieve: 99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
- if header :comparator "i;ascii-numeric" "X-Sieve" "98" {
- fileinto "inbox.JUNK";
- stop;
- }
-Message-Id: <E10HmbH-0005vi-00@mail.test.ex>
-From: CALLER_NAME <CALLER@test.ex>
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-
-Test 9
-
-From someone@test.ex Tue Mar 02 09:44:33 1999
-Return-path: <someone@test.ex>
-Envelope-to: userx@test.ex
-Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
-Received: from CALLER by mail.test.ex with local (Exim x.yz)
- (envelope-from <someone@test.ex>)
- id 10HmbL-0005vi-00
- for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-To: userx@test.ex
-Filter: require ["vacation"];
- vacation "I am gone. Not here.";
-Message-Id: <E10HmbL-0005vi-00@mail.test.ex>
-From: someone@test.ex
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-
-Test 13
-
-From someone@test.ex Tue Mar 02 09:44:33 1999
-Return-path: <someone@test.ex>
-Envelope-to: userx-suffix2@test.ex
-Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
-Received: from CALLER by mail.test.ex with local (Exim x.yz)
- (envelope-from <someone@test.ex>)
- id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
-To: userx-suffix@test.ex
-Filter: require ["envelope","fileinto"];
- if envelope :matches :localpart "to" "*-suffix" {
- fileinto "userx-sawsuffix";
- stop;
- }
-Message-Id: <E10HmbN-0005vi-00@mail.test.ex>
-From: someone@test.ex
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-
-Test 14
-
From someone@test.ex Tue Mar 02 09:44:33 1999
Return-path: <someone@test.ex>
-Envelope-to: userx-suffix@test.ex
+Envelope-to: userx14-suffix@test.ex
Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
Received: from CALLER by mail.test.ex with local (Exim x.yz)
(envelope-from <someone@test.ex>)
id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
-To: userx-suffix@test.ex
-Filter: require ["envelope","fileinto"];
- if envelope :matches :localpart "to" "*-suffix" {
- fileinto "userx-sawsuffix";
- stop;
- }
Message-Id: <E10HmbN-0005vi-00@mail.test.ex>
From: someone@test.ex
Date: Tue, 2 Mar 1999 09:44:33 +0000
+ require ["envelope","fileinto"];
+ if envelope :matches :localpart "to" "*-suffix" {
+ fileinto "userx-sawsuffix";
+ stop;
+ }
Test 14
--- /dev/null
+From someone@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <someone@test.ex>
+Envelope-to: userx13@test.ex
+Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
+Received: from CALLER by mail.test.ex with local (Exim x.yz)
+ (envelope-from <someone@test.ex>)
+ id 10HmbL-0005vi-00
+ for userx13@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+To: userx13@test.ex
+Message-Id: <E10HmbL-0005vi-00@mail.test.ex>
+From: someone@test.ex
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+ require ["vacation"];
+ vacation "I am gone. Not here.";
+Test 13
+
--- /dev/null
+From someone@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <someone@test.ex>
+Envelope-to: userx14-suffix2@test.ex
+Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
+Received: from CALLER by mail.test.ex with local (Exim x.yz)
+ (envelope-from <someone@test.ex>)
+ id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbN-0005vi-00@mail.test.ex>
+From: someone@test.ex
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+ require ["envelope","fileinto"];
+ if envelope :matches :localpart "to" "*-suffix" {
+ fileinto "userx-sawsuffix";
+ stop;
+ }
+Test 14
+
--- /dev/null
+From CALLER@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <CALLER@test.ex>
+Envelope-to: userx9@test.ex
+Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000
+Received: from CALLER by mail.test.ex with local (Exim x.yz)
+ (envelope-from <CALLER@test.ex>)
+ id 10HmbH-0005vi-00
+ for userx9@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+X-Sieve: 99
+Message-Id: <E10HmbH-0005vi-00@mail.test.ex>
+From: CALLER_NAME <CALLER@test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+ require["fileinto","comparator-i;ascii-numeric"];
+ if header :comparator "i;ascii-numeric" "X-Sieve" "98" {
+ fileinto "inbox.JUNK";
+ stop;
+ }
+Test 9
+
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaY-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_
Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaZ-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_
Message-Id: <E10HmaZ-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbB-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_tagged_
-tag:Ssss
Message-Id: <E10HmbB-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbC-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_tagged_
-tag:,S=sss
Message-Id: <E10HmbC-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbD-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_tagged_
-tag:${if eq{0}{1}{rhubarb}fail}
Message-Id: <E10HmbD-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbE-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_tagged_
-tag:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Message-Id: <E10HmbE-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbF-0005vi-00
for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_taggedX_
-tag:,S=sss:2,S
Message-Id: <E10HmbF-0005vi-00@myhost.test.ex>
From: CALLER_NAME <CALLER@myhost.test.ex>
Date: Tue, 2 Mar 1999 09:44:33 +0000
1999-03-02 09:44:33 Received from CALLER@myhost.test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected
+1999-03-02 09:44:33 userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found ""
+1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found ""
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected
}
# Port in host address in spool file output from -Mvh
- s/^-host_address (.*)\.\d+/-host_address $1.9999/;
+ s/^(--?host_address) (.*)\.\d+/$1 $2.9999/;
if ($dynamic_socket and $dynamic_socket->opened and my $port = $dynamic_socket->sockport) {
s/^Connecting to 127\.0\.0\.1 port \K$port/<dynamic port>/;
escape: ${escape:B7·F2ò}
****
# Checkout expansion debugging
-exim -d-all+expand -be
+exim -d-all+expand -f sndr@dom -be
primary_hostname: $primary_hostname
+sender_address: $sender_address
match: ${if match{abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
match: ${if match{wxyz}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
${if eq {1}{1}{yes}{${lookup{xx}lsearch{/non/exist}}}}
# mailbox locking
-exim -odi userx
+exim -odi -f t1@foo userx
Subject: First
-Transport: local_delivery
+
+local_delivery
First message.
****
exim_lock -v test-mail/userx
-exim -odi userx
+exim -odi -f t1@foo userx
Subject: Second
-Transport: local_delivery
+
+local_delivery
Second message
****
exim -qf
****
exim_lock -v -fcntl test-mail/userx
-exim -odi userx
+exim -odi -f t2@foo userx
Subject: Third
-Transport: local_delivery_fcntl
+
+local_delivery_fcntl
Third message
****
exim -qf
****
exim_lock -v -fcntl test-mail/userx
-exim -odi userx
+exim -odi -f t3@foo userx
Subject: Fourth
-Transport: local_delivery_fcntl_blocking
+
+local_delivery_fcntl_blocking
Fourth message
****
exim -qf
Test 7
****
# This should fileinto inbox.JUNK (99 equal 99):
-exim -odi userx
+exim -odi userx8
X-Sieve: 99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
+
+ require["fileinto","comparator-i;ascii-numeric"];
if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
fileinto "inbox.JUNK";
stop;
Test 8
****
# This should not fileinto inbox.JUNK (98 not equal 99):
-exim -odi userx
+exim -odi userx9
X-Sieve: 99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
+
+ require["fileinto","comparator-i;ascii-numeric"];
if header :comparator "i;ascii-numeric" "X-Sieve" "98" {
fileinto "inbox.JUNK";
stop;
Test 9
****
# This should fileinto inbox.JUNK (99-suffix equal 99):
-exim -odi userx
+exim -odi userx10
X-Sieve: 99-
-Filter: require["fileinto","comparator-i;ascii-numeric"];
+
+ require["fileinto","comparator-i;ascii-numeric"];
if header :comparator "i;ascii-numeric" "X-Sieve" "99" {
fileinto "inbox.JUNK";
stop;
Test 10
****
# This should fileinto inbox.JUNK (non-numeric equal non-numeric):
-exim -odi userx
+exim -odi userx11
X-Sieve: -99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
+
+ require["fileinto","comparator-i;ascii-numeric"];
if header :comparator "i;ascii-numeric" "X-Sieve" "-99" {
fileinto "inbox.JUNK";
stop;
Test 11
****
# This should fileinto inbox.JUNK (non-numeric equal non-numeric):
-exim -odi userx
+exim -odi userx12
X-Sieve: -99
-Filter: require["fileinto","comparator-i;ascii-numeric"];
+
+ require["fileinto","comparator-i;ascii-numeric"];
if header :comparator "i;ascii-numeric" "X-Sieve" "-98" {
fileinto "inbox.JUNK";
stop;
Test 12
****
# This is a simple test of "vacation"
-exim -odi -f someone@test.ex userx
-To: userx@test.ex
-Filter: require ["vacation"];
+exim -odi -f someone@test.ex userx13
+To: userx13@test.ex
+
+ require ["vacation"];
vacation "I am gone. Not here.";
Test 13
****
# Test use of suffix
-exim -odi -f someone@test.ex userx-suffix userx-suffix2
-To: userx-suffix@test.ex
-Filter: require ["envelope","fileinto"];
+exim -odi -f someone@test.ex userx14-suffix userx14-suffix2
+
+ require ["envelope","fileinto"];
if envelope :matches :localpart "to" "*-suffix" {
fileinto "userx-sawsuffix";
stop;
# transport filter command fails to execute
need_ipv4
#
-exim -odi userx
-transport: t1
+exim -DOPT=t1 -odi userx
Test 1
****
-exim -odi userx
-transport: t2
+exim -DOPT=t2 -odi userx
Test 2
****
server PORT_S
DATA
354 Send it
****
-exim -odi userx
-transport: t3
+exim -DOPT=t3 -odi userx
Test 3
****
no_msglog_check
#
exim -DSERVER=server -bd -oX PORT_D
****
-exim userx@test.ex
-hosts: 127.0.0.1
+exim user4@h1.test.ex
+
+1
interface: <; ::1 ; HOSTIPV4
****
-exim userx@test.ex
-hosts: <; ::1
+exim user6@h2.test.ex
+
+2
interface: <; HOSTIPV6 ; HOSTIPV4
****
-exim userx@test.ex
-hosts: <; ::1
+exim user6@h3.test.ex
+
+3
interface: <; ${if eq{0}{1}{HOSTIPV6}fail}
****
-exim userx@test.ex
-hosts: <; ::1
+exim user6@h4.test.ex
+
+4
interface: <; ${if eq{0}{1}{HOSTIPV6}{ }}
****
-exim userx@test.ex
-hosts: <; ::1
+exim user6@h5.test.ex
+
+5
interface: <; ${if
****
exim -qf
# exim quota + warn threshold with maildir
-exim -odi userx
-maildir:maildir_
+exim -DVALUE=maildir_appendfile -odi userx
This is a test message
****
write test-data 100x60
-maildir:maildir_
++++
****
-exim -odi userx <test-data
+exim -DVALUE=maildir_appendfile -odi userx <test-data
****
-exim -odi userx <test-data
+exim -DVALUE=maildir_appendfile -odi userx <test-data
****
-exim -odi userx
-maildir:maildir_tagged_
-tag:S370
+exim -DVALUE=maildir_tagged_appendfile -DOPT=S370 -odi userx
This is a test message
****
sleep 1
-exim -odi userx
-maildir:maildir_tagged_
-tag:,S=370
+exim -DVALUE=maildir_tagged_appendfile -DOPT=,S=370 -odi userx
This is a test message
****
sleep 1
-exim -odi userx
-maildir:maildir_tagged_
-tag:${if eq{0}{1}{rhubarb}fail}
+exim -DVALUE=maildir_tagged_appendfile -DOPT='${if eq{0}{1}{rhubarb}fail}' -odi userx
This is a test message
****
sleep 1
# deliberate syntax fail
-exim -odi userx
-maildir:maildir_tagged_
-tag:${if eq{0}{1}{rhubarb}
+exim -DVALUE=maildir_tagged_appendfile -DOPT='${if eq{0}{1}{rhubarb}' -odi userx
This is a test message
****
sleep 1
# overlongname
-exim -odi userx
-maildir:maildir_tagged_
-tag:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+exim -DVALUE=maildir_tagged_appendfile -DOPT=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -odi userx
This is a test message
****
sleep 1
-exim -odi userx
-maildir:maildir_taggedX_
-tag:,S=10694953:2,S
+exim -DVALUE=maildir_taggedX_appendfile -DOPT=,S=10694953:2,S -odi userx
****
-exim -d-all+transport -odi userx
-maildir:maildir_taggedX_
-tag:,S=412:2,S
+exim -d-all+transport -DVALUE=maildir_taggedX_appendfile -DOPT=,S=412:2,S -odi userx
****
need_ipv4
#
exim -odi userx
-transport: t1
Test 1
****
no_msglog_check
╭considering: primary_hostname: $primary_hostname
├──expanding: primary_hostname: $primary_hostname
╰─────result: primary_hostname: myhost.test.ex
+ ╭considering: sender_address: $sender_address
+ ├──expanding: sender_address: $sender_address
+ ╰─────result: sender_address: sndr@dom
+ ╰──(tainted)
╭considering: match: ${if match{abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
╭considering: abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
├──expanding: abcd
╭considering: no}}
├──expanding: no
╰─────result: no
- ├──expanding: a.b.c
+ ├──expanding: match_address: ${if match_address{a.b.c}{a.b.c}{yes}{no}}
╰─────result: match_address: no
>>>>>>>>>>>>>>>> Exim pid=pppp (main: expansion test) terminating with rc=0 >>>>>>>>>>>>>>>>
Exim version x.yz ....
/considering: no}}
|--expanding: no
\_____result: no
- |--expanding: a.b.c
+ |--expanding: match_address: ${if match_address{a.b.c}{a.b.c}{yes}{no}}
\_____result: match_address: no
>>>>>>>>>>>>>>>> Exim pid=pppp (main: expansion test) terminating with rc=0 >>>>>>>>>>>>>>>>
Exim version x.yz ....
╭considering: -oMai authenticated_id = $authenticated_id
├──expanding: -oMai authenticated_id = $authenticated_id
╰─────result: -oMai authenticated_id = philip
+ ╰──(tainted)
╭considering: -oMas authenticated_sender = $authenticated_sender
├──expanding: -oMas authenticated_sender = $authenticated_sender
╰─────result: -oMas authenticated_sender = xx@yy.zz
+ ╰──(tainted)
╭considering: -oMi interface_address = $interface_address
├──expanding: -oMi interface_address = $interface_address
╰─────result: -oMi interface_address = 1.1.1.1
╭considering: -oMai authenticated_id = $authenticated_id
├──expanding: -oMai authenticated_id = $authenticated_id
╰─────result: -oMai authenticated_id = philip
+ ╰──(tainted)
╭considering: -oMas authenticated_sender = $authenticated_sender
├──expanding: -oMas authenticated_sender = $authenticated_sender
╰─────result: -oMas authenticated_sender = xx@yy.zz
+ ╰──(tainted)
╭considering: -oMi interface_address = $interface_address
├──expanding: -oMi interface_address = $interface_address
╰─────result: -oMi interface_address = 1.1.1.1
sender_rcvhost = ten-1.test.ex ([V4NET.0.0.1] ident=me)
├──expanding: -oMs sender_host_name = $sender_host_name
╰─────result: -oMs sender_host_name = ten-1.test.ex
+ ╰──(tainted)
╭considering: -oMt sender_ident = $sender_ident
├──expanding: -oMt sender_ident = $sender_ident
╰─────result: -oMt sender_ident = me
>>> check dnslists = test.ex/$sender_address_domain+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END
>>> = test.ex/y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END
>>> DNS list check: test.ex/y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END
-LOG: dnslist query is too long (ignored): y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+...
+LOG: dnslist query is too long (ignored): y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+e...
>>> deny: condition test failed in ACL "acl_31_31_31"
>>> processing "accept" (TESTSUITE/test-config 168)
>>> accept: condition test succeeded in ACL "acl_31_31_31"
trusted user
admin user
dropping to exim gid; retaining priv uid
-rda_interpret (file): TESTSUITE/aux-var/0037.F
-expanded: TESTSUITE/aux-var/0037.F
+rda_interpret (file): 'TESTSUITE/aux-var/0037.F'
+expanded: 'TESTSUITE/aux-var/0037.F'
ssss bytes read from TESTSUITE/aux-var/0037.F
data is an Exim filter program
Filter: start of processing
checking local_parts
$home = >/usr<
calling userfilter router
-rda_interpret (file): TESTSUITE/aux-var/0037.f-user
-expanded: TESTSUITE/aux-var/0037.f-user
+rda_interpret (file): 'TESTSUITE/aux-var/0037.f-user'
+expanded: 'TESTSUITE/aux-var/0037.f-user'
ssss bytes read from TESTSUITE/aux-var/0037.f-user
data is an Exim filter program
Filter: start of processing
expanded file: TESTSUITE/aux-fixed/0084.yes
stat() yielded 0
calling forward router
-rda_interpret (file): TESTSUITE/aux-fixed/0084.$local_part
-expanded: TESTSUITE/aux-fixed/0084.yes
+rda_interpret (file): 'TESTSUITE/aux-fixed/0084.$local_part'
+expanded: 'TESTSUITE/aux-fixed/0084.yes'
ssss bytes read from TESTSUITE/aux-fixed/0084.yes
file is not a filter file
parse_forward_list: userx@test.ex
y.z in "test.ex : myhost.test.ex"? no (end of list)
y.z in "! +local_domains"? yes (end of list)
calling fail_remote_domains router
-rda_interpret (string): :fail: unrouteable mail domain "$domain"
-expanded: :fail: unrouteable mail domain "y.z"
+rda_interpret (string): ':fail: unrouteable mail domain "$domain"'
+expanded: ':fail: unrouteable mail domain "y.z"'
file is not a filter file
parse_forward_list: :fail: unrouteable mail domain "y.z"
extract item: :fail: unrouteable mail domain "y.z"
y.z in "test.ex : myhost.test.ex"? no (end of list)
y.z in "! +local_domains"? yes (end of list)
calling fail_remote_domains router
-rda_interpret (string): :fail: unrouteable mail domain "$domain"
-expanded: :fail: unrouteable mail domain "y.z"
+rda_interpret (string): ':fail: unrouteable mail domain "$domain"'
+expanded: ':fail: unrouteable mail domain "y.z"'
file is not a filter file
parse_forward_list: :fail: unrouteable mail domain "y.z"
extract item: :fail: unrouteable mail domain "y.z"
smart.domain in "test.ex : myhost.test.ex"? no (end of list)
smart.domain in "! +local_domains"? yes (end of list)
calling fail_remote_domains router
-rda_interpret (string): :fail: unrouteable mail domain "$domain"
-expanded: :fail: unrouteable mail domain "smart.domain"
+rda_interpret (string): ':fail: unrouteable mail domain "$domain"'
+expanded: ':fail: unrouteable mail domain "smart.domain"'
file is not a filter file
parse_forward_list: :fail: unrouteable mail domain "smart.domain"
extract item: :fail: unrouteable mail domain "smart.domain"
--------> l1 router <--------
local_part=x domain=test.ex
calling l1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1"
search_find: file="TESTSUITE/aux-fixed/0123.aliases1"
key="x" partial=-1 affix=NULL starflags=0
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases1
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l1 router declined for x@test.ex
--------> l2 router <--------
local_part=x domain=test.ex
calling l2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2"
search_find: file="TESTSUITE/aux-fixed/0123.aliases2"
key="x" partial=-1 affix=NULL starflags=0
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases2
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l2 router declined for x@test.ex
--------> l3 router <--------
local_part=x domain=test.ex
calling l3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3"
Too many lookup files open
closing 0TESTSUITE/aux-fixed/0123.aliases1
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases3
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l3 router declined for x@test.ex
--------> c1 router <--------
local_part=x domain=test.ex
calling c1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4"
Too many lookup files open
closing 0TESTSUITE/aux-fixed/0123.aliases2
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases4
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c1 router declined for x@test.ex
--------> c2 router <--------
local_part=x domain=test.ex
calling c2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5"
Too many lookup files open
closing 0TESTSUITE/aux-fixed/0123.aliases3
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases5
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c2 router declined for x@test.ex
--------> c3 router <--------
local_part=x domain=test.ex
calling c3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6"
Too many lookup files open
closing 0TESTSUITE/aux-fixed/0123.aliases4
file lookup required for x
in TESTSUITE/aux-fixed/0123.aliases6
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c3 router declined for x@test.ex
--------> l1 router <--------
local_part=y domain=test.ex
calling l1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases1
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l1 router declined for y@test.ex
--------> l2 router <--------
local_part=y domain=test.ex
calling l2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases2
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l2 router declined for y@test.ex
--------> l3 router <--------
local_part=y domain=test.ex
calling l3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases3
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l3 router declined for y@test.ex
--------> c1 router <--------
local_part=y domain=test.ex
calling c1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases4
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c1 router declined for y@test.ex
--------> c2 router <--------
local_part=y domain=test.ex
calling c2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases5
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c2 router declined for y@test.ex
--------> c3 router <--------
local_part=y domain=test.ex
calling c3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6"
cached closed
Too many lookup files open
file lookup required for y
in TESTSUITE/aux-fixed/0123.aliases6
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c3 router declined for y@test.ex
--------> l1 router <--------
local_part=z domain=test.ex
calling l1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases1
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l1 router declined for z@test.ex
--------> l2 router <--------
local_part=z domain=test.ex
calling l2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases2
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l2 router declined for z@test.ex
--------> l3 router <--------
local_part=z domain=test.ex
calling l3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases3
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
l3 router declined for z@test.ex
--------> c1 router <--------
local_part=z domain=test.ex
calling c1 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases4
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c1 router declined for z@test.ex
--------> c2 router <--------
local_part=z domain=test.ex
calling c2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases5
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c2 router declined for z@test.ex
--------> c3 router <--------
local_part=z domain=test.ex
calling c3 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}'
search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6"
cached closed
Too many lookup files open
file lookup required for z
in TESTSUITE/aux-fixed/0123.aliases6
lookup failed
-expanded:
+expanded: ''
file is not a filter file
parse_forward_list:
c3 router declined for z@test.ex
fun.1 in "+funny_domains"? yes (matched "+funny_domains")
user1@fun.1 in "user1@+funny_domains"? yes (matched "user1@+funny_domains")
calling rr1 router
-rda_interpret (string): :fail: matched *@+funny_domains
-expanded: :fail: matched *@+funny_domains
+rda_interpret (string): ':fail: matched *@+funny_domains'
+expanded: ':fail: matched *@+funny_domains'
file is not a filter file
parse_forward_list: :fail: matched *@+funny_domains
extract item: :fail: matched *@+funny_domains
--------> r1 router <--------
local_part=/a/b/c domain=myhost.test.ex
calling r1 router
-rda_interpret (string): $local_part
-expanded: /a/b/c
+rda_interpret (string): '$local_part'
+expanded: '/a/b/c'
file is not a filter file
parse_forward_list: /a/b/c
extract item: /a/b/c
--------> r1 router <--------
local_part=/x/y/z domain=myhost.test.ex
calling r1 router
-rda_interpret (string): $local_part
-expanded: /x/y/z
+rda_interpret (string): '$local_part'
+expanded: '/x/y/z'
file is not a filter file
parse_forward_list: /x/y/z
extract item: /x/y/z
--------> r2 router <--------
local_part=cms domain=test.ex
calling r2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}
-expanded: unknown@recurse.test.ex, defer
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}'
+expanded: 'unknown@recurse.test.ex, defer'
file is not a filter file
parse_forward_list: unknown@recurse.test.ex, defer
extract item: unknown@recurse.test.ex
--------> r2 router <--------
local_part=defer domain=test.ex
calling r2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}
-expanded: :defer: Forcibly deferred
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}'
+expanded: ':defer: Forcibly deferred'
file is not a filter file
parse_forward_list: :defer: Forcibly deferred
extract item: :defer: Forcibly deferred
--------> r2 router <--------
local_part=cms domain=test.ex
calling r2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}
-expanded: unknown@recurse.test.ex, defer
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}'
+expanded: 'unknown@recurse.test.ex, defer'
file is not a filter file
parse_forward_list: unknown@recurse.test.ex, defer
extract item: unknown@recurse.test.ex
--------> r2 router <--------
local_part=defer domain=test.ex
calling r2 router
-rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}
-expanded: :defer: Forcibly deferred
+rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}'
+expanded: ':defer: Forcibly deferred'
file is not a filter file
parse_forward_list: :defer: Forcibly deferred
extract item: :defer: Forcibly deferred
checking local_parts
kilos in "kilos"? yes (matched "kilos")
calling r3 router
-rda_interpret (string): $local_part@$domain
-expanded: kilos@recurse.test.ex.test.ex
+rda_interpret (string): '$local_part@$domain'
+expanded: 'kilos@recurse.test.ex.test.ex'
file is not a filter file
parse_forward_list: kilos@recurse.test.ex.test.ex
extract item: kilos@recurse.test.ex.test.ex
checking domains
processing address_data
calling r1 router
-rda_interpret (string): $local_part@$domain.test.ex
-expanded: kilos@thishost.test.ex
+rda_interpret (string): '$local_part@$domain.test.ex'
+expanded: 'kilos@thishost.test.ex'
file is not a filter file
parse_forward_list: kilos@thishost.test.ex
extract item: kilos@thishost.test.ex
checking domains
processing address_data
calling r1 router
-rda_interpret (string): $local_part@$domain.test.ex
-expanded: solik@otherhost.test.ex
+rda_interpret (string): '$local_part@$domain.test.ex'
+expanded: 'solik@otherhost.test.ex'
file is not a filter file
parse_forward_list: solik@otherhost.test.ex
extract item: solik@otherhost.test.ex
checking "condition" "${if eq{$address_data}{}{no}{yes}}"...
processing address_data
calling r3 router
-rda_interpret (string): $local_part@$original_domain.sub.test.ex
-expanded: solik@otherhost.sub.test.ex
+rda_interpret (string): '$local_part@$original_domain.sub.test.ex'
+expanded: 'solik@otherhost.sub.test.ex'
file is not a filter file
parse_forward_list: solik@otherhost.sub.test.ex
extract item: solik@otherhost.sub.test.ex
local_part=solik domain=otherhost.sub.test.ex
checking domains
calling r4 router
-rda_interpret (string): :fail:Can't route to $domain
-expanded: :fail:Can't route to otherhost.sub.test.ex
+rda_interpret (string): ':fail:Can't route to $domain'
+expanded: ':fail:Can't route to otherhost.sub.test.ex'
file is not a filter file
parse_forward_list: :fail:Can't route to otherhost.sub.test.ex
extract item: :fail:Can't route to otherhost.sub.test.ex
checking domains
processing address_data
calling r1 router
-rda_interpret (string): $local_part@$domain.test.ex
-expanded: xxx@ten-1.test.ex
+rda_interpret (string): '$local_part@$domain.test.ex'
+expanded: 'xxx@ten-1.test.ex'
file is not a filter file
parse_forward_list: xxx@ten-1.test.ex
extract item: xxx@ten-1.test.ex
checking domains
processing address_data
calling r1 router
-rda_interpret (string): $local_part@$domain.test.ex
-expanded: xxx@testsub.test.ex
+rda_interpret (string): '$local_part@$domain.test.ex'
+expanded: 'xxx@testsub.test.ex'
file is not a filter file
parse_forward_list: xxx@testsub.test.ex
extract item: xxx@testsub.test.ex
checking "condition" "${if eq{$address_data}{}{no}{yes}}"...
processing address_data
calling r3 router
-rda_interpret (string): $local_part@$original_domain.sub.test.ex
-expanded: xxx@testsub.sub.test.ex
+rda_interpret (string): '$local_part@$original_domain.sub.test.ex'
+expanded: 'xxx@testsub.sub.test.ex'
file is not a filter file
parse_forward_list: xxx@testsub.sub.test.ex
extract item: xxx@testsub.sub.test.ex
uid=EXIM_UID gid=CALLER_GID pid=pppp
t1 transport entered
direct command:
- argv[0] = TESTSUITE/bin/iefbr14
+ argv[0] = 'TESTSUITE/bin/iefbr14'
Writing message to pipe
writing data block fd=dddd size=sss timeout=3600
writing error 32: Broken pipe
local_part=cccc domain=myhost.test.ex
checking local_parts
calling cccc_redirect router
-rda_interpret (string): cccc@$domain, defer_cccc@$domain
-expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
+rda_interpret (string): 'cccc@$domain, defer_cccc@$domain'
+expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
extract item: cccc@myhost.test.ex
checking local_parts
checking "condition" "${if first_delivery{yes}{no}}"...
calling bbbb router
-rda_interpret (string): bbbb@$domain, defer_bbbb@$domain
-expanded: bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex
+rda_interpret (string): 'bbbb@$domain, defer_bbbb@$domain'
+expanded: 'bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex'
file is not a filter file
parse_forward_list: bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex
extract item: bbbb@myhost.test.ex
checking local_parts
checking "condition" "${if first_delivery{yes}{no}}"...
calling unseen_aaaa router
-rda_interpret (string): defer_aaaa@$domain
-expanded: defer_aaaa@myhost.test.ex
+rda_interpret (string): 'defer_aaaa@$domain'
+expanded: 'defer_aaaa@myhost.test.ex'
file is not a filter file
parse_forward_list: defer_aaaa@myhost.test.ex
extract item: defer_aaaa@myhost.test.ex
local_part=defer_cccc domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=defer_bbbb domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=defer_aaaa domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
checking local_parts
checking "condition" "${if first_delivery{no}{yes}}"...
calling cccc_2nd_time router
-rda_interpret (string): $local_part@$domain
-expanded: cccc@myhost.test.ex
+rda_interpret (string): '$local_part@$domain'
+expanded: 'cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: cccc@myhost.test.ex
extract item: cccc@myhost.test.ex
local_part=cccc domain=myhost.test.ex
checking local_parts
calling cccc_redirect router
-rda_interpret (string): cccc@$domain, defer_cccc@$domain
-expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
+rda_interpret (string): 'cccc@$domain, defer_cccc@$domain'
+expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
extract item: cccc@myhost.test.ex
local_part=defer_cccc domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
checking local_parts
checking "condition" "${if first_delivery{no}{yes}}"...
calling cccc_2nd_time router
-rda_interpret (string): $local_part@$domain
-expanded: cccc@myhost.test.ex
+rda_interpret (string): '$local_part@$domain'
+expanded: 'cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: cccc@myhost.test.ex
extract item: cccc@myhost.test.ex
local_part=cccc domain=myhost.test.ex
checking local_parts
calling cccc_redirect router
-rda_interpret (string): cccc@$domain, defer_cccc@$domain
-expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
+rda_interpret (string): 'cccc@$domain, defer_cccc@$domain'
+expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex
extract item: cccc@myhost.test.ex
local_part=defer_cccc domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=aaaa domain=myhost.test.ex
checking local_parts
calling aaaa router
-rda_interpret (string): # Exim filter
-deliver defer_aaaa@$domain
-save TESTSUITE/test-mail/file
-pipe "/bin/sh -c exit"
-mail subject autoreply
-text "This is an autoreply"
-expanded: # Exim filter
+rda_interpret (string): '# Exim filter\ndeliver defer_aaaa@$domain\nsave TESTSUITE/test-mail/file\npipe "/bin/sh -c exit"\nmail subject autoreply\ntext "This is an autoreply"'
+expanded: '# Exim filter
deliver defer_aaaa@myhost.test.ex
save TESTSUITE/test-mail/file
pipe "/bin/sh -c exit"
mail subject autoreply
-text "This is an autoreply"
+text "This is an autoreply"'
data is an Exim filter program
Filter: start of processing
Filter: end of processing
local_part=defer_aaaa domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=CALLER domain=myhost.test.ex
checking senders
calling bounce router
-rda_interpret (string): :blackhole:
-expanded: :blackhole:
+rda_interpret (string): ':blackhole:'
+expanded: ':blackhole:'
file is not a filter file
parse_forward_list: :blackhole:
extract item: :blackhole:
checking local_parts
checking "condition" "${if first_delivery{no}{yes}}"...
calling aaaa_2nd_time router
-rda_interpret (string): aaaa@$domain
-expanded: aaaa@myhost.test.ex
+rda_interpret (string): 'aaaa@$domain'
+expanded: 'aaaa@myhost.test.ex'
file is not a filter file
parse_forward_list: aaaa@myhost.test.ex
extract item: aaaa@myhost.test.ex
local_part=aaaa domain=myhost.test.ex
checking local_parts
calling aaaa router
-rda_interpret (string): # Exim filter
-deliver defer_aaaa@$domain
-save TESTSUITE/test-mail/file
-pipe "/bin/sh -c exit"
-mail subject autoreply
-text "This is an autoreply"
-expanded: # Exim filter
+rda_interpret (string): '# Exim filter\ndeliver defer_aaaa@$domain\nsave TESTSUITE/test-mail/file\npipe "/bin/sh -c exit"\nmail subject autoreply\ntext "This is an autoreply"'
+expanded: '# Exim filter
deliver defer_aaaa@myhost.test.ex
save TESTSUITE/test-mail/file
pipe "/bin/sh -c exit"
mail subject autoreply
-text "This is an autoreply"
+text "This is an autoreply"'
data is an Exim filter program
Filter: start of processing
Filter: end of processing
local_part=defer_aaaa domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=aaaa domain=myhost.test.ex
checking local_parts
calling aaaa_redirect router
-rda_interpret (string): bbbb@$domain, cccc@$domain
-expanded: bbbb@myhost.test.ex, cccc@myhost.test.ex
+rda_interpret (string): 'bbbb@$domain, cccc@$domain'
+expanded: 'bbbb@myhost.test.ex, cccc@myhost.test.ex'
file is not a filter file
parse_forward_list: bbbb@myhost.test.ex, cccc@myhost.test.ex
extract item: bbbb@myhost.test.ex
local_part=defer domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=defer domain=myhost.test.ex
checking local_parts
calling defer router
-rda_interpret (string): :defer: forced defer
-expanded: :defer: forced defer
+rda_interpret (string): ':defer: forced defer'
+expanded: ':defer: forced defer'
file is not a filter file
parse_forward_list: :defer: forced defer
extract item: :defer: forced defer
local_part=bbbb domain=myhost.test.ex
checking local_parts
calling r2 router
-rda_interpret (file): TESTSUITE/non-exist/$local_part
-expanded: TESTSUITE/non-exist/bbbb
+rda_interpret (file): 'TESTSUITE/non-exist/$local_part'
+expanded: 'TESTSUITE/non-exist/bbbb'
TESTSUITE/non-exist/bbbb does not exist
ignore_enotdir set => skip checking parent directory
r2 router declined for bbbb@myhost.test.ex
local_part=aaaa domain=myhost.test.ex
checking local_parts
calling r1 router
-rda_interpret (file): TESTSUITE/non-exist/$local_part
-expanded: TESTSUITE/non-exist/aaaa
+rda_interpret (file): 'TESTSUITE/non-exist/$local_part'
+expanded: 'TESTSUITE/non-exist/aaaa'
TESTSUITE/non-exist/aaaa does not exist
checking parent directory
stat(TESTSUITE/non-exist/.)=-1
admin user
dropping to exim gid; retaining priv uid
running system filter
-Filtering did not set up a significant delivery.
-Normal delivery will occur.
+ Filtering did not set up a significant delivery.
+ Normal delivery will occur.
system filter returned 1
LOG: MAIN
** userx@test.ex R=r1: forced fail
admin user
dropping to exim gid; retaining priv uid
running system filter
-Filtering did not set up a significant delivery.
-Normal delivery will occur.
+ Filtering did not set up a significant delivery.
+ Normal delivery will occur.
system filter returned 1
LOG: MAIN
** CALLER@test.ex: Unrouteable address
log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100
SMTP>> 250 OK
SMTP<< rcpt to:<1@b>
-read ACL from file TESTSUITE/aux-fixed/0386.acl1
-processing "accept" (TESTSUITE/test-config 32)
-check hosts = :
+using ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 18)
+check local_parts = 1
+1 in "1"? yes (matched "1")
+check acl = TESTSUITE/aux-fixed/0386.acl1
+ read ACL from file TESTSUITE/aux-fixed/0386.acl1
+ processing "accept" (TESTSUITE/test-config 43)
+ check hosts = :
host in ":"? no (end of list)
-accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "deny" (TESTSUITE/test-config 32)
-check local_parts = ^.*[@%!/|]
+ accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "deny" (TESTSUITE/test-config 43)
+ check local_parts = ^.*[@%!/|]
1 in "^.*[@%!/|]"? no (end of list)
-deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "require" (TESTSUITE/test-config 32)
-l_message: Invalid sender
- message: Couldn't verify the sender
-check verify = sender/defer_ok
+ deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "require" (TESTSUITE/test-config 43)
+ l_message: Invalid sender
+ message: Couldn't verify the sender
+ check verify = sender/defer_ok
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Verifying x@y
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
routed by r1 router
envelope to: x@y
transport: t1
------------ end verify ------------
-sender x@y verified ok
-require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "deny" (TESTSUITE/test-config 32)
- message: No such user here
-deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
-end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY
+ ----------- end verify ------------
+ sender x@y verified ok
+ require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "deny" (TESTSUITE/test-config 43)
+ message: No such user here
+ deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY
+accept: condition test failed in ACL "chk_rcpt"
+accept: endpass encountered - denying access
SMTP>> 550 No such user here
LOG: MAIN REJECT
H=[V4NET.9.8.7] F=<x@y> rejected RCPT <1@b>: No such user here
log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100
SMTP>> 250 OK
SMTP<< rcpt to:<1@b>
-using ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "accept" (TESTSUITE/test-config 32)
-check hosts = :
+using ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 18)
+check local_parts = 1
+1 in "1"? yes (matched "1")
+check acl = TESTSUITE/aux-fixed/0386.acl1
+ using ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "accept" (TESTSUITE/test-config 43)
+ check hosts = :
host in ":"? no (end of list)
-accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "deny" (TESTSUITE/test-config 32)
-check local_parts = ^.*[@%!/|]
+ accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "deny" (TESTSUITE/test-config 43)
+ check local_parts = ^.*[@%!/|]
1 in "^.*[@%!/|]"? no (end of list)
-deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "require" (TESTSUITE/test-config 32)
-l_message: Invalid sender
- message: Couldn't verify the sender
-check verify = sender/defer_ok
+ deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "require" (TESTSUITE/test-config 43)
+ l_message: Invalid sender
+ message: Couldn't verify the sender
+ check verify = sender/defer_ok
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Verifying x@y
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
routed by r1 router
envelope to: x@y
transport: t1
------------ end verify ------------
-sender x@y verified ok
-require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
-processing "deny" (TESTSUITE/test-config 32)
- message: No such user here
-deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
-end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY
+ ----------- end verify ------------
+ sender x@y verified ok
+ require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ processing "deny" (TESTSUITE/test-config 43)
+ message: No such user here
+ deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1"
+ end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY
+accept: condition test failed in ACL "chk_rcpt"
+accept: endpass encountered - denying access
SMTP>> 550 No such user here
LOG: MAIN REJECT
H=[V4NET.9.8.7] F=<x@y> rejected RCPT <1@b>: No such user here
log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100
SMTP>> 250 OK
SMTP<< rcpt to:<2@b>
-read ACL from file TESTSUITE/aux-fixed/0386.acl2
-processing "warn" (TESTSUITE/test-config 32)
- message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text
-l_message: found in $dnslist_domain: $dnslist_text
-check dnslists = rbl.test.ex
+using ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 18)
+check local_parts = 1
+2 in "1"? no (end of list)
+accept: condition test failed in ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 21)
+check local_parts = 2
+2 in "2"? yes (matched "2")
+check acl = TESTSUITE/aux-fixed/0386.acl2
+ read ACL from file TESTSUITE/aux-fixed/0386.acl2
+ processing "warn" (TESTSUITE/test-config 43)
+ message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text
+ l_message: found in $dnslist_domain: $dnslist_text
+ check dnslists = rbl.test.ex
DNS list check: rbl.test.ex
new DNS lookup for 13.12.11.V4NET.rbl.test.ex
DNS lookup of 13.12.11.V4NET.rbl.test.ex (A) using fakens
DNS lookup of 13.12.11.V4NET.rbl.test.ex (TXT) using fakens
DNS lookup of 13.12.11.V4NET.rbl.test.ex (TXT) succeeded
=> that means V4NET.11.12.13 is listed at rbl.test.ex
-warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
+ warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
LOG: MAIN
H=[V4NET.11.12.13] U=CALLER Warning: found in rbl.test.ex: This is a test blacklisting message
created log directory TESTSUITE/spool/log
-processing "accept" (TESTSUITE/test-config 32)
-accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
-end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT
+ processing "accept" (TESTSUITE/test-config 43)
+ accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
+ end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT
+accept: condition test succeeded in ACL "chk_rcpt"
+end of ACL "chk_rcpt": ACCEPT
SMTP>> 250 Accepted
DSN: orcpt: NULL flags: 0
SMTP<< data
log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100
SMTP>> 250 OK
SMTP<< rcpt to:<2@b>
-using ACL "TESTSUITE/aux-fixed/0386.acl2"
-processing "warn" (TESTSUITE/test-config 32)
- message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text
-l_message: found in $dnslist_domain: $dnslist_text
-check dnslists = rbl.test.ex
+using ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 18)
+check local_parts = 1
+2 in "1"? no (end of list)
+accept: condition test failed in ACL "chk_rcpt"
+processing "accept" (TESTSUITE/test-config 21)
+check local_parts = 2
+2 in "2"? yes (matched "2")
+check acl = TESTSUITE/aux-fixed/0386.acl2
+ using ACL "TESTSUITE/aux-fixed/0386.acl2"
+ processing "warn" (TESTSUITE/test-config 43)
+ message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text
+ l_message: found in $dnslist_domain: $dnslist_text
+ check dnslists = rbl.test.ex
DNS list check: rbl.test.ex
using result of previous DNS lookup
DNS lookup for 13.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2)
=> that means V4NET.11.12.13 is listed at rbl.test.ex
-warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
+ warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
LOG: MAIN
H=[V4NET.11.12.13] U=CALLER Warning: found in rbl.test.ex: This is a test blacklisting message
-processing "accept" (TESTSUITE/test-config 32)
-accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
-end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT
+ processing "accept" (TESTSUITE/test-config 43)
+ accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2"
+ end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT
+accept: condition test succeeded in ACL "chk_rcpt"
+end of ACL "chk_rcpt": ACCEPT
SMTP>> 250 Accepted
DSN: orcpt: NULL flags: 0
SMTP<< data
address match test: subject= pattern=
in ":"? yes (matched "")
calling r0 router
-rda_interpret (string): :blackhole:
-expanded: :blackhole:
+rda_interpret (string): ':blackhole:'
+expanded: ':blackhole:'
file is not a filter file
parse_forward_list: :blackhole:
extract item: :blackhole:
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>
--------> userx@test.ex <--------
direct command:
- argv[0] = /bin/cat
+ argv[0] = '/bin/cat'
direct command after expansion:
argv[0] = /bin/cat
appendfile transport entered
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>
--------> userx@test.ex <--------
direct command:
- argv[0] = ${if={1}{1}{}{}}
+ argv[0] = '${if={1}{1}{}{}}'
direct command after expansion:
argv[0] =
appendfile transport entered
--------> r1 router <--------
local_part=x domain=y
calling r1 router
-rda_interpret (string):
-expanded:
+rda_interpret (string): ''
+expanded: ''
file is not a filter file
parse_forward_list:
r1 router declined for x@y
--------> r2 router <--------
local_part=x domain=y
calling r2 router
-rda_interpret (string):
-expanded:
+rda_interpret (string): ''
+expanded: ''
file is not a filter file
parse_forward_list:
r2 router declined for x@y
--------> r3 router <--------
local_part=x domain=y
calling r3 router
-rda_interpret (string):
-expanded:
+rda_interpret (string): ''
+expanded: ''
file is not a filter file
parse_forward_list:
r3 router declined for x@y
╭considering: /non-exist/$domain
├──expanding: /non-exist/$domain
╰─────result: /non-exist/test.ex
+ ╰──(tainted)
calling r5 router
-rda_interpret (string): TESTSUITE/test-mail/junk
-expanded: TESTSUITE/test-mail/junk
+rda_interpret (string): 'TESTSUITE/test-mail/junk'
+expanded: 'TESTSUITE/test-mail/junk'
file is not a filter file
parse_forward_list: TESTSUITE/test-mail/junk
extract item: TESTSUITE/test-mail/junk
╭considering: /non-exist/$local_part
├──expanding: /non-exist/$local_part
╰─────result: /non-exist/CALLER
+ ╰──(tainted)
calling r4 router
-rda_interpret (string): TESTSUITE/test-mail/junk
-expanded: TESTSUITE/test-mail/junk
+rda_interpret (string): 'TESTSUITE/test-mail/junk'
+expanded: 'TESTSUITE/test-mail/junk'
file is not a filter file
parse_forward_list: TESTSUITE/test-mail/junk
extract item: TESTSUITE/test-mail/junk
╭considering: /non-exist/$domain
├──expanding: /non-exist/$domain
╰─────result: /non-exist/test.ex
+ ╰──(tainted)
calling r3 router
r3 router called for userz@test.ex
domain = test.ex
╭considering: /non-exist/$domain
├──expanding: /non-exist/$domain
╰─────result: /non-exist/test.ex
+ ╰──(tainted)
calling r2 router
r2 router called for usery@test.ex
domain = test.ex
╭considering: /non-exist/$local_part
├──expanding: /non-exist/$local_part
╰─────result: /non-exist/CALLER
+ ╰──(tainted)
calling r1 router
r1 router called for CALLER@test.ex
domain = test.ex
╭considering: /non-exist/$local_part
├──expanding: /non-exist/$local_part
╰─────result: /non-exist/usery
+ ╰──(tainted)
search_tidyup called
changed uid/gid: local delivery to TESTSUITE/test-mail/junk <TESTSUITE/test-mail/junk> transport=ft1
uid=CALLER_UID gid=CALLER_GID pid=pppp
├──expanding: $return_path
╰─────result: CALLER@test.ex
+ ╰──(tainted)
╭───scanning: MAILER-DAEMON}} ${tod_bsdinbox}
├──expanding: MAILER-DAEMON
╰─────result: From CALLER@test.ex Tue Mar 02 09:44:33 1999
+ ╰──(tainted)
writing data block fd=dddd size=sss timeout=0
cannot use sendfile for body: spoolfile not wireformat
writing data block fd=dddd size=sss timeout=0
├──expanding: $return_path
╰─────result: CALLER@test.ex
+ ╰──(tainted)
╭───scanning: MAILER-DAEMON}} ${tod_bsdinbox}
├──expanding: MAILER-DAEMON
╰─────result: From CALLER@test.ex Tue Mar 02 09:44:33 1999
+ ╰──(tainted)
writing data block fd=dddd size=sss timeout=0
cannot use sendfile for body: spoolfile not wireformat
writing data block fd=dddd size=sss timeout=0
╭considering: /non-exist/$local_part
├──expanding: /non-exist/$local_part
╰─────result: /non-exist/usery
+ ╰──(tainted)
search_tidyup called
changed uid/gid: local delivery to usery <usery@test.ex> transport=t1
uid=CALLER_UID gid=CALLER_GID pid=pppp
╭considering: /$local_part
├──expanding: /$local_part
╰─────result: /userz
+ ╰──(tainted)
search_tidyup called
changed uid/gid: local delivery to userz <userz@test.ex> transport=t2
uid=CALLER_UID gid=CALLER_GID pid=pppp
+++home=/usr
processing address_data
calling r1 router
-rda_interpret (string): TESTSUITE/test-mail/junk
-expanded: TESTSUITE/test-mail/junk
+rda_interpret (string): 'TESTSUITE/test-mail/junk'
+expanded: 'TESTSUITE/test-mail/junk'
file is not a filter file
parse_forward_list: TESTSUITE/test-mail/junk
extract item: TESTSUITE/test-mail/junk
--------> r2 router <--------
local_part=userx domain=test.ex
calling r2 router
-rda_interpret (string): #Exim filter\nmail text rhubarb\nseen finish
-expanded: #Exim filter
+rda_interpret (string): '#Exim filter\nmail text rhubarb\nseen finish'
+expanded: '#Exim filter
mail text rhubarb
-seen finish
+seen finish'
search_tidyup called
changed uid/gid: r2 router (recipient is userx@test.ex)
uid=CALLER_UID gid=CALLER_GID pid=pppp
local_part=CALLER domain=test.ex
checking senders
calling r0 router
-rda_interpret (string): :blackhole:
-expanded: :blackhole:
+rda_interpret (string): ':blackhole:'
+expanded: ':blackhole:'
file is not a filter file
parse_forward_list: :blackhole:
extract item: :blackhole:
cached lookup data = data for domain1
domain1 in "+special_domains"? yes (matched "+special_domains" - cached)
calling r1 router
-rda_interpret (string): $local_part@xxx.$domain
-expanded: abc@xxx.domain1
+rda_interpret (string): '$local_part@xxx.$domain'
+expanded: 'abc@xxx.domain1'
file is not a filter file
parse_forward_list: abc@xxx.domain1
extract item: abc@xxx.domain1
cached lookup data = data for domain1
domain1 in "+special_domains"? yes (matched "+special_domains" - cached)
calling r1 router
-rda_interpret (string): $local_part@xxx.$domain
-expanded: abc@xxx.domain1
+rda_interpret (string): '$local_part@xxx.$domain'
+expanded: 'abc@xxx.domain1'
file is not a filter file
parse_forward_list: abc@xxx.domain1
extract item: abc@xxx.domain1
╭considering: $domain
├──expanding: $domain
╰─────result: domain1.ex
+ ╰──(tainted)
LOG: MAIN
== userx@domain1.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
LOG: MAIN
╭considering: $sender_helo_name}{+dlist}}
├──expanding: $sender_helo_name
╰─────result: ehlo.domain
+ ╰──(tainted)
╭considering: +dlist}}
├──expanding: +dlist
╰─────result: +dlist
╭considering: $domain
├──expanding: $domain
╰─────result: ehlo.domain
+ ╰──(tainted)
├──condition: match_domain {$sender_helo_name}{+dlist}
├─────result: true
├──expanding: ${if match_domain {$sender_helo_name}{+dlist}}
╭considering: domain=$domain/sender_domain=$sender_address_domain
├──expanding: domain=$domain/sender_domain=$sender_address_domain
╰─────result: domain=/sender_domain=sender.domain
+ ╰──(tainted)
╭considering: domain=$domain/sender_domain=$sender_address_domain
├──expanding: domain=$domain/sender_domain=$sender_address_domain
╰─────result: domain=recipient.domain/sender_domain=sender.domain
+ ╰──(tainted)
╭considering: domain=$domain/sender_domain=$sender_address_domain
├──expanding: domain=$domain/sender_domain=$sender_address_domain
╰─────result: domain=recipient.domain/sender_domain=sender.domain
+ ╰──(tainted)
LOG: smtp_connection MAIN
SMTP connection from CALLER closed by QUIT
>>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>>
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found ""
+1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found ""
******** SERVER ********
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected
Exim version x.yz ....
configuration file is TESTSUITE/test-config
admin user
--------> r1 router <--------
local_part=userx domain=test.ex
calling r1 router
-rda_interpret (string): # Exim filter
-save TESTSUITE/test-mail
-expanded: # Exim filter
-save TESTSUITE/test-mail
+rda_interpret (string): '# Exim filter\nsave TESTSUITE/test-mail'
+expanded: '# Exim filter
+save TESTSUITE/test-mail'
search_tidyup called
changed uid/gid: r1 router (recipient is userx@test.ex)
uid=CALLER_UID gid=CALLER_GID pid=pppp
q router called for "REDIRECT postmaster@test.ex"@some.host: domain = some.host
requires uid=EXIM_UID gid=EXIM_GID current_directory=/
command wrote: REDIRECT postmaster@test.ex
-rda_interpret (string): postmaster@test.ex
-expanded: postmaster@test.ex
+rda_interpret (string): 'postmaster@test.ex'
+expanded: 'postmaster@test.ex'
file is not a filter file
parse_forward_list: postmaster@test.ex
extract item: postmaster@test.ex
╭considering: $local_part
├──expanding: $local_part
╰─────result: userx
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: userx
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: userx
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╭considering: $address_data}{userz}{*}{:}}
├──expanding: $address_data
╰─────result: userx
+ ╰──(tainted)
╭considering: userz}{*}{:}}
├──expanding: userz
╰─────result: userz
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for userx@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaX-0005vi-00
for userx@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: usery
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for usery@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaZ-0005vi-00
for usery@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: usery
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for usery@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbB-0005vi-00
for usery@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
╭considering: $local_part
├──expanding: $local_part
╰─────result: userx
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: userx
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: userx
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╭considering: $address_data}{userz}{*}{:}}
├──expanding: $address_data
╰─────result: userx
+ ╰──(tainted)
╭considering: userz}{*}{:}}
├──expanding: userz
╰─────result: userz
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for userx@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaX-0005vi-00
for userx@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: usery
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for usery@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmaZ-0005vi-00
for usery@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
----------- end verify ------------
accept: condition test succeeded in ACL "cutthrough"
╭considering: $local_part
├──expanding: $local_part
╰─────result: usery
+ ╰──(tainted)
domain.com in "*"? yes (matched "*")
Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected
╭considering: $primary_hostname
╭considering: $address_data}{usery}{*}{:}}
├──expanding: $address_data
╰─────result: usery
+ ╰──(tainted)
╭considering: usery}{*}{:}}
├──expanding: usery
╰─────result: usery
╰─────result: (helo=myhost.test.ex)
+ ╰──(tainted)
├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}
╰─────result: from CALLER (helo=myhost.test.ex)
+ ╰──(tainted)
├──condition: def:received_protocol
├─────result: true
╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
for $received_for
╰─────result:
for usery@domain.com
+ ╰──(tainted)
├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
}{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
}}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
(envelope-from <CALLER@myhost.test.ex>)
id 10HmbB-0005vi-00
for usery@domain.com
+ ╰──(tainted)
----------- start cutthrough headers send -----------
----------- done cutthrough headers send ------------
╭considering: ${tod_full}
> escape: B7·F2ò
>
> primary_hostname: myhost.test.ex
+> sender_address: sndr@dom
> match: cdab
> Failed: "if" failed and "fail" requested
> yes
<notsubmit@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<a@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<notsubmit@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<a@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<a@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<a@y>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-interface_address 127.0.0.1.1225
-received_protocol esmtp
<username@myhost.test.ex>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-host_auth au1
-interface_address 127.0.0.1.1225
-received_protocol esmtpa
-body_linecount 0
-max_received_linelength 0
--auth_id username
+--auth_id username
-deliver_firsttime
XX
1
<>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-host_auth au1
-interface_address 127.0.0.1.1225
-received_protocol esmtpa
-body_linecount 0
-max_received_linelength 0
--auth_id username
+--auth_id username
-deliver_firsttime
XX
1
<>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-host_auth au1
-interface_address 127.0.0.1.1225
-received_protocol esmtpa
-body_linecount 0
-max_received_linelength 0
--auth_id username
+--auth_id username
-deliver_firsttime
XX
1
<>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-host_auth au1
-interface_address 127.0.0.1.1225
-received_protocol esmtpa
-body_linecount 0
-max_received_linelength 0
--auth_id username@auth.id.domain
+--auth_id username@auth.id.domain
-deliver_firsttime
XX
1
<>
ddddddddd 0
-received_time_usec .uuuuuu
--helo_name rhu.barb
+--helo_name rhu.barb
-host_address 127.0.0.1.9999
-host_auth au1
-interface_address 127.0.0.1.1225
-received_protocol esmtpa
-body_linecount 0
-max_received_linelength 15
--auth_id username@auth.id.domain
+--auth_id username@auth.id.domain
-deliver_firsttime
XX
1