diff options
-rw-r--r-- | cache.cxx | 7 | ||||
-rw-r--r-- | runtime/staprun/staprun.c | 10 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 22 | ||||
-rw-r--r-- | runtime/staprun/staprun_funcs.c | 85 | ||||
-rw-r--r-- | tapsets.cxx | 30 |
5 files changed, 118 insertions, 36 deletions
@@ -68,6 +68,9 @@ add_to_cache(systemtap_session& s) s.use_cache = false; return; } + // Copy the signature file, if any. It is not an error if this fails. + if (file_exists (module_src_path + ".sgn")) + copy_file(module_src_path + ".sgn", s.hash_path + ".sgn", verbose); string c_dest_path = s.hash_path; if (c_dest_path.rfind(".ko") == (c_dest_path.size() - 3)) @@ -155,6 +158,10 @@ get_from_cache(systemtap_session& s) close(fd_c); return false; } + // Copy the module signature file, if any. + // It is not an error if this fails. + if (file_exists (s.hash_path + ".sgn")) + copy_file(s.hash_path + ".sgn", module_dest_path + ".sgn"); } // We're done with these file handles. diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index c5651d9a..078be4cf 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -129,14 +129,12 @@ static int enable_uprobes(void) if (run_as(0, 0, 0, argv[0], argv) == 0) return 0; + /* This module may be signed, so use insert_module to load it. */ snprintf (runtimeko, sizeof(runtimeko), "%s/uprobes/uprobes.ko", (getenv("SYSTEMTAP_RUNTIME") ?: PKGDATADIR "/runtime")); dbug(2, "Inserting uprobes module from SystemTap runtime %s.\n", runtimeko); - i = 0; - argv[i++] = "/sbin/insmod"; - argv[i++] = runtimeko; - argv[i] = NULL; - if (run_as(0, 0, 0, argv[0], argv) == 0) + argv[0] = NULL; + if (insert_module(runtimeko, NULL, argv, assert_uprobes_module_permissions) == 0) return 0; return 1; /* failure */ @@ -150,7 +148,7 @@ static int insert_stap_module(void) if (snprintf_chk(special_options, sizeof (special_options), "_stp_bufsize=%d", buffer_size)) return -1; - return insert_module(modpath, special_options, modoptions); + return insert_module(modpath, special_options, modoptions, assert_stap_module_permissions); } static int remove_module(const char *name, int verb); diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 9cdbc861..91761bde 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -131,8 +131,28 @@ time_t read_backlog(int cpu, int fnum); /* staprun_funcs.c */ void setup_staprun_signals(void); const char *moderror(int err); + +/* insert_module helper functions. */ +typedef void (*assert_permissions_func) ( + const char *module_path __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)), + const void *module_data __attribute__ ((unused)), + off_t module_size __attribute__ ((unused)) +); + +void assert_uprobes_module_permissions ( + const char *module_path __attribute__ ((unused)), + const void *module_data __attribute__ ((unused)), + off_t module_size __attribute__ ((unused)) +); int insert_module(const char *path, const char *special_options, - char **options); + char **options, assert_permissions_func apf); + int mountfs(void); void start_symbol_thread(void); void stop_symbol_thread(void); diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index aef8a2da..47ad6a19 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -23,7 +23,6 @@ #include <assert.h> extern long init_module(void *, unsigned long, const char *); -static void assert_permissions(const void *, off_t); /* Module errors get translated. */ const char *moderror(int err) @@ -42,8 +41,12 @@ const char *moderror(int err) } } -int insert_module(const char *path, const char *special_options, char **options) -{ +int insert_module( + const char *path, + const char *special_options, + char **options, + assert_permissions_func assert_permissions +) { int i; long ret; void *file; @@ -74,23 +77,23 @@ int insert_module(const char *path, const char *special_options, char **options) dbug(2, "module options: %s\n", opts); /* Use realpath() to canonicalize the module path. */ - if (realpath(modpath, module_realpath) == NULL) { - perr("Unable to canonicalize path \"%s\"", modpath); + if (realpath(path, module_realpath) == NULL) { + perr("Unable to canonicalize path \"%s\"", path); return -1; } - /* Overwrite the modpath with the canonicalized one, to defeat + /* Overwrite the path with the canonicalized one, to defeat a possible race between path and signature checking below and, somewhat later, module loading. */ - modpath = strdup (module_realpath); - if (modpath == NULL) { + path = strdup (module_realpath); + if (path == NULL) { _perr("allocating memory failed"); exit (1); } /* Open the module file. Work with the open file descriptor from this point on to avoid TOCTOU problems. */ - fd = open(modpath, O_RDONLY); + fd = open(path, O_RDONLY); if (fd < 0) { perr("Error opening '%s'", path); return -1; @@ -114,8 +117,7 @@ int insert_module(const char *path, const char *special_options, char **options) /* Check whether this module can be loaded by the current user. * check_permissions will exit(-1) if permissions are insufficient*/ - assert_permissions (file, sbuf.st_size); - + assert_permissions (path, file, sbuf.st_size); STAP_PROBE1(staprun, insert__module, path); /* Actually insert the module */ @@ -239,20 +241,20 @@ int mountfs(void) * Returns: -1 on errors, 0 on failure, 1 on success. */ static int -check_signature(const void *module_data, off_t module_size) +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", modpath); + dbug(2, "checking signature for %s\n", path); /* Add the .sgn suffix to the canonicalized module path to get the signature file path. */ - if (strlen (modpath) >= PATH_MAX - 4) { - err("Path \"%s.sgn\" is too long.", modpath); + if (strlen (path) >= PATH_MAX - 4) { + err("Path \"%s.sgn\" is too long.", path); return -1; } - sprintf (signature_realpath, "%s.sgn", modpath); + sprintf (signature_realpath, "%s.sgn", path); rc = verify_module (signature_realpath, module_data, module_size); @@ -270,7 +272,7 @@ check_signature(const void *module_data, off_t module_size) * Returns: -1 on errors, 0 on failure, 1 on success. */ static int -check_path(void) +check_path(const char *module_path) { char staplib_dir_path[PATH_MAX]; char staplib_dir_realpath[PATH_MAX]; @@ -330,18 +332,18 @@ check_path(void) if (strlen(staplib_dir_realpath) < (PATH_MAX - 1)) strcat(staplib_dir_realpath, "/"); else { - err("Path \"%s\" is too long.", modpath); + err("Path \"%s\" is too long.", staplib_dir_realpath); return -1; } /* Now we've got two canonicalized paths. Make sure - * modpath starts with staplib_dir_realpath. */ - if (strncmp(staplib_dir_realpath, modpath, + * path starts with staplib_dir_realpath. */ + if (strncmp(staplib_dir_realpath, module_path, strlen(staplib_dir_realpath)) != 0) { err("ERROR: Members of the \"stapusr\" group can only use modules within\n" " the \"%s\" directory.\n" " Module \"%s\" does not exist within that directory.\n", - staplib_dir_path, modpath); + staplib_dir_path, module_path); return 0; } return 1; @@ -359,7 +361,7 @@ check_path(void) * 1 on success */ static int -check_groups (void) +check_groups (const char *module_path) { gid_t gid, gidlist[NGROUPS_MAX]; gid_t stapdev_gid, stapusr_gid; @@ -430,7 +432,7 @@ check_groups (void) * group. Members of the 'stapusr' group can only use modules * in /lib/modules/KVER/systemtap. Make sure the module path * is in that directory. */ - return check_path(); + return check_path (module_path); } /* @@ -445,10 +447,9 @@ check_groups (void) * 4) anyone can load a module which has been signed by a trusted signer * * It is only an error if all 4 levels of checking fail - * - * Returns: -1 on errors, 0 on failure, 1 on success. */ -void assert_permissions( +void assert_stap_module_permissions( + const char *module_path __attribute__ ((unused)), const void *module_data __attribute__ ((unused)), off_t module_size __attribute__ ((unused)) ) { @@ -457,7 +458,7 @@ void assert_permissions( #if HAVE_NSS /* Attempt to verify the module against its signature. Return failure if the module has been tampered with (altered). */ - int check_signature_rc = check_signature (module_data, module_size); + int check_signature_rc = check_signature (module_path, module_data, module_size); if (check_signature_rc == MODULE_ALTERED) exit(-1); #endif @@ -480,7 +481,7 @@ void assert_permissions( } /* Check permissions for group membership. */ - check_groups_rc = check_groups (); + check_groups_rc = check_groups (module_path); if (check_groups_rc == 1) return; @@ -507,6 +508,32 @@ void assert_permissions( "For more information, please consult the \"SAFETY AND SECURITY\" section of the \"stap(1)\" manpage\n"); #endif - /* Combine the return codes. They are either 0 or -1. */ exit(-1); } + +/* + * For the uprobes.ko module, if we have NSS, then + * check the signature. Otherwise go ahead and load it. + */ +void assert_uprobes_module_permissions( + const char *module_path __attribute__ ((unused)), + const void *module_data __attribute__ ((unused)), + off_t module_size __attribute__ ((unused)) +) { +#if HAVE_NSS + /* Attempt to verify the module against its signature. Return failure + if the module has been tampered with (altered). */ + int rc = check_signature (module_path, module_data, module_size); + if (rc == MODULE_ALTERED) + exit(-1); + if (rc == MODULE_OK) + return; + assert (rc == MODULE_UNTRUSTED || rc == MODULE_CHECK_ERROR); + err("Signature verification failed for module %s.\n", module_path); + if (rc == MODULE_UNTRUSTED) { + err("Run '" BINDIR "/stap-sign-module %s' as root and/or\n", module_path); + err("run '" BINDIR "/stap-authorize-signing-cert %s' as root\n", + SYSCONFDIR "/systemtap/ssl/server/stap.cert"); + } +#endif +} diff --git a/tapsets.cxx b/tapsets.cxx index e0346c69..2a893c95 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -358,6 +358,9 @@ struct dwarf_derived_probe: public derived_probe void emit_probe_local_init(translator_output * o); void getargs(std::set<std::string> &arg_set) const; + void emit_unprivileged_assertion (translator_output*); + void print_dupe_stamp(ostream& o); + // Pattern registration helpers. static void register_statement_variants(match_node * root, dwarf_builder * dw); @@ -672,6 +675,11 @@ struct dwarf_builder: public derived_probe_builder probe_point * location, literal_map_t const & parameters, vector<derived_probe *> & finished_results); + + // No action required. These probes are allowed for unprivileged users. + // as process owners. + virtual void check_unprivileged (const systemtap_session & sess, + const literal_map_t & parameters) {} }; @@ -2307,6 +2315,8 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) if (! lvalue) ec->code += "/* pure */"; + + ec->code += "/* unprivileged */"; } catch (const semantic_error& er) { @@ -2962,6 +2972,24 @@ dwarf_derived_probe::getargs(std::set<std::string> &arg_set) const void +dwarf_derived_probe::emit_unprivileged_assertion (translator_output* o) +{ + // These probes are allowed for unprivileged users, but only in the + // context of processes which they own. + emit_process_owner_assertion (o); +} + + +void +dwarf_derived_probe::print_dupe_stamp(ostream& o) +{ + // These probes are allowed for unprivileged users, but only in the + // context of processes which they own. + print_dupe_stamp_unprivileged_process_owner (o); +} + + +void dwarf_derived_probe::register_statement_variants(match_node * root, dwarf_builder * dw) { @@ -5581,6 +5609,8 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) else ec->code += "/* pure */"; + ec->code += "/* unprivileged */"; + dw.sess.functions[fdecl->name] = fdecl; // Synthesize a functioncall. |