Let's track the signing/checking code here.
Let me begin by summarizing my understanding of what has been discussed in email and on Red Hat's internal IRC channel. Purpose ******* The purpose of module signing is as a form of authentication in addition to the existing forms of authentication currently used by staprun. When a stap module has been signed, it gives a system administrator the opportunity to assert a level of trust in the signer. By accepting the signer's certificate, the sysadmin asserts that he trusts the stap process which created the module. Once signed, staprun can verify that the module has not been tampered with and can load the module based upon the sysadmin's assertion of trust in the signer. The notion of module signing is often discussed in the context of the stap client/server. It is true that the stap server will soon sign each module before returning it to the client. This is not for the purpose of detecting tampering during transmission. An SSL connection between the client and server is used for that purpose. The purpose of module signing by the server is for authentication on the client side. It allows the sysadmin of the client host to assert that the server is secure and is a trusted source of stap modules. There is an important distinction between trusting the connection to the server (SSL) and trusting the module returned by the server. Both assert that the server is secure and a trusted source of stap modules. However, since the module may be kept indefinitely or subsequently copied from one host to another, it can be submitted to staprun for loading by any user at any time after it is created. Regardless of whether the source of a stap module is a stap server or stap run by another user, signing the module allows a sysadmin to assert to staprun that the source of the module is trusted and allows staprun to verify that the module has not been tampered with since its creation. Framework ********* With these principles in mind, here is a proposed framework for authentication based on module signing in systemtap. Existing Authentication in staprun ---------------------------------- staprun already implements two levels of authentication: 1) root and users who are members of the group stapdev may load any module. The assertion is that these users are trusted and would not load a module which is harmful to the system or are authorized to risk harm to the system (e.g. during development of stap). 2) Users who are members of the group stapusr may load modules found in the directory /lib/modules/<kernel-version>/systemtap, where <kernel-version> is the version of the kernel which is currently running. Since this directory is writable only by trusted users, the assertion is that these users would not place a module which is harmful to the system there or that members of stapusr are authorized to risk harm to the system (e.g. during testing or debugging of stap or a stap script). Question: Is the above assertion correct? Proposed Authentication Using Module Signing -------------------------------------------- We already know that module signing can be used to prevent a hostile user from loading a module which has been tampered with after it was created by a trusted module source. While module signing allows us to verify the integrity and source of the module, it says nothing about the safety of the stap script which was used to create the module. Care must also be taken to prevent a hostile user from using staprun to load a module created using a hostile script and a trusted module source. The following authentication procedure for staprun is designed to meet these goals. Authentication proceeds in the following order. 1) The existing authentication procedures (see above) apply first. These procedures allow modules to be loaded based upon the authority of the user and/or the location of the module. Trust is being placed on the integrity of users who are loading and/or placing the modules. Whether these modules have been signed or not is irrelevant in this context. 2) If the authentication above fails, we are left with a member of stapusr or an ordinary unprivileged user and, a module in an unspecified location. In either case we can still load the module provided that a) it has been signed by a trusted signer and can be verified. b) the script used to create the module is not hostile to the system or to other users. Condition a) is easily checked by verifying the module signature against the signer's certificate. Condition b) can be enforced at translation time using an option which instructs the translator to reject dangerous constructs as follows: o For all modules, if the option is specified and the script is deemed safe by the translator, code will be generated to set a runtime flag asserting that the module is not hostile. Otherwise, code will be generated setting the flag to indicate that the module may be hostile. Since signing and verifying the resulting module ensures that it has not been tampered with after it is created, this runtime assertion will still hold once condition a) has been satisfied. o All modules will contain code to check whether the user is privileged or not and, if not, to check the assertion of safety. Should the module be loaded by an unprivileged user and the assertion fails, the module will be unloaded immediately. Comments, concerns and suggestions are welcome and should be posted to this bug.
Heading up the list of constructs or features to be rejected by the translator in "safe" mode are constructs enabled by -g (guru mode). A module can still be certified as safe by the translator, even in -g is specified, provided that no guru mode features are actually used. Others to be posted as they are discovered.
An option has been proposed for certifying module safety at translation time. It is proposed that 1 - when the option is specified, the translator checks for the use of unsafe constructs and generates code to set a runtime flag indicating whether the module is safe to load for unprivileged users. Even if the module is deemed unsafe, a privileged user could still load it. Two other possibilities are: 2 - The option enforces safety. i.e. translation fails if unsafe constructs are used. The module, which may have still been useful to a privileged user, is not created. Safety is asserted by the runtime flag otherwise. 3 - No option at all. The translator always checks for unsafe constructs and generates code to set the runtime flag accordingly. The module is always created (if there are no other errors) and may be loadable based upon the user's authority and/or its safety. In 2, if there are safety concerns, they are listed as errors at translation time. In 1, they could be listed both at translation time and at runtime before the module is unloaded. For 3, they would be listed at runtime. I'm currently leaning toward 3.
Further thoughts re: the module safety option. The runtime flag is only trustworthy if the module has been signed, so if checking is enabled, then signing should also be automatically enabled. This is independent of which alternative is chosen for enabling module safety checking. However it now makes me lean more toward alternative 1: >> 1 - when the option is specified, the translator checks for the use of unsafe >> constructs and generates code to set a runtime flag indicating whether the >> module is safe to load for unprivileged users. Even if the module is deemed >> unsafe, a privileged user could still load it.
It is possible to distinguish between failure to verify a signed module because: 1) it has been tampered with or 2) the signer's certificate is not in the database of trusted signers. This has me leaning once again toward module safety alternative 3: >3 - No option at all. The translator always checks for unsafe constructs and >generates code to set the runtime flag accordingly. The module is always >created >(if there are no other errors) and may be loadable based upon the user's >authority and/or its safety. The implication when combined with comment #4 is that we would also sign every module we create (with no option specified). This has a couple of benefits: 1) staprun would verify all modules before loading regardless of who is attempting to load it. A module which has been tampered with will not be loaded for any user, even root. 2) Once we know that the module has not been tampered with, all users will automatically get the correct level of authentication without having to specify any options.
(In reply to comment #5) > It is possible to distinguish between failure to verify a signed module because: > > 1) it has been tampered with or > 2) the signer's certificate is not in the database of trusted signers. A verification failure of the signature precludes even testing the run-time "safety" flag, so seems to be a separate issue entirely. > This has me leaning once again toward module safety alternative 3: > > >3 - No option at all. The translator always checks for unsafe constructs and > >generates code to set the runtime flag accordingly. One problem with this is that a user would not realize that their proposed script stepped over the "safety" line, until the translator was all done (quietly marking the module "unsafe"), and the hapless user tried to run the thing. I think there is value in telling the translator that "we intend this script to be `safe'" (in the sense of safe to run on behalf of an unprivileged user), and furthermore "please tell us where/how the script fails that standard".
(In reply to comment #6) > A verification failure of the signature precludes even testing the run-time > "safety" flag, so seems to be a separate issue entirely. Exactly. That's why I propose not loading such a module for any user. > > This has me leaning once again toward module safety alternative 3: > > > > >3 - No option at all. The translator always checks for unsafe constructs and > > >generates code to set the runtime flag accordingly. > > One problem with this is that a user would not realize that their proposed > script stepped over the "safety" line, until the translator was all done > (quietly marking the module "unsafe"), and the hapless user tried to run > the thing. I see your point. Using an option, we could then generate warnings about this, whereas generating these all the time would likely be considered useless noise by most users. > I think there is value in telling the translator that "we intend this script > to be `safe'" (in the sense of safe to run on behalf of an unprivileged user), > and furthermore "please tell us where/how the script fails that standard". How about we check all scripts and always set the runtime flag (enabling automatic acceptance of modules as appropriate) and, if requested (by command line option), generate safety warnings in order to help those who are intentionally trying to write scripts which are safe for unprivileged users?
> How about we check all scripts and always set the runtime flag (enabling > automatic acceptance of modules as appropriate) It may help to think of this not just as a passive flag, but rather as active code emitted into the module-initialization code. > and, if requested (by command line option), generate safety warnings in order > to help those who are intentionally trying to write scripts which are safe > for unprivileged users? It should be more than warnings. An unprivileged user should be given errors if they try to probe kernel.function("*"), or access a kernel global variable (via tapset functions like kernel_long(), or bug #4906, in whatever syntactic form it may take), or probably in requesting timer probes of too-short interval, of probably many other constraints. I see unprivileged operation more of an explicit mode switch than a derived property of the script ... at least as a first step.
(In reply to comment #8) > It may help to think of this not just as a passive flag, but rather as active > code emitted into the module-initialization code. > I see unprivileged operation more of an explicit mode switch than a derived > property of the script ... at least as a first step. > This is where we differ philosophically, I guess. I see no reason for staprun to reject a module if it is safe to load, regardless of whether it was created with that intent. I see the command line option to stap as an aid for those wishing to intentionally create a "safe" module.
> This is where we differ philosophically, I guess. I see no reason for staprun to > reject a module if it is safe to load I didn't say that *staprun* should reject a module. staprun would be limited to checking signatures. My point about "run time flags" being a misnomer is more about clarifying that such a flag would only be checkable by module-embedded (i.e., translator-generated) code that is already running (i.e., approved by staprun's signature checks).
(In reply to comment #10) > I didn't say that *staprun* should reject a module. staprun would be limited > to checking signatures. My point about "run time flags" being a misnomer is > more about clarifying that such a flag would only be checkable by module-embedded > (i.e., translator-generated) code that is already running (i.e., approved by > staprun's signature checks). Yes. Sorry for any confusion. To summarize where I think we are: Signing ------- 1) stap signs all modules, provided nss libraries are available 2) staprun checks module signature and authentication as follows: If a signed module has been tampered with, it will not be loaded for any user else if the user is root or a member of stapdev the module will be loaded else if the user is a member of staprun and the module is in /lib/modules/<kernel-version>/systemtap, the module will be loaded else we have an unprivileged user. If the module is signed by a trusted signer, it will be loaded Safety ------ 1) Every module contains generated code which either a) unloads the module if the user is unprivileged (with generated messages explaining why) or b) allows the module to remain active for unprivileged users 2) Whether code a) or b) is generated is determined by the "safety" of the script for unprivileged users as determined by the translator 3) stap will have a command line option which will generate errors at translation time if a script uses constructs which are "unsafe" for unprivileged users. I'm under the impression that you would like code b) to be generated only if the module is safe and the stap command line option is specified (module is "safe" and was created with that intent) whereas I think that code b) should be generated if the module is safe regardless of whether the stap command line option is specified (module is "safe" regardless of the intent).
(In reply to comment #11) > I'm under the impression that you would like code b) to be generated only if the > module is safe and the stap command line option is specified (module is "safe" > and was created with that intent) whereas I think that code b) should be > generated if the module is safe regardless of whether the stap command line > option is specified (module is "safe" regardless of the intent). I think so. The main difference may be implementation complexity. If we use my proposed assert-safe flag, the translator can immediately disable certain code paths or even entire tapsets.cxx pieces. It could start by enabling only a small kernel of features, and immediately & contextually warn if anything else is attempted. Without that assertive flag, it would have to review a processed script after the fact to see what it ended up with -- perhaps after the probe derivation process. This seems less reliable, and could poorly duplicate code associated with the warning messages with item 3).
There are problems with the latest proposal as outlined in comment #11. It is not possible for the generated code in the module to obtain the group ids (gid) for the groups stapdev and staprun. It is therefore not possible for the generated code in the module to determine that the user has these privilege levels and thus, not possible to know whether the user is completely unprivileged. It has been suggested that the stap option(s) which enforce(s) safety take user and/or group arguments which assert that the module has been compiled for specific users or users belonging to specified groups. In this way, the generated code could compare the actual uid or gid of the user loading the module against the list of "approved" uids/gids. The problem I can see with this approach is that other members of stapdev or stapusr who may legitimately wish to load module will no longer be able to do so. One alternative I can see is to only sign modules which are built with the safety enforcing option specified. In this way, no runtime code need be generated at all, since all signed modules will be verified by staprun and the signature is the assertion that the module is "safe" for anyone to load. This also preserves the existing authority of root users and users belonging to stapdev and staprun.
Should have updated this when it was decided.... The solution for the problem in comment #13 is to have staprun pass an argument to the module indicating whether the user is unprivileged or not.
Created attachment 3949 [details] Proposed restrictions on the use of systemtap features by unprivileged users
(In reply to comment #15) > Created an attachment (id=3949) > Proposed restrictions on the use of systemtap features by unprivileged users >[...] > o Note that -g is not explicitely restricted. -g may be specified as long > as no guru-mode features are actually used. What's the reason for this? Why should -g be semi-allowed?
(In reply to comment #16) > (In reply to comment #15) > > Created an attachment (id=3949) > > Proposed restrictions on the use of systemtap features by unprivileged users > >[...] > > o Note that -g is not explicitely restricted. -g may be specified as long > > as no guru-mode features are actually used. > > What's the reason for this? Why should -g be semi-allowed? Just being pedantic, I suppose, as is my nature. Simply specifying -g when translating a "safe" script does not make it dangerous. The translator is checking all the features enabled by -g anyhow. Similarly, using -I, -R or -r is not dangerous provided that known locations are selected. Sometimes options generated by tools are not easy to control, so my philosophy is not to ban the option, but to check whether the result should be restricted.
(In reply to comment #17) > Just being pedantic, I suppose, as is my nature. Simply specifying -g when > translating a "safe" script does not make it dangerous. The translator is > checking all the features enabled by -g anyhow. Similarly, using -I, -R or -r > is not dangerous provided that known locations are selected. I guess I see your point, though -I will never be correct for a safe script. The only safe path for -I is the built-in one, and if you specify that on the command-line you'll get errors about duplicate probe points (since -I augments the path rather than replacing it). > Sometimes options generated by tools are not easy to control, so my philosophy > is not to ban the option, but to check whether the result should be > restricted. Well, as a matter of principle, I'd rather people don't use -g unless it's really needed, and I don't mind slapping the hand of tools with such bad defaults. The only option you listed that should be expected of a well-behaved tool is "-r VERSION".
(In reply to comment #18) > > I guess I see your point, though -I will never be correct for a safe script. > The only safe path for -I is the built-in one, and if you specify that on the > command-line you'll get errors about duplicate probe points (since -I augments > the path rather than replacing it). Didn't know that about -I. Thanks for pointing it out. > Well, as a matter of principle, I'd rather people don't use -g unless it's > really needed, and I don't mind slapping the hand of tools with such bad > defaults. My opinion here is not absolute. If fche wants to make a ruling, then I'll abide by it. > The only option you listed that should be expected of a well-behaved > tool is "-r VERSION". What about -R which happens to specify the default path? Or -r DIR which happens to specify a legitimate VERSION path? Same philosophy for me as -g, so same willingness to submit to a ruling.
(In reply to comment #19) > (In reply to comment #18) > > Well, as a matter of principle, I'd rather people don't use -g unless it's > > really needed, and I don't mind slapping the hand of tools with such bad > > defaults. > > My opinion here is not absolute. If fche wants to make a ruling, then I'll > abide by it. > > > The only option you listed that should be expected of a well-behaved > > tool is "-r VERSION". > > What about -R which happens to specify the default path? Or -r DIR which > happens to specify a legitimate VERSION path? Same philosophy for me as -g, > so same willingness to submit to a ruling. What I don't like is that these become non-options. You can use -g, but don't do anything that needs -g. You can use -R, as long as you specify the default value anyway. Tell the kids they can have whatever they want for dinner, as long as they want what I'm making. Or another analogy, I wouldn't be too thrilled with a tool that insisted calling gcc as: gcc -nostdinc -isystem =/usr/local/include -isystem =/usr/lib/gcc/x86_64-redhat-linux/4.4.0/include -isystem =/usr/include ... I think part of our philosophy difference comes from your idea of automatically marking modules as safe for unprivileged users. If it always requires an explict --unprivileged option, then it's easy for us to say that -g/-R/-I are not allowed with --unprivileged. But when you try to do it implicitly, then I can see that it makes more sense to relax the other options a little bit. I'm more in favor of an explicit option. But of course, as you say, I will yield to popular opinion / BDFL ruling...
(In reply to comment #15) > Created an attachment (id=3949) > Proposed restrictions on the use of systemtap features by unprivileged users Environment variables should be added to this list, especially SYSTEMTAP_RUNTIME and SYSTEMTAP_TAPSET.
Created attachment 3957 [details] Updated Restrictions on Systemtap Features for Unprivileged Users
(In reply to comment #21) > Environment variables should be added to this list, especially SYSTEMTAP_RUNTIME > and SYSTEMTAP_TAPSET. Thanks. I've added SYSTEMTAP_RUNTIME, SYSTEMTAP_TAPSET and SYSTEMTAP_DEBUGINFO_PATH
(In reply to comment #20) > ... I think part of our philosophy difference comes from your idea of automatically > marking modules as safe for unprivileged users. If it always requires an > explict --unprivileged option, then it's easy for us to say that -g/-R/-I are > not allowed with --unprivileged. But when you try to do it implicitly, then I > can see that it makes more sense to relax the other options a little bit. I'm > more in favor of an explicit option. If my tiebreaking vote helps, I agree that an explicit option will be necessary. Part of its implementation could be a pass through systemtap_session->flags after the getopt() loop, checking that every potentially modified relevant flag still has a value set at setup time, and otherwise spit out an error. (This would include s.guru_mode still == false.) You can't disable the flags during the getopt loop trivially since the --unprivileged flag can occur later on in the list.
Created attachment 3969 [details] Updated Restrictions on Systemtap Features for Unprivileged Users
uprobes.ko needs to be signed when it is created. The user's own certificate can be used for this. Once this is implemented, it should be loaded using insert_module (vs /sbin/insmod).
re: comment #26: commit ffe4285b81564e098aa7a2d056ff62a8700f321d Since only root builds uprobes.ko, root's certificate is used to sign it making it loadable by anyone.
commit 64211010978d0e35c80ec7c119f1986a48f97543 Corrected bug in insert_module and its helpers which were using the global variable 'modpath' interchangably with the parameter 'path'. This did not cause problems in the past, since it was 'modpath' that was passed in as 'path' when loading the stap module. Problems were exposed when we tried to also load uprobes.ko using insert_module.
commit fd212bd5d99abc3518cf523eb7af2fea5ae206ba More cleanup of 'modpath' use.
(In reply to comment #27) > Since only root builds uprobes.ko, root's certificate is used to sign it making > it loadable by anyone. > commit 4ab8323bec774a2e4f78900681bf88e36dacaa49 uprobes.ko could actually be built by a user other than root. Most likely users running a systemtap server. uprobes.ko is now signed using the certificate of the builder.
Created attachment 4470 [details] Updated Restrictions on Systemtap Features for Unprivileged Users
Created attachment 4471 [details] Final Implementation Spec
Now implemented as specified in the attachments.