* Uses file locking to avoid sequence number collisions.
*/
void
-io_nextid(char *iolog_dir, char sessid[7])
+io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
{
struct stat sb;
char buf[32], *ep;
log_fatal(USE_ERRNO, _("unable to open %s"), pathbuf);
lock_file(fd, SUDO_LOCK);
- /* Read seq number (base 36). */
- nread = read(fd, buf, sizeof(buf));
- if (nread != 0) {
- if (nread == -1)
- log_fatal(USE_ERRNO, _("unable to read %s"), pathbuf);
- id = strtoul(buf, &ep, 36);
- if (buf == ep || id >= SESSID_MAX)
- log_fatal(0, _("invalid sequence number %s"), pathbuf);
+ /*
+ * If there is no seq file in iolog_dir and a fallback dir was
+ * specified, look for seq in the fallback dir. This is to work
+ * around a bug in sudo 1.8.5 and older where iolog_dir was not
+ * expanded before the sequence number was updated.
+ */
+ if (iolog_dir_fallback != NULL && fstat(fd, &sb) == 0 && sb.st_size == 0) {
+ char fallback[PATH_MAX];
+
+ len = snprintf(fallback, sizeof(fallback), "%s/seq",
+ iolog_dir_fallback);
+ if (len > 0 && len < sizeof(fallback)) {
+ int fd2 = open(fallback, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+ if (fd2 != -1) {
+ nread = read(fd2, buf, sizeof(buf));
+ if (nread > 0) {
+ id = strtoul(buf, &ep, 36);
+ if (buf == ep || id >= SESSID_MAX)
+ id = 0;
+ }
+ close(fd2);
+ }
+ }
+ }
+
+ /* Read current seq number (base 36). */
+ if (id == 0) {
+ nread = read(fd, buf, sizeof(buf));
+ if (nread != 0) {
+ if (nread == -1)
+ log_fatal(USE_ERRNO, _("unable to read %s"), pathbuf);
+ id = strtoul(buf, &ep, 36);
+ if (buf == ep || id >= SESSID_MAX)
+ log_fatal(0, _("invalid sequence number %s"), pathbuf);
+ }
}
id++;
/* Get next session ID and convert it into a path. */
tofree = emalloc(sizeof(_PATH_SUDO_IO_LOGDIR) + sizeof(sessid) + 2);
memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR));
- io_nextid(tofree, sessid);
+ io_nextid(tofree, NULL, sessid);
snprintf(tofree + sizeof(_PATH_SUDO_IO_LOGDIR), sizeof(sessid) + 2,
"%c%c/%c%c/%c%c", sessid[0], sessid[1], sessid[2], sessid[3],
sessid[4], sessid[5]);
struct path_escape {
const char *name;
- size_t (*copy_fn)(char *, size_t);
+ size_t (*copy_fn)(char *, size_t, char *);
};
-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 size_t fill_seq(char *, size_t, char *);
+static size_t fill_user(char *, size_t, char *);
+static size_t fill_group(char *, size_t, char *);
+static size_t fill_runas_user(char *, size_t, char *);
+static size_t fill_runas_group(char *, size_t, char *);
+static size_t fill_hostname(char *, size_t, char *);
+static size_t fill_command(char *, size_t, char *);
-static struct path_escape escapes[] = {
+/* Note: "seq" must be first in the list. */
+static struct path_escape io_path_escapes[] = {
{ "seq", fill_seq },
{ "user", fill_user },
{ "group", fill_group },
};
static size_t
-fill_seq(char *str, size_t strsize)
+fill_seq(char *str, size_t strsize, char *logdir)
{
static char sessid[7];
int len;
debug_decl(sudoers_io_version, SUDO_DEBUG_UTIL)
if (sessid[0] == '\0')
- io_nextid(def_iolog_dir, sessid);
+ io_nextid(logdir, def_iolog_dir, sessid);
/* Path is of the form /var/log/sudo-io/00/00/01. */
len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sessid[0],
}
static size_t
-fill_user(char *str, size_t strsize)
+fill_user(char *str, size_t strsize, char *unused)
{
debug_decl(fill_user, SUDO_DEBUG_UTIL)
debug_return_size_t(strlcpy(str, user_name, strsize));
}
static size_t
-fill_group(char *str, size_t strsize)
+fill_group(char *str, size_t strsize, char *unused)
{
struct group *grp;
size_t len;
}
static size_t
-fill_runas_user(char *str, size_t strsize)
+fill_runas_user(char *str, size_t strsize, char *unused)
{
debug_decl(fill_runas_user, SUDO_DEBUG_UTIL)
debug_return_size_t(strlcpy(str, runas_pw->pw_name, strsize));
}
static size_t
-fill_runas_group(char *str, size_t strsize)
+fill_runas_group(char *str, size_t strsize, char *unused)
{
struct group *grp;
size_t len;
}
static size_t
-fill_hostname(char *str, size_t strsize)
+fill_hostname(char *str, size_t strsize, char *unused)
{
debug_decl(fill_hostname, SUDO_DEBUG_UTIL)
debug_return_size_t(strlcpy(str, user_shost, strsize));
}
static size_t
-fill_command(char *str, size_t strsize)
+fill_command(char *str, size_t strsize, char *unused)
{
debug_decl(fill_command, SUDO_DEBUG_UTIL)
debug_return_size_t(strlcpy(str, user_base, strsize));
{
size_t len, prelen = 0;
char *dst, *dst0, *path, *pathend, tmpbuf[PATH_MAX];
+ char *slash = NULL;
const char *endbrace, *src = dir;
+ static struct path_escape *escapes;
int pass;
bool strfit;
debug_decl(expand_iolog_path, SUDO_DEBUG_UTIL)
switch (pass) {
case 0:
src = dir;
+ escapes = io_path_escapes + 1; /* skip "${seq}" */
break;
case 1:
/* Trim trailing slashes from dir component. */
while (dst - path - 1 > prelen && dst[-1] == '/')
dst--;
- if (slashp)
- *slashp = dst;
- src = "/";
- break;
+ /* The NUL will be replaced with a '/' at the end. */
+ if (dst + 1 >= pathend)
+ goto bad;
+ slash = dst++;
+ continue;
case 2:
src = file;
+ escapes = io_path_escapes;
break;
}
dst0 = dst;
break;
}
if (esc->name != NULL) {
- len = esc->copy_fn(dst, (size_t)(pathend - dst));
+ len = esc->copy_fn(dst, (size_t)(pathend - dst),
+ path + prelen);
if (len >= (size_t)(pathend - dst))
goto bad;
dst += len;
*dst = '\0';
}
}
+ if (slashp)
+ *slashp = slash;
+ *slash = '/';
debug_return_str(path);
bad: