version 1.1.1.1, 2012/02/21 16:23:02
|
version 1.1.1.6, 2014/06/15 16:12:54
|
Line 1
|
Line 1
|
/* |
/* |
* Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com> | * Copyright (c) 2009-2014 Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
Line 17
|
Line 17
|
#include <config.h> |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/time.h> |
#include <sys/time.h> |
#include <stdio.h> |
#include <stdio.h> |
Line 38
|
Line 37
|
#ifdef HAVE_UNISTD_H |
#ifdef HAVE_UNISTD_H |
# include <unistd.h> |
# include <unistd.h> |
#endif /* HAVE_UNISTD_H */ |
#endif /* HAVE_UNISTD_H */ |
#if TIME_WITH_SYS_TIME | #ifdef TIME_WITH_SYS_TIME |
# include <time.h> |
# include <time.h> |
#endif |
#endif |
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <signal.h> |
#include <signal.h> |
#include <setjmp.h> |
|
#include <pwd.h> |
#include <pwd.h> |
#include <grp.h> |
#include <grp.h> |
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
Line 52
|
Line 50
|
#endif |
#endif |
|
|
#include "sudoers.h" |
#include "sudoers.h" |
|
#include "iolog.h" |
|
|
/* plugin_error.c */ |
|
extern sigjmp_buf error_jmp; |
|
|
|
union io_fd { |
|
FILE *f; |
|
#ifdef HAVE_ZLIB_H |
|
gzFile g; |
|
#endif |
|
void *v; |
|
}; |
|
|
|
struct script_buf { |
struct script_buf { |
int len; /* buffer length (how much read in) */ |
int len; /* buffer length (how much read in) */ |
int off; /* write position (how much already consumed) */ |
int off; /* write position (how much already consumed) */ |
Line 82 struct iolog_details {
|
Line 70 struct iolog_details {
|
const char *iolog_path; |
const char *iolog_path; |
struct passwd *runas_pw; |
struct passwd *runas_pw; |
struct group *runas_gr; |
struct group *runas_gr; |
int iolog_stdin; | int lines; |
int iolog_stdout; | int cols; |
int iolog_stderr; | |
int iolog_ttyin; | |
int iolog_ttyout; | |
}; |
}; |
|
|
#define IOFD_STDIN 0 |
|
#define IOFD_STDOUT 1 |
|
#define IOFD_STDERR 2 |
|
#define IOFD_TTYIN 3 |
|
#define IOFD_TTYOUT 4 |
|
#define IOFD_TIMING 5 |
|
#define IOFD_MAX 6 |
|
|
|
#define SESSID_MAX 2176782336U |
|
|
|
static int iolog_compress; |
static int iolog_compress; |
static struct timeval last_time; |
static struct timeval last_time; |
static union io_fd io_fds[IOFD_MAX]; | static unsigned int sessid_max = SESSID_MAX; |
extern struct io_plugin sudoers_io; | |
|
|
|
/* sudoers_io is declared at the end of this file. */ |
|
extern __dso_public struct io_plugin sudoers_io; |
|
|
/* |
/* |
* Create parent directories for path as needed, but not path itself. | * Create path and any parent directories as needed. |
| * If is_temp is set, use mkdtemp() for the final directory. |
*/ |
*/ |
static void |
static void |
mkdir_parents(char *path) | io_mkdirs(char *path, mode_t mode, bool is_temp) |
{ |
{ |
struct stat sb; |
struct stat sb; |
|
gid_t parent_gid = 0; |
char *slash = path; |
char *slash = path; |
|
debug_decl(io_mkdirs, SUDO_DEBUG_UTIL) |
|
|
for (;;) { | /* Fast path: not a temporary and already exists. */ |
if ((slash = strchr(slash + 1, '/')) == NULL) | if (!is_temp && stat(path, &sb) == 0) { |
break; | if (!S_ISDIR(sb.st_mode)) { |
| log_fatal(0, N_("%s exists but is not a directory (0%o)"), |
| path, (unsigned int) sb.st_mode); |
| } |
| debug_return; |
| } |
| |
| while ((slash = strchr(slash + 1, '/')) != NULL) { |
*slash = '\0'; |
*slash = '\0'; |
if (stat(path, &sb) != 0) { |
if (stat(path, &sb) != 0) { |
if (mkdir(path, S_IRWXU) != 0) | if (mkdir(path, mode) != 0) |
log_error(USE_ERRNO, _("unable to mkdir %s"), path); | log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path); |
| ignore_result(chown(path, (uid_t)-1, parent_gid)); |
} else if (!S_ISDIR(sb.st_mode)) { |
} else if (!S_ISDIR(sb.st_mode)) { |
log_error(0, _("%s: %s"), path, strerror(ENOTDIR)); | log_fatal(0, N_("%s exists but is not a directory (0%o)"), |
| path, (unsigned int) sb.st_mode); |
| } else { |
| /* Inherit gid of parent dir for ownership. */ |
| parent_gid = sb.st_gid; |
} |
} |
*slash = '/'; |
*slash = '/'; |
} |
} |
|
/* Create final path component. */ |
|
if (is_temp) { |
|
if (mkdtemp(path) == NULL) |
|
log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path); |
|
ignore_result(chown(path, (uid_t)-1, parent_gid)); |
|
} else { |
|
if (mkdir(path, mode) != 0 && errno != EEXIST) |
|
log_fatal(USE_ERRNO, N_("unable to mkdir %s"), path); |
|
ignore_result(chown(path, (uid_t)-1, parent_gid)); |
|
} |
|
debug_return; |
} |
} |
|
|
/* |
/* |
|
* Set max session ID (aka sequence number) |
|
*/ |
|
int |
|
io_set_max_sessid(const char *maxval) |
|
{ |
|
const char *errstr; |
|
unsigned int value; |
|
debug_decl(io_set_max_sessid, SUDO_DEBUG_UTIL) |
|
|
|
value = strtonum(maxval, 0, SESSID_MAX, &errstr); |
|
if (errstr != NULL) { |
|
if (errno != ERANGE) { |
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
|
"bad maxseq: %s: %s", maxval, errstr); |
|
debug_return_bool(false); |
|
} |
|
/* Out of range, clamp to SESSID_MAX as documented. */ |
|
value = SESSID_MAX; |
|
} |
|
sessid_max = value; |
|
debug_return_bool(true); |
|
} |
|
|
|
/* |
* Read the on-disk sequence number, set sessid to the next |
* Read the on-disk sequence number, set sessid to the next |
* 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 |
void |
io_nextid(char *iolog_dir, char sessid[7]) | io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]) |
{ |
{ |
struct stat sb; |
struct stat sb; |
char buf[32], *ep; |
char buf[32], *ep; |
Line 143 io_nextid(char *iolog_dir, char sessid[7])
|
Line 170 io_nextid(char *iolog_dir, char sessid[7])
|
ssize_t nread; |
ssize_t nread; |
char pathbuf[PATH_MAX]; |
char pathbuf[PATH_MAX]; |
static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
|
debug_decl(io_nextid, SUDO_DEBUG_UTIL) |
|
|
/* |
/* |
* Create I/O log directory if it doesn't already exist. |
* Create I/O log directory if it doesn't already exist. |
*/ |
*/ |
mkdir_parents(iolog_dir); | io_mkdirs(iolog_dir, S_IRWXU, false); |
if (stat(iolog_dir, &sb) != 0) { | |
if (mkdir(iolog_dir, S_IRWXU) != 0) | |
log_error(USE_ERRNO, _("unable to mkdir %s"), iolog_dir); | |
} else if (!S_ISDIR(sb.st_mode)) { | |
log_error(0, _("%s exists but is not a directory (0%o)"), | |
iolog_dir, (unsigned int) sb.st_mode); | |
} | |
|
|
/* |
/* |
* Open sequence file |
* Open sequence file |
*/ |
*/ |
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir); |
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir); |
if (len <= 0 || len >= sizeof(pathbuf)) { | if (len <= 0 || (size_t)len >= sizeof(pathbuf)) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
log_error(USE_ERRNO, "%s/seq", pathbuf); | log_fatal(USE_ERRNO, "%s/seq", pathbuf); |
} |
} |
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_error(USE_ERRNO, _("unable to open %s"), pathbuf); | log_fatal(USE_ERRNO, N_("unable to open %s"), pathbuf); |
lock_file(fd, SUDO_LOCK); |
lock_file(fd, SUDO_LOCK); |
|
|
/* Read seq number (base 36). */ | /* |
nread = read(fd, buf, sizeof(buf)); | * If there is no seq file in iolog_dir and a fallback dir was |
if (nread != 0) { | * specified, look for seq in the fallback dir. This is to work |
if (nread == -1) | * around a bug in sudo 1.8.5 and older where iolog_dir was not |
log_error(USE_ERRNO, _("unable to read %s"), pathbuf); | * expanded before the sequence number was updated. |
id = strtoul(buf, &ep, 36); | */ |
if (buf == ep || id >= SESSID_MAX) | if (iolog_dir_fallback != NULL && fstat(fd, &sb) == 0 && sb.st_size == 0) { |
log_error(0, _("invalid sequence number %s"), pathbuf); | char fallback[PATH_MAX]; |
| |
| len = snprintf(fallback, sizeof(fallback), "%s/seq", |
| iolog_dir_fallback); |
| if (len > 0 && (size_t)len < sizeof(fallback)) { |
| int fd2 = open(fallback, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); |
| if (fd2 != -1) { |
| nread = read(fd2, buf, sizeof(buf) - 1); |
| if (nread > 0) { |
| if (buf[nread - 1] == '\n') |
| nread--; |
| buf[nread] = '\0'; |
| id = strtoul(buf, &ep, 36); |
| if (ep == buf || *ep != '\0' || id >= sessid_max) { |
| sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
| "%s: bad sequence number: %s", fallback, buf); |
| id = 0; |
| } |
| } |
| close(fd2); |
| } |
| } |
} |
} |
|
|
|
/* Read current seq number (base 36). */ |
|
if (id == 0) { |
|
nread = read(fd, buf, sizeof(buf) - 1); |
|
if (nread != 0) { |
|
if (nread == -1) |
|
log_fatal(USE_ERRNO, N_("unable to read %s"), pathbuf); |
|
if (buf[nread - 1] == '\n') |
|
nread--; |
|
buf[nread] = '\0'; |
|
id = strtoul(buf, &ep, 36); |
|
if (ep == buf || *ep != '\0' || id >= sessid_max) { |
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
|
"%s: bad sequence number: %s", pathbuf, buf); |
|
id = 0; |
|
} |
|
} |
|
} |
id++; |
id++; |
|
|
/* |
/* |
Line 190 io_nextid(char *iolog_dir, char sessid[7])
|
Line 250 io_nextid(char *iolog_dir, char sessid[7])
|
} |
} |
buf[6] = '\n'; |
buf[6] = '\n'; |
|
|
/* Stash id logging purposes */ | /* Stash id for logging purposes. */ |
memcpy(sessid, buf, 6); |
memcpy(sessid, buf, 6); |
sessid[6] = '\0'; |
sessid[6] = '\0'; |
|
|
/* Rewind and overwrite old seq file. */ | /* Rewind and overwrite old seq file, including the NUL byte. */ |
if (lseek(fd, 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_error(USE_ERRNO, _("unable to write to %s"), pathbuf); | log_fatal(USE_ERRNO, N_("unable to write to %s"), pathbuf); |
close(fd); |
close(fd); |
|
|
|
debug_return; |
} |
} |
|
|
/* |
/* |
Line 208 static size_t
|
Line 270 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) |
{ |
{ |
size_t len; |
size_t len; |
|
bool is_temp = false; |
|
debug_decl(mkdir_iopath, SUDO_DEBUG_UTIL) |
|
|
len = strlcpy(pathbuf, iolog_path, pathsize); |
len = strlcpy(pathbuf, iolog_path, pathsize); |
if (len >= pathsize) { |
if (len >= pathsize) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
log_error(USE_ERRNO, "%s", iolog_path); | log_fatal(USE_ERRNO, "%s", iolog_path); |
} |
} |
|
|
/* |
/* |
* Create path and intermediate subdirs as needed. |
* Create path and intermediate subdirs as needed. |
* If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp(). |
* If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp(). |
*/ |
*/ |
mkdir_parents(pathbuf); | if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) |
if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) { | is_temp = true; |
if (mkdtemp(pathbuf) == NULL) | io_mkdirs(pathbuf, S_IRWXU, is_temp); |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
if (mkdir(pathbuf, S_IRWXU) != 0) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} | |
|
|
return len; | debug_return_size_t(len); |
} |
} |
|
|
/* |
/* |
* Append suffix to pathbuf after len chars and open the resulting file. |
* Append suffix to pathbuf after len chars and open the resulting file. |
* Note that the size of pathbuf is assumed to be PATH_MAX. |
* Note that the size of pathbuf is assumed to be PATH_MAX. |
* Uses zlib if docompress is TRUE. | * Uses zlib if docompress is true. |
* Returns 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 void |
open_io_fd(char *pathbuf, size_t len, const char *suffix, int docompress) | open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress) |
{ |
{ |
void *vfd = NULL; |
|
int fd; |
int fd; |
|
debug_decl(open_io_fd, SUDO_DEBUG_UTIL) |
|
|
pathbuf[len] = '\0'; |
pathbuf[len] = '\0'; |
strlcat(pathbuf, suffix, PATH_MAX); | strlcat(pathbuf, iol->suffix, PATH_MAX); |
fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); | if (iol->enabled) { |
if (fd != -1) { | fd = open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); |
fcntl(fd, F_SETFD, FD_CLOEXEC); | if (fd != -1) { |
| fcntl(fd, F_SETFD, FD_CLOEXEC); |
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
if (docompress) | if (docompress) |
vfd = gzdopen(fd, "w"); | iol->fd.g = gzdopen(fd, "w"); |
else | else |
#endif |
#endif |
vfd = fdopen(fd, "w"); | iol->fd.f = fdopen(fd, "w"); |
| } |
| if (fd == -1 || iol->fd.v == NULL) |
| log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf); |
| } else { |
| /* Remove old log file if we recycled sequence numbers. */ |
| unlink(pathbuf); |
} |
} |
return vfd; | debug_return; |
} |
} |
|
|
/* |
/* |
* Pull out I/O log related data from user_info and command_info arrays. |
* Pull out I/O log related data from user_info and command_info arrays. |
|
* Returns true if I/O logging is enabled, else false. |
*/ |
*/ |
static void | static bool |
iolog_deserialize_info(struct iolog_details *details, char * const user_info[], |
iolog_deserialize_info(struct iolog_details *details, char * const user_info[], |
char * const command_info[]) |
char * const command_info[]) |
{ |
{ |
const char *runas_uid_str = "0", *runas_euid_str = NULL; |
const char *runas_uid_str = "0", *runas_euid_str = NULL; |
const char *runas_gid_str = "0", *runas_egid_str = NULL; |
const char *runas_gid_str = "0", *runas_egid_str = NULL; |
char id[MAX_UID_T_LEN + 2], *ep; | const char *errstr; |
| char idbuf[MAX_UID_T_LEN + 2]; |
char * const *cur; |
char * const *cur; |
unsigned long ulval; | id_t id; |
uid_t runas_uid = 0; |
uid_t runas_uid = 0; |
gid_t runas_gid = 0; |
gid_t runas_gid = 0; |
|
debug_decl(iolog_deserialize_info, SUDO_DEBUG_UTIL) |
|
|
memset(details, 0, sizeof(*details)); | details->lines = 24; |
| details->cols = 80; |
|
|
for (cur = user_info; *cur != NULL; cur++) { |
for (cur = user_info; *cur != NULL; cur++) { |
switch (**cur) { |
switch (**cur) { |
case 'c': |
case 'c': |
|
if (strncmp(*cur, "cols=", sizeof("cols=") - 1) == 0) { |
|
int n = strtonum(*cur + sizeof("cols=") - 1, 1, INT_MAX, NULL); |
|
if (n > 0) |
|
details->cols = n; |
|
continue; |
|
} |
if (strncmp(*cur, "cwd=", sizeof("cwd=") - 1) == 0) { |
if (strncmp(*cur, "cwd=", sizeof("cwd=") - 1) == 0) { |
details->cwd = *cur + sizeof("cwd=") - 1; |
details->cwd = *cur + sizeof("cwd=") - 1; |
continue; |
continue; |
} |
} |
break; |
break; |
|
case 'l': |
|
if (strncmp(*cur, "lines=", sizeof("lines=") - 1) == 0) { |
|
int n = strtonum(*cur + sizeof("lines=") - 1, 1, INT_MAX, NULL); |
|
if (n > 0) |
|
details->lines = n; |
|
continue; |
|
} |
|
break; |
case 't': |
case 't': |
if (strncmp(*cur, "tty=", sizeof("tty=") - 1) == 0) { |
if (strncmp(*cur, "tty=", sizeof("tty=") - 1) == 0) { |
details->tty = *cur + sizeof("tty=") - 1; |
details->tty = *cur + sizeof("tty=") - 1; |
Line 312 iolog_deserialize_info(struct iolog_details *details,
|
Line 396 iolog_deserialize_info(struct iolog_details *details,
|
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) { |
if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_stdin=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_stdin=") - 1) == true) |
details->iolog_stdin = TRUE; | io_log_files[IOFD_STDIN].enabled = true; |
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) { |
if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_stdout=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_stdout=") - 1) == true) |
details->iolog_stdout = TRUE; | io_log_files[IOFD_STDOUT].enabled = true; |
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) { |
if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_stderr=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_stderr=") - 1) == true) |
details->iolog_stderr = TRUE; | io_log_files[IOFD_STDERR].enabled = true; |
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) { |
if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_ttyin=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_ttyin=") - 1) == true) |
details->iolog_ttyin = TRUE; | io_log_files[IOFD_TTYIN].enabled = true; |
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) { |
if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_ttyout=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_ttyout=") - 1) == true) |
details->iolog_ttyout = TRUE; | io_log_files[IOFD_TTYOUT].enabled = true; |
continue; |
continue; |
} |
} |
if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) { |
if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) { |
if (atobool(*cur + sizeof("iolog_compress=") - 1) == TRUE) | if (atobool(*cur + sizeof("iolog_compress=") - 1) == true) |
iolog_compress = TRUE; /* must be global */ | iolog_compress = true; /* must be global */ |
continue; |
continue; |
} |
} |
break; |
break; |
|
case 'm': |
|
if (strncmp(*cur, "maxseq=", sizeof("maxseq=") - 1) == 0) |
|
io_set_max_sessid(*cur + sizeof("maxseq=") - 1); |
|
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) { |
runas_gid_str = *cur + sizeof("runas_gid=") - 1; |
runas_gid_str = *cur + sizeof("runas_gid=") - 1; |
Line 369 iolog_deserialize_info(struct iolog_details *details,
|
Line 457 iolog_deserialize_info(struct iolog_details *details,
|
if (runas_euid_str != NULL) |
if (runas_euid_str != NULL) |
runas_uid_str = runas_euid_str; |
runas_uid_str = runas_euid_str; |
if (runas_uid_str != NULL) { |
if (runas_uid_str != NULL) { |
errno = 0; | id = atoid(runas_uid_str, NULL, NULL, &errstr); |
ulval = strtoul(runas_uid_str, &ep, 0); | if (errstr != NULL) |
if (*runas_uid_str != '\0' && *ep == '\0' && | warningx("runas uid %s: %s", runas_uid_str, U_(errstr)); |
(errno != ERANGE || ulval != ULONG_MAX)) { | else |
runas_uid = (uid_t)ulval; | runas_uid = (uid_t)id; |
} | |
} |
} |
if (runas_egid_str != NULL) |
if (runas_egid_str != NULL) |
runas_gid_str = runas_egid_str; |
runas_gid_str = runas_egid_str; |
if (runas_gid_str != NULL) { |
if (runas_gid_str != NULL) { |
errno = 0; | id = atoid(runas_gid_str, NULL, NULL, &errstr); |
ulval = strtoul(runas_gid_str, &ep, 0); | if (errstr != NULL) |
if (*runas_gid_str != '\0' && *ep == '\0' && | warningx("runas gid %s: %s", runas_gid_str, U_(errstr)); |
(errno != ERANGE || ulval != ULONG_MAX)) { | else |
runas_gid = (gid_t)ulval; | runas_gid = (gid_t)id; |
} | |
} |
} |
|
|
details->runas_pw = sudo_getpwuid(runas_uid); |
details->runas_pw = sudo_getpwuid(runas_uid); |
if (details->runas_pw == NULL) { |
if (details->runas_pw == NULL) { |
id[0] = '#'; | idbuf[0] = '#'; |
strlcpy(&id[1], runas_uid_str, sizeof(id) - 1); | strlcpy(&idbuf[1], runas_uid_str, sizeof(idbuf) - 1); |
details->runas_pw = sudo_fakepwnam(id, runas_gid); | details->runas_pw = sudo_fakepwnam(idbuf, runas_gid); |
} |
} |
|
|
if (runas_gid != details->runas_pw->pw_gid) { |
if (runas_gid != details->runas_pw->pw_gid) { |
details->runas_gr = sudo_getgrgid(runas_gid); |
details->runas_gr = sudo_getgrgid(runas_gid); |
if (details->runas_gr == NULL) { |
if (details->runas_gr == NULL) { |
id[0] = '#'; | idbuf[0] = '#'; |
strlcpy(&id[1], runas_gid_str, sizeof(id) - 1); | strlcpy(&idbuf[1], runas_gid_str, sizeof(idbuf) - 1); |
details->runas_gr = sudo_fakegrnam(id); | details->runas_gr = sudo_fakegrnam(idbuf); |
} |
} |
} |
} |
|
debug_return_bool( |
|
io_log_files[IOFD_STDIN].enabled || io_log_files[IOFD_STDOUT].enabled || |
|
io_log_files[IOFD_STDERR].enabled || io_log_files[IOFD_TTYIN].enabled || |
|
io_log_files[IOFD_TTYOUT].enabled); |
} |
} |
|
|
|
/* |
|
* Write the "/log" file that contains the user and command info. |
|
*/ |
|
void |
|
write_info_log(char *pathbuf, size_t len, struct iolog_details *details, |
|
char * const argv[], struct timeval *now) |
|
{ |
|
char * const *av; |
|
FILE *fp; |
|
int fd; |
|
|
|
pathbuf[len] = '\0'; |
|
strlcat(pathbuf, "/log", PATH_MAX); |
|
fd = open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); |
|
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) |
|
log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf); |
|
|
|
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->runas_gr ? details->runas_gr->gr_name : "", |
|
details->tty ? details->tty : "unknown", details->lines, details->cols, |
|
details->cwd ? details->cwd : "unknown", |
|
details->command ? details->command : "unknown"); |
|
for (av = argv + 1; *av != NULL; av++) { |
|
fputc(' ', fp); |
|
fputs(*av, fp); |
|
} |
|
fputc('\n', fp); |
|
fclose(fp); |
|
} |
|
|
static int |
static int |
sudoers_io_open(unsigned int version, sudo_conv_t conversation, |
sudoers_io_open(unsigned int version, sudo_conv_t conversation, |
sudo_printf_t plugin_printf, char * const settings[], |
sudo_printf_t plugin_printf, char * const settings[], |
char * const user_info[], char * const command_info[], |
char * const user_info[], char * const command_info[], |
int argc, char * const argv[], char * const user_env[]) | int argc, char * const argv[], char * const user_env[], char * const args[]) |
{ |
{ |
struct iolog_details details; |
struct iolog_details details; |
char pathbuf[PATH_MAX], sessid[7]; |
char pathbuf[PATH_MAX], sessid[7]; |
char *tofree = NULL; |
char *tofree = NULL; |
char * const *cur; |
char * const *cur; |
FILE *io_logfile; | const char *debug_flags = NULL; |
size_t len; |
size_t len; |
int rval = -1; | int i, rval = -1; |
| debug_decl(sudoers_io_open, SUDO_DEBUG_PLUGIN) |
|
|
if (!sudo_conv) | sudo_conv = conversation; |
sudo_conv = conversation; | sudo_printf = plugin_printf; |
if (!sudo_printf) | |
sudo_printf = plugin_printf; | |
|
|
/* If we have no command (because -V was specified) just return. */ |
/* If we have no command (because -V was specified) just return. */ |
if (argc == 0) |
if (argc == 0) |
return TRUE; | debug_return_bool(true); |
|
|
if (sigsetjmp(error_jmp, 1)) { | memset(&details, 0, sizeof(details)); |
/* called via error(), errorx() or log_error() */ | |
| if (fatal_setjmp() != 0) { |
| /* called via fatal(), fatalx() or log_fatal() */ |
rval = -1; |
rval = -1; |
goto done; |
goto done; |
} |
} |
Line 439 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
Line 561 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
sudo_setgrent(); |
sudo_setgrent(); |
|
|
/* |
/* |
* Pull iolog settings out of command_info, if any. | * Check for debug flags in settings list. |
*/ |
*/ |
iolog_deserialize_info(&details, user_info, command_info); | for (cur = settings; *cur != NULL; cur++) { |
/* Did policy module disable I/O logging? */ | if (strncmp(*cur, "debug_flags=", sizeof("debug_flags=") - 1) == 0) |
if (!details.iolog_stdin && !details.iolog_ttyin && | debug_flags = *cur + sizeof("debug_flags=") - 1; |
!details.iolog_stdout && !details.iolog_stderr && | } |
!details.iolog_ttyout) { | if (debug_flags != NULL) |
rval = FALSE; | sudo_debug_init(NULL, debug_flags); |
| |
| /* |
| * Pull iolog settings out of command_info. |
| */ |
| if (!iolog_deserialize_info(&details, user_info, command_info)) { |
| rval = false; |
goto done; |
goto done; |
} |
} |
|
|
Line 455 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
Line 583 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
/* 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, sessid); | io_nextid(tofree, NULL, sessid); |
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]); |
Line 470 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
Line 598 sudoers_io_open(unsigned int version, sudo_conv_t conv
|
if (len >= sizeof(pathbuf)) |
if (len >= sizeof(pathbuf)) |
goto done; |
goto done; |
|
|
/* | /* Write log file with user and command details. */ |
* We create 7 files: a log file, a timing file and 5 for input/output. | gettimeofday(&last_time, NULL); |
*/ | write_info_log(pathbuf, len, &details, argv, &last_time); |
io_logfile = open_io_fd(pathbuf, len, "/log", FALSE); | |
if (io_logfile == NULL) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
|
|
io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing", | /* Create the timing and I/O log files. */ |
iolog_compress); | for (i = 0; i < IOFD_MAX; i++) |
if (io_fds[IOFD_TIMING].v == NULL) | open_io_fd(pathbuf, len, &io_log_files[i], iolog_compress); |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
|
|
if (details.iolog_ttyin) { | /* |
io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin", | * Clear I/O log function pointers for disabled log functions. |
iolog_compress); | */ |
if (io_fds[IOFD_TTYIN].v == NULL) | if (!io_log_files[IOFD_STDIN].enabled) |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
sudoers_io.log_ttyin = NULL; | |
} | |
if (details.iolog_stdin) { | |
io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin", | |
iolog_compress); | |
if (io_fds[IOFD_STDIN].v == NULL) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
sudoers_io.log_stdin = NULL; |
sudoers_io.log_stdin = NULL; |
} | if (!io_log_files[IOFD_STDOUT].enabled) |
if (details.iolog_ttyout) { | |
io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout", | |
iolog_compress); | |
if (io_fds[IOFD_TTYOUT].v == NULL) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
sudoers_io.log_ttyout = NULL; | |
} | |
if (details.iolog_stdout) { | |
io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout", | |
iolog_compress); | |
if (io_fds[IOFD_STDOUT].v == NULL) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
sudoers_io.log_stdout = NULL; |
sudoers_io.log_stdout = NULL; |
} | if (!io_log_files[IOFD_STDERR].enabled) |
if (details.iolog_stderr) { | |
io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr", | |
iolog_compress); | |
if (io_fds[IOFD_STDERR].v == NULL) | |
log_error(USE_ERRNO, _("unable to create %s"), pathbuf); | |
} else { | |
sudoers_io.log_stderr = NULL; |
sudoers_io.log_stderr = NULL; |
} | if (!io_log_files[IOFD_TTYIN].enabled) |
| sudoers_io.log_ttyin = NULL; |
| if (!io_log_files[IOFD_TTYOUT].enabled) |
| sudoers_io.log_ttyout = NULL; |
|
|
gettimeofday(&last_time, NULL); | rval = true; |
|
|
fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", (long)last_time.tv_sec, |
|
details.user ? details.user : "unknown", details.runas_pw->pw_name, |
|
details.runas_gr ? details.runas_gr->gr_name : "", |
|
details.tty ? details.tty : "unknown"); |
|
fputs(details.cwd ? details.cwd : "unknown", io_logfile); |
|
fputc('\n', io_logfile); |
|
fputs(details.command ? details.command : "unknown", io_logfile); |
|
for (cur = &argv[1]; *cur != NULL; cur++) { |
|
fputc(' ', io_logfile); |
|
fputs(*cur, io_logfile); |
|
} |
|
fputc('\n', io_logfile); |
|
fclose(io_logfile); |
|
|
|
rval = TRUE; |
|
|
|
done: |
done: |
|
fatal_disable_setjmp(); |
efree(tofree); |
efree(tofree); |
if (details.runas_pw) |
if (details.runas_pw) |
pw_delref(details.runas_pw); | sudo_pw_delref(details.runas_pw); |
sudo_endpwent(); |
sudo_endpwent(); |
if (details.runas_gr) |
if (details.runas_gr) |
gr_delref(details.runas_gr); | sudo_gr_delref(details.runas_gr); |
sudo_endgrent(); |
sudo_endgrent(); |
|
|
return rval; | debug_return_bool(rval); |
} |
} |
|
|
static void |
static void |
sudoers_io_close(int exit_status, int error) |
sudoers_io_close(int exit_status, int error) |
{ |
{ |
int i; |
int i; |
|
debug_decl(sudoers_io_close, SUDO_DEBUG_PLUGIN) |
|
|
if (sigsetjmp(error_jmp, 1)) { | if (fatal_setjmp() != 0) { |
/* called via error(), errorx() or log_error() */ | /* called via fatal(), fatalx() or log_fatal() */ |
return; | fatal_disable_setjmp(); |
| debug_return; |
} |
} |
|
|
for (i = 0; i < IOFD_MAX; i++) { |
for (i = 0; i < IOFD_MAX; i++) { |
if (io_fds[i].v == NULL) | if (io_log_files[i].fd.v == NULL) |
continue; |
continue; |
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
if (iolog_compress) |
if (iolog_compress) |
gzclose(io_fds[i].g); | gzclose(io_log_files[i].fd.g); |
else |
else |
#endif |
#endif |
fclose(io_fds[i].f); | fclose(io_log_files[i].fd.f); |
} |
} |
|
debug_return; |
} |
} |
|
|
static int |
static int |
sudoers_io_version(int verbose) |
sudoers_io_version(int verbose) |
{ |
{ |
if (sigsetjmp(error_jmp, 1)) { | debug_decl(sudoers_io_version, SUDO_DEBUG_PLUGIN) |
/* called via error(), errorx() or log_error() */ | |
return -1; | if (fatal_setjmp() != 0) { |
| /* called via fatal(), fatalx() or log_fatal() */ |
| fatal_disable_setjmp(); |
| debug_return_bool(-1); |
} |
} |
|
|
sudo_printf(SUDO_CONV_INFO_MSG, "Sudoers I/O plugin version %s\n", |
sudo_printf(SUDO_CONV_INFO_MSG, "Sudoers I/O plugin version %s\n", |
PACKAGE_VERSION); |
PACKAGE_VERSION); |
|
|
return TRUE; | debug_return_bool(true); |
} |
} |
|
|
/* |
/* |
Line 596 static int
|
Line 684 static int
|
sudoers_io_log(const char *buf, unsigned int len, int idx) |
sudoers_io_log(const char *buf, unsigned int len, int idx) |
{ |
{ |
struct timeval now, delay; |
struct timeval now, delay; |
|
debug_decl(sudoers_io_version, SUDO_DEBUG_PLUGIN) |
|
|
gettimeofday(&now, NULL); |
gettimeofday(&now, NULL); |
|
|
if (sigsetjmp(error_jmp, 1)) { | if (fatal_setjmp() != 0) { |
/* called via error(), errorx() or log_error() */ | /* called via fatal(), fatalx() or log_fatal() */ |
return -1; | fatal_disable_setjmp(); |
| debug_return_bool(-1); |
} |
} |
|
|
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
if (iolog_compress) |
if (iolog_compress) |
gzwrite(io_fds[idx].g, buf, len); | ignore_result(gzwrite(io_log_files[idx].fd.g, (const voidp)buf, len)); |
else |
else |
#endif |
#endif |
fwrite(buf, 1, len, io_fds[idx].f); | ignore_result(fwrite(buf, 1, len, io_log_files[idx].fd.f)); |
delay.tv_sec = now.tv_sec; | sudo_timevalsub(&now, &last_time, &delay); |
delay.tv_usec = now.tv_usec; | |
timevalsub(&delay, &last_time); | |
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
if (iolog_compress) |
if (iolog_compress) |
gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx, | gzprintf(io_log_files[IOFD_TIMING].fd.g, "%d %f %u\n", idx, |
delay.tv_sec + ((double)delay.tv_usec / 1000000), len); |
delay.tv_sec + ((double)delay.tv_usec / 1000000), len); |
else |
else |
#endif |
#endif |
fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx, | fprintf(io_log_files[IOFD_TIMING].fd.f, "%d %f %u\n", idx, |
delay.tv_sec + ((double)delay.tv_usec / 1000000), len); |
delay.tv_sec + ((double)delay.tv_usec / 1000000), len); |
last_time.tv_sec = now.tv_sec; |
last_time.tv_sec = now.tv_sec; |
last_time.tv_usec = now.tv_usec; |
last_time.tv_usec = now.tv_usec; |
|
|
return TRUE; | debug_return_bool(true); |
} |
} |
|
|
static int |
static int |
Line 657 sudoers_io_log_stderr(const char *buf, unsigned int le
|
Line 745 sudoers_io_log_stderr(const char *buf, unsigned int le
|
return sudoers_io_log(buf, len, IOFD_STDERR); |
return sudoers_io_log(buf, len, IOFD_STDERR); |
} |
} |
|
|
struct io_plugin sudoers_io = { | __dso_public struct io_plugin sudoers_io = { |
SUDO_IO_PLUGIN, |
SUDO_IO_PLUGIN, |
SUDO_API_VERSION, |
SUDO_API_VERSION, |
sudoers_io_open, |
sudoers_io_open, |