* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* This module contains code for extracting addresses from a forwarding list
match_tag(const uschar *s, const uschar *tag)
{
for (; *tag != 0; s++, tag++)
- {
if (*tag == ' ')
{
while (*s == ' ' || *s == '\t') s++;
s--;
}
- else if (tolower(*s) != tolower(*tag)) break;
- }
+ else
+ if (tolower(*s) != tolower(*tag)) break;
+
return (*tag == 0);
}
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);
+ ALARM(30);
+ 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(0);
+ 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;
}
if (rdata->pw != NULL && statbuf.st_uid == rdata->pw->pw_uid)
uid_ok = TRUE;
else if (rdata->owners != NULL)
- {
- int i;
- for (i = 1; i <= (int)(rdata->owners[0]); i++)
+ for (int i = 1; i <= (int)(rdata->owners[0]); i++)
if (rdata->owners[i] == statbuf.st_uid) { uid_ok = TRUE; break; }
- }
}
if (!gid_ok)
if (rdata->pw != NULL && statbuf.st_gid == rdata->pw->pw_gid)
gid_ok = TRUE;
else if (rdata->owngroups != NULL)
- {
- int i;
- for (i = 1; i <= (int)(rdata->owngroups[0]); i++)
+ for (int i = 1; i <= (int)(rdata->owngroups[0]); i++)
if (rdata->owngroups[i] == statbuf.st_gid) { gid_ok = TRUE; break; }
- }
}
if (!uid_ok || !gid_ok)
/* 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)
{
}
else data = rdata->string;
-*filtertype = system_filtering? FILTER_EXIM : rda_is_filter(data);
+*filtertype = f.system_filtering ? FILTER_EXIM : rda_is_filter(data);
/* Filter interpretation is done by a general function that is also called from
the filter testing option (-bf). There are two versions: one for Exim filtering
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 */
* Write string down pipe *
*************************************************/
-/* This function is used for tranferring a string down a pipe between
+/* This function is used for transferring a string down a pipe between
processes. If the pointer is NULL, a length of zero is written.
Arguments:
fd the pipe
s the string
-Returns: nothing
+Returns: -1 on error, else 0
*/
-static void
-rda_write_string(int fd, uschar *s)
+static int
+rda_write_string(int fd, const uschar *s)
{
int len = (s == NULL)? 0 : Ustrlen(s) + 1;
-(void)write(fd, &len, sizeof(int));
-if (s != NULL) (void)write(fd, s, len);
+return ( write(fd, &len, sizeof(int)) != sizeof(int)
+ || (s != NULL && write(fd, s, len) != len)
+ )
+ ? -1 : 0;
}
int len;
if (read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
-if (len == 0) *sp = NULL; else
- {
- *sp = store_get(len);
- if (read(fd, *sp, len) != len) return FALSE;
- }
+if (len == 0)
+ *sp = NULL;
+else
+ /* We know we have enough memory so disable the error on "len" */
+ /* coverity[tainted_data] */
+ /* We trust the data source, so untainted */
+ if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
return TRUE;
}
/* This function is passed a forward list string (unexpanded) or the name of a
file (unexpanded) whose contents are the forwarding list. The list may in fact
be a filter program if it starts with "#Exim filter" or "#Sieve filter". Other
-types of filter, with different inital tag strings, may be introduced in due
+types of filter, with different initial tag strings, may be introduced in due
course.
The job of the function is to process the forwarding list or filter. It is
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 (expand_string_forcedfail) return FF_NOTDELIVERED;
+ if (f.expand_string_forcedfail) return FF_NOTDELIVERED;
*error = string_sprintf("failed to expand \"%s\": %s", rdata->string,
expand_string_message);
return FF_ERROR;
}
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] != '/')
{
{
DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
"root or exim in this process)\n");
- log_write_selector &= ~L_address_rewrite;
+ BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
}
/* Now do the business */
/* Pass back whether it was a filter, and the return code and any overall
error text via the pipe. */
- (void)write(fd, filtertype, sizeof(int));
- (void)write(fd, &yield, sizeof(int));
- rda_write_string(fd, *error);
+ if ( write(fd, filtertype, sizeof(int)) != sizeof(int)
+ || write(fd, &yield, sizeof(int)) != sizeof(int)
+ || rda_write_string(fd, *error) != 0
+ )
+ goto bad;
/* Pass back the contents of any syntax error blocks if we have a pointer */
- if (eblockp != NULL)
+ if (eblockp)
{
- error_block *ep;
- for (ep = *eblockp; ep != NULL; ep = ep->next)
- {
- rda_write_string(fd, ep->text1);
- rda_write_string(fd, ep->text2);
- }
- rda_write_string(fd, NULL); /* Indicates end of eblocks */
+ for (error_block * ep = *eblockp; ep; ep = ep->next)
+ if ( rda_write_string(fd, ep->text1) != 0
+ || rda_write_string(fd, ep->text2) != 0
+ )
+ goto bad;
+ if (rda_write_string(fd, NULL) != 0) /* Indicates end of eblocks */
+ goto bad;
}
/* If this is a system filter, we have to pass back the numbers of any
original header lines that were removed, and then any header lines that were
added but not subsequently removed. */
- if (system_filtering)
+ if (f.system_filtering)
{
int i = 0;
- header_line *h;
- for (h = header_list; h != waslast->next; i++, h = h->next)
- {
- if (h->type == htype_old) (void)write(fd, &i, sizeof(i));
- }
+ for (header_line * h = header_list; h != waslast->next; i++, h = h->next)
+ if ( h->type == htype_old
+ && write(fd, &i, sizeof(i)) != sizeof(i)
+ )
+ goto bad;
+
i = -1;
- (void)write(fd, &i, sizeof(i));
+ if (write(fd, &i, sizeof(i)) != sizeof(i))
+ goto bad;
while (waslast != header_last)
{
waslast = waslast->next;
if (waslast->type != htype_old)
- {
- rda_write_string(fd, waslast->text);
- (void)write(fd, &(waslast->type), sizeof(waslast->type));
- }
+ if ( rda_write_string(fd, waslast->text) != 0
+ || write(fd, &(waslast->type), sizeof(waslast->type))
+ != sizeof(waslast->type)
+ )
+ goto bad;
}
- rda_write_string(fd, NULL); /* Indicates end of added headers */
+ if (rda_write_string(fd, NULL) != 0) /* Indicates end of added headers */
+ goto bad;
}
/* Write the contents of the $n variables */
- (void)write(fd, filter_n, sizeof(filter_n));
+ if (write(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n))
+ goto bad;
/* If the result was DELIVERED or NOTDELIVERED, we pass back the generated
addresses, and their associated information, through the pipe. This is
if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
yield == FF_FAIL || yield == FF_FREEZE)
{
- address_item *addr;
- for (addr = *generated; addr != NULL; addr = addr->next)
+ for (address_item * addr = *generated; addr; addr = addr->next)
{
int reply_options = 0;
-
- rda_write_string(fd, addr->address);
- (void)write(fd, &(addr->mode), sizeof(addr->mode));
- (void)write(fd, &(addr->flags), sizeof(addr->flags));
- rda_write_string(fd, addr->p.errors_address);
-
- if (addr->pipe_expandn != NULL)
- {
- uschar **pp;
- for (pp = addr->pipe_expandn; *pp != NULL; pp++)
- rda_write_string(fd, *pp);
- }
- rda_write_string(fd, NULL);
-
- if (addr->reply == NULL)
- (void)write(fd, &reply_options, sizeof(int)); /* 0 means no reply */
+ int ig_err = addr->prop.ignore_error ? 1 : 0;
+
+ if ( rda_write_string(fd, addr->address) != 0
+ || write(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+ || write(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+ || rda_write_string(fd, addr->prop.errors_address) != 0
+ || write(fd, &ig_err, sizeof(ig_err)) != sizeof(ig_err)
+ )
+ goto bad;
+
+ if (addr->pipe_expandn)
+ for (uschar ** pp = addr->pipe_expandn; *pp; pp++)
+ if (rda_write_string(fd, *pp) != 0)
+ goto bad;
+ if (rda_write_string(fd, NULL) != 0)
+ goto bad;
+
+ if (!addr->reply)
+ {
+ if (write(fd, &reply_options, sizeof(int)) != sizeof(int)) /* 0 means no reply */
+ goto bad;
+ }
else
{
reply_options |= REPLY_EXISTS;
if (addr->reply->file_expand) reply_options |= REPLY_EXPAND;
if (addr->reply->return_message) reply_options |= REPLY_RETURN;
- (void)write(fd, &reply_options, sizeof(int));
- (void)write(fd, &(addr->reply->expand_forbid), sizeof(int));
- (void)write(fd, &(addr->reply->once_repeat), sizeof(time_t));
- rda_write_string(fd, addr->reply->to);
- rda_write_string(fd, addr->reply->cc);
- rda_write_string(fd, addr->reply->bcc);
- rda_write_string(fd, addr->reply->from);
- rda_write_string(fd, addr->reply->reply_to);
- rda_write_string(fd, addr->reply->subject);
- rda_write_string(fd, addr->reply->headers);
- rda_write_string(fd, addr->reply->text);
- rda_write_string(fd, addr->reply->file);
- rda_write_string(fd, addr->reply->logfile);
- rda_write_string(fd, addr->reply->oncelog);
+ if ( write(fd, &reply_options, sizeof(int)) != sizeof(int)
+ || write(fd, &(addr->reply->expand_forbid), sizeof(int))
+ != sizeof(int)
+ || write(fd, &(addr->reply->once_repeat), sizeof(time_t))
+ != sizeof(time_t)
+ || rda_write_string(fd, addr->reply->to) != 0
+ || rda_write_string(fd, addr->reply->cc) != 0
+ || rda_write_string(fd, addr->reply->bcc) != 0
+ || rda_write_string(fd, addr->reply->from) != 0
+ || rda_write_string(fd, addr->reply->reply_to) != 0
+ || rda_write_string(fd, addr->reply->subject) != 0
+ || rda_write_string(fd, addr->reply->headers) != 0
+ || rda_write_string(fd, addr->reply->text) != 0
+ || rda_write_string(fd, addr->reply->file) != 0
+ || rda_write_string(fd, addr->reply->logfile) != 0
+ || rda_write_string(fd, addr->reply->oncelog) != 0
+ )
+ goto bad;
}
}
- rda_write_string(fd, NULL); /* Marks end of addresses */
+ if (rda_write_string(fd, NULL) != 0) /* Marks end of addresses */
+ goto bad;
}
/* OK, this process is now done. Free any cached resources. Must use _exit()
and not exit() !! */
+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");
+ goto out;
}
/* Back in the main process: panic if the fork did not succeed. */
/* Read the contents of any syntax error blocks if we have a pointer */
-if (eblockp != NULL)
+if (eblockp)
{
- uschar *s;
error_block *e;
- error_block **p = eblockp;
- for (;;)
+ for (error_block ** p = eblockp; ; p = &e->next)
{
+ uschar *s;
if (!rda_read_string(fd, &s)) goto DISASTER;
- if (s == NULL) break;
- e = store_get(sizeof(error_block));
+ if (!s) break;
+ e = store_get(sizeof(error_block), FALSE);
e->next = NULL;
e->text1 = s;
if (!rda_read_string(fd, &s)) goto DISASTER;
e->text2 = s;
*p = e;
- p = &(e->next);
}
}
/* If this is a system filter, read the identify of any original header lines
that were removed, and then read data for any new ones that were added. */
-if (system_filtering)
+if (f.system_filtering)
{
int hn = 0;
header_line *h = header_list;
while (hn < n)
{
hn++;
- h = h->next;
- if (h == NULL) goto DISASTER_NO_HEADER;
+ if (!(h = h->next)) goto DISASTER_NO_HEADER;
}
h->type = htype_old;
}
uschar *s;
int type;
if (!rda_read_string(fd, &s)) goto DISASTER;
- if (s == NULL) break;
+ if (!s) break;
if (read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
header_add(type, "%s", s);
}
/* 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 */
/* Next comes the mode and the flags fields */
- if (read(fd, &(addr->mode), sizeof(addr->mode)) != sizeof(addr->mode) ||
- read(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) ||
- !rda_read_string(fd, &(addr->p.errors_address))) goto DISASTER;
+ if ( read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+ || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+ || !rda_read_string(fd, &addr->prop.errors_address)
+ || read(fd, &i, sizeof(i)) != sizeof(i)
+ )
+ goto DISASTER;
+ addr->prop.ignore_error = (i != 0);
/* Next comes a possible setting for $thisaddress and any numerical
variables for pipe expansion, terminated by a NULL string. The maximum
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;