secmodel(9)
- NetBSD Manual Pages
SECMODEL(9) NetBSD Kernel Developer's Manual SECMODEL(9)
NAME
secmodel -- security model development guidelines
SYNOPSIS
#include <secmodel/secmodel.h>
DESCRIPTION
NetBSD provides a complete abstraction of the underlying security model
used with the operating system to a set of kauth(9) scopes and actions.
It is possible to modify the security model -- either slightly or using
an entirely different model -- by attaching/detaching kauth(9) listeners.
This document describes this process.
Background
In NetBSD 4.0, Kernel Authorization -- kauth(9) -- was introduced as the
subsystem responsible for authorization and credential management.
Before its introduction, there were several ways for providing resource
access control:
- Checking if the user in question is the superuser via suser().
- Comparing the user-id against hard-coded values, often zero,
- Checking the system securelevel.
The problem with the above is that the interface ("can X do Y?") was
tightly coupled with the implementation ("is X Z?"). kauth(9) allowed us
to separate them, dispatching requests with highly detailed context using
a consistent and clear KPI.
The result is a pluggable framework for attaching "listeners" that can
modify the behavior of the system, security-wise. It allows us to main-
tain the existing security model (based on a single superuser and above-
superuser restrictions known as securelevel) but easily decouple it from
the system, given we want to use a different one.
The different security model can be implemented in the kernel or loaded
as an LKM, base its decisions on available information, dispatch the
decision to a userspace daemon, or even to a centralized network autho-
rization server.
The kauth(9) KPI
Before writing a new security model, one should be familiar with the
kauth(9) KPI, its limitations, requirements, and so on.
First, some terminology. According to kauth(9), the system is logically
divided to scopes, where each scope denotes a different area of interest
in the system -- something like a namespace. For example, NetBSD has the
process, network, and machdep scopes, representing process-related, net-
work-related, and machdep-related actions.
Each scope has a collection of actions -- or requests -- forming the high
level indication of the request type. Each request is automatically
associated with credentials and between zero to four arguments providing
the request context.
For example, in the process scope there are requests such as "can sig-
nal", "can change rlimits", and "can change corename".
Each scope in the system is associated with listeners, which are actually
callback routines, that get called when an authorization request on the
relevant scope takes place.
Every listener receives the request and its context, and can make a deci-
sion of either "allow", "deny", or "defer" (if it doesn't want to be the
one deciding).
It is important to note that a single "deny" is enough to fail a request,
and at least a single "allow" is required to allow it. In other words,
it is impossible to attach listeners that weaken the security of the sys-
tem or override decisions made by other listeners.
At last, there are several things you should remember about kauth(9):
- Authorization requests can not be issued when the kernel is
holding any locks. This is a requirement from kernel code, to
allow designing security models where the request should be
dispatched to userspace or a different host.
- Private listener data -- such as internal data-structures -- is
entirely under the responsibility of the developer. Locking,
synchronization, and garbage collection are all things that
kauth(9) does not take care of for you!
Writing a new security model
A security model is composed of (code-wise) the following components:
1. Entry routines, named secmodel_<model>_init() and
secmodel_<model>_start(), used to initialize and start the
security model.
If the security model is to be started automatically by the
kernel and is compiled in it, a function called
secmodel_start() can be added to call the model's start rou-
tine.
If the security model is to be built and used as an LKM,
another function called secmodel_<model>_stop(), to stop the
security model in case the module is to be unloaded.
2. A sysctl(9) setup routine for the model. This should create
an entry for the model in the sysctl(9) namespace, under the
"security.models.<model>" hierarchy.
All "knobs" for the model should be located under the new
node, as well as a mandatory "name" variable, indicating a
descriptive human-readable name for the model.
If the module is to be used as an LKM, explicit calls to the
setup routine and sysctl_teardown() are to be used to create
and destroy the sysctl(9) tree.
3. If the model uses any private data inside credentials, listen-
ing on the credentials scope, KAUTH_SCOPE_CRED, is required.
4. Optionally, internal data-structures used by the model. These
must all be prefixed with "secmodel_<model>_".
5. A set of listeners, attached to various scopes, used to
enforce the policy the model intends to implement.
6. Finally, a security model should register itself when loaded
using secmodel_register() and deregister it when unloaded (if
used as an LKM) using secmodel_deregister().
Below is sample code for a kauth(9) network scope listener for the jenna
security model. It is used to allow users with a user-id below 1000 bind
to reserved ports (for example, 22/TCP):
int
secmodel_jenna_network_cb(kauth_cred_t cred, kauth_action_t action,
void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
{
int result;
/* Default defer. */
result = KAUTH_RESULT_DEFER;
switch (action) {
case KAUTH_NETWORK_BIND:
/*
* We only care about bind(2) requests to privileged
* ports.
*/
if ((u_long)arg0 == KAUTH_REQ_NETWORK_BIND_PRIVPORT) {
/*
* If the user-id is below 1000, which may
* indicate a "reserved" user-id, allow the
* request.
*/
if (kauth_cred_geteuid(cred) < 1000)
result = KAUTH_RESULT_ALLOW;
}
break;
}
return (result);
}
There are two main issues, however, with that listener, that you should
be aware of when approaching to write your own security model:
1. As mentioned, kauth(9) uses restrictive decisions: if you
attach this listener on-top of an existing security model,
even if it would allow the request, it could still be failed.
2. If you attach this listener as the only listener for the net-
work scope, there are many other requests that will be
deferred and, eventually, denied -- which may not be desired.
That's why before implementing listeners, it should be clear whether they
implement an entirely new from scratch security model, or add on-top of
an existing one.
Adding on-top of an existing security model
One of the shortcomings of kauth(9) is that it does not provide any
stacking mechanism, similar to Linux Security Modules (LSM). This, how-
ever, is considered a feature in reducing dependency on other people's
code.
To properly "stack" minor adjustments on-top of an existing security
model, one could use one of two approaches:
- Registering an internal scope for the security model to be used as a
fall-back when requests are deferred.
This requires the security model developer to add an internal scope
for every scope the model partly covers, and registering the fall-
back listeners to it. In the model's listener(s) for the scope, when
a defer decision is made, the request is passed to be authorized on
the internal scope, effectively using the fall-back security model.
Here's example code that implements the above:
#include <secmodel/bsd44/bsd44.h>
/*
* Internal fall-back scope for the network scope.
*/
#define JENNA_ISCOPE_NETWORK "jenna.iscope.network"
static kauth_scope_t secmodel_jenna_iscope_network;
/*
* Jenna's entry point. Register internal scope for the network scope
* which we partly cover for fall-back authorization.
*/
void
secmodel_jenna_start(void)
{
secmodel_jenna_iscope_network = kauth_register_scope(
JENNA_ISCOPE_NETWORK, NULL, NULL);
kauth_listen_scope(JENNA_ISCOPE_NETWORK,
secmodel_bsd44_suser_network_cb, NULL);
kauth_listen_scope(JENNA_ISCOPE_NETWORK,
secmodel_bsd44_securelevel_network_cb, NULL);
}
/*
* Jenna sits on top of another model, effectively filtering requests.
* If it has nothing to say, it discards the request. This is a good
* example for fine-tuning a security model for a special need.
*/
int
secmodel_jenna_network_cb(kauth_cred_t cred, kauth_action_t action,
void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
{
int result;
/* Default defer. */
result = KAUTH_RESULT_DEFER;
switch (action) {
case KAUTH_NETWORK_BIND:
/*
* We only care about bind(2) requests to privileged
* ports.
*/
if ((u_long)arg0 == KAUTH_REQ_NETWORK_BIND_PRIVPORT) {
if (kauth_cred_geteuid(cred) < 1000)
result = KAUTH_RESULT_ALLOW;
}
break;
}
/*
* If we have don't have a decision, fall-back to the bsd44
* security model.
*/
if (result == KAUTH_RESULT_DEFER)
result = kauth_authorize_action(
secmodel_jenna_iscope_network, cred, action,
arg0, arg1, arg2, arg3);
return (result);
}
- If the above is not desired, or cannot be used for any reason, there
is always the ability to manually call the fall-back routine:
int
secmodel_jenna_network_cb(kauth_cred_t cred, kauth_action_t action,
void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
{
int result;
/* Default defer. */
result = KAUTH_RESULT_DEFER;
switch (action) {
case KAUTH_NETWORK_BIND:
/*
* We only care about bind(2) requests to privileged
* ports.
*/
if ((u_long)arg0 == KAUTH_REQ_NETWORK_BIND_PRIVPORT) {
if (kauth_cred_geteuid(cred) < 1000)
result = KAUTH_RESULT_ALLOW;
}
break;
}
/*
* If we have don't have a decision, fall-back to the bsd44
* security model's suser behavior.
*/
if (result == KAUTH_RESULT_DEFER)
result = secmodel_bsd44_suser_network_cb(cred, action,
cookie, arg0, arg1, arg2, arg3);
return (result);
}
Writing a new security model from scratch
When writing a security model from scratch, aside from the obvious issues
of carefully following the desired policy to be implemented and paying
attention to all of the issues outlined above, one must also remember
that any unhandled requests will be denied by default.
To make it easier on developers to write new security models from
scratch, NetBSD maintains skeleton listeners that contain every possible
request and arguments.
Available security models
The following is a list of security models available in the default
NetBSD distribution. To choose, one should edit /usr/src/sys/conf/std.
secmodel_bsd44
Traditional NetBSD security model, derived from 4.4BSD.
secmodel_overlay
Sample overlay security model, sitting on-top of
secmodel_bsd44(9).
FILES
/usr/share/examples/secmodel
SEE ALSO
kauth(9), secmodel_bsd44(9), secmodel_overlay(9)
AUTHORS
Elad Efrat <elad@NetBSD.org>
NetBSD 5.0 January 31, 2007 NetBSD 5.0
Powered by man-cgi (2024-03-20).
Maintained for NetBSD
by Kimmo Suominen.
Based on man-cgi by Panagiotis Christias.