1 /* $OpenBSD: logger.c,v 1.24 2021/01/27 07:21:53 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/uio.h> 22 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 #include <fcntl.h> 29 #include <imsg.h> 30 31 #include "httpd.h" 32 33 int logger_dispatch_parent(int, struct privsep_proc *, 34 struct imsg *); 35 int logger_dispatch_server(int, struct privsep_proc *, 36 struct imsg *); 37 void logger_shutdown(void); 38 void logger_close(void); 39 struct log_file *logger_open_file(const char *); 40 int logger_open_fd(struct imsg *); 41 int logger_open(struct server *, struct server_config *, void *); 42 void logger_init(struct privsep *, struct privsep_proc *p, void *); 43 int logger_start(void); 44 int logger_log(struct imsg *); 45 46 static uint32_t last_log_id = 0; 47 48 struct log_files log_files; 49 50 static struct privsep_proc procs[] = { 51 { "parent", PROC_PARENT, logger_dispatch_parent }, 52 { "server", PROC_SERVER, logger_dispatch_server } 53 }; 54 55 void 56 logger(struct privsep *ps, struct privsep_proc *p) 57 { 58 proc_run(ps, p, procs, nitems(procs), logger_init, NULL); 59 } 60 61 void 62 logger_shutdown(void) 63 { 64 logger_close(); 65 config_purge(httpd_env, CONFIG_ALL); 66 } 67 68 void 69 logger_init(struct privsep *ps, struct privsep_proc *p, void *arg) 70 { 71 if (pledge("stdio recvfd", NULL) == -1) 72 fatal("pledge"); 73 74 if (config_init(ps->ps_env) == -1) 75 fatal("failed to initialize configuration"); 76 77 /* We use a custom shutdown callback */ 78 p->p_shutdown = logger_shutdown; 79 80 TAILQ_INIT(&log_files); 81 } 82 83 void 84 logger_close(void) 85 { 86 struct log_file *log, *next; 87 88 TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) { 89 if (log->log_fd != -1) { 90 close(log->log_fd); 91 log->log_fd = -1; 92 } 93 TAILQ_REMOVE(&log_files, log, log_entry); 94 free(log); 95 } 96 } 97 98 struct log_file * 99 logger_open_file(const char *name) 100 { 101 struct log_file *log; 102 struct iovec iov[2]; 103 104 if ((log = calloc(1, sizeof(*log))) == NULL) { 105 log_warn("failed to allocate log %s", name); 106 return (NULL); 107 } 108 109 log->log_id = ++last_log_id; 110 (void)strlcpy(log->log_name, name, sizeof(log->log_name)); 111 112 /* The file will be opened by the parent process */ 113 log->log_fd = -1; 114 115 iov[0].iov_base = &log->log_id; 116 iov[0].iov_len = sizeof(log->log_id); 117 iov[1].iov_base = log->log_name; 118 iov[1].iov_len = strlen(log->log_name) + 1; 119 120 if (proc_composev(httpd_env->sc_ps, PROC_PARENT, IMSG_LOG_OPEN, 121 iov, 2) != 0) { 122 log_warn("%s: failed to compose IMSG_LOG_OPEN imsg", __func__); 123 goto err; 124 } 125 126 TAILQ_INSERT_TAIL(&log_files, log, log_entry); 127 128 return (log); 129 130 err: 131 free(log); 132 133 return (NULL); 134 } 135 136 int 137 logger_open_fd(struct imsg *imsg) 138 { 139 struct log_file *log; 140 uint32_t id; 141 142 IMSG_SIZE_CHECK(imsg, &id); 143 memcpy(&id, imsg->data, sizeof(id)); 144 145 TAILQ_FOREACH(log, &log_files, log_entry) { 146 if (log->log_id == id) { 147 DPRINTF("%s: received log fd %d, file %s", 148 __func__, imsg->fd, log->log_name); 149 log->log_fd = imsg->fd; 150 return (0); 151 } 152 } 153 154 return (-1); 155 } 156 157 int 158 logger_open_priv(struct imsg *imsg) 159 { 160 char path[PATH_MAX]; 161 char name[PATH_MAX], *p; 162 uint32_t id; 163 size_t len; 164 int fd; 165 166 /* called from the privileged process */ 167 IMSG_SIZE_CHECK(imsg, &id); 168 memcpy(&id, imsg->data, sizeof(id)); 169 p = (char *)imsg->data + sizeof(id); 170 171 if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name)) 172 return (-1); 173 if ((len = strlcpy(path, httpd_env->sc_logdir, sizeof(path))) 174 >= sizeof(path)) 175 return (-1); 176 177 p = path + len; 178 len = sizeof(path) - len; 179 180 if (canonicalize_path(name, p, len) == NULL) { 181 log_warnx("invalid log name"); 182 return (-1); 183 } 184 185 if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) { 186 log_warn("failed to open %s", path); 187 return (-1); 188 } 189 190 proc_compose_imsg(httpd_env->sc_ps, PROC_LOGGER, -1, 191 IMSG_LOG_OPEN, -1, fd, &id, sizeof(id)); 192 193 DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd); 194 195 return (0); 196 } 197 198 int 199 logger_open(struct server *srv, struct server_config *srv_conf, void *arg) 200 { 201 struct log_file *log, *logfile = NULL, *errfile = NULL; 202 203 if (srv_conf->flags & (SRVFLAG_SYSLOG | SRVFLAG_NO_LOG)) 204 return (0); 205 206 /* disassociate */ 207 srv_conf->logaccess = srv_conf->logerror = NULL; 208 209 TAILQ_FOREACH(log, &log_files, log_entry) { 210 if (strcmp(log->log_name, srv_conf->accesslog) == 0) 211 logfile = log; 212 if (strcmp(log->log_name, srv_conf->errorlog) == 0) 213 errfile = log; 214 } 215 216 if (logfile == NULL) { 217 if ((srv_conf->logaccess = 218 logger_open_file(srv_conf->accesslog)) == NULL) 219 return (-1); 220 } else 221 srv_conf->logaccess = logfile; 222 223 if (errfile == NULL) { 224 if ((srv_conf->logerror = 225 logger_open_file(srv_conf->errorlog)) == NULL) 226 return (-1); 227 } else 228 srv_conf->logerror = errfile; 229 230 return (0); 231 } 232 233 int 234 logger_start(void) 235 { 236 logger_close(); 237 if (server_foreach(logger_open, NULL) == -1) 238 fatalx("failed to open log files"); 239 return (0); 240 } 241 242 int 243 logger_log(struct imsg *imsg) 244 { 245 char *logline; 246 uint32_t id; 247 struct server_config *srv_conf; 248 struct log_file *log; 249 250 IMSG_SIZE_CHECK(imsg, &id); 251 memcpy(&id, imsg->data, sizeof(id)); 252 253 if ((srv_conf = serverconfig_byid(id)) == NULL) 254 fatalx("invalid logging requestr"); 255 256 if (imsg->hdr.type == IMSG_LOG_ACCESS) 257 log = srv_conf->logaccess; 258 else 259 log = srv_conf->logerror; 260 261 if (log == NULL || log->log_fd == -1) { 262 log_warnx("log file %s not opened", log ? log->log_name : ""); 263 return (0); 264 } 265 266 /* XXX get_string() would sanitize the string, but add a malloc */ 267 logline = (char *)imsg->data + sizeof(id); 268 269 /* For debug output */ 270 log_debug("%s", logline); 271 272 if (dprintf(log->log_fd, "%s\n", logline) == -1) { 273 if (logger_start() == -1) 274 return (-1); 275 } 276 277 return (0); 278 } 279 280 int 281 logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 282 { 283 switch (imsg->hdr.type) { 284 case IMSG_CFG_SERVER: 285 config_getserver(httpd_env, imsg); 286 break; 287 case IMSG_CFG_DONE: 288 config_getcfg(httpd_env, imsg); 289 break; 290 case IMSG_CTL_START: 291 case IMSG_CTL_REOPEN: 292 logger_start(); 293 break; 294 case IMSG_CTL_RESET: 295 config_getreset(httpd_env, imsg); 296 break; 297 case IMSG_LOG_OPEN: 298 return (logger_open_fd(imsg)); 299 default: 300 return (-1); 301 } 302 303 return (0); 304 } 305 306 int 307 logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg) 308 { 309 switch (imsg->hdr.type) { 310 case IMSG_LOG_ACCESS: 311 case IMSG_LOG_ERROR: 312 logger_log(imsg); 313 break; 314 default: 315 return (-1); 316 } 317 318 return (0); 319 } 320