Subject: Integrating securelevel and kauth(9)
To: None <tech-security@netbsd.org, tech-kern@netbsd.org>
From: Elad Efrat <elad@NetBSD.org>
List: tech-kern
Date: 03/24/2006 19:56:27
Hello,
Outlined in this mail is my proposal for integrating the traditional BSD
securelevel with the kauth(9) interface.
This mail lightly uses some kauth(9) terminology; an online version of
the man-page is available from http://www.bsd.org.il/netbsd/kauth.9.html
It was discussed in the past ("The reason for securelevel") that we need
to separate the impact of securelevel to allow finer-grained control
over what can or cannot be done in our system. For example, an
administrator might like to customize the impact of the securelevel
combining several implications from securelevel 2 but otherwise all the
affects of securelevel 1.
On the other hand, some people may not want any of this. The solution
must allow people who would like to stick to the current model of
securelevel the ability to do so.
The proposal in this mail is divided in two. The first part will discuss
the proposed kernel backend to replace securelevel. The second part will
discuss possible (optional) userland enhancements that will allow the
finer-grained access mentioned above to people who need it.
The only issue with replacing the securelevel implementation is
third-party LKMs. This is because they may rely on the availability of a
"securelevel" variable. For example, a driver that according to the
securelevel allows or denies a certain request.
The magnitude of this issue needs to be realized: this affects only LKMs
that were written specifically for BSD systems, as a vanilla Linux (and
perhaps Solaris? I don't know) kernel does not provide a securelevel
mechanism.
The first part of this proposal will also describe possible solutions to
this problem.
1. Kernel backend
At the moment, the kernel implementation of securelevel is very
simple: a single (raise-only, sysctl-able) variable, "securelevel",
that is checked in various parts of the kernel whenever a
securelevel-relevant operation is requested. This means that the
interface to securelevel is also its implementation.
The suggested change is inspecting references to securelevel, creating
a list of common requests made that rely on securelevel. This list
should be able to describe the operations used in our kernel, and
offer operations that third-party LKMs may need.
Then, we could replace the securelevel variable with a bitmap. With
each bit representing a different operation, we could both support
fine-grained control over the securelevel by setting the individual
bits, or the traditional model by setting to specific pre-defined
bitmasks that will represent securelevels -1 to 2.
There is, however, a special case to this (pointed out by Thor
Lancelot Simon in a private mail) where going from multi-user to
single-user mode. At the moment, what happens is that the securelevel
is lowered to 0 when that happens, and when exiting the single-user
shell the securelevel is raised back to 1. Later, the startup scripts
may raise it further depending on /etc/sysctl.conf.
In a case of multiple knobs, a mechanism for saving state will have to
be in place so that the current bits set in the securelevel bitmap
will be restored once the single-user shell is exited.
To give an example, if some drivers guards their ioctl() in
securelevel > 1, we might introduce a "allow driver ioctl" request
that third-party driver LKMs can make.
These requests will be implemented as a new kauth(9) scope, called the
"system" scope.
Giving a code example, if we have something like:
/* Don't allow ioctl() requests if securelevel is > 1 */
if (securelevel > 1)
return (EPERM);
It might be replaced with:
/* See if we can issue ioctl() requests */
if (kauth_authorize_system(curproc->p_cred,
KAUTH_SYSTEM_DRIVER_IOCTL, NULL) != 0)
return (EPERM);
(note that this is just for illustration; we might need to pass more
arguments to kauth_authorize_system(). That is why an inspection of
securelevel references is required.. :)
For those who are not too familiar with kauth(9), this authorization
request will then be dispatched to the default listener for the system
scope. That listener will perform something like this:
[...]
error = 0;
switch (operation) {
[...]
case KAUTH_SYSTEM_DRIVER_IOCTL:
if (securelevel & KAUTH_SYSTEM_DRIVER_IOCTL)
error = EPERM;
break;
[...]
}
return (error);
(if you're asking yourself "why not just do a single if statement to
check for the requested operation in securelevel?", the answer is that
some securelevel implications also have other considerations taken
into account.)
Summarizing so far, we've managed to:
- Maintain existing traditional interface and implications for
people who want it;
- Allow finer-grained knobs to be implemented by making sure the
kernel backend does not check a single variable, but rather a
single "privilege".
- Allow future expansion of the securelevel on a per-user basis by
passing the user credentials along with the request.
...but we broke compatibility with third-party LKMs that rely on
securelevel being a simple integer.
Addressing that problem, we have several options:
- For people who are interested in keeping the traditional
securelevel, this is simple: because our securelevel bitmap will
be in a new variable, we can maintain the existing securelevel
variable to represent the system's securelevel for third-party
LKMs that need it.
That is, whenever the securelevel is changed, and the bitmasks are
set, we will also modify the securelevel variable. In the NetBSD
kernel it will not be used anywhere, but it will exist for
third-party LKMs.
- For people who are interested in the finer-grained securelevel
knobs, this is somewhat more complicated.
By changing the meaning of securelevel, a third-party LKM no longer
has anything to refer to as a "system security level". I offer two
possible solutions:
(a) The securelevel variable will be maintained as "third-party LKM
securelevel", maintained only for backwards binary
compatibility. It will have no meaning in the NetBSD kernel,
but will retain the raise-only property of the current
securelevel.
(b) Third-party LKMs will be encouraged to remove references to
securelevel and use the kauth(9) interface, possibly with the
operation they want guarded from the list of common operations
for the system scope.
We can always add more operations to the system scope if they
are needed, however authors of third-party LKMs that heavily
depend on securelevel's multiple levels will be advised to make
use of the new kauth(9) interface's ability to introduce new
scopes to the system.
An LKM relying on securelevel might consider code like:
#ifdef __HAVE_KERNELAUTH
/* Use kauth(9) */
#else
/* Check securelevel */
#endif /* __HAVE_KERNELAUTH */
To summarize, we maintained binary compatibility with existing LKMs,
offer an easy way for LKM authors to adapt to the new kauth(9)
interface, and suggest a way for supporting both earlier pre-kauth(9)
NetBSD kernels as well as newer ones that do provide the interface.
2. Userland enhancements
To properly support multiple-knobs, we'll have to provide a
user-interface for accessing them. This is not a problem with our
sysctl(9) system, but may pose a problem to third-party scripts that
may query or even modify kern.securelevel.
Just as with the third-party LKMs, we will continue to maintain
kern.securelevel as a "third-party securelevel" that can be used, with
the same semantics it has today.
Unlike LKMs, "fixing" this issue for third-party software (programs or
sciprts) isn't as easy, because the user-interface (sysctl(8) and
sysctl(3)) will show either the single knob or multiple knobs
depending on how the kernel was compiled.
I am not sure about the scale of this issue -- ie. how many references
we really have in third-party software -- but my best suggestion at
this point is to leave the existing kern.securelevel sysctl knob to
function as a "third-party reference", not just for LKMs. :)
3. Benefit
The changes outlined above are a rough proposal to, first and
foremost, integrate the securelevel mechanism with the kauth(9)
interface. Although initially the benefit will be minor to people
using the traditional securelevel interface, in the future it will
allow us to implement features like capabilities or MACs (choose your
favorite buzzword ;) -- the required changes to support such
mechanisms will be mostly internal to the kauth(9) framework, and not
system-wide changes.
Waiting for any kind of feedback,
-e.
--
Elad Efrat