Bug 9973 - module signing for unprivileged users
Summary: module signing for unprivileged users
Status: RESOLVED FIXED
Alias: None
Product: systemtap
Classification: Unclassified
Component: translator (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Dave Brolley
URL:
Keywords:
Depends on:
Blocks: blockers-1.1
  Show dependency treegraph
 
Reported: 2009-03-20 19:59 UTC by Frank Ch. Eigler
Modified: 2009-12-15 20:08 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Proposed restrictions on the use of systemtap features by unprivileged users (1.18 KB, text/plain)
2009-05-20 15:44 UTC, Dave Brolley
Details
Updated Restrictions on Systemtap Features for Unprivileged Users (1.25 KB, text/plain)
2009-05-22 16:01 UTC, Dave Brolley
Details
Updated Restrictions on Systemtap Features for Unprivileged Users (1.30 KB, text/plain)
2009-05-27 16:06 UTC, Dave Brolley
Details
Updated Restrictions on Systemtap Features for Unprivileged Users (1.65 KB, text/plain)
2009-12-15 19:58 UTC, Dave Brolley
Details
Final Implementation Spec (4.01 KB, text/plain)
2009-12-15 20:06 UTC, Dave Brolley
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Frank Ch. Eigler 2009-03-20 19:59:49 UTC
Let's track the signing/checking code here.
Comment 1 Dave Brolley 2009-03-25 16:17:01 UTC
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.
Comment 2 Dave Brolley 2009-03-25 16:21:32 UTC
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.
Comment 3 Dave Brolley 2009-03-25 19:19:50 UTC
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.
Comment 4 Dave Brolley 2009-03-26 16:12:38 UTC
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.
Comment 5 Dave Brolley 2009-03-30 18:35:09 UTC
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.
Comment 6 Frank Ch. Eigler 2009-03-30 18:52:49 UTC
(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".
Comment 7 Dave Brolley 2009-03-31 16:16:33 UTC
(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?


Comment 8 Frank Ch. Eigler 2009-03-31 20:40:06 UTC
> 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.
Comment 9 Dave Brolley 2009-04-01 15:03:42 UTC
(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.
Comment 10 Frank Ch. Eigler 2009-04-01 15:09:24 UTC
> 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).
Comment 11 Dave Brolley 2009-04-01 16:05:59 UTC
(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).
Comment 12 Frank Ch. Eigler 2009-04-01 16:57:23 UTC
(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).
Comment 13 Dave Brolley 2009-04-06 18:37:45 UTC
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.
Comment 14 Dave Brolley 2009-04-28 15:58:03 UTC
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.
Comment 15 Dave Brolley 2009-05-20 15:44:03 UTC
Created attachment 3949 [details]
Proposed restrictions on the use of systemtap features by unprivileged users
Comment 16 Josh Stone 2009-05-21 16:48:44 UTC
(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?
Comment 17 Dave Brolley 2009-05-21 18:38:42 UTC
(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.

Comment 18 Josh Stone 2009-05-21 19:24:57 UTC
(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".
Comment 19 Dave Brolley 2009-05-21 21:42:30 UTC
(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.
Comment 20 Josh Stone 2009-05-21 23:48:56 UTC
(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...
Comment 21 Josh Stone 2009-05-21 23:53:26 UTC
(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.
Comment 22 Dave Brolley 2009-05-22 16:01:13 UTC
Created attachment 3957 [details]
Updated Restrictions on Systemtap Features for Unprivileged Users
Comment 23 Dave Brolley 2009-05-22 16:02:33 UTC
(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
Comment 24 Frank Ch. Eigler 2009-05-22 22:05:02 UTC
(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.
Comment 25 Dave Brolley 2009-05-27 16:06:26 UTC
Created attachment 3969 [details]
Updated Restrictions on Systemtap Features for Unprivileged Users
Comment 26 Dave Brolley 2009-08-11 20:31:13 UTC
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).
Comment 27 Dave Brolley 2009-11-05 20:28:28 UTC
re: comment #26: commit ffe4285b81564e098aa7a2d056ff62a8700f321d

Since only root builds uprobes.ko, root's certificate is used to sign it making
it loadable by anyone.
Comment 28 Dave Brolley 2009-11-10 16:02:13 UTC
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.
Comment 29 Dave Brolley 2009-11-10 19:30:52 UTC
commit fd212bd5d99abc3518cf523eb7af2fea5ae206ba

More cleanup of 'modpath' use.
Comment 30 Dave Brolley 2009-11-16 15:58:54 UTC
(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.
Comment 31 Dave Brolley 2009-12-15 19:58:04 UTC
Created attachment 4470 [details]
Updated Restrictions on Systemtap Features for Unprivileged Users
Comment 32 Dave Brolley 2009-12-15 20:06:33 UTC
Created attachment 4471 [details]
Final Implementation Spec
Comment 33 Dave Brolley 2009-12-15 20:08:05 UTC
Now implemented as specified in the attachments.