diff options
Diffstat (limited to 'src/global/maillog_client.c')
-rw-r--r-- | src/global/maillog_client.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/global/maillog_client.c b/src/global/maillog_client.c new file mode 100644 index 0000000..7f79a1f --- /dev/null +++ b/src/global/maillog_client.c @@ -0,0 +1,304 @@ +/*++ +/* NAME +/* maillog_client 3 +/* SUMMARY +/* choose between syslog client and postlog client +/* SYNOPSIS +/* #include <maillog_client.h> +/* +/* int maillog_client_init( +/* const char *progname, +/* int flags) +/* DESCRIPTION +/* maillog_client_init() chooses between logging to the syslog +/* service or to the internal postlog service. +/* +/* maillog_client_init() may be called before configuration +/* parameters are initialized. During this time, logging is +/* controlled by the presence or absence of POSTLOG_SERVICE +/* in the process environment (this is ignored if a program +/* runs with set-uid or set-gid permissions). +/* +/* maillog_client_init() may also be called after configuration +/* parameters are initialized. During this time, logging is +/* controlled by the "maillog_file" parameter value. +/* +/* Arguments: +/* .IP progname +/* The program name that will be prepended to logfile records. +/* .IP flags +/* Specify one of the following: +/* .RS +/* .IP MAILLOG_CLIENT_FLAG_NONE +/* No special processing. +/* .IP MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK +/* Try to fall back to writing the "maillog_file" directly, +/* if logging to the internal postlog service is enabled, but +/* the postlog service is unavailable. If the fallback fails, +/* die with a fatal error. +/* .RE +/* ENVIRONMENT +/* .ad +/* .fi +/* When logging to the internal postlog service is enabled, +/* each process exports the following information, to help +/* initialize the logging in a child process, before the child +/* has initialized its configuration parameters. +/* .IP POSTLOG_SERVICE +/* The pathname of the public postlog service endpoint, usually +/* "$queue_directory/public/$postlog_service_name". +/* .IP POSTLOG_HOSTNAME +/* The hostname to prepend to information that is sent to the +/* internal postlog logging service, usually "$myhostname". +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* .IP "maillog_file (empty)" +/* The name of an optional logfile. If the value is empty, or +/* unitialized and the process environment does not specify +/* POSTLOG_SERVICE, the program will log to the syslog service +/* instead. +/* .IP "myhostname (default: see postconf -d output)" +/* The internet hostname of this mail system. +/* .IP "postlog_service_name (postlog)" +/* The name of the internal postlog logging service. +/* SEE ALSO +/* msg_syslog(3) syslog client +/* msg_logger(3) internal logger +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this +/* software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include <sys_defs.h> +#include <stdlib.h> +#include <string.h> + + /* + * Utility library. + */ +#include <argv.h> +#include <logwriter.h> +#include <msg_logger.h> +#include <msg_syslog.h> +#include <safe.h> +#include <stringops.h> + + /* + * Global library. + */ +#include <mail_params.h> +#include <mail_proto.h> +#include <maillog_client.h> +#include <msg.h> + + /* + * Using logging to debug logging is painful. + */ +#define MAILLOG_CLIENT_DEBUG 0 + + /* + * Application-specific. + */ +static int maillog_client_flags; + +#define POSTLOG_SERVICE_ENV "POSTLOG_SERVICE" +#define POSTLOG_HOSTNAME_ENV "POSTLOG_HOSTNAME" + +/* maillog_client_logwriter_fallback - fall back to logfile writer or bust */ + +static void maillog_client_logwriter_fallback(const char *text) +{ + static int fallback_guard = 0; + + /* + * Guard against recursive calls. + * + * If an error happened before the maillog_file parameter was initialized, + * or if maillog_file logging is disabled, then we cannot fall back to a + * logfile. All we can do is to hope that stderr logging will bring out + * the bad news. + */ + if (fallback_guard == 0 && var_maillog_file && *var_maillog_file + && logwriter_one_shot(var_maillog_file, text, strlen(text)) < 0) { + fallback_guard = 1; + msg_fatal("logfile '%s' write error: %m", var_maillog_file); + } +} + +/* maillog_client_init - set up syslog or internal log client */ + +void maillog_client_init(const char *progname, int flags) +{ + char *import_service_path; + char *import_hostname; + + /* + * Crucially, only one logger mode can be in effect at any time, + * otherwise postlogd(8) may go into a loop. + */ + enum { + MAILLOG_CLIENT_MODE_SYSLOG, MAILLOG_CLIENT_MODE_POSTLOG, + } logger_mode; + + /* + * Security: this code may run before the import_environment setting has + * taken effect. It has to guard against privilege escalation attacks on + * setgid programs, using malicious environment settings. + * + * Import the postlog service name and hostname from the environment. + * + * - These will be used and kept if the process has not yet initialized its + * configuration parameters. + * + * - These will be set or updated if the configuration enables postlog + * logging. + * + * - These will be removed if the configuration does not enable postlog + * logging. + */ + if ((import_service_path = safe_getenv(POSTLOG_SERVICE_ENV)) != 0 + && *import_service_path == 0) + import_service_path = 0; + if ((import_hostname = safe_getenv(POSTLOG_HOSTNAME_ENV)) != 0 + && *import_hostname == 0) + import_hostname = 0; + +#if MAILLOG_CLIENT_DEBUG +#define STRING_OR_NULL(s) ((s) ? (s) : "(null)") + msg_syslog_init(progname, LOG_PID, LOG_FACILITY); + msg_info("import_service_path=%s", STRING_OR_NULL(import_service_path)); + msg_info("import_hostname=%s", STRING_OR_NULL(import_hostname)); +#endif + + /* + * Before configuration parameters are initialized, the logging mode is + * controlled by the presence or absence of POSTLOG_SERVICE in the + * process environment. After configuration parameters are initialized, + * the logging mode is controlled by the "maillog_file" parameter value. + * + * The configured mode may change after a process is started. The + * postlogd(8) server will proxy logging to syslogd where needed. + */ + if (var_maillog_file ? *var_maillog_file == 0 : import_service_path == 0) { + logger_mode = MAILLOG_CLIENT_MODE_SYSLOG; + } else { + /* var_maillog_file ? *var_maillog_file : import_service_path != 0 */ + logger_mode = MAILLOG_CLIENT_MODE_POSTLOG; + } + + /* + * Postlog logging is enabled. Update the 'progname' as that may have + * changed since an earlier call, and update the environment settings if + * they differ from configuration settings. This blends two code paths, + * one code path where configuration parameters are initialized (the + * preferred path), and one code path that uses imports from environment. + */ + if (logger_mode == MAILLOG_CLIENT_MODE_POSTLOG) { + char *myhostname; + char *service_path; + + if (var_maillog_file && *var_maillog_file) { + ARGV *good_prefixes = argv_split(var_maillog_file_pfxs, + CHARS_COMMA_SP); + char **cpp; + + for (cpp = good_prefixes->argv; /* see below */ ; cpp++) { + if (*cpp == 0) + msg_fatal("%s value '%s' does not match any prefix in %s", + VAR_MAILLOG_FILE, var_maillog_file, + VAR_MAILLOG_FILE_PFXS); + if (strncmp(var_maillog_file, *cpp, strlen(*cpp)) == 0) + break; + } + argv_free(good_prefixes); + } + if (var_myhostname && *var_myhostname) { + myhostname = var_myhostname; + } else if ((myhostname = import_hostname) == 0) { + myhostname = "amnesiac"; + } +#if MAILLOG_CLIENT_DEBUG + msg_info("myhostname=%s", STRING_OR_NULL(myhostname)); +#endif + if (var_postlog_service) { + service_path = concatenate(var_queue_dir, "/", MAIL_CLASS_PUBLIC, + "/", var_postlog_service, (char *) 0); + } else { + + /* + * var_postlog_service == 0, therefore var_maillog_file == 0. + * logger_mode == MAILLOG_CLIENT_MODE_POSTLOG && var_maillog_file == + * 0, therefore import_service_path != 0. + */ + service_path = import_service_path; + } + maillog_client_flags = flags; + msg_logger_init(progname, myhostname, service_path, + (flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK) ? + maillog_client_logwriter_fallback : + (MSG_LOGGER_FALLBACK_FN) 0); + + /* + * Export or update the exported postlog service pathname and the + * hostname, so that a child process can bootstrap postlog logging + * before it has processed main.cf and command-line options. + */ + if (import_service_path == 0 + || strcmp(service_path, import_service_path) != 0) { +#if MAILLOG_CLIENT_DEBUG + msg_info("export %s=%s", POSTLOG_SERVICE_ENV, service_path); +#endif + if (setenv(POSTLOG_SERVICE_ENV, service_path, 1) < 0) + msg_fatal("setenv: %m"); + } + if (import_hostname == 0 || strcmp(myhostname, import_hostname) != 0) { +#if MAILLOG_CLIENT_DEBUG + msg_info("export %s=%s", POSTLOG_HOSTNAME_ENV, myhostname); +#endif + if (setenv(POSTLOG_HOSTNAME_ENV, myhostname, 1) < 0) + msg_fatal("setenv: %m"); + } + if (service_path != import_service_path) + myfree(service_path); + msg_logger_control(CA_MSG_LOGGER_CTL_CONNECT_NOW, + CA_MSG_LOGGER_CTL_END); + } + + /* + * Postlog logging is disabled. Silence the msg_logger client, and remove + * the environment settings that bootstrap postlog logging in a child + * process. + */ + else { + msg_logger_control(CA_MSG_LOGGER_CTL_DISABLE, CA_MSG_LOGGER_CTL_END); + if ((import_service_path && unsetenv(POSTLOG_SERVICE_ENV)) + || (import_hostname && unsetenv(POSTLOG_HOSTNAME_ENV))) + msg_fatal("unsetenv: %m"); + } + + /* + * Syslog logging is enabled. Update the 'progname' as that may have + * changed since an earlier call. + */ + if (logger_mode == MAILLOG_CLIENT_MODE_SYSLOG) { + msg_syslog_init(progname, LOG_PID, LOG_FACILITY); + } + + /* + * Syslog logging is disabled, silence the syslog client. + */ + else { + msg_syslog_disable(); + } +} |