xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/maillog_client.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: maillog_client.c,v 1.2 2020/03/18 19:05:16 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 	    service_path = import_service_path;
241 	}
242 	maillog_client_flags = flags;
243 	msg_logger_init(progname, myhostname, service_path,
244 			(flags & MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK) ?
245 			maillog_client_logwriter_fallback :
246 			(MSG_LOGGER_FALLBACK_FN) 0);
247 
248 	/*
249 	 * Export or update the exported postlog service pathname and the
250 	 * hostname, so that a child process can bootstrap postlog logging
251 	 * before it has processed main.cf and command-line options.
252 	 */
253 	if (import_service_path == 0
254 	    || strcmp(service_path, import_service_path) != 0) {
255 #if MAILLOG_CLIENT_DEBUG
256 	    msg_info("export %s=%s", POSTLOG_SERVICE_ENV, service_path);
257 #endif
258 	    if (setenv(POSTLOG_SERVICE_ENV, service_path, 1) < 0)
259 		msg_fatal("setenv: %m");
260 	}
261 	if (import_hostname == 0 || strcmp(myhostname, import_hostname) != 0) {
262 #if MAILLOG_CLIENT_DEBUG
263 	    msg_info("export %s=%s", POSTLOG_HOSTNAME_ENV, myhostname);
264 #endif
265 	    if (setenv(POSTLOG_HOSTNAME_ENV, myhostname, 1) < 0)
266 		msg_fatal("setenv: %m");
267 	}
268 	if (service_path != import_service_path)
269 	    myfree(service_path);
270 	msg_logger_control(CA_MSG_LOGGER_CTL_CONNECT_NOW,
271 			   CA_MSG_LOGGER_CTL_END);
272     }
273 
274     /*
275      * Postlog logging is disabled. Silence the msg_logger client, and remove
276      * the environment settings that bootstrap postlog logging in a child
277      * process.
278      */
279     else {
280 	msg_logger_control(CA_MSG_LOGGER_CTL_DISABLE, CA_MSG_LOGGER_CTL_END);
281 	if ((import_service_path && unsetenv(POSTLOG_SERVICE_ENV))
282 	    || (import_hostname && unsetenv(POSTLOG_HOSTNAME_ENV)))
283 	    msg_fatal("unsetenv: %m");
284     }
285 
286     /*
287      * Syslog logging is enabled. Update the 'progname' as that may have
288      * changed since an earlier call.
289      */
290     if (logger_mode == MAILLOG_CLIENT_MODE_SYSLOG) {
291 	msg_syslog_init(progname, LOG_PID, LOG_FACILITY);
292     }
293 
294     /*
295      * Syslog logging is disabled, silence the syslog client.
296      */
297     else {
298 	msg_syslog_disable();
299     }
300 }
301