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