2 * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/param.h>
31 #endif /* STDC_HEADERS */
35 # include "compat/stdbool.h"
39 #endif /* HAVE_STRING_H */
42 #endif /* HAVE_STRINGS_H */
45 #endif /* HAVE_UNISTD_H */
55 #include "sudo_plugin.h"
56 #include "sudo_debug.h"
59 * The debug priorities and subsystems are currently hard-coded.
60 * In the future we might consider allowing plugins to register their
61 * own subsystems and provide direct access to the debugging API.
64 /* Note: this must match the order in sudo_debug.h */
65 const char *const sudo_debug_priorities[] = {
77 /* Note: this must match the order in sudo_debug.h */
78 const char *const sudo_debug_subsystems[] = {
108 #define NUM_SUBSYSTEMS (sizeof(sudo_debug_subsystems) / sizeof(sudo_debug_subsystems[0]) - 1)
110 /* Values for sudo_debug_mode */
111 #define SUDO_DEBUG_MODE_DISABLED 0
112 #define SUDO_DEBUG_MODE_FILE 1
113 #define SUDO_DEBUG_MODE_CONV 2
115 static int sudo_debug_settings[NUM_SUBSYSTEMS];
116 static int sudo_debug_fd = -1;
117 static int sudo_debug_mode;
118 static char sudo_debug_pidstr[(((sizeof(int) * 8) + 2) / 3) + 3];
119 static size_t sudo_debug_pidlen;
121 extern sudo_conv_t sudo_conv;
124 * Parse settings string from sudo.conf and open debugfile.
125 * Returns 1 on success, 0 if cannot open debugfile.
126 * Unsupported subsystems and priorities are silently ignored.
128 int sudo_debug_init(const char *debugfile, const char *settings)
130 char *buf, *cp, *subsys, *pri;
133 /* Init per-subsystems settings to -1 since 0 is a valid priority. */
134 for (i = 0; i < NUM_SUBSYSTEMS; i++)
135 sudo_debug_settings[i] = -1;
137 /* Open debug file if specified. */
138 if (debugfile != NULL) {
139 if (sudo_debug_fd != -1)
140 close(sudo_debug_fd);
141 sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND|O_CREAT,
143 if (sudo_debug_fd == -1)
145 (void)fcntl(sudo_debug_fd, F_SETFD, FD_CLOEXEC);
146 sudo_debug_mode = SUDO_DEBUG_MODE_FILE;
148 /* Called from the plugin, no debug file. */
149 sudo_debug_mode = SUDO_DEBUG_MODE_CONV;
152 /* Parse settings string. */
153 buf = estrdup(settings);
154 for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
155 /* Should be in the form subsys@pri. */
157 if ((pri = strchr(cp, '@')) == NULL)
161 /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
162 for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
163 if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
164 for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
165 if (strcasecmp(subsys, "all") == 0) {
166 sudo_debug_settings[j] = i;
169 if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
170 sudo_debug_settings[j] = i;
180 (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
182 sudo_debug_pidlen = strlen(sudo_debug_pidstr);
188 sudo_debug_fork(void)
192 if ((pid = fork()) == 0) {
193 (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
195 sudo_debug_pidlen = strlen(sudo_debug_pidstr);
202 sudo_debug_enter(const char *func, const char *file, int line,
205 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
206 "-> %s @ %s:%d", func, file, line);
209 void sudo_debug_exit(const char *func, const char *file, int line,
212 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
213 "<- %s @ %s:%d", func, file, line);
216 void sudo_debug_exit_int(const char *func, const char *file, int line,
217 int subsys, int rval)
219 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
220 "<- %s @ %s:%d := %d", func, file, line, rval);
223 void sudo_debug_exit_long(const char *func, const char *file, int line,
224 int subsys, long rval)
226 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
227 "<- %s @ %s:%d := %ld", func, file, line, rval);
230 void sudo_debug_exit_size_t(const char *func, const char *file, int line,
231 int subsys, size_t rval)
233 /* XXX - should use %zu but our snprintf.c doesn't support it */
234 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
235 "<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
238 /* We use int, not bool, here for functions that return -1 on error. */
239 void sudo_debug_exit_bool(const char *func, const char *file, int line,
240 int subsys, int rval)
242 if (rval == true || rval == false) {
243 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
244 "<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
246 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
247 "<- %s @ %s:%d := %d", func, file, line, rval);
251 void sudo_debug_exit_str(const char *func, const char *file, int line,
252 int subsys, const char *rval)
254 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
255 "<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
258 void sudo_debug_exit_str_masked(const char *func, const char *file, int line,
259 int subsys, const char *rval)
261 static const char stars[] = "********************************************************************************";
262 int len = rval ? strlen(rval) : sizeof("(null)") - 1;
264 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
265 "<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
268 void sudo_debug_exit_ptr(const char *func, const char *file, int line,
269 int subsys, const void *rval)
271 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
272 "<- %s @ %s:%d := %p", func, file, line, rval);
276 sudo_debug_write_conv(const char *func, const char *file, int lineno,
277 const char *str, int len, int errno_val)
279 struct sudo_conv_message msg;
280 struct sudo_conv_reply repl;
283 /* Call conversation function */
284 if (sudo_conv != NULL) {
285 /* Remove the newline at the end if appending extra info. */
286 if (str[len - 1] == '\n')
289 if (func != NULL && file != NULL && lineno != 0) {
291 easprintf(&buf, "%.*s: %s @ %s() %s:%d", len, str,
292 strerror(errno_val), func, file, lineno);
294 easprintf(&buf, "%.*s @ %s() %s:%d", len, str,
298 } else if (errno_val) {
299 easprintf(&buf, "%.*s: %s", len, str, strerror(errno_val));
302 memset(&msg, 0, sizeof(msg));
303 memset(&repl, 0, sizeof(repl));
304 msg.msg_type = SUDO_CONV_DEBUG_MSG;
306 sudo_conv(1, &msg, &repl);
313 sudo_debug_write_file(const char *func, const char *file, int lineno,
314 const char *str, int len, int errno_val)
316 char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
318 struct iovec iov[12];
320 bool need_newline = false;
322 /* Prepend program name and pid with a trailing space. */
323 iov[1].iov_base = (char *)getprogname();
324 iov[1].iov_len = strlen(iov[1].iov_base);
325 iov[2].iov_base = sudo_debug_pidstr;
326 iov[2].iov_len = sudo_debug_pidlen;
328 /* Add string along with newline if it doesn't have one. */
329 iov[3].iov_base = (char *)str;
330 iov[3].iov_len = len;
331 if (str[len - 1] != '\n')
334 /* Append error string if errno is specified. */
336 iov[iovcnt].iov_base = ": ";
337 iov[iovcnt].iov_len = 2;
339 iov[iovcnt].iov_base = strerror(errno_val);
340 iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
343 /* Move newline to the end. */
350 /* If function, file and lineno are specified, append them. */
351 if (func != NULL && file != NULL && lineno != 0) {
352 iov[iovcnt].iov_base = " @ ";
353 iov[iovcnt].iov_len = 3;
356 iov[iovcnt].iov_base = (char *)func;
357 iov[iovcnt].iov_len = strlen(func);
360 iov[iovcnt].iov_base = "() ";
361 iov[iovcnt].iov_len = 3;
364 iov[iovcnt].iov_base = (char *)file;
365 iov[iovcnt].iov_len = strlen(file);
368 (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
369 iov[iovcnt].iov_base = numbuf;
370 iov[iovcnt].iov_len = strlen(numbuf);
373 /* Move newline to the end. */
380 /* Append newline as needed. */
383 iov[iovcnt].iov_base = "\n";
384 iov[iovcnt].iov_len = 1;
388 /* Do timestamp last due to ctime's static buffer. */
390 timestr = ctime(&now) + 4;
391 timestr[15] = ' '; /* replace year with a space */
393 iov[0].iov_base = timestr;
396 /* Write message in a single syscall */
397 ignore_result(writev(sudo_debug_fd, iov, iovcnt));
401 sudo_debug_write2(const char *func, const char *file, int lineno,
402 const char *str, int len, int errno_val)
407 switch (sudo_debug_mode) {
408 case SUDO_DEBUG_MODE_CONV:
409 sudo_debug_write_conv(func, file, lineno, str, len, errno_val);
411 case SUDO_DEBUG_MODE_FILE:
412 sudo_debug_write_file(func, file, lineno, str, len, errno_val);
417 /* XXX - turn into a macro */
419 sudo_debug_write(const char *str, int len, int errno_val)
421 sudo_debug_write2(NULL, NULL, 0, str, len, errno_val);
425 sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
426 const char *fmt, ...)
428 int buflen, pri, subsys, saved_errno = errno;
432 if (!sudo_debug_mode)
435 /* Extract pri and subsystem from level. */
436 pri = SUDO_DEBUG_PRI(level);
437 subsys = SUDO_DEBUG_SUBSYS(level);
439 /* Make sure we want debug info at this level. */
440 if (subsys < NUM_SUBSYSTEMS && sudo_debug_settings[subsys] >= pri) {
442 buflen = vasprintf(&buf, fmt, ap);
445 int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
446 if (ISSET(level, SUDO_DEBUG_LINENO))
447 sudo_debug_write2(func, file, lineno, buf, buflen, errcode);
449 sudo_debug_write2(NULL, NULL, 0, buf, buflen, errcode);
458 sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
462 int buflen, pri, subsys, log_envp = 0;
465 if (!sudo_debug_mode)
468 /* Extract pri and subsystem from level. */
469 pri = SUDO_DEBUG_PRI(level);
470 subsys = SUDO_DEBUG_SUBSYS(level);
472 /* Make sure we want debug info at this level. */
473 if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
476 /* Log envp for debug level "debug". */
477 if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
480 #define EXEC_PREFIX "exec "
482 /* Alloc and build up buffer. */
484 buflen = sizeof(EXEC_PREFIX) -1 + plen;
485 if (argv[0] != NULL) {
486 buflen += sizeof(" []") - 1;
487 for (av = argv; *av; av++)
488 buflen += strlen(*av) + 1;
492 buflen += sizeof(" []") - 1;
493 for (av = envp; *av; av++)
494 buflen += strlen(*av) + 1;
497 buf = malloc(buflen + 1);
501 /* Copy prefix and command. */
502 memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
503 cp = buf + sizeof(EXEC_PREFIX) - 1;
504 memcpy(cp, path, plen);
508 if (argv[0] != NULL) {
511 for (av = argv; *av; av++) {
512 size_t avlen = strlen(*av);
513 memcpy(cp, *av, avlen);
523 for (av = envp; *av; av++) {
524 size_t avlen = strlen(*av);
525 memcpy(cp, *av, avlen);
534 sudo_debug_write(buf, buflen, 0);
539 * Dup sudo_debug_fd to the specified value so we don't
540 * close it when calling closefrom().
543 sudo_debug_fd_set(int fd)
545 if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
546 if (dup2(sudo_debug_fd, fd) == -1)
548 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
549 close(sudo_debug_fd);
552 return sudo_debug_fd;