1 /* $NetBSD: postlog.c,v 1.5 2023/12/23 20:30:44 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postlog 1 6 /* SUMMARY 7 /* Postfix-compatible logging utility 8 /* SYNOPSIS 9 /* .fi 10 /* .ad 11 /* \fBpostlog\fR [\fB-iv\fR] [\fB-c \fIconfig_dir\fR] 12 /* [\fB-p \fIpriority\fR] [\fB-t \fItag\fR] [\fItext...\fR] 13 /* DESCRIPTION 14 /* The \fBpostlog\fR(1) command implements a Postfix-compatible logging 15 /* interface for use in, for example, shell scripts. 16 /* 17 /* By default, \fBpostlog\fR(1) logs the \fItext\fR given on the command 18 /* line as one record. If no \fItext\fR is specified on the command 19 /* line, \fBpostlog\fR(1) reads from standard input and logs each input 20 /* line as one record. 21 /* 22 /* By default, logging is sent to \fBsyslogd\fR(8) or 23 /* \fBpostlogd\fR(8); when the 24 /* standard error stream is connected to a terminal, logging 25 /* is sent there as well. 26 /* 27 /* The following options are implemented: 28 /* .IP "\fB-c \fIconfig_dir\fR" 29 /* Read the \fBmain.cf\fR configuration file in the named directory 30 /* instead of the default configuration directory. 31 /* .IP "\fB-i\fR (obsolete)" 32 /* Include the process ID in the logging tag. This flag is ignored as 33 /* of Postfix 3.4, where the PID is always included. 34 /* .IP "\fB-p \fIpriority\fR (default: \fBinfo\fR)" 35 /* Specifies the logging severity: \fBinfo\fR, \fBwarn\fR, 36 /* \fBerror\fR, \fBfatal\fR, or \fBpanic\fR. With Postfix 3.1 37 /* and later, the program will pause for 1 second after reporting 38 /* a \fBfatal\fR or \fBpanic\fR condition, just like other 39 /* Postfix programs. 40 /* .IP "\fB-t \fItag\fR" 41 /* Specifies the logging tag, that is, the identifying name that 42 /* appears at the beginning of each logging record. A default tag 43 /* is used when none is specified. 44 /* .IP \fB-v\fR 45 /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR 46 /* options make the software increasingly verbose. 47 /* SECURITY 48 /* .ad 49 /* .fi 50 /* The \fBpostlog\fR(1) command is designed to run with 51 /* set-groupid privileges, so that it can connect to the 52 /* \fBpostlogd\fR(8) daemon process (Postfix 3.7 and later; 53 /* earlier implementations of this command must not have 54 /* set-groupid or set-userid permissions). 55 /* ENVIRONMENT 56 /* .ad 57 /* .fi 58 /* .IP MAIL_CONFIG 59 /* Directory with the \fBmain.cf\fR file. 60 /* CONFIGURATION PARAMETERS 61 /* .ad 62 /* .fi 63 /* The following \fBmain.cf\fR parameters are especially relevant to 64 /* this program. 65 /* 66 /* The text below provides only a parameter summary. See 67 /* \fBpostconf\fR(5) for more details including examples. 68 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" 69 /* The default location of the Postfix main.cf and master.cf 70 /* configuration files. 71 /* .IP "\fBimport_environment (see 'postconf -d' output)\fR" 72 /* The list of environment variables that a privileged Postfix 73 /* process will import from a non-Postfix parent process, or name=value 74 /* environment overrides. 75 /* .IP "\fBsyslog_facility (mail)\fR" 76 /* The syslog facility of Postfix logging. 77 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" 78 /* A prefix that is prepended to the process name in syslog 79 /* records, so that, for example, "smtpd" becomes "prefix/smtpd". 80 /* .PP 81 /* Available in Postfix 3.4 and later: 82 /* .IP "\fBmaillog_file (empty)\fR" 83 /* The name of an optional logfile that is written by the Postfix 84 /* \fBpostlogd\fR(8) service. 85 /* .IP "\fBpostlog_service_name (postlog)\fR" 86 /* The name of the \fBpostlogd\fR(8) service entry in master.cf. 87 /* SEE ALSO 88 /* postconf(5), configuration parameters 89 /* postlogd(8), Postfix logging 90 /* syslogd(8), system logging 91 /* LICENSE 92 /* .ad 93 /* .fi 94 /* The Secure Mailer license must be distributed with this software. 95 /* HISTORY 96 /* The \fBpostlog\fR(1) command was introduced with Postfix 97 /* version 3.4. 98 /* AUTHOR(S) 99 /* Wietse Venema 100 /* IBM T.J. Watson Research 101 /* P.O. Box 704 102 /* Yorktown Heights, NY 10598, USA 103 /* 104 /* Wietse Venema 105 /* Google, Inc. 106 /* 111 8th Avenue 107 /* New York, NY 10011, USA 108 /*--*/ 109 110 /* System library. */ 111 112 #include <sys_defs.h> 113 #include <sys/stat.h> 114 #include <string.h> 115 #include <fcntl.h> 116 #include <stdlib.h> 117 #include <unistd.h> 118 119 #ifdef STRCASECMP_IN_STRINGS_H 120 #include <strings.h> 121 #endif 122 123 /* Utility library. */ 124 125 #include <msg.h> 126 #include <vstring.h> 127 #include <vstream.h> 128 #include <vstring_vstream.h> 129 #include <msg_output.h> 130 #include <msg_vstream.h> 131 #include <warn_stat.h> 132 #include <clean_env.h> 133 #include <stringops.h> 134 135 /* Global library. */ 136 137 #include <mail_params.h> /* XXX right place for LOG_FACILITY? */ 138 #include <mail_version.h> 139 #include <mail_conf.h> 140 #include <mail_task.h> 141 #include <mail_parm_split.h> 142 #include <maillog_client.h> 143 144 /* Application-specific. */ 145 146 /* 147 * WARNING WARNING WARNING 148 * 149 * This software is designed to run set-gid. In order to avoid exploitation of 150 * privilege, this software should not run any external commands, nor should 151 * it take any information from the user, unless that information can be 152 * properly sanitized. To get an idea of how much information a process can 153 * inherit from a potentially hostile user, examine all the members of the 154 * process structure (typically, in /usr/include/sys/proc.h): the current 155 * directory, open files, timers, signals, environment, command line, umask, 156 * and so on. 157 */ 158 159 /* 160 * Access lists. 161 */ 162 #if 0 163 char *var_postlog_acl; 164 165 X static const CONFIG_STR_TABLE str_table[] = { 166 X VAR_POSTLOG_ACL, DEF_POSTLOG_ACL, &var_postlog_acl, 0, 0, 167 X 0, 168 X}; 169 170 #endif 171 172 /* 173 * Support for the severity level mapping. 174 */ 175 struct level_table { 176 char *name; 177 int level; 178 }; 179 180 static struct level_table level_table[] = { 181 "info", MSG_INFO, 182 "warn", MSG_WARN, 183 "warning", MSG_WARN, 184 "error", MSG_ERROR, 185 "err", MSG_ERROR, 186 "fatal", MSG_FATAL, 187 "crit", MSG_FATAL, 188 "panic", MSG_PANIC, 189 0, 190 }; 191 192 #define POSTLOG_CMD "postlog" 193 194 /* level_map - lookup facility or severity value */ 195 196 static int level_map(char *name) 197 { 198 struct level_table *t; 199 200 for (t = level_table; t->name; t++) 201 if (strcasecmp(t->name, name) == 0) 202 return (t->level); 203 msg_fatal("bad severity: \"%s\"", name); 204 } 205 206 /* log_argv - log the command line */ 207 208 static void log_argv(int level, char **argv) 209 { 210 VSTRING *buf = vstring_alloc(100); 211 212 while (*argv) { 213 vstring_strcat(buf, *argv++); 214 if (*argv) 215 vstring_strcat(buf, " "); 216 } 217 msg_printf(level, "%s", vstring_str(buf)); 218 vstring_free(buf); 219 } 220 221 /* log_stream - log lines from a stream */ 222 223 static void log_stream(int level, VSTREAM *fp) 224 { 225 VSTRING *buf = vstring_alloc(100); 226 227 while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) 228 msg_printf(level, "%s", vstring_str(buf)); 229 vstring_free(buf); 230 } 231 232 MAIL_VERSION_STAMP_DECLARE; 233 234 /* main - logger */ 235 236 int main(int argc, char **argv) 237 { 238 struct stat st; 239 int fd; 240 int ch; 241 const char *tag; 242 char *unsanitized_tag; 243 int level = MSG_INFO; 244 ARGV *import_env; 245 246 /* 247 * Fingerprint executables and core dumps. 248 */ 249 MAIL_VERSION_STAMP_ALLOCATE; 250 251 /* 252 * Be consistent with file permissions. 253 */ 254 umask(022); 255 256 /* 257 * To minimize confusion, make sure that the standard file descriptors 258 * are open before opening anything else. XXX Work around for 44BSD where 259 * fstat can return EBADF on an open file descriptor. 260 */ 261 for (fd = 0; fd < 3; fd++) 262 if (fstat(fd, &st) == -1 263 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) 264 msg_fatal("open /dev/null: %m"); 265 266 /* 267 * Set up diagnostics. Censor the process name: it is provided by the 268 * user. 269 */ 270 argv[0] = POSTLOG_CMD; 271 tag = mail_task(argv[0]); 272 msg_vstream_init(tag, VSTREAM_ERR); 273 maillog_client_init(tag, MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK); 274 275 /* 276 * Check the Postfix library version as soon as we enable logging. 277 */ 278 MAIL_VERSION_CHECK; 279 280 /* 281 * Parse switches. This program is set-gid and must sanitize all 282 * command-line parameters. The configuration directory argument is 283 * validated by the mail configuration read routine. Don't do complex 284 * things until we have completed initializations. 285 */ 286 unsanitized_tag = 0; 287 while ((ch = GETOPT(argc, argv, "c:ip:t:v")) > 0) { 288 switch (ch) { 289 default: 290 msg_fatal("usage: %s [-c config_dir] [-i] [-p priority] [-t tag] [-v] [text]", argv[0]); 291 break; 292 case 'c': 293 if (setenv(CONF_ENV_PATH, optarg, 1) < 0) 294 msg_fatal("out of memory"); 295 break; 296 case 'i': 297 break; 298 case 'p': 299 level = level_map(optarg); 300 break; 301 case 't': 302 unsanitized_tag = optarg; 303 break; 304 case 'v': 305 msg_verbose++; 306 break; 307 } 308 } 309 310 /* 311 * Process the main.cf file. This may change the syslog_name setting and 312 * may require that mail_task() be re-evaluated. 313 */ 314 mail_conf_read(); 315 /* Re-evaluate mail_task() after reading main.cf. */ 316 maillog_client_init(mail_task(POSTLOG_CMD), MAILLOG_CLIENT_FLAG_NONE); 317 #if 0 318 mail_dict_init(); /* proxy, sql, ldap */ 319 get_mail_conf_str_table(str_table); 320 #endif 321 322 /* 323 * This program is designed to be set-gid, which makes it a potential 324 * target for attack. Strip and optionally override the process 325 * environment so that we don't have to trust the C library. 326 */ 327 import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); 328 clean_env(import_env->argv); 329 argv_free(import_env); 330 331 /* 332 * Sanitize the user-specified tag. The result depends on the value of 333 * var_smtputf8_enable, therefore this code is after the mail_conf_read() 334 * call. 335 */ 336 if (unsanitized_tag != 0) 337 tag = printable(unsanitized_tag, '?'); 338 339 /* 340 * Re-initialize the logging, this time with the tag specified in main.cf 341 * or on the command line. 342 */ 343 if (isatty(STDERR_FILENO)) 344 msg_vstream_init(tag, VSTREAM_ERR); 345 maillog_client_init(tag, MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK); 346 347 #if 0 348 uid_t uid = getuid(); 349 350 if (uid != 0 && uid != var_owner_uid 351 && (errstr = check_user_acl_byuid(VAR_SHOWQ_ACL, var_showq_acl, 352 uid)) != 0) 353 msg_fatal_status(EX_NOPERM, 354 "User %s(%ld) is not allowed to invoke 'postlog'", 355 errstr, (long) uid); 356 #endif 357 358 /* 359 * Log the command line or log lines from standard input. 360 */ 361 if (argc > optind) { 362 log_argv(level, argv + optind); 363 } else { 364 log_stream(level, VSTREAM_IN); 365 } 366 367 /* 368 * Consistency with msg(3) functions. 369 */ 370 if (level >= MSG_FATAL) 371 sleep(1); 372 exit(0); 373 } 374