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