1 /* $NetBSD: maillog_client.c,v 1.3 2022/10/08 16:12:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* maillog_client 3 6 /* SUMMARY 7 /* choose between syslog client and postlog client 8 /* SYNOPSIS 9 /* #include <maillog_client.h> 10 /* 11 /* int maillog_client_init( 12 /* const char *progname, 13 /* int flags) 14 /* DESCRIPTION 15 /* maillog_client_init() chooses between logging to the syslog 16 /* service or to the internal postlog service. 17 /* 18 /* maillog_client_init() may be called before configuration 19 /* parameters are initialized. During this time, logging is 20 /* controlled by the presence or absence of POSTLOG_SERVICE 21 /* in the process environment (this is ignored if a program 22 /* runs with set-uid or set-gid permissions). 23 /* 24 /* maillog_client_init() may also be called after configuration 25 /* parameters are initialized. During this time, logging is 26 /* controlled by the "maillog_file" parameter value. 27 /* 28 /* Arguments: 29 /* .IP progname 30 /* The program name that will be prepended to logfile records. 31 /* .IP flags 32 /* Specify one of the following: 33 /* .RS 34 /* .IP MAILLOG_CLIENT_FLAG_NONE 35 /* No special processing. 36 /* .IP MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK 37 /* Try to fall back to writing the "maillog_file" directly, 38 /* if logging to the internal postlog service is enabled, but 39 /* the postlog service is unavailable. If the fallback fails, 40 /* die with a fatal error. 41 /* .RE 42 /* ENVIRONMENT 43 /* .ad 44 /* .fi 45 /* When logging to the internal postlog service is enabled, 46 /* each process exports the following information, to help 47 /* initialize the logging in a child process, before the child 48 /* has initialized its configuration parameters. 49 /* .IP POSTLOG_SERVICE 50 /* The pathname of the public postlog service endpoint, usually 51 /* "$queue_directory/public/$postlog_service_name". 52 /* .IP POSTLOG_HOSTNAME 53 /* The hostname to prepend to information that is sent to the 54 /* internal postlog logging service, usually "$myhostname". 55 /* CONFIGURATION PARAMETERS 56 /* .ad 57 /* .fi 58 /* .IP "maillog_file (empty)" 59 /* The name of an optional logfile. If the value is empty, or 60 /* unitialized and the process environment does not specify 61 /* POSTLOG_SERVICE, the program will log to the syslog service 62 /* instead. 63 /* .IP "myhostname (default: see postconf -d output)" 64 /* The internet hostname of this mail system. 65 /* .IP "postlog_service_name (postlog)" 66 /* The name of the internal postlog logging service. 67 /* SEE ALSO 68 /* msg_syslog(3) syslog client 69 /* msg_logger(3) internal logger 70 /* LICENSE 71 /* .ad 72 /* .fi 73 /* The Secure Mailer license must be distributed with this 74 /* software. 75 /* AUTHOR(S) 76 /* Wietse Venema 77 /* Google, Inc. 78 /* 111 8th Avenue 79 /* New York, NY 10011, USA 80 /*--*/ 81 82 /* 83 * System library. 84 */ 85 #include <sys_defs.h> 86 #include <stdlib.h> 87 #include <string.h> 88 89 /* 90 * Utility library. 91 */ 92 #include <argv.h> 93 #include <logwriter.h> 94 #include <msg_logger.h> 95 #include <msg_syslog.h> 96 #include <safe.h> 97 #include <stringops.h> 98 99 /* 100 * Global library. 101 */ 102 #include <mail_params.h> 103 #include <mail_proto.h> 104 #include <maillog_client.h> 105 #include <msg.h> 106 107 /* 108 * Using logging to debug logging is painful. 109 */ 110 #define MAILLOG_CLIENT_DEBUG 0 111 112 /* 113 * Application-specific. 114 */ 115 static int maillog_client_flags; 116 117 #define POSTLOG_SERVICE_ENV "POSTLOG_SERVICE" 118 #define POSTLOG_HOSTNAME_ENV "POSTLOG_HOSTNAME" 119 120 /* maillog_client_logwriter_fallback - fall back to logfile writer or bust */ 121 122 static void maillog_client_logwriter_fallback(const char *text) 123 { 124 static int fallback_guard = 0; 125 126 /* 127 * Guard against recursive calls. 128 * 129 * If an error happened before the maillog_file parameter was initialized, 130 * or if maillog_file logging is disabled, then we cannot fall back to a 131 * logfile. All we can do is to hope that stderr logging will bring out 132 * the bad news. 133 */ 134 if (fallback_guard == 0 && var_maillog_file && *var_maillog_file 135 && logwriter_one_shot(var_maillog_file, text, strlen(text)) < 0) { 136 fallback_guard = 1; 137 msg_fatal("logfile '%s' write error: %m", var_maillog_file); 138 } 139 } 140 141 /* maillog_client_init - set up syslog or internal log client */ 142 143 void maillog_client_init(const char *progname, int flags) 144 { 145 char *import_service_path; 146 char *import_hostname; 147 148 /* 149 * Crucially, only one logger mode can be in effect at any time, 150 * otherwise postlogd(8) may go into a loop. 151 */ 152 enum { 153 MAILLOG_CLIENT_MODE_SYSLOG, MAILLOG_CLIENT_MODE_POSTLOG, 154 } logger_mode; 155 156 /* 157 * Security: this code may run before the import_environment setting has 158 * taken effect. It has to guard against privilege escalation attacks on 159 * setgid programs, using malicious environment settings. 160 * 161 * Import the postlog service name and hostname from the environment. 162 * 163 * - These will be used and kept if the process has not yet initialized its 164 * configuration parameters. 165 * 166 * - These will be set or updated if the configuration enables postlog 167 * logging. 168 * 169 * - These will be removed if the configuration does not enable postlog 170 * logging. 171 */ 172 if ((import_service_path = safe_getenv(POSTLOG_SERVICE_ENV)) != 0 173 && *import_service_path == 0) 174 import_service_path = 0; 175 if ((import_hostname = safe_getenv(POSTLOG_HOSTNAME_ENV)) != 0 176 && *import_hostname == 0) 177 import_hostname = 0; 178 179 #if MAILLOG_CLIENT_DEBUG 180 #define STRING_OR_NULL(s) ((s) ? (s) : "(null)") 181 msg_syslog_init(progname, LOG_PID, LOG_FACILITY); 182 msg_info("import_service_path=%s", STRING_OR_NULL(import_service_path)); 183 msg_info("import_hostname=%s", STRING_OR_NULL(import_hostname)); 184 #endif 185 186 /* 187 * Before configuration parameters are initialized, the logging mode is 188 * controlled by the presence or absence of POSTLOG_SERVICE in the 189 * process environment. After configuration parameters are initialized, 190 * the logging mode is controlled by the "maillog_file" parameter value. 191 * 192 * The configured mode may change after a process is started. The 193 * postlogd(8) server will proxy logging to syslogd where needed. 194 */ 195 if (var_maillog_file ? *var_maillog_file == 0 : import_service_path == 0) { 196 logger_mode = MAILLOG_CLIENT_MODE_SYSLOG; 197 } else { 198 /* var_maillog_file ? *var_maillog_file : import_service_path != 0 */ 199 logger_mode = MAILLOG_CLIENT_MODE_POSTLOG; 200 } 201 202 /* 203 * Postlog logging is enabled. Update the 'progname' as that may have 204 * changed since an earlier call, and update the environment settings if 205 * they differ from configuration settings. This blends two code paths, 206 * one code path where configuration parameters are initialized (the 207 * preferred path), and one code path that uses imports from environment. 208 */ 209 if (logger_mode == MAILLOG_CLIENT_MODE_POSTLOG) { 210 char *myhostname; 211 char *service_path; 212 213 if (var_maillog_file && *var_maillog_file) { 214 ARGV *good_prefixes = argv_split(var_maillog_file_pfxs, 215 CHARS_COMMA_SP); 216 char **cpp; 217 218 for (cpp = good_prefixes->argv; /* see below */ ; cpp++) { 219 if (*cpp == 0) 220 msg_fatal("%s value '%s' does not match any prefix in %s", 221 VAR_MAILLOG_FILE, var_maillog_file, 222 VAR_MAILLOG_FILE_PFXS); 223 if (strncmp(var_maillog_file, *cpp, strlen(*cpp)) == 0) 224 break; 225 } 226 argv_free(good_prefixes); 227 } 228 if (var_myhostname && *var_myhostname) { 229 myhostname = var_myhostname; 230 } else if ((myhostname = import_hostname) == 0) { 231 myhostname = "amnesiac"; 232 } 233 #if MAILLOG_CLIENT_DEBUG 234 msg_info("myhostname=%s", STRING_OR_NULL(myhostname)); 235 #endif 236 if (var_postlog_service) { 237 service_path = concatenate(var_queue_dir, "/", MAIL_CLASS_PUBLIC, 238 "/", var_postlog_service, (char *) 0); 239 } else { 240 241 /* 242 * var_postlog_service == 0, therefore var_maillog_file == 0. 243 * logger_mode == MAILLOG_CLIENT_MODE_POSTLOG && var_maillog_file == 244 * 0, therefore import_service_path != 0. 245 */ 246 service_path = import_service_path; 247 } 248 maillog_client_flags = flags; 249 msg_logger_init(progname, myhostname, service_path, 250 (flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK) ? 251 maillog_client_logwriter_fallback : 252 (MSG_LOGGER_FALLBACK_FN) 0); 253 254 /* 255 * Export or update the exported postlog service pathname and the 256 * hostname, so that a child process can bootstrap postlog logging 257 * before it has processed main.cf and command-line options. 258 */ 259 if (import_service_path == 0 260 || strcmp(service_path, import_service_path) != 0) { 261 #if MAILLOG_CLIENT_DEBUG 262 msg_info("export %s=%s", POSTLOG_SERVICE_ENV, service_path); 263 #endif 264 if (setenv(POSTLOG_SERVICE_ENV, service_path, 1) < 0) 265 msg_fatal("setenv: %m"); 266 } 267 if (import_hostname == 0 || strcmp(myhostname, import_hostname) != 0) { 268 #if MAILLOG_CLIENT_DEBUG 269 msg_info("export %s=%s", POSTLOG_HOSTNAME_ENV, myhostname); 270 #endif 271 if (setenv(POSTLOG_HOSTNAME_ENV, myhostname, 1) < 0) 272 msg_fatal("setenv: %m"); 273 } 274 if (service_path != import_service_path) 275 myfree(service_path); 276 msg_logger_control(CA_MSG_LOGGER_CTL_CONNECT_NOW, 277 CA_MSG_LOGGER_CTL_END); 278 } 279 280 /* 281 * Postlog logging is disabled. Silence the msg_logger client, and remove 282 * the environment settings that bootstrap postlog logging in a child 283 * process. 284 */ 285 else { 286 msg_logger_control(CA_MSG_LOGGER_CTL_DISABLE, CA_MSG_LOGGER_CTL_END); 287 if ((import_service_path && unsetenv(POSTLOG_SERVICE_ENV)) 288 || (import_hostname && unsetenv(POSTLOG_HOSTNAME_ENV))) 289 msg_fatal("unsetenv: %m"); 290 } 291 292 /* 293 * Syslog logging is enabled. Update the 'progname' as that may have 294 * changed since an earlier call. 295 */ 296 if (logger_mode == MAILLOG_CLIENT_MODE_SYSLOG) { 297 msg_syslog_init(progname, LOG_PID, LOG_FACILITY); 298 } 299 300 /* 301 * Syslog logging is disabled, silence the syslog client. 302 */ 303 else { 304 msg_syslog_disable(); 305 } 306 } 307