|
|
@@ -85,55 +85,70 @@ extern __dso_public struct io_plugin sudoers_io;
|
|
|
|
* Create path and any parent directories as needed.
|
|
|
|
* Create path and any parent directories as needed.
|
|
|
|
* If is_temp is set, use mkdtemp() for the final directory.
|
|
|
|
* If is_temp is set, use mkdtemp() for the final directory.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static bool
|
|
|
|
io_mkdirs(char *path, mode_t mode, bool is_temp)
|
|
|
|
io_mkdirs(char *path, mode_t mode, bool is_temp)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
struct stat sb;
|
|
|
|
gid_t parent_gid = 0;
|
|
|
|
gid_t parent_gid = 0;
|
|
|
|
char *slash = path;
|
|
|
|
char *slash = path;
|
|
|
|
|
|
|
|
bool ok = true;
|
|
|
|
debug_decl(io_mkdirs, SUDO_DEBUG_UTIL)
|
|
|
|
debug_decl(io_mkdirs, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
|
|
|
|
/* Fast path: not a temporary and already exists. */
|
|
|
|
/* Fast path: not a temporary and already exists. */
|
|
|
|
if (!is_temp && stat(path, &sb) == 0) {
|
|
|
|
if (!is_temp && stat(path, &sb) == 0) {
|
|
|
|
if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
log_fatal(0, N_("%s exists but is not a directory (0%o)"),
|
|
|
|
log_warning(0, N_("%s exists but is not a directory (0%o)"),
|
|
|
|
path, (unsigned int) sb.st_mode);
|
|
|
|
path, (unsigned int) sb.st_mode);
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug_return;
|
|
|
|
debug_return_bool(ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while ((slash = strchr(slash + 1, '/')) != NULL) {
|
|
|
|
while ((slash = strchr(slash + 1, '/')) != NULL) {
|
|
|
|
*slash = '\0';
|
|
|
|
*slash = '\0';
|
|
|
|
if (stat(path, &sb) != 0) {
|
|
|
|
if (stat(path, &sb) != 0) {
|
|
|
|
if (mkdir(path, mode) != 0)
|
|
|
|
if (mkdir(path, mode) != 0) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
log_fatal(0, N_("%s exists but is not a directory (0%o)"),
|
|
|
|
log_warning(0, N_("%s exists but is not a directory (0%o)"),
|
|
|
|
path, (unsigned int) sb.st_mode);
|
|
|
|
path, (unsigned int) sb.st_mode);
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* Inherit gid of parent dir for ownership. */
|
|
|
|
/* Inherit gid of parent dir for ownership. */
|
|
|
|
parent_gid = sb.st_gid;
|
|
|
|
parent_gid = sb.st_gid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*slash = '/';
|
|
|
|
*slash = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Create final path component. */
|
|
|
|
if (ok) {
|
|
|
|
if (is_temp) {
|
|
|
|
/* Create final path component. */
|
|
|
|
if (mkdtemp(path) == NULL)
|
|
|
|
if (is_temp) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
if (mkdtemp(path) == NULL) {
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
log_warning(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
} else {
|
|
|
|
ok = false;
|
|
|
|
if (mkdir(path, mode) != 0 && errno != EEXIST)
|
|
|
|
} else {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (mkdir(path, mode) != 0 && errno != EEXIST) {
|
|
|
|
|
|
|
|
log_warning(USE_ERRNO, N_("unable to mkdir %s"), path);
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
ignore_result(chown(path, (uid_t)-1, parent_gid));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug_return;
|
|
|
|
debug_return_bool(ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Set max session ID (aka sequence number)
|
|
|
|
* Set max session ID (aka sequence number)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bool
|
|
|
|
io_set_max_sessid(const char *maxval)
|
|
|
|
io_set_max_sessid(const char *maxval)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const char *errstr;
|
|
|
|
const char *errstr;
|
|
|
@@ -159,7 +174,7 @@ io_set_max_sessid(const char *maxval)
|
|
|
|
* number, and update the on-disk copy.
|
|
|
|
* number, and update the on-disk copy.
|
|
|
|
* Uses file locking to avoid sequence number collisions.
|
|
|
|
* Uses file locking to avoid sequence number collisions.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
bool
|
|
|
|
io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
struct stat sb;
|
|
|
@@ -175,7 +190,8 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Create I/O log directory if it doesn't already exist.
|
|
|
|
* Create I/O log directory if it doesn't already exist.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
io_mkdirs(iolog_dir, S_IRWXU, false);
|
|
|
|
if (!io_mkdirs(iolog_dir, S_IRWXU, false))
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Open sequence file
|
|
|
|
* Open sequence file
|
|
|
@@ -183,11 +199,14 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir);
|
|
|
|
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir);
|
|
|
|
if (len <= 0 || (size_t)len >= sizeof(pathbuf)) {
|
|
|
|
if (len <= 0 || (size_t)len >= sizeof(pathbuf)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
log_fatal(USE_ERRNO, "%s/seq", pathbuf);
|
|
|
|
log_warning(USE_ERRNO, "%s/seq", pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
|
|
|
|
fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
|
|
|
|
if (fd == -1)
|
|
|
|
if (fd == -1) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to open %s"), pathbuf);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to open %s"), pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
lock_file(fd, SUDO_LOCK);
|
|
|
|
lock_file(fd, SUDO_LOCK);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -225,8 +244,10 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
if (id == 0) {
|
|
|
|
if (id == 0) {
|
|
|
|
nread = read(fd, buf, sizeof(buf) - 1);
|
|
|
|
nread = read(fd, buf, sizeof(buf) - 1);
|
|
|
|
if (nread != 0) {
|
|
|
|
if (nread != 0) {
|
|
|
|
if (nread == -1)
|
|
|
|
if (nread == -1) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to read %s"), pathbuf);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to read %s"), pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
if (buf[nread - 1] == '\n')
|
|
|
|
if (buf[nread - 1] == '\n')
|
|
|
|
nread--;
|
|
|
|
nread--;
|
|
|
|
buf[nread] = '\0';
|
|
|
|
buf[nread] = '\0';
|
|
|
@@ -255,16 +276,19 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7])
|
|
|
|
sessid[6] = '\0';
|
|
|
|
sessid[6] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
/* Rewind and overwrite old seq file, including the NUL byte. */
|
|
|
|
/* Rewind and overwrite old seq file, including the NUL byte. */
|
|
|
|
if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
|
|
|
|
if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to write to %s"), pathbuf);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to write to %s"), pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
|
|
debug_return;
|
|
|
|
debug_return_bool(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Copy iolog_path to pathbuf and create the directory and any intermediate
|
|
|
|
* Copy iolog_path to pathbuf and create the directory and any intermediate
|
|
|
|
* directories. If iolog_path ends in 'XXXXXX', use mkdtemp().
|
|
|
|
* directories. If iolog_path ends in 'XXXXXX', use mkdtemp().
|
|
|
|
|
|
|
|
* Returns SIZE_MAX on error.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
static size_t
|
|
|
|
mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
|
|
|
|
mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
|
|
|
@@ -276,7 +300,8 @@ mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
|
|
|
|
len = strlcpy(pathbuf, iolog_path, pathsize);
|
|
|
|
len = strlcpy(pathbuf, iolog_path, pathsize);
|
|
|
|
if (len >= pathsize) {
|
|
|
|
if (len >= pathsize) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
log_fatal(USE_ERRNO, "%s", iolog_path);
|
|
|
|
log_warning(USE_ERRNO, "%s", iolog_path);
|
|
|
|
|
|
|
|
debug_return_size_t((size_t)-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -285,7 +310,8 @@ mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0)
|
|
|
|
if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0)
|
|
|
|
is_temp = true;
|
|
|
|
is_temp = true;
|
|
|
|
io_mkdirs(pathbuf, S_IRWXU, is_temp);
|
|
|
|
if (!io_mkdirs(pathbuf, S_IRWXU, is_temp))
|
|
|
|
|
|
|
|
len = (size_t)-1;
|
|
|
|
|
|
|
|
|
|
|
|
debug_return_size_t(len);
|
|
|
|
debug_return_size_t(len);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -296,7 +322,7 @@ mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
|
|
|
|
* Uses zlib if docompress is true.
|
|
|
|
* Uses zlib if docompress is true.
|
|
|
|
* Stores the open file handle which has the close-on-exec flag set.
|
|
|
|
* Stores the open file handle which has the close-on-exec flag set.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static bool
|
|
|
|
open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress)
|
|
|
|
open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int fd;
|
|
|
@@ -315,13 +341,15 @@ open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
iol->fd.f = fdopen(fd, "w");
|
|
|
|
iol->fd.f = fdopen(fd, "w");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fd == -1 || iol->fd.v == NULL)
|
|
|
|
if (fd == -1 || iol->fd.v == NULL) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to create %s"), pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* Remove old log file if we recycled sequence numbers. */
|
|
|
|
/* Remove old log file if we recycled sequence numbers. */
|
|
|
|
unlink(pathbuf);
|
|
|
|
unlink(pathbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug_return;
|
|
|
|
debug_return_bool(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -427,8 +455,10 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
case 'm':
|
|
|
|
if (strncmp(*cur, "maxseq=", sizeof("maxseq=") - 1) == 0)
|
|
|
|
if (strncmp(*cur, "maxseq=", sizeof("maxseq=") - 1) == 0) {
|
|
|
|
io_set_max_sessid(*cur + sizeof("maxseq=") - 1);
|
|
|
|
io_set_max_sessid(*cur + sizeof("maxseq=") - 1);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
case 'r':
|
|
|
|
if (strncmp(*cur, "runas_gid=", sizeof("runas_gid=") - 1) == 0) {
|
|
|
|
if (strncmp(*cur, "runas_gid=", sizeof("runas_gid=") - 1) == 0) {
|
|
|
@@ -497,19 +527,22 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Write the "/log" file that contains the user and command info.
|
|
|
|
* Write the "/log" file that contains the user and command info.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
static bool
|
|
|
|
write_info_log(char *pathbuf, size_t len, struct iolog_details *details,
|
|
|
|
write_info_log(char *pathbuf, size_t len, struct iolog_details *details,
|
|
|
|
char * const argv[], struct timeval *now)
|
|
|
|
char * const argv[], struct timeval *now)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
char * const *av;
|
|
|
|
char * const *av;
|
|
|
|
FILE *fp;
|
|
|
|
FILE *fp;
|
|
|
|
int fd;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
debug_decl(write_info_log, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
|
|
|
|
pathbuf[len] = '\0';
|
|
|
|
pathbuf[len] = '\0';
|
|
|
|
strlcat(pathbuf, "/log", PATH_MAX);
|
|
|
|
strlcat(pathbuf, "/log", PATH_MAX);
|
|
|
|
fd = open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
|
|
|
|
fd = open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
|
|
|
|
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL)
|
|
|
|
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) {
|
|
|
|
log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
|
|
|
|
log_warning(USE_ERRNO, N_("unable to create %s"), pathbuf);
|
|
|
|
|
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n%s", (long long)now->tv_sec,
|
|
|
|
fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n%s", (long long)now->tv_sec,
|
|
|
|
details->user ? details->user : "unknown", details->runas_pw->pw_name,
|
|
|
|
details->user ? details->user : "unknown", details->runas_pw->pw_name,
|
|
|
@@ -523,6 +556,7 @@ write_info_log(char *pathbuf, size_t len, struct iolog_details *details,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fputc('\n', fp);
|
|
|
|
fputc('\n', fp);
|
|
|
|
fclose(fp);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
debug_return_bool(ferror(fp) == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
@@ -583,7 +617,10 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation,
|
|
|
|
/* Get next session ID and convert it into a path. */
|
|
|
|
/* Get next session ID and convert it into a path. */
|
|
|
|
tofree = emalloc(sizeof(_PATH_SUDO_IO_LOGDIR) + sizeof(sessid) + 2);
|
|
|
|
tofree = emalloc(sizeof(_PATH_SUDO_IO_LOGDIR) + sizeof(sessid) + 2);
|
|
|
|
memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR));
|
|
|
|
memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR));
|
|
|
|
io_nextid(tofree, NULL, sessid);
|
|
|
|
if (!io_nextid(tofree, NULL, sessid)) {
|
|
|
|
|
|
|
|
rval = false;
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
}
|
|
|
|
snprintf(tofree + sizeof(_PATH_SUDO_IO_LOGDIR), sizeof(sessid) + 2,
|
|
|
|
snprintf(tofree + sizeof(_PATH_SUDO_IO_LOGDIR), sizeof(sessid) + 2,
|
|
|
|
"%c%c/%c%c/%c%c", sessid[0], sessid[1], sessid[2], sessid[3],
|
|
|
|
"%c%c/%c%c/%c%c", sessid[0], sessid[1], sessid[2], sessid[3],
|
|
|
|
sessid[4], sessid[5]);
|
|
|
|
sessid[4], sessid[5]);
|
|
|
@@ -603,8 +640,10 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation,
|
|
|
|
write_info_log(pathbuf, len, &details, argv, &last_time);
|
|
|
|
write_info_log(pathbuf, len, &details, argv, &last_time);
|
|
|
|
|
|
|
|
|
|
|
|
/* Create the timing and I/O log files. */
|
|
|
|
/* Create the timing and I/O log files. */
|
|
|
|
for (i = 0; i < IOFD_MAX; i++)
|
|
|
|
for (i = 0; i < IOFD_MAX; i++) {
|
|
|
|
open_io_fd(pathbuf, len, &io_log_files[i], iolog_compress);
|
|
|
|
if (!open_io_fd(pathbuf, len, &io_log_files[i], iolog_compress))
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Clear I/O log function pointers for disabled log functions.
|
|
|
|
* Clear I/O log function pointers for disabled log functions.
|
|
|
|