summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-11-27 14:13:15 -0500
committerDave Brolley <brolley@redhat.com>2009-11-27 14:13:15 -0500
commitcf4ed91947a0716e25053eb5332761b6ae4f33a2 (patch)
tree920b688d1c2b83387eb969d6fb43d60bb5085eec /runtime
parenta367e8991d2faf7d803b79309f80943339b0c284 (diff)
downloadsystemtap-steved-cf4ed91947a0716e25053eb5332761b6ae4f33a2.tar.gz
systemtap-steved-cf4ed91947a0716e25053eb5332761b6ae4f33a2.tar.xz
systemtap-steved-cf4ed91947a0716e25053eb5332761b6ae4f33a2.zip
PR 10984 Additional Work. TOCTOU race checking access permissions before canonicalizing /lib/modules/KVER/systemtap.
Diffstat (limited to 'runtime')
-rw-r--r--runtime/staprun/staprun.h3
-rw-r--r--runtime/staprun/staprun_funcs.c128
2 files changed, 80 insertions, 51 deletions
diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h
index 91761bde..252c9ed5 100644
--- a/runtime/staprun/staprun.h
+++ b/runtime/staprun/staprun.h
@@ -135,18 +135,21 @@ const char *moderror(int err);
/* insert_module helper functions. */
typedef void (*assert_permissions_func) (
const char *module_path __attribute__ ((unused)),
+ int module_fd __attribute__ ((unused)),
const void *module_data __attribute__ ((unused)),
off_t module_size __attribute__ ((unused))
);
void assert_stap_module_permissions (
const char *module_path __attribute__ ((unused)),
+ int module_fd __attribute__ ((unused)),
const void *module_data __attribute__ ((unused)),
off_t module_size __attribute__ ((unused))
);
void assert_uprobes_module_permissions (
const char *module_path __attribute__ ((unused)),
+ int module_fd __attribute__ ((unused)),
const void *module_data __attribute__ ((unused)),
off_t module_size __attribute__ ((unused))
);
diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c
index 75d56b50..8f3d84ad 100644
--- a/runtime/staprun/staprun_funcs.c
+++ b/runtime/staprun/staprun_funcs.c
@@ -20,7 +20,7 @@
#include <pwd.h>
#include <assert.h>
-typedef int (*check_module_path_func)(const char *module_path);
+typedef int (*check_module_path_func)(const char *module_path, int module_fd);
extern long init_module(void *, unsigned long, const char *);
@@ -49,10 +49,11 @@ int insert_module(
) {
int i;
long ret;
- void *file;
+ void *module_file;
char *opts;
- int fd, saved_errno;
+ int saved_errno;
char module_realpath[PATH_MAX];
+ int module_fd;
struct stat sbuf;
dbug(2, "inserting module\n");
@@ -89,43 +90,43 @@ int insert_module(
/* Open the module file. Work with the open file descriptor from this
point on to avoid TOCTOU problems. */
- fd = open(module_realpath, O_RDONLY);
- if (fd < 0) {
+ module_fd = open(module_realpath, O_RDONLY);
+ if (module_fd < 0) {
perr("Error opening '%s'", module_realpath);
return -1;
}
/* Now that the file is open, figure out how big it is. */
- if (fstat(fd, &sbuf) < 0) {
+ if (fstat(module_fd, &sbuf) < 0) {
_perr("Error stat'ing '%s'", module_realpath);
- close(fd);
+ close(module_fd);
return -1;
}
/* mmap in the entire module. Work with the memory mapped data from this
point on to avoid a TOCTOU race between path and signature checking
below and module loading. */
- file = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (file == MAP_FAILED) {
+ module_file = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, module_fd, 0);
+ if (module_file == MAP_FAILED) {
_perr("Error mapping '%s'", module_realpath);
- close(fd);
+ close(module_fd);
free(opts);
return -1;
}
/* Check whether this module can be loaded by the current user.
* check_permissions will exit(-1) if permissions are insufficient*/
- assert_permissions (module_realpath, file, sbuf.st_size);
+ assert_permissions (module_realpath, module_fd, module_file, sbuf.st_size);
STAP_PROBE1(staprun, insert__module, (char*)module_realpath);
/* Actually insert the module */
- ret = init_module(file, sbuf.st_size, opts);
+ ret = init_module(module_file, sbuf.st_size, opts);
saved_errno = errno;
/* Cleanup. */
free(opts);
- munmap(file, sbuf.st_size);
- close(fd);
+ munmap(module_file, sbuf.st_size);
+ close(module_fd);
if (ret != 0) {
err("Error inserting module '%s': %s\n", module_realpath, moderror(saved_errno));
@@ -268,12 +269,13 @@ check_signature(const char *path, const void *module_data, off_t module_size)
* Returns: -1 on errors, 1 on success.
*/
static int
-check_stap_module_path(const char *module_path)
+check_stap_module_path(const char *module_path, int module_fd)
{
char staplib_dir_path[PATH_MAX];
char staplib_dir_realpath[PATH_MAX];
struct utsname utsbuf;
struct stat sb;
+ int rc = 1;
/* First, we need to figure out what the kernel
* version is and build the '/lib/modules/KVER/systemtap' path. */
@@ -284,11 +286,33 @@ check_stap_module_path(const char *module_path)
if (sprintf_chk(staplib_dir_path, "/lib/modules/%s/systemtap", utsbuf.release))
return -1;
- /* Validate /lib/modules/KVER/systemtap. */
- if (stat(staplib_dir_path, &sb) < 0) {
+ /* Use realpath() to canonicalize the module directory path. */
+ if (realpath(staplib_dir_path, staplib_dir_realpath) == NULL) {
perr("Unable to verify the signature for the module %s.\n"
" Members of the \"stapusr\" group can only use unsigned modules within\n"
" the \"%s\" directory.\n"
+ " Unable to canonicalize that directory",
+ module_path, staplib_dir_path);
+ return -1;
+ }
+
+ /* To make sure the user can't specify something like
+ * /lib/modules/`uname -r`/systemtapmod.ko, put a '/' on the
+ * end of staplib_dir_realpath. */
+ if (strlen(staplib_dir_realpath) < (PATH_MAX - 1))
+ strcat(staplib_dir_realpath, "/");
+ else {
+ err("ERROR: Path \"%s\" is too long.", staplib_dir_realpath);
+ return -1;
+ }
+
+ /* Validate /lib/modules/KVER/systemtap. No need to use fstat on
+ an open file descriptor to avoid TOCTOU, since the path will
+ not be used to access the file system. */
+ if (stat(staplib_dir_path, &sb) < 0) {
+ perr("Unable to verify the signature for the module %s.\n"
+ " Members of the \"stapusr\" group can only use such modules within\n"
+ " the \"%s\" directory.\n"
" Error getting information on that directory",
module_path, staplib_dir_path);
return -1;
@@ -296,7 +320,7 @@ check_stap_module_path(const char *module_path)
/* Make sure it is a directory. */
if (! S_ISDIR(sb.st_mode)) {
err("ERROR: Unable to verify the signature for the module %s.\n"
- " Members of the \"stapusr\" group can only use unsigned modules within\n"
+ " Members of the \"stapusr\" group can only use such modules within\n"
" the \"%s\" directory.\n"
" That path must refer to a directory.\n",
module_path, staplib_dir_path);
@@ -305,7 +329,7 @@ check_stap_module_path(const char *module_path)
/* Make sure it is owned by root. */
if (sb.st_uid != 0) {
err("ERROR: Unable to verify the signature for the module %s.\n"
- " Members of the \"stapusr\" group can only use unsigned modules within\n"
+ " Members of the \"stapusr\" group can only use such modules within\n"
" the \"%s\" directory.\n"
" That directory should be owned by root.\n",
module_path, staplib_dir_path);
@@ -314,36 +338,15 @@ check_stap_module_path(const char *module_path)
/* Make sure it isn't world writable. */
if (sb.st_mode & S_IWOTH) {
err("ERROR: Unable to verify the signature for the module %s.\n"
- " Members of the \"stapusr\" group can only use unsigned modules within\n"
+ " Members of the \"stapusr\" group can only use such modules within\n"
" the \"%s\" directory.\n"
" That directory should not be world writable.\n",
module_path, staplib_dir_path);
return -1;
}
- /* Use realpath() to canonicalize the module directory
- * path. */
- if (realpath(staplib_dir_path, staplib_dir_realpath) == NULL) {
- perr("Unable to verify the signature for the module %s.\n"
- " Members of the \"stapusr\" group can only use unsigned modules within\n"
- " the \"%s\" directory.\n"
- " Unable to canonicalize that directory",
- module_path, staplib_dir_path);
- return -1;
- }
-
- /* To make sure the user can't specify something like
- * /lib/modules/`uname -r`/systemtapmod.ko, put a '/' on the
- * end of staplib_dir_realpath. */
- if (strlen(staplib_dir_realpath) < (PATH_MAX - 1))
- strcat(staplib_dir_realpath, "/");
- else {
- err("Path \"%s\" is too long.", staplib_dir_realpath);
- return -1;
- }
-
/* Now we've got two canonicalized paths. Make sure
- * path starts with staplib_dir_realpath. */
+ * module_path starts with staplib_dir_realpath. */
if (strncmp(staplib_dir_realpath, module_path,
strlen(staplib_dir_realpath)) != 0) {
err("ERROR: Unable to verify the signature for the module %s.\n"
@@ -353,7 +356,24 @@ check_stap_module_path(const char *module_path)
module_path, staplib_dir_path, module_path);
return -1;
}
- return 1;
+
+ /* Validate the module permisions. */
+ if (fstat(module_fd, &sb) < 0) {
+ perr("Error getting information on the module\"%s\"", module_path);
+ return -1;
+ }
+ /* Make sure it is owned by root. */
+ if (sb.st_uid != 0) {
+ err("ERROR: The module \"%s\" must be owned by root.\n", module_path);
+ rc = -1;
+ }
+ /* Make sure it isn't world writable. */
+ if (sb.st_mode & S_IWOTH) {
+ err("ERROR: The module \"%s\" must not be world writable.\n", module_path);
+ rc = -1;
+ }
+
+ return rc;
}
/*
@@ -363,7 +383,10 @@ check_stap_module_path(const char *module_path)
* Returns: -1 on errors, 0 on failure, 1 on success.
*/
static int
-check_uprobes_module_path (const char *module_path __attribute__ ((unused)))
+check_uprobes_module_path (
+ const char *module_path __attribute__ ((unused)),
+ int module_fd __attribute__ ((unused))
+)
{
return 1;
}
@@ -383,6 +406,7 @@ check_uprobes_module_path (const char *module_path __attribute__ ((unused)))
static int
check_groups (
const char *module_path,
+ int module_fd,
int module_signature_status,
check_module_path_func check_path
)
@@ -464,7 +488,7 @@ check_groups (
/* Could not verify the module's signature, so check whether this module
can be loaded based on its path. check_path is a pointer to a
module-specific function which will do this. */
- return check_path (module_path);
+ return check_path (module_path, module_fd);
}
/*
@@ -481,9 +505,10 @@ check_groups (
* It is only an error if all 4 levels of checking fail
*/
void assert_stap_module_permissions(
- const char *module_path __attribute__ ((unused)),
- const void *module_data __attribute__ ((unused)),
- off_t module_size __attribute__ ((unused))
+ const char *module_path,
+ int module_fd,
+ const void *module_data,
+ off_t module_size
) {
int check_groups_rc;
int check_signature_rc;
@@ -517,7 +542,7 @@ void assert_stap_module_permissions(
}
/* Check permissions for group membership. */
- check_groups_rc = check_groups (module_path, check_signature_rc, check_stap_module_path);
+ check_groups_rc = check_groups (module_path, module_fd, check_signature_rc, check_stap_module_path);
if (check_groups_rc == 1)
return;
@@ -546,7 +571,8 @@ void assert_stap_module_permissions(
* It is only an error if all 3 levels of checking fail
*/
void assert_uprobes_module_permissions(
- const char *module_path __attribute__ ((unused)),
+ const char *module_path,
+ int module_fd,
const void *module_data __attribute__ ((unused)),
off_t module_size __attribute__ ((unused))
) {
@@ -571,7 +597,7 @@ void assert_uprobes_module_permissions(
return;
/* Members of the groups stapdev and stapusr can still load this module. */
- check_groups_rc = check_groups (module_path, check_signature_rc, check_uprobes_module_path);
+ check_groups_rc = check_groups (module_path, module_fd, check_signature_rc, check_uprobes_module_path);
if (check_groups_rc == 1)
return;