2 * Copyright (c) 2011-2013 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>
30 #endif /* STDC_HEADERS */
34 # include "compat/stdbool.h"
38 #endif /* HAVE_STRING_H */
41 #endif /* HAVE_STRINGS_H */
44 #endif /* HAVE_UNISTD_H */
53 #include "sudo_plugin.h"
54 #include "sudo_debug.h"
56 #define DEFAULT_TEXT_DOMAIN "sudo"
60 * The debug priorities and subsystems are currently hard-coded.
61 * In the future we might consider allowing plugins to register their
62 * own subsystems and provide direct access to the debugging API.
65 /* Note: this must match the order in sudo_debug.h */
66 const char *const sudo_debug_priorities[] = {
78 /* Note: this must match the order in sudo_debug.h */
79 const char *const sudo_debug_subsystems[] = {
109 #define NUM_SUBSYSTEMS (sizeof(sudo_debug_subsystems) / sizeof(sudo_debug_subsystems[0]) - 1)
111 /* Values for sudo_debug_mode */
112 #define SUDO_DEBUG_MODE_DISABLED 0
113 #define SUDO_DEBUG_MODE_FILE 1
114 #define SUDO_DEBUG_MODE_CONV 2
116 static int sudo_debug_settings[NUM_SUBSYSTEMS];
117 static int sudo_debug_fd = -1;
118 static int sudo_debug_mode;
119 static char sudo_debug_pidstr[(((sizeof(int) * 8) + 2) / 3) + 3];
120 static size_t sudo_debug_pidlen;
123 * Parse settings string from sudo.conf and open debugfile.
124 * Returns 1 on success, 0 if cannot open debugfile.
125 * Unsupported subsystems and priorities are silently ignored.
127 int sudo_debug_init(const char *debugfile, const char *settings)
129 char *buf, *cp, *subsys, *pri;
132 /* Init per-subsystems settings to -1 since 0 is a valid priority. */
133 for (i = 0; i < NUM_SUBSYSTEMS; i++)
134 sudo_debug_settings[i] = -1;
136 /* Open debug file if specified. */
137 if (debugfile != NULL) {
138 if (sudo_debug_fd != -1)
139 close(sudo_debug_fd);
140 sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
141 if (sudo_debug_fd == -1) {
142 /* Create debug file as needed and set group ownership. */
143 if (errno == ENOENT) {
144 sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND|O_CREAT,
147 if (sudo_debug_fd == -1)
149 ignore_result(fchown(sudo_debug_fd, (uid_t)-1, 0));
151 (void)fcntl(sudo_debug_fd, F_SETFD, FD_CLOEXEC);
152 sudo_debug_mode = SUDO_DEBUG_MODE_FILE;
154 /* Called from the plugin, no debug file. */
155 sudo_debug_mode = SUDO_DEBUG_MODE_CONV;
158 /* Parse settings string. */
159 buf = estrdup(settings);
160 for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
161 /* Should be in the form subsys@pri. */
163 if ((pri = strchr(cp, '@')) == NULL)
167 /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
168 for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
169 if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
170 for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
171 if (strcasecmp(subsys, "all") == 0) {
172 sudo_debug_settings[j] = i;
175 if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
176 sudo_debug_settings[j] = i;
186 (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
188 sudo_debug_pidlen = strlen(sudo_debug_pidstr);
194 sudo_debug_fork(void)
198 if ((pid = fork()) == 0) {
199 (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
201 sudo_debug_pidlen = strlen(sudo_debug_pidstr);
208 sudo_debug_enter(const char *func, const char *file, int line,
211 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
212 "-> %s @ %s:%d", func, file, line);
215 void sudo_debug_exit(const char *func, const char *file, int line,
218 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
219 "<- %s @ %s:%d", func, file, line);
222 void sudo_debug_exit_int(const char *func, const char *file, int line,
223 int subsys, int rval)
225 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
226 "<- %s @ %s:%d := %d", func, file, line, rval);
229 void sudo_debug_exit_long(const char *func, const char *file, int line,
230 int subsys, long rval)
232 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
233 "<- %s @ %s:%d := %ld", func, file, line, rval);
236 void sudo_debug_exit_size_t(const char *func, const char *file, int line,
237 int subsys, size_t rval)
239 /* XXX - should use %zu but our snprintf.c doesn't support it */
240 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
241 "<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
244 /* We use int, not bool, here for functions that return -1 on error. */
245 void sudo_debug_exit_bool(const char *func, const char *file, int line,
246 int subsys, int rval)
248 if (rval == true || rval == false) {
249 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
250 "<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
252 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
253 "<- %s @ %s:%d := %d", func, file, line, rval);
257 void sudo_debug_exit_str(const char *func, const char *file, int line,
258 int subsys, const char *rval)
260 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
261 "<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
264 void sudo_debug_exit_str_masked(const char *func, const char *file, int line,
265 int subsys, const char *rval)
267 static const char stars[] = "********************************************************************************";
268 int len = rval ? strlen(rval) : sizeof("(null)") - 1;
270 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
271 "<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
274 void sudo_debug_exit_ptr(const char *func, const char *file, int line,
275 int subsys, const void *rval)
277 sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
278 "<- %s @ %s:%d := %p", func, file, line, rval);
282 sudo_debug_write_conv(const char *func, const char *file, int lineno,
283 const char *str, int len, int errno_val)
285 /* Remove the newline at the end if appending extra info. */
286 if (str[len - 1] == '\n')
289 if (func != NULL && file != NULL) {
291 sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s @ %s() %s:%d",
292 len, str, strerror(errno_val), func, file, lineno);
294 sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s @ %s() %s:%d",
295 len, str, func, file, lineno);
299 sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s",
300 len, str, strerror(errno_val));
302 sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s", len, str);
308 sudo_debug_write_file(const char *func, const char *file, int lineno,
309 const char *str, int len, int errno_val)
311 char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
313 struct iovec iov[12];
315 bool need_newline = false;
317 /* Prepend program name and pid with a trailing space. */
318 iov[1].iov_base = (char *)getprogname();
319 iov[1].iov_len = strlen(iov[1].iov_base);
320 iov[2].iov_base = sudo_debug_pidstr;
321 iov[2].iov_len = sudo_debug_pidlen;
323 /* Add string along with newline if it doesn't have one. */
324 iov[3].iov_base = (char *)str;
325 iov[3].iov_len = len;
326 if (str[len - 1] != '\n')
329 /* Append error string if errno is specified. */
331 iov[iovcnt].iov_base = ": ";
332 iov[iovcnt].iov_len = 2;
334 iov[iovcnt].iov_base = strerror(errno_val);
335 iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
338 /* Move newline to the end. */
345 /* If function, file and lineno are specified, append them. */
346 if (func != NULL && file != NULL && lineno != 0) {
347 iov[iovcnt].iov_base = " @ ";
348 iov[iovcnt].iov_len = 3;
351 iov[iovcnt].iov_base = (char *)func;
352 iov[iovcnt].iov_len = strlen(func);
355 iov[iovcnt].iov_base = "() ";
356 iov[iovcnt].iov_len = 3;
359 iov[iovcnt].iov_base = (char *)file;
360 iov[iovcnt].iov_len = strlen(file);
363 (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
364 iov[iovcnt].iov_base = numbuf;
365 iov[iovcnt].iov_len = strlen(numbuf);
368 /* Move newline to the end. */
375 /* Append newline as needed. */
378 iov[iovcnt].iov_base = "\n";
379 iov[iovcnt].iov_len = 1;
383 /* Do timestamp last due to ctime's static buffer. */
385 timestr = ctime(&now) + 4;
386 timestr[15] = ' '; /* replace year with a space */
388 iov[0].iov_base = timestr;
391 /* Write message in a single syscall */
392 ignore_result(writev(sudo_debug_fd, iov, iovcnt));
396 sudo_debug_write2(const char *func, const char *file, int lineno,
397 const char *str, int len, int errno_val)
402 switch (sudo_debug_mode) {
403 case SUDO_DEBUG_MODE_CONV:
404 sudo_debug_write_conv(func, file, lineno, str, len, errno_val);
406 case SUDO_DEBUG_MODE_FILE:
407 sudo_debug_write_file(func, file, lineno, str, len, errno_val);
412 /* XXX - turn into a macro */
414 sudo_debug_write(const char *str, int len, int errno_val)
416 sudo_debug_write2(NULL, NULL, 0, str, len, errno_val);
420 sudo_debug_vprintf2(const char *func, const char *file, int lineno, int level,
421 const char *fmt, va_list ap)
423 int buflen, pri, subsys, saved_errno = errno;
426 if (!sudo_debug_mode)
429 /* Extract pri and subsystem from level. */
430 pri = SUDO_DEBUG_PRI(level);
431 subsys = SUDO_DEBUG_SUBSYS(level);
433 /* Make sure we want debug info at this level. */
434 if (subsys < NUM_SUBSYSTEMS && sudo_debug_settings[subsys] >= pri) {
435 buflen = vasprintf(&buf, fmt, ap);
438 int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
439 if (ISSET(level, SUDO_DEBUG_LINENO))
440 sudo_debug_write2(func, file, lineno, buf, buflen, errcode);
442 sudo_debug_write2(NULL, NULL, 0, buf, buflen, errcode);
451 sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
452 const char *fmt, ...)
457 sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
462 sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
466 int buflen, pri, subsys, log_envp = 0;
469 if (!sudo_debug_mode)
472 /* Extract pri and subsystem from level. */
473 pri = SUDO_DEBUG_PRI(level);
474 subsys = SUDO_DEBUG_SUBSYS(level);
476 /* Make sure we want debug info at this level. */
477 if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
480 /* Log envp for debug level "debug". */
481 if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
484 #define EXEC_PREFIX "exec "
486 /* Alloc and build up buffer. */
488 buflen = sizeof(EXEC_PREFIX) -1 + plen;
489 if (argv[0] != NULL) {
490 buflen += sizeof(" []") - 1;
491 for (av = argv; *av; av++)
492 buflen += strlen(*av) + 1;
496 buflen += sizeof(" []") - 1;
497 for (av = envp; *av; av++)
498 buflen += strlen(*av) + 1;
501 buf = malloc(buflen + 1);
505 /* Copy prefix and command. */
506 memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
507 cp = buf + sizeof(EXEC_PREFIX) - 1;
508 memcpy(cp, path, plen);
512 if (argv[0] != NULL) {
515 for (av = argv; *av; av++) {
516 size_t avlen = strlen(*av);
517 memcpy(cp, *av, avlen);
527 for (av = envp; *av; av++) {
528 size_t avlen = strlen(*av);
529 memcpy(cp, *av, avlen);
538 sudo_debug_write(buf, buflen, 0);
543 * Dup sudo_debug_fd to the specified value so we don't
544 * close it when calling closefrom().
547 sudo_debug_fd_set(int fd)
549 if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
550 if (dup2(sudo_debug_fd, fd) == -1)
552 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
553 close(sudo_debug_fd);
556 return sudo_debug_fd;