staprun/staprun_funcs.c - systemtap
Data types defined
Functions defined
Source code
#include "../config.h"
#include "staprun.h"
#include <sys/mount.h>
#include <sys/utsname.h>
#include <grp.h>
#include <pwd.h>
#include <assert.h>
#include <libelf.h>
#include <gelf.h>
#include <math.h>
#include "modverify.h"
#include "../privilege.h"
typedef int (*check_module_path_func)(const char *module_path, int module_fd);
extern long init_module(void *, unsigned long, const char *);
extern gid_t get_gid (const char *group_name);
const char *moderror(int err)
{
switch (err) {
case ENOEXEC:
return "Invalid module format";
case ENOENT:
return "Unknown symbol in module";
case ESRCH:
return "Module has wrong symbol version";
case EINVAL:
return "Invalid parameters";
default:
return strerror(err);
}
}
int insert_module(
const char *path,
const char *special_options,
char **options,
assert_permissions_func assert_permissions,
privilege_t *user_credentials
) {
int i;
long ret, module_read;
void *module_file;
char *opts;
int saved_errno;
char module_realpath[PATH_MAX];
int module_fd;
struct stat sbuf;
int rename_this_module;
dbug(2, "inserting module %s\n", path);
rename_this_module = rename_mod && (strcmp(path, modpath) == 0);
if (special_options)
opts = strdup(special_options);
else
opts = strdup("");
if (opts == NULL) {
_perr("allocating memory failed");
return -1;
}
for (i = 0; options[i] != NULL; i++) {
opts = realloc(opts, strlen(opts) + strlen(options[i]) + 2);
if (opts == NULL) {
_perr("[re]allocating memory failed");
return -1;
}
strcat(opts, " ");
strcat(opts, options[i]);
}
dbug(2, "module options: %s\n", opts);
if (realpath(path, module_realpath) == NULL) {
perr("Unable to canonicalize path \"%s\"", path);
free(opts);
return -1;
}
dbug(2, "module path canonicalized to '%s'\n", module_realpath);
path = NULL;
module_fd = open(module_realpath, O_RDONLY);
if (module_fd < 0) {
perr("Couldn't open '%s'", module_realpath);
free(opts);
return -1;
}
if (fstat(module_fd, &sbuf) < 0) {
_perr("Error stat'ing '%s'", module_realpath);
close(module_fd);
free(opts);
return -1;
}
module_file = calloc(1, sbuf.st_size);
if (module_file == NULL) {
_perr("Error allocating memory to read '%s'", module_realpath);
close(module_fd);
free(opts);
return -1;
}
module_read = 0;
while (module_read < sbuf.st_size) {
ret = read(module_fd, module_file + module_read,
sbuf.st_size - module_read);
if (ret > 0)
module_read += ret;
else if (ret == 0) {
_err("Unexpected EOF reading '%s'", module_realpath);
free(module_file);
close(module_fd);
free(opts);
return -1;
} else if (errno != EINTR) {
_perr("Error reading '%s'", module_realpath);
free(module_file);
close(module_fd);
free(opts);
return -1;
}
}
assert_permissions (module_realpath, module_fd, module_file, sbuf.st_size, user_credentials);
if (rename_this_module) {
dbug(2,"Renaming module '%s'\n", modname);
if(rename_module(module_file, sbuf.st_size) < 0) {
_err("Error renaming module");
close(module_fd);
free(opts);
return -1;
}
dbug(2,"Renamed module to '%s'\n", modname);
}
if (getenv ("SYSTEMTAP_SYNC") != NULL)
sync();
dbug(1,"Module %s inserted from file %s\n", modname, module_realpath);
PROBE1(staprun, insert__module, (char*)module_realpath);
ret = init_module(module_file, sbuf.st_size, opts);
saved_errno = errno;
free(opts);
free(module_file);
close(module_fd);
if (ret != 0) {
errno = saved_errno;
return -errno;
}
return 0;
}
static Elf_Scn *
find_section_in_module(const void* module_file, const __off_t st_size, const char *section_name)
{
char *name;
size_t shstrndx;
Elf* elf;
Elf_Scn *scn = 0;
GElf_Shdr shdr_mem;
if((elf = elf_memory ((void *)module_file, st_size))== NULL) {
_err("Error creating Elf object.\n");
return NULL;
}
if(elf_getshdrstrndx (elf, &shstrndx) < 0) {
_err("Error getting section index.\n");
return NULL;
}
while ((scn = elf_nextscn (elf, scn))) {
if((gelf_getshdr (scn, &shdr_mem))==NULL) {
_err("Error getting section header.\n");
return NULL;
}
name = elf_strptr (elf, shstrndx, shdr_mem.sh_name);
if (name == NULL) {
_err("Error getting section name.\n");
return NULL;
}
if(strcmp(name, section_name) == 0) {
break;
}
}
return scn;
}
int
rename_module(void* module_file, const __off_t st_size)
{
int length_to_replace;
char newname[MODULE_NAME_LEN];
char *p;
pid_t pid;
Elf_Scn *scn = 0;
Elf_Data *data = 0;
scn = find_section_in_module (module_file, st_size, ".gnu.linkonce.this_module");
if(!scn) {
_err("Section name \".gnu.linkonce.this_module\" not found in module.\n");
return -1;
}
if ((data = elf_rawdata (scn, data)) == NULL) {
_err("Error getting Elf data from section.\n");
return -1;
}
if (strlen(modname) >= MODULE_NAME_LEN) {
_err("Old module name is too long.\n");
return -1;
}
pid = getpid();
length_to_replace = (int)strlen(modname)-((int)log10(pid)+1) - 1;
if(length_to_replace < 0 || length_to_replace > (int)strlen(modname)) {
_err("Error getting length of oldname to replace in newname.\n");
return -1;
}
if (snprintf(newname, sizeof(newname), "%.*s_%d", length_to_replace, modname, pid) < 0) {
_err("Creating newname failed./n");
return -1;
}
for (p = data->d_buf; p < (char *)data->d_buf + data->d_size - strlen(modname); p++) {
if (memcmp(p, modname, strlen(modname)) == 0) {
strncpy(p, newname, strlen(p)); modname = strdup(newname); if (modname == NULL) {
_perr("allocating memory failed");
return -1;
}
return 0;
}
}
_err("Could not find old name to replace!\n");
return -1;
}
int mountfs(void)
{
struct stat sb;
struct statfs st;
int rc;
if (statfs(DEBUGFSDIR, &st) == 0
&& (int) st.f_type == (int) DEBUGFS_MAGIC)
return 0;
rc = stat(DEBUGFSDIR, &sb);
if (rc == 0 && S_ISDIR(sb.st_mode)) {
rc = mount ("debugfs", DEBUGFSDIR, "debugfs", 0, NULL);
if (rc == 0)
return 0;
else if (errno != ENODEV) {
perr("Couldn't mount %s", DEBUGFSDIR);
return -1;
}
}
if (statfs(RELAYFSDIR, &st) == 0
&& (int)st.f_type == (int)RELAYFS_MAGIC)
return 0;
rc = stat(RELAYFSDIR, &sb);
if (rc == 0 && ! S_ISDIR(sb.st_mode)) {
err("%s exists but isn't a directory.\n", RELAYFSDIR);
return -1;
}
else if (rc < 0) {
mode_t old_umask;
int saved_errno;
gid_t gid = getgid();
uid_t uid = getuid();
old_umask = umask(0002);
XXX if (setuid (0) < 0) {
_perr("Couldn't change user while creating %s", RELAYFSDIR);
return -1;
}
if (setgid (0) < 0) {
_perr("Couldn't change group while creating %s", RELAYFSDIR);
return -1;
}
rc = mkdir(RELAYFSDIR, 0755);
saved_errno = errno;
if (setgid (gid) < 0) {
_perr("Couldn't restore group while creating %s", RELAYFSDIR);
return -1;
}
if (setuid (uid) < 0) {
_perr("Couldn't restore user while creating %s", RELAYFSDIR);
return -1;
}
umask(old_umask);
if (rc < 0) {
err("Couldn't create %s: %s\n", RELAYFSDIR, strerror(saved_errno));
return -1;
}
}
if (mount ("relayfs", RELAYFSDIR, "relayfs", 0, NULL) < 0) {
perr("Couldn't mount %s", RELAYFSDIR);
return -1;
}
return 0;
}
#if ! HAVE_NSS
static int
check_signature(const char *path __attribute__((unused)),
const void *module_data __attribute__((unused)),
off_t module_size __attribute__((unused)))
{
return MODULE_UNTRUSTED;
}
#else
static int
check_signature(const char *path, const void *module_data, off_t module_size)
{
char signature_realpath[PATH_MAX];
int rc;
dbug(2, "checking signature for %s\n", path);
if (strlen (path) >= PATH_MAX - 5) {
err("Path \"%s.sgn\" is too long.", path);
return -1;
}
sprintf (signature_realpath, "%s.sgn", path);
rc = verify_module (signature_realpath, path, module_data, module_size);
dbug(2, "verify_module returns %d\n", rc);
return rc;
}
#endif
static int
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;
if (uname(&utsbuf) != 0) {
_perr("ERROR: Unable to determine kernel version, uname failed");
return -1;
}
if (sprintf_chk(staplib_dir_path, "/lib/modules/%s/systemtap", utsbuf.release))
return -1;
if (realpath(staplib_dir_path, staplib_dir_realpath) == NULL) {
perr("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" Unable to canonicalize that directory",
staplib_dir_path);
return -1;
}
if (strlen(staplib_dir_realpath) < (PATH_MAX - 1))
strcat(staplib_dir_realpath, "/");
else {
err("Path \"%s\" is too long.", staplib_dir_realpath);
return -1;
}
if (stat(staplib_dir_path, &sb) < 0) {
perr("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" Error getting information on that directory",
staplib_dir_path);
return -1;
}
if (! S_ISDIR(sb.st_mode)) {
err("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" That path must refer to a directory.\n",
staplib_dir_path);
return -1;
}
if (sb.st_uid != 0) {
err("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" That directory should be owned by root.\n",
staplib_dir_path);
return -1;
}
if (sb.st_mode & S_IWOTH) {
err("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" That directory should not be world writable.\n",
staplib_dir_path);
return -1;
}
if (strncmp(staplib_dir_realpath, module_path,
strlen(staplib_dir_realpath)) != 0) {
err("Members of the \"stapusr\" and \"stapsys\" groups can only use unsigned modules within\n"
" the \"%s\" directory.\n"
" Module \"%s\" does not exist within that directory.\n",
staplib_dir_path, module_path);
return -1;
}
if (fstat(module_fd, &sb) < 0) {
perr("Couldn't get information on the module\"%s\"", module_path);
return -1;
}
if (sb.st_uid != 0) {
err("The module \"%s\" must be owned by root.\n", module_path);
rc = -1;
}
if (sb.st_mode & S_IWOTH) {
err("The module \"%s\" must not be world writable.\n", module_path);
rc = -1;
}
return rc;
}
static int
check_uprobes_module_path (
const char *module_path __attribute__ ((unused)),
int module_fd __attribute__ ((unused))
)
{
return 0;
}
static privilege_t get_module_required_credentials (
const char *module_path,
const void* module_file __attribute__ ((unused)),
const __off_t st_size __attribute__ ((unused))
)
{
Elf_Scn *scn = 0;
Elf_Data *data = 0;
GElf_Shdr shdr;
privilege_t privilege;
scn = find_section_in_module (module_file, st_size, STAP_PRIVILEGE_SECTION);
if (! scn) {
if (verbose >= 1) {
eprintf ("Section name \"%s\" not found in module %s.\n", STAP_PRIVILEGE_SECTION,
module_path);
eprintf ("Assuming required privilege level of %s.\n", pr_name (pr_stapusr));
}
return pr_stapusr;
}
if (gelf_getshdr (scn, & shdr) == NULL) {
if (verbose >= 1) {
eprintf ("Error getting section header from section %s in module %s.\n", STAP_PRIVILEGE_SECTION,
module_path);
eprintf ("Assuming required privilege level of %s.\n", pr_name (pr_highest));
}
return pr_highest;
}
if (shdr.sh_size < 1) {
if (verbose >= 1) {
eprintf ("Section header from section %s in module %s has no items\n", STAP_PRIVILEGE_SECTION,
module_path);
eprintf ("Assuming required privilege level of %s.\n", pr_name (pr_highest));
}
return pr_highest;
}
if ((data = elf_getdata (scn, data)) == NULL) {
if (verbose >= 1) {
eprintf ("Error getting data from section %s in module %s\n", STAP_PRIVILEGE_SECTION,
module_path);
eprintf ("Assuming required privilege level of %s.\n", pr_name (pr_highest));
}
return pr_highest;
}
if (data->d_size != sizeof (privilege)) {
if (verbose >= 1) {
eprintf ("Data in section %s is in module %s not the correct size\n", STAP_PRIVILEGE_SECTION,
module_path);
eprintf ("Assuming required privilege level of %s.\n", pr_name (pr_highest));
}
return pr_highest;
}
privilege = *(privilege_t*)data->d_buf;
switch (privilege) {
case pr_stapusr:
case pr_stapsys:
case pr_stapdev:
break; default:
if (verbose >= 1) {
eprintf ("Unknown privilege data, 0x%x in section %s in module %s\n",
(int)privilege, STAP_PRIVILEGE_SECTION, module_path);
err ("Assuming required privilege level of %s.\n", pr_name (pr_highest));
}
return pr_highest;
}
return privilege;
}
static int
check_groups (
const char *module_path,
int module_fd,
int module_signature_status,
check_module_path_func check_path,
const void *module_data,
off_t module_size,
privilege_t *assigned_user_credentials
)
{
privilege_t user_credentials, module_required_credentials;
int rc;
user_credentials = get_privilege_credentials ();
if (assigned_user_credentials)
*assigned_user_credentials = user_credentials;
if (pr_contains (user_credentials, pr_stapdev))
return 1;
if (module_signature_status == MODULE_OK) {
module_required_credentials = get_module_required_credentials (module_path, module_data, module_size);
if (pr_contains (user_credentials, module_required_credentials)) {
return 1;
}
err("Your privilege credentials (%s) are insufficient to load the module %s (%s required)\n",
pr_name (user_credentials), module_path, pr_name (module_required_credentials));
if (user_credentials == pr_none)
return -2;
return 0;
}
assert (module_signature_status == MODULE_UNTRUSTED ||
module_signature_status == MODULE_CHECK_ERROR);
rc = check_path (module_path, module_fd);
if (rc == 1) {
if (assigned_user_credentials)
*assigned_user_credentials = pr_all;
}
else
err("Unable to verify the signature for the module %s.\n", module_path);
return rc;
}
void assert_stap_module_permissions(
const char *module_path,
int module_fd,
const void *module_data,
off_t module_size,
privilege_t *user_credentials
) {
int check_groups_rc;
int check_signature_rc;
check_signature_rc = check_signature (module_path, module_data, module_size);
if (check_signature_rc == MODULE_ALTERED)
exit(-1);
if (getuid() == 0) {
const char *env_id = getenv("SYSTEMTAP_REAL_UID");
if (env_id && setreuid(atoi(env_id), -1))
warn("Couldn't set staprun UID to '%s': %s",
env_id, strerror(errno));
env_id = getenv("SYSTEMTAP_REAL_GID");
if (env_id && setregid(atoi(env_id), -1))
warn("Couldn't set staprun GID to '%s': %s",
env_id, strerror(errno));
if (user_credentials)
*user_credentials = pr_all;
return;
}
check_groups_rc = check_groups (module_path, module_fd, check_signature_rc,
check_stap_module_path, module_data, module_size,
user_credentials);
if (check_groups_rc == 1)
return;
if (check_groups_rc == -2) {
err("You are trying to run systemtap as a normal user.\n"
"You should either be root, or be part of "
"group \"stapusr\" and possibly the groups \"stapsys\" or \"stapdev\".\n");
}
exit(-1);
}
void assert_uprobes_module_permissions(
const char *module_path,
int module_fd,
const void *module_data,
off_t module_size,
privilege_t *user_credentials
) {
int check_groups_rc;
int check_signature_rc;
check_signature_rc = check_signature (module_path, module_data, module_size);
if (check_signature_rc == MODULE_ALTERED)
exit(-1);
if (getuid() == 0) {
if (user_credentials)
*user_credentials = pr_all;
return;
}
check_groups_rc = check_groups (module_path, module_fd, check_signature_rc,
check_uprobes_module_path, module_data, module_size,
user_credentials);
if (check_groups_rc == 1)
return;
if (check_groups_rc == -2) {
err("You are trying to load the module %s as a normal user.\n"
"You should either be root, or be part of "
"group \"stapusr\" and possibly the groups \"stapsys\" or \"stapdev\".\n",
module_path);
}
exit(-1);
}