Description: Fix null pointer dereference with syslog only setup Author: Jeremy Harris Bug: http://bugs.exim.org/show_bug.cgi?id=2733 Last-Update: 2021-05-07 Bug-Debian: https://bugs.debian.org/988086 --- a/src/log.c +++ b/src/log.c @@ -285,12 +285,15 @@ { BOOL created; uschar *lastslash = Ustrrchr(name, '/'); *lastslash = 0; created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE); - DEBUG(D_any) debug_printf("%s log directory %s\n", - created ? "created" : "failed to create", name); + DEBUG(D_any) + if (created) + debug_printf("created log directory %s\n", name); + else + debug_printf("failed to create log directory %s: %s\n", name, strerror(errno)); *lastslash = '/'; if (created) fd = Uopen(name, flags, LOG_MODE); } return fd; @@ -392,13 +395,11 @@ { int fd = -1; const uid_t euid = geteuid(); if (euid == exim_uid) - { fd = log_open_already_exim(name); - } else if (euid == root_uid) { int sock[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == 0) { @@ -497,34 +498,44 @@ case lt_main: /* Save the name of the mainlog for rollover processing. Without a datestamp, it gets statted to see if it has been cycled. With a datestamp, the datestamp will be compared. The static slot for saving it is the same size as buffer, and the text has been checked above to fit, so this use of strcpy() is OK. */ + Ustrcpy(mainlog_name, buffer); if (string_datestamp_offset > 0) mainlog_datestamp = mainlog_name + string_datestamp_offset; + break; + case lt_reject: /* Ditto for the reject log */ + Ustrcpy(rejectlog_name, buffer); if (string_datestamp_offset > 0) rejectlog_datestamp = rejectlog_name + string_datestamp_offset; + break; + case lt_debug: /* and deal with the debug log (which keeps the datestamp, but does not update it) */ + Ustrcpy(debuglog_name, buffer); if (tag) { /* this won't change the offset of the datestamp */ ok2 = string_format(buffer, sizeof(buffer), "%s%s", debuglog_name, tag); if (ok2) Ustrcpy(debuglog_name, buffer); } + break; + default: /* Remove any datestamp if this is the panic log. This is rare, so there's no need to optimize getting the datestamp length. We remove one non-alphanumeric char afterwards if at the start, otherwise one before. */ + if (string_datestamp_offset >= 0) { uschar * from = buffer + string_datestamp_offset; uschar * to = from + string_datestamp_length; @@ -537,10 +548,11 @@ /* This copy is ok, because we know that to is a substring of from. But due to overlap we must use memmove() not Ustrcpy(). */ memmove(from, to, Ustrlen(to)+1); } + break; } /* If the file name is too long, it is an unrecoverable disaster */ if (!ok) @@ -550,13 +562,11 @@ /* We now have the file name. After a successful open, return. */ *fd = log_open_as_exim(buffer); if (*fd >= 0) - { return; - } euid = geteuid(); /* Creation failed. There are some circumstances in which we get here when the effective uid is not root or exim, which is the problem. (For example, a @@ -704,41 +714,57 @@ } return total_written; } +/* Pull the file out of the configured or the compiled-in list. +Called for an empty log_file_path element, for debug logging activation +when file_path has not previously been set, and from the appenfile transport setup. */ + void set_file_path(BOOL *multiple) { uschar *s; int sep = ':'; /* Fixed separator - outside use */ const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH; -logging_mode = 0; -while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) - { - if (Ustrcmp(s, "syslog") == 0) - logging_mode |= LOG_MODE_SYSLOG; - else if (logging_mode & LOG_MODE_FILE) /* we know a file already */ - { - if (multiple) *multiple = TRUE; - } - else +if (*ss) + for (logging_mode = 0; + s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE); ) { - logging_mode |= LOG_MODE_FILE; + if (Ustrcmp(s, "syslog") == 0) + logging_mode |= LOG_MODE_SYSLOG; + else if (!(logging_mode & LOG_MODE_FILE)) /* no file yet */ + { + /* If a non-empty path is given, use it */ - /* If a non-empty path is given, use it */ + if (*s) + file_path = string_copy(s); - if (*s) - file_path = string_copy(s); + /* If handling the config option, and the element is empty, we want to use + the first non-empty, non-syslog item in LOG_FILE_PATH, if there is one, + since the value of log_file_path may have been set at runtime. If there is + no such item, use the ultimate default in the spool directory. */ + + else if (*log_file_path && LOG_FILE_PATH[0]) + { + ss = US LOG_FILE_PATH; + continue; + } - /* If the path is empty, we want to use the first non-empty, non- - syslog item in LOG_FILE_PATH, if there is one, since the value of - log_file_path may have been set at runtime. If there is no such item, - use the ultimate default in the spool directory. */ + logging_mode |= LOG_MODE_FILE; + } + else + if (multiple) *multiple = TRUE; } - } + else + logging_mode = LOG_MODE_FILE; + +/* Set up the ultimate default if necessary. */ + +if (logging_mode & LOG_MODE_FILE && !*file_path) + file_path = string_sprintf("%s/log/%%slog", spool_directory); } void mainlog_close(void) @@ -868,15 +894,12 @@ if (logging_mode == 0) die(US"Neither syslog nor file logging set in log_file_path", US"Unexpected logging failure"); - /* Set up the ultimate default if necessary. Then revert to the old store - pool, and record that we've sorted out the path. */ + /* Revert to the old store pool, and record that we've sorted out the path. */ - if (logging_mode & LOG_MODE_FILE && !file_path[0]) - file_path = string_sprintf("%s/log/%%slog", spool_directory); store_pool = old_pool; path_inspected = TRUE; /* If more than one file path was given, log a complaint. This recursive call should work since we have now set up the routing. */ @@ -1226,10 +1249,11 @@ /* If this panic logging was caused by a failure to open the main log, the original log line is in panic_save_buffer. Make an attempt to write it. */ if (logging_mode & LOG_MODE_FILE) { + if (!*file_path) set_file_path(NULL); panic_recurseflag = TRUE; open_log(&paniclogfd, lt_panic, NULL); /* Won't return on failure */ panic_recurseflag = FALSE; if (panic_save_buffer) @@ -1503,14 +1527,16 @@ fclose(debug_file); debug_file = NULL; unlink_log(lt_debug); } +/* Called from the appendfile transport setup. */ void open_logs(void) { set_file_path(NULL); +if (!(logging_mode & LOG_MODE_FILE)) return; open_log(&mainlogfd, lt_main, 0); open_log(&rejectlogfd, lt_reject, 0); } /* End of log.c */