X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexim.c;h=9cef9226b52e395dc30fa6f3e3b87aa94446b2f5;hp=f4d537800c48859f4606eac62a8f2f26a3a73d1a;hb=f6c332bd03c89f108c7fe531156cb18d7888ba35;hpb=9cec981fa37f4ad2f5234a2a5c296ae6ebebd019 diff --git a/src/src/exim.c b/src/src/exim.c index f4d537800..9cef9226b 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/exim.c,v 1.26 2005/08/23 11:25:38 ph10 Exp $ */ +/* $Cambridge: exim/src/src/exim.c,v 1.46 2006/10/24 12:56:06 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2006 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -375,6 +375,38 @@ va_end(ap); +/************************************************* +* Call fopen() with umask 777 and adjust mode * +*************************************************/ + +/* Exim runs with umask(0) so that files created with open() have the mode that +is specified in the open() call. However, there are some files, typically in +the spool directory, that are created with fopen(). They end up world-writeable +if no precautions are taken. Although the spool directory is not accessible to +the world, this is an untidiness. So this is a wrapper function for fopen() +that sorts out the mode of the created file. + +Arguments: + filename the file name + options the fopen() options + mode the required mode + +Returns: the fopened FILE or NULL +*/ + +FILE * +modefopen(uschar *filename, char *options, mode_t mode) +{ +mode_t saved_umask = umask(0777); +FILE *f = Ufopen(filename, options); +(void)umask(saved_umask); +if (f != NULL) (void)fchmod(fileno(f), mode); +return f; +} + + + + /************************************************* * Ensure stdin, stdout, and stderr exist * *************************************************/ @@ -842,6 +874,9 @@ fprintf(f, "Support for:"); #if HAVE_IPV6 fprintf(f, " IPv6"); #endif +#ifdef HAVE_SETCLASSRESOURCES + fprintf(f, " use_setclassresources"); +#endif #ifdef SUPPORT_PAM fprintf(f, " PAM"); #endif @@ -861,6 +896,12 @@ fprintf(f, "Support for:"); fprintf(f, " OpenSSL"); #endif #endif +#ifdef SUPPORT_TRANSLATE_IP_ADDRESS + fprintf(f, " translate_ip_address"); +#endif +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES + fprintf(f, " move_frozen_messages"); +#endif #ifdef WITH_CONTENT_SCAN fprintf(f, " Content_Scanning"); #endif @@ -939,6 +980,9 @@ fprintf(f, "Authenticators:"); #ifdef AUTH_CYRUS_SASL fprintf(f, " cyrus_sasl"); #endif +#ifdef AUTH_DOVECOT + fprintf(f, " dovecot"); +#endif #ifdef AUTH_PLAINTEXT fprintf(f, " plaintext"); #endif @@ -1006,6 +1050,8 @@ if (fixed_never_users[0] > 0) fprintf(f, "%d:", (unsigned int)fixed_never_users[i]); fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]); } + +fprintf(f, "Size of off_t: %d\n", sizeof(off_t)); } @@ -1131,7 +1177,7 @@ int size = 0; int ptr = 0; uschar *yield = NULL; -if (fn_readline == NULL) printf("> "); +if (fn_readline == NULL) { printf("> "); fflush(stdout); } for (i = 0;; i++) { @@ -1244,6 +1290,7 @@ BOOL more = TRUE; BOOL one_msg_action = FALSE; BOOL queue_only_set = FALSE; BOOL receiving_message = TRUE; +BOOL sender_ident_set = FALSE; BOOL unprivileged; BOOL removed_privilege = FALSE; BOOL verify_address_mode = FALSE; @@ -1253,6 +1300,7 @@ uschar *alias_arg = NULL; uschar *called_as = US""; uschar *start_queue_run_id = NULL; uschar *stop_queue_run_id = NULL; +uschar *expansion_test_message = NULL; uschar *ftest_domain = NULL; uschar *ftest_localpart = NULL; uschar *ftest_prefix = NULL; @@ -1429,10 +1477,17 @@ message_id_external[0] = 'E'; message_id = message_id_external + 1; message_id[0] = 0; -/* Set the umask to zero so that any files that Exim creates are created -with the modes that it specifies. */ +/* Set the umask to zero so that any files Exim creates using open() are +created with the modes that it specifies. NOTE: Files created with fopen() have +a problem, which was not recognized till rather late (February 2006). With this +umask, such files will be world writeable. (They are all content scanning files +in the spool directory, which isn't world-accessible, so this is not a +disaster, but it's untidy.) I don't want to change this overall setting, +however, because it will interact badly with the open() calls. Instead, there's +now a function called modefopen() that fiddles with the umask while calling +fopen(). */ -umask(0); +(void)umask(0); /* Precompile the regular expression for matching a message id. Keep this in step with the code that generates ids in the accept.c module. We need to do @@ -1442,6 +1497,13 @@ using mac_ismsgid, which uses this. */ regex_ismsgid = regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE); +/* Precompile the regular expression that is used for matching an SMTP error +code, possibly extended, at the start of an error message. */ + +regex_smtp_code = + regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?", + FALSE, TRUE); + /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ @@ -1619,10 +1681,21 @@ for (i = 1; i < argc; i++) else if (*argrest != 0) { badarg = TRUE; break; } } - /* -be: Run in expansion test mode */ + /* -be: Run in expansion test mode + -bem: Ditto, but read a message from a file first + */ else if (*argrest == 'e') + { expansion_test = checking = TRUE; + if (argrest[1] == 'm') + { + if (++i >= argc) { badarg = TRUE; break; } + expansion_test_message = argv[i]; + argrest++; + } + if (argrest[1] != 0) { badarg = TRUE; break; } + } /* -bF: Run system filter test */ @@ -1993,6 +2066,7 @@ for (i = 1; i < argc; i++) { badarg = TRUE; break; } } originator_name = argrest; + sender_name_forced = TRUE; break; @@ -2190,6 +2264,7 @@ for (i = 1; i < argc; i++) -Mmad mark all recipients delivered -Mmd mark recipients(s) delivered -Mes edit sender + -Mset load a message for use with -be -Mvb show body -Mvh show header -Mvl show log @@ -2227,6 +2302,11 @@ for (i = 1; i < argc; i++) one_msg_action = TRUE; } else if (Ustrcmp(argrest, "rm") == 0) msg_action = MSG_REMOVE; + else if (Ustrcmp(argrest, "set") == 0) + { + msg_action = MSG_LOAD; + one_msg_action = TRUE; + } else if (Ustrcmp(argrest, "t") == 0) msg_action = MSG_THAW; else if (Ustrcmp(argrest, "vb") == 0) { @@ -2462,7 +2542,11 @@ for (i = 1; i < argc; i++) /* -oMt: Set sender ident */ - else if (Ustrcmp(argrest, "Mt") == 0) sender_ident = argv[++i]; + else if (Ustrcmp(argrest, "Mt") == 0) + { + sender_ident_set = TRUE; + sender_ident = argv[++i]; + } /* Else a bad argument */ @@ -2820,13 +2904,14 @@ if (( ) || ( msg_action_arg > 0 && - (daemon_listen || queue_interval >= 0 || list_options || checking || - bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) + (daemon_listen || queue_interval >= 0 || list_options || + (checking && msg_action != MSG_LOAD) || + bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) ) || ( (daemon_listen || queue_interval >= 0) && (sender_address != NULL || list_options || list_queue || checking || - bi_option) + bi_option) ) || ( daemon_listen && queue_interval == 0 @@ -2851,6 +2936,10 @@ if (( ) || ( deliver_selectstring != NULL && queue_interval < 0 + ) || + ( + msg_action == MSG_LOAD && + (!expansion_test || expansion_test_message != NULL) ) ) { @@ -3546,7 +3635,9 @@ root privilege above as a result of -C, -D, -be, -bf or -bF, remove it now except when starting the daemon or doing some kind of delivery or address testing (-bt). These are the only cases when root need to be retained. We run as exim for -bv and -bh. However, if deliver_drop_privilege is set, root is -retained only for starting the daemon. */ +retained only for starting the daemon. We always do the initgroups() in this +situation (controlled by the TRUE below), in order to be as close as possible +to the state Exim usually runs in. */ if (!unprivileged && /* originally had root AND */ !removed_privilege && /* still got root AND */ @@ -3562,7 +3653,7 @@ if (!unprivileged && /* originally had root AND */ ) )) { - exim_setugid(exim_uid, exim_gid, FALSE, US"privilege not needed"); + exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed"); } /* When we are retaining a privileged uid, we still change to the exim gid. */ @@ -3587,12 +3678,12 @@ if (count_queue) exit(EXIT_SUCCESS); } -/* Handle actions on specific messages, except for the force delivery action, -which is done below. Some actions take a whole list of message ids, which -are known to continue up to the end of the arguments. Others take a single -message id and then operate on the recipients list. */ +/* Handle actions on specific messages, except for the force delivery and +message load actions, which are done below. Some actions take a whole list of +message ids, which are known to continue up to the end of the arguments. Others +take a single message id and then operate on the recipients list. */ -if (msg_action_arg > 0 && msg_action != MSG_DELIVER) +if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) { int yield = EXIT_SUCCESS; set_process_info("acting on specified messages"); @@ -3674,11 +3765,13 @@ if (test_retry_arg >= 0) return EXIT_FAILURE; } - /* For the rcpt_4xx errors, a value of 255 means "any", and a code > 100 as - an error is for matching codes to the decade. Turn them into a real error - code, off the decade. */ + /* For the {MAIL,RCPT,DATA}_4xx errors, a value of 255 means "any", and a + code > 100 as an error is for matching codes to the decade. Turn them into + a real error code, off the decade. */ - if (basic_errno == ERRNO_RCPT4XX) + if (basic_errno == ERRNO_MAIL4XX || + basic_errno == ERRNO_RCPT4XX || + basic_errno == ERRNO_DATA4XX) { int code = (more_errno >> 8) & 255; if (code == 255) @@ -3770,16 +3863,19 @@ if (list_options) /* Handle a request to deliver one or more messages that are already on the -queue. Values of msg_action other than MSG_DELIVER are dealt with above. This -is typically used for a small number when prodding by hand (when the option -forced_delivery will be set) or when re-execing to regain root privilege. -Each message delivery must happen in a separate process, so we fork a process -for each one, and run them sequentially so that debugging output doesn't get -intertwined, and to avoid spawning too many processes if a long list is given. -However, don't fork for the last one; this saves a process in the common case -when Exim is called to deliver just one message. */ - -if (msg_action_arg > 0) +queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with +above. MSG_LOAD is handled with -be (which is the only time it applies) below. + +Delivery of specific messages is typically used for a small number when +prodding by hand (when the option forced_delivery will be set) or when +re-execing to regain root privilege. Each message delivery must happen in a +separate process, so we fork a process for each one, and run them sequentially +so that debugging output doesn't get intertwined, and to avoid spawning too +many processes if a long list is given. However, don't fork for the last one; +this saves a process in the common case when Exim is called to deliver just one +message. */ + +if (msg_action_arg > 0 && msg_action != MSG_LOAD) { if (prod_requires_admin && !admin_user) { @@ -3912,7 +4008,7 @@ for (i = 0;;) /* If we cannot get a user login, log the incident and give up, unless the configuration specifies something to use. When running in the test harness, -any setting of unknown_login overrides the actual login name. */ +any setting of unknown_login overrides the actual name. */ if (originator_login == NULL || running_in_test_harness) { @@ -3993,12 +4089,14 @@ if ((sender_address == NULL && !smtp_input) || sender_local = TRUE; /* A trusted caller can supply authenticated_sender and authenticated_id - via -oMas and -oMai and if so, they will already be set. */ + via -oMas and -oMai and if so, they will already be set. Otherwise, force + defaults except when host checking. */ - if (authenticated_sender == NULL) + if (authenticated_sender == NULL && !host_checking) authenticated_sender = string_sprintf("%s@%s", originator_login, qualify_domain_sender); - if (authenticated_id == NULL) authenticated_id = originator_login; + if (authenticated_id == NULL && !host_checking) + authenticated_id = originator_login; } /* Trusted callers are always permitted to specify the sender address. @@ -4097,18 +4195,63 @@ if (verify_address_mode || address_test_mode) exim_exit(exit_value); } -/* Handle expansion checking */ +/* Handle expansion checking. Either expand items on the command line, or read +from stdin if there aren't any. If -Mset was specified, load the message so +that its variables can be used, but restrict this facility to admin users. +Otherwise, if -bem was used, read a message from stdin. */ if (expansion_test) { + if (msg_action_arg > 0 && msg_action == MSG_LOAD) + { + uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */ + if (!admin_user) + { + fprintf(stderr, "exim: permission denied\n"); + exit(EXIT_FAILURE); + } + message_id = argv[msg_action_arg]; + (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id); + if (!spool_open_datafile(message_id)) + printf ("Failed to load message datafile %s\n", message_id); + if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK) + printf ("Failed to load message %s\n", message_id); + } + + /* Read a test message from a file. We fudge it up to be on stdin, saving + stdin itself for later reading of expansion strings. */ + + else if (expansion_test_message != NULL) + { + int save_stdin = dup(0); + int fd = Uopen(expansion_test_message, O_RDONLY, 0); + if (fd < 0) + { + fprintf(stderr, "exim: failed to open %s: %s\n", expansion_test_message, + strerror(errno)); + return EXIT_FAILURE; + } + (void) dup2(fd, 0); + filter_test = FTEST_USER; /* Fudge to make it look like filter test */ + message_ended = END_NOTENDED; + read_message_body(receive_msg(extract_recipients)); + (void)dup2(save_stdin, 0); + (void)close(save_stdin); + } + + /* Allow $recipients for this testing */ + + enable_dollar_recipients = TRUE; + + /* Expand command line items */ + if (recipients_arg < argc) { while (recipients_arg < argc) { uschar *s = argv[recipients_arg++]; uschar *ss = expand_string(s); - if (ss == NULL) - printf ("Failed: %s\n", expand_string_message); + if (ss == NULL) printf ("Failed: %s\n", expand_string_message); else printf("%s\n", CS ss); } } @@ -4140,6 +4283,14 @@ if (expansion_test) #endif } + /* The data file will be open after -Mset */ + + if (deliver_datafile >= 0) + { + (void)close(deliver_datafile); + deliver_datafile = -1; + } + exim_exit(EXIT_SUCCESS); } @@ -4163,20 +4314,24 @@ if (raw_active_hostname != NULL) } /* Handle host checking: this facility mocks up an incoming SMTP call from a -given IP address so that the blocking and relay configuration can be tested. An -RFC 1413 call is made only if we are running in the test harness and an -incoming interface and both ports are specified, because there is no TCP/IP -call to find the ident for. */ +given IP address so that the blocking and relay configuration can be tested. +Unless a sender_ident was set by -oMt, we discard it (the default is the +caller's login name). An RFC 1413 call is made only if we are running in the +test harness and an incoming interface and both ports are specified, because +there is no TCP/IP call to find the ident for. */ if (host_checking) { int x[4]; int size; - sender_ident = NULL; - if (running_in_test_harness && sender_host_port != 0 && - interface_address != NULL && interface_port != 0) - verify_get_ident(1413); + if (!sender_ident_set) + { + sender_ident = NULL; + if (running_in_test_harness && sender_host_port != 0 && + interface_address != NULL && interface_port != 0) + verify_get_ident(1413); + } /* In case the given address is a non-canonical IPv6 address, canonicize it. The code works for both IPv4 and IPv6, as it happens. */ @@ -4367,12 +4522,12 @@ if (smtp_input) } } -/* Otherwise, set up the input size limit here */ +/* Otherwise, set up the input size limit here. */ else { - thismessage_size_limit = expand_string_integer(message_size_limit); - if (thismessage_size_limit < 0) + thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); + if (expand_string_message != NULL) { if (thismessage_size_limit == -1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand " @@ -4583,6 +4738,19 @@ while (more) } } + /* Run the acl_not_smtp_start ACL if required. The result of the ACL is + ignored; rejecting here would just add complication, and it can just as + well be done later. Allow $recipients to be visible in the ACL. */ + + if (acl_not_smtp_start != NULL) + { + uschar *user_msg, *log_msg; + enable_dollar_recipients = TRUE; + (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, + &user_msg, &log_msg); + enable_dollar_recipients = FALSE; + } + /* Read the data for the message. If filter_test is not FTEST_NONE, this will just read the headers for the message, and not write anything onto the spool. */ @@ -4731,12 +4899,6 @@ while (more) close_unwanted(); /* Close unwanted file descriptors and TLS */ exim_nullstd(); /* Ensure std{in,out,err} exist */ - /* Occasionally in the test harness we don't have synchronous delivery - set (can happen with bounces). In that case, let the old process finish - before continuing, to keep the debug output the same. */ - - if (running_in_test_harness && !synchronous_delivery) millisleep(100); - /* Re-exec Exim if we need to regain privilege (note: in mua_wrapper mode, deliver_drop_privilege is forced TRUE). */