-/* $Cambridge: exim/src/src/rda.c,v 1.2 2004/11/04 10:42:11 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* 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
}
filebuf[statbuf.st_size] = 0;
-/* Don't pass statbuf.st_size directly to debug_printf. On some systems it
-is a long, which may not be the same as an int. */
-
DEBUG(D_route)
- {
- int size = (int)statbuf.st_size;
- debug_printf("%d bytes read from %s\n", size, filename);
- }
+ debug_printf(OFF_T_FMT " bytes read from %s\n", statbuf.st_size, filename);
-fclose(fwd);
+(void)fclose(fwd);
return filebuf;
/* Return an error: the string is already set up. */
ERROR_RETURN:
*yield = FF_ERROR;
-fclose(fwd);
+(void)fclose(fwd);
return NULL;
}
options the options bits
include_directory restrain to this directory
sieve_vacation_directory passed to sieve_interpret
+ sieve_enotify_mailto_owner passed to sieve_interpret
+ sieve_useraddress passed to sieve_interpret
+ sieve_subaddress passed to sieve_interpret
generated where to hang generated addresses
error for error messages
eblockp for details of skipped syntax errors
static int
rda_extract(redirect_block *rdata, int options, uschar *include_directory,
- uschar *sieve_vacation_directory, address_item **generated, uschar **error,
- error_block **eblockp, int *filtertype)
+ uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
+ uschar *sieve_useraddress, uschar *sieve_subaddress,
+ address_item **generated, uschar **error, error_block **eblockp,
+ int *filtertype)
{
uschar *data;
if (rdata->isfile)
{
- int yield;
+ int yield = 0;
data = rda_get_file_contents(rdata, options, error, &yield);
if (data == NULL) return yield;
}
(*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve");
/* RDO_FILTER is an "allow" bit */
-
+
if ((options & RDO_FILTER) == 0)
{
*error = US"filtering not enabled";
expand_forbid =
(expand_forbid & ~RDO_FILTER_EXPANSIONS) |
(options & RDO_FILTER_EXPANSIONS);
-
+
/* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
-
+
if (*filtertype == FILTER_EXIM)
{
if ((options & RDO_EXIM_FILTER) != 0)
{
*error = US"Exim filtering not enabled";
return FF_ERROR;
- }
+ }
frc = filter_interpret(data, options, generated, error);
- }
+ }
else
{
if ((options & RDO_SIEVE_FILTER) != 0)
*error = US"Sieve filtering not enabled";
return FF_ERROR;
}
- frc = sieve_interpret(data, options, sieve_vacation_directory, generated,
- error);
- }
+ frc = sieve_interpret(data, options, sieve_vacation_directory,
+ sieve_enotify_mailto_owner, sieve_useraddress, sieve_subaddress,
+ generated, error);
+ }
expand_forbid = old_expand_forbid;
return frc;
* 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;
-write(fd, &len, sizeof(int));
-if (s != NULL) 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] */
+ if (read(fd, *sp = store_get(len), len) != len) return FALSE;
return TRUE;
}
options options to pass to the extraction functions,
plus ENOTDIR and EACCES handling bits
include_directory restrain :include: to this directory
- sieve_vacation_directory directory passed to sieve_interpret()
+ sieve_vacation_directory directory passed to sieve_interpret
+ sieve_enotify_mailto_owner passed to sieve_interpret
+ sieve_useraddress passed to sieve_interpret
+ sieve_subaddress passed to sieve_interpret
ugid uid/gid to run under - if NULL, no change
generated where to hang generated addresses, initially NULL
error pointer for error message
int
rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
- uschar *sieve_vacation_directory, ugid_block *ugid, address_item **generated,
- uschar **error, error_block **eblockp, int *filtertype, uschar *rname)
+ uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
+ uschar *sieve_useraddress, uschar *sieve_subaddress, ugid_block *ugid,
+ address_item **generated, uschar **error, error_block **eblockp,
+ int *filtertype, uschar *rname)
{
int fd, rc, pfd[2];
int yield, status;
Ustrstr(data, ":include:") == NULL)) /* and there's no :include: */
{
return rda_extract(rdata, options, include_directory,
- sieve_vacation_directory, generated, error, eblockp, filtertype);
+ sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
+ sieve_subaddress, generated, error, eblockp, filtertype);
}
/* We need to run the processing code in a sub-process. However, if we can
/* Ensure that SIGCHLD is set to SIG_DFL before forking, so that the child
process can be waited for. We sometimes get here with it set otherwise. Save
-the old state for resetting on the wait. */
+the old state for resetting on the wait. Ensure that all cached resources are
+freed so that the subprocess starts with a clean slate and doesn't interfere
+with the parent process. */
oldsignal = signal(SIGCHLD, SIG_DFL);
+search_tidyup();
+
if ((pid = fork()) == 0)
{
header_line *waslast = header_last; /* Save last header */
fd = pfd[pipe_write];
- close(pfd[pipe_read]);
+ (void)close(pfd[pipe_read]);
exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
/* Addresses can get rewritten in filters; if we are not root or the exim
{
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 */
yield = rda_extract(rdata, options, include_directory,
- sieve_vacation_directory, generated, error, eblockp, filtertype);
+ sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
+ sieve_subaddress, generated, error, eblockp, filtertype);
/* Pass back whether it was a filter, and the return code and any overall
error text via the pipe. */
- write(fd, filtertype, sizeof(int));
- 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 */
{
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 */
+ 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
int i = 0;
header_line *h;
for (h = header_list; h != waslast->next; i++, h = h->next)
- {
- if (h->type == htype_old) write(fd, &i, sizeof(i));
- }
+ if ( h->type == htype_old
+ && write(fd, &i, sizeof(i)) != sizeof(i)
+ )
+ goto bad;
+
i = -1;
- 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);
- 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 */
- 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
yield == FF_FAIL || yield == FF_FREEZE)
{
address_item *addr;
- for (addr = *generated; addr != NULL; addr = addr->next)
+ for (addr = *generated; addr; addr = addr->next)
{
int reply_options = 0;
- rda_write_string(fd, addr->address);
- write(fd, &(addr->mode), sizeof(addr->mode));
- write(fd, &(addr->flags), sizeof(addr->flags));
- rda_write_string(fd, addr->p.errors_address);
+ 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
+ )
+ goto bad;
- if (addr->pipe_expandn != NULL)
+ if (addr->pipe_expandn)
{
uschar **pp;
- for (pp = addr->pipe_expandn; *pp != NULL; pp++)
- rda_write_string(fd, *pp);
+ for (pp = addr->pipe_expandn; *pp; pp++)
+ if (rda_write_string(fd, *pp) != 0)
+ goto bad;
}
- rda_write_string(fd, NULL);
-
- if (addr->reply == NULL)
- write(fd, &reply_options, sizeof(int)); /* 0 means no reply */
+ 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;
- write(fd, &reply_options, sizeof(int));
- write(fd, &(addr->reply->expand_forbid), sizeof(int));
- 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. Must use _exit() and not exit() !! */
+ /* OK, this process is now done. Free any cached resources. Must use _exit()
+ and not exit() !! */
- close(fd);
+out:
+ (void)close(fd);
+ search_tidyup();
_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. */
writing end must be closed first, as otherwise read() won't return zero on an
empty pipe. Afterwards, close the reading end. */
-close(pfd[pipe_write]);
+(void)close(pfd[pipe_write]);
/* Read initial data, including yield and contents of *error */
read(fd, &yield, sizeof(int)) != sizeof(int) ||
!rda_read_string(fd, error)) goto DISASTER;
-DEBUG(D_route)
- debug_printf("rda_interpret: subprocess yield=%d error=%s\n", yield, *error);
-
/* 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 (;;)
+ error_block **p;
+ for (p = eblockp; ; p = &e->next)
{
+ uschar *s;
if (!rda_read_string(fd, &s)) goto DISASTER;
- if (s == NULL) break;
+ if (!s) break;
e = store_get(sizeof(error_block));
e->next = NULL;
e->text1 = s;
if (!rda_read_string(fd, &s)) goto DISASTER;
e->text2 = s;
*p = e;
- p = &(e->next);
}
}
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);
}
/* 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)) goto DISASTER;
/* 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 *));
addr->pipe_expandn[i] = NULL;
while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
}
}
}
+DEBUG(D_route)
+ debug_printf("rda_interpret: subprocess yield=%d error=%s\n", yield, *error);
+
if (had_disaster)
{
*error = string_sprintf("internal problem in %s: failure to transfer "
}
FINAL_EXIT:
-close(fd);
+(void)close(fd);
signal(SIGCHLD, oldsignal); /* restore */
return yield;