timestr.lo toke.lo redblack.lo
SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo env.lo goodpath.lo \
- group_plugin.lo find_path.lo interfaces.lo logging.lo parse.lo \
- set_perms.lo sudoers.lo sudo_nss.lo iolog.lo @SUDOERS_OBJS@
+ group_plugin.lo find_path.lo interfaces.lo logging.lo \
+ parse.lo set_perms.lo sudoers.lo sudo_nss.lo iolog.lo \
+ iolog_path.lo @SUDOERS_OBJS@
VISUDO_OBJS = visudo.o goodpath.o find_path.o error.o
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/interfaces.c
iolog.lo: $(srcdir)/iolog.c $(SUDODEP)
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/iolog.c
+iolog_path.lo: $(srcdir)/iolog_path.c $(SUDODEP)
+ $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/iolog_path.c
ldap.lo: $(srcdir)/ldap.c $(SUDODEP) $(srcdir)/parse.h $(incdir)/list.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/ldap.c
linux_audit.lo: $(srcdir)/linux_audit.c $(SUDODEP) $(srcdir)/linux_audit.h
"iolog_dir", T_STR|T_PATH,
"Directory in which to store input/output logs",
NULL,
+ }, {
+ "iolog_file", T_STR,
+ "File in which to store the input/output log",
+ NULL,
}, {
NULL, 0, NULL
}
#define I_GROUP_PLUGIN 76
#define def_iolog_dir (sudo_defs_table[77].sd_un.str)
#define I_IOLOG_DIR 77
+#define def_iolog_file (sudo_defs_table[78].sd_un.str)
+#define I_IOLOG_FILE 78
enum def_tupple {
never,
iolog_dir
T_STR|T_PATH
"Directory in which to store input/output logs"
+iolog_file
+ T_STR
+ "File in which to store the input/output log"
#ifdef UMASK_OVERRIDE
def_umask_override = TRUE;
#endif
- def_iolog_dir = _PATH_SUDO_IO_LOGDIR;
+ def_iolog_file = estrdup("%{seq}");
+ def_iolog_dir = estrdup(_PATH_SUDO_IO_LOGDIR);
def_sudoers_locale = estrdup("C");
def_env_reset = TRUE;
def_set_logname = TRUE;
static union io_fd io_fds[IOFD_MAX];
extern struct io_plugin sudoers_io;
+static void
+mkdir_parents(char *path)
+{
+ struct stat sb;
+ char *slash = path;
+
+ for (;;) {
+ if ((slash = strchr(slash + 1, '/')) == NULL)
+ break;
+ *slash = '\0';
+ if (stat(path, &sb) != 0) {
+ if (mkdir(path, S_IRWXU) != 0)
+ log_error(USE_ERRNO, "Can't mkdir %s", path);
+ } else if (!S_ISDIR(sb.st_mode)) {
+ log_error(0, "%s: %s", path, strerror(ENOTDIR));
+ }
+ *slash = '/';
+ }
+}
+
void
io_nextid(void)
{
/*
* Create I/O log directory if it doesn't already exist.
*/
+ mkdir_parents(def_iolog_dir);
if (stat(def_iolog_dir, &sb) != 0) {
if (mkdir(def_iolog_dir, S_IRWXU) != 0)
log_error(USE_ERRNO, "Can't mkdir %s", def_iolog_dir);
}
static int
-build_idpath(const char *iolog_dir, const char *sessid, char *pathbuf, size_t pathsize)
+build_iopath(const char *iolog_dir, const char *iolog_file, char *pathbuf,
+ size_t pathsize)
{
- struct stat sb;
- char *cp;
- int i, len;
-
- if (sessid[0] == '\0')
- log_error(0, "tried to build a session id path without a session id");
-
- /* Check whether or not we have a real session ID. */
- for (i = 0; i < 6; i++) {
- switch (sessid[i]) {
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9': case 'A': case 'B':
- case 'C': case 'D': case 'E': case 'F': case 'G': case 'H':
- case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
- case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
- break;
- default:
- goto checked;
- }
+ int dirlen, filelen, len;
+
+ /* Trim extraneous slashes. */
+ dirlen = strlen(iolog_dir);
+ while (dirlen > 1 && iolog_dir[dirlen - 1] == '/')
+ dirlen--;
+ while (*iolog_file == '/')
+ iolog_file++;
+ filelen = strlen(iolog_file);
+ while (filelen > 1 && iolog_file[filelen - 1] == '/')
+ filelen--;
+
+ if (*iolog_dir != '/' || *iolog_file == '\0')
+ log_error(0, "invalid I/O log path: %s/%s", iolog_dir, iolog_file);
+
+ len = snprintf(pathbuf, pathsize, "%.*s/%.*s", dirlen, iolog_dir,
+ filelen, iolog_file);
+ if (len <= 0 && len >= pathsize) {
+ errno = ENAMETOOLONG;
+ log_error(USE_ERRNO, "%.*s/%.*s", dirlen, iolog_dir,
+ filelen, iolog_file);
}
-checked:
- if (i == 6 && sessid[6] == '\0') {
- /* Path is of the form /var/log/sudo-io/00/00/01. */
- len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", iolog_dir,
- sessid[0], sessid[1], sessid[2], sessid[3], sessid[4], sessid[5]);
- if (len <= 0 && len >= pathsize) {
- errno = ENAMETOOLONG;
- log_error(USE_ERRNO, "%s/%s", iolog_dir, sessid);
- }
- /* Create the intermediate subdirs as needed. */
- for (i = 6; i > 0; i -= 3) {
- pathbuf[len - i] = '\0';
- if (stat(pathbuf, &sb) != 0) {
- if (mkdir(pathbuf, S_IRWXU) != 0)
- log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
- } else if (!S_ISDIR(sb.st_mode)) {
- log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
- }
- pathbuf[len - i] = '/';
- }
- } else {
- /* Not a session ID, just append dir + file. */
- len = snprintf(pathbuf, pathsize, "%s/%s", iolog_dir, sessid);
- if (len <= 0 && len >= pathsize) {
- errno = ENAMETOOLONG;
- log_error(USE_ERRNO, "%s/%s", iolog_dir, sessid);
- }
-
- /* Create the intermediate subdirs as needed. */
- cp = &pathbuf[strlen(iolog_dir)];
- do {
- *cp = '\0';
- if (stat(pathbuf, &sb) != 0) {
- if (mkdir(pathbuf, S_IRWXU) != 0)
- log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
- } else if (!S_ISDIR(sb.st_mode)) {
- log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
- }
- *cp++ = '/';
- cp = strchr(cp, '/');
- } while (cp != NULL);
- }
+ /* Create path and intermediate subdirs as needed. */
+ mkdir_parents(pathbuf);
+ if (mkdtemp(pathbuf) == NULL)
+ log_error(USE_ERRNO, "Can't create %s", pathbuf);
return(len);
}
/*
* Pull iolog settings out of command_info, if any.
*/
- iolog_dir = def_iolog_dir;
- iolog_file = sudo_user.sessid;
+ iolog_dir = _PATH_SUDO_IO_LOGDIR; /* XXX */
+ iolog_file = sudo_user.sessid; /* XXX */
iolog_stdin = iolog_ttyin = def_log_input;
iolog_stdout = iolog_stderr = iolog_ttyout = def_log_output;
for (cur = command_info; *cur != NULL; cur++) {
return FALSE;
/* If no I/O log file defined there is nothing to do. */
- if (iolog_file == NULL)
+ if (iolog_file == NULL || iolog_dir == NULL)
return FALSE;
/*
- * Build a path containing the session ID split into two-digit subdirs,
- * so ID 000001 becomes /var/log/sudo-io/00/00/01.
+ * Build a path from I/O file and dir, creating intermediate subdirs
+ * and calling mkdtemp() on the result.
*/
- len = build_idpath(iolog_dir, iolog_file, pathbuf, sizeof(pathbuf));
- if (len == -1)
+ len = build_iopath(iolog_dir, iolog_file, pathbuf, sizeof(pathbuf));
+ if (len < 0 || len >= sizeof(pathbuf))
return -1;
- if (mkdir(pathbuf, S_IRUSR|S_IWUSR|S_IXUSR) != 0)
- log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
-
/*
* We create 7 files: a log file, a timing file and 5 for input/output.
*/
--- /dev/null
+/*
+ * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
+# include <memory.h>
+# endif
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_SETLOCALE
+# include <locale.h>
+#endif
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+#include "sudoers.h"
+
+struct path_escape {
+ const char *name;
+ size_t (*copy_fn)(char *, size_t);
+};
+
+static size_t fill_seq(char *, size_t);
+static size_t fill_user(char *, size_t);
+static size_t fill_group(char *, size_t);
+static size_t fill_runas_user(char *, size_t);
+static size_t fill_runas_group(char *, size_t);
+static size_t fill_hostname(char *, size_t);
+static size_t fill_command(char *, size_t);
+
+static struct path_escape escapes[] = {
+ { "seq", fill_seq },
+ { "user", fill_user },
+ { "group", fill_group },
+ { "runas_user", fill_runas_user },
+ { "runas_group", fill_runas_group },
+ { "hostname", fill_hostname },
+ { "command", fill_command },
+ { NULL, NULL }
+};
+
+static size_t
+fill_seq(char *str, size_t strsize)
+{
+ int len;
+
+ /* Path is of the form /var/log/sudo-io/00/00/01. */
+ len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sudo_user.sessid[0],
+ sudo_user.sessid[1], sudo_user.sessid[2], sudo_user.sessid[3],
+ sudo_user.sessid[4], sudo_user.sessid[5]);
+ if (len < 0)
+ return strsize; /* handle non-standard snprintf() */
+ return (size_t)len;
+}
+
+static size_t
+fill_user(char *str, size_t strsize)
+{
+ return strlcpy(str, user_name, strsize);
+}
+
+static size_t
+fill_group(char *str, size_t strsize)
+{
+ struct group *grp;
+ size_t len;
+
+ if ((grp = sudo_getgrgid(user_gid)) != NULL) {
+ len = strlcpy(str, grp->gr_name, strsize);
+ gr_delref(grp);
+ } else {
+ len = strlen(str);
+ len = snprintf(str + len, strsize - len, "#%u",
+ (unsigned int) user_gid);
+ }
+ return len;
+}
+
+static size_t
+fill_runas_user(char *str, size_t strsize)
+{
+ return strlcpy(str, runas_pw->pw_name, strsize);
+}
+
+static size_t
+fill_runas_group(char *str, size_t strsize)
+{
+ struct group *grp;
+ size_t len;
+
+ if (runas_gr != NULL) {
+ len = strlcpy(str, runas_gr->gr_name, strsize);
+ } else {
+ if ((grp = sudo_getgrgid(runas_pw->pw_gid)) != NULL) {
+ len = strlcpy(str, grp->gr_name, strsize);
+ gr_delref(grp);
+ } else {
+ len = strlen(str);
+ len = snprintf(str + len, strsize - len, "#%u",
+ (unsigned int) runas_pw->pw_gid);
+ }
+ }
+ return len;
+}
+
+static size_t
+fill_hostname(char *str, size_t strsize)
+{
+ return strlcpy(str, user_shost, strsize);
+}
+
+static size_t
+fill_command(char *str, size_t strsize)
+{
+ return strlcpy(str, user_base, strsize);
+}
+
+char *
+expand_iolog_path(const char *prefix, const char *opath)
+{
+ size_t plen = 0, psize = 1024;
+ char *path, *dst;
+ const char *src, *ep;
+ int strfit = FALSE;
+
+ /* Copy opath -> path, expanding any escape sequences. */
+ dst = path = emalloc(psize);
+ *path = '\0';
+
+ if (prefix != NULL) {
+ plen = strlcpy(path, prefix, psize);
+ dst += plen;
+ }
+ for (src = opath; *src != '\0'; src++) {
+ if (src[0] == '%') {
+ if (src[1] == '{') {
+ ep = strchr(src + 2, '}');
+ if (ep != NULL) {
+ struct path_escape *esc;
+ size_t len = (size_t)(ep - src - 2);
+ for (esc = escapes; esc->name != NULL; esc++) {
+ if (strncmp(src + 2, esc->name, len) == 0 &&
+ esc->name[len] == '\0')
+ break;
+ }
+ for (;;) {
+ len = esc->copy_fn(dst, psize - (dst - path));
+ if (len < psize - (dst - path))
+ break;
+ path = erealloc3(path, 2, psize);
+ psize *= 2;
+ dst = path + plen;
+ }
+ dst += len;
+ src = ep;
+ continue;
+ }
+ } else {
+ /* May need strftime() */
+ strfit = 1;
+ }
+ }
+ /* Need at least 2 chars, including the NUL terminator. */
+ if (plen + 2 >= psize) {
+ path = erealloc3(path, 2, psize);
+ psize *= 2;
+ dst = path + plen;
+ }
+ *dst++ = *src;
+ }
+ *dst = '\0';
+
+ if (strfit) {
+ time_t now;
+ struct tm *timeptr;
+ char *buf = NULL;
+
+ time(&now);
+ timeptr = localtime(&now);
+
+#ifdef HAVE_SETLOCALE
+ if (!setlocale(LC_ALL, def_sudoers_locale)) {
+ warningx("unable to set locale to \"%s\", using \"C\"",
+ def_sudoers_locale);
+ setlocale(LC_ALL, "C");
+ }
+#endif
+ /* Double the size of the buffer until it is big enough to expand. */
+ do {
+ psize * 2;
+ buf = erealloc(buf, psize);
+ buf[psize - 1] = '\0';
+ } while (!strftime(buf, psize, path, timeptr) || buf[psize - 1] != '\0');
+ setlocale(LC_ALL, "");
+ efree(path);
+ path = buf;
+ }
+
+ return path;
+}
static void _warning(int, const char *, va_list);
void plugin_cleanup(int);
-extern sigjmp_buf error_jmp;
+sigjmp_buf error_jmp;
extern sudo_conv_t sudo_conv;
int NewArgc;
char **NewArgv;
-/* error.c */
-sigjmp_buf error_jmp;
+/* plugin_error.c */
+extern sigjmp_buf error_jmp;
static int
sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
}
if (ISSET(sudo_mode, (MODE_RUN | MODE_EDIT)) && (def_log_input || def_log_output)) {
- io_nextid();
- command_info[info_len++] = fmt_string("iolog_dir", def_iolog_dir);
- command_info[info_len++] = fmt_string("iolog_file", sudo_user.sessid);
+ if (def_iolog_file) {
+ if (strstr(def_iolog_file, "%{seq}") != NULL) /* XXX - inline? */
+ io_nextid();
+ command_info[info_len++] = expand_iolog_path("iolog_file=", def_iolog_file);
+ }
+ if (def_iolog_dir)
+ command_info[info_len++] = expand_iolog_path("iolog_dir=", def_iolog_dir);
if (def_log_input) {
command_info[info_len++] = estrdup("iolog_stdin=true");
command_info[info_len++] = estrdup("iolog_ttyin=true");
/* iolog.c */
void io_nextid(void);
+/* iolog_path.c */
+char *expand_iolog_path(const char *prefix, const char *opath);
+
/* env.c */
char **env_get(void);
void env_init(char * const envp[]);