| SECMODEL(9) | Kernel Developer's Manual | SECMODEL(9) |
secmodel —
#include <secmodel/secmodel.h>
int
secmodel_register(secmodel_t
*sm, const char
*id, const char
*name, prop_dictionary_t
behavior, secmodel_eval_t
sm_eval,
secmodel_setinfo_t
sm_setinfo);
int
secmodel_deregister(secmodel_t
sm);
int
secmodel_eval(const
char *id, const char
*what, void *arg,
void *ret);
static int
secmodel_<model>_eval(const
char *what, void
*arg, void
*ret);
It is possible to modify the security model — either
slightly or using an entirely different model — by
attaching/detaching kauth(9)
listeners. This can be done via the secmodel
pluggable framework.
A secmodel is typically implemented as a
kernel module(9), and can be
either built-in statically or loaded dynamically at run-time. They base
their decisions on available information, either directly from kernel, from
a userspace daemon or even from a centralized network authorization
server.
secmodel framework offers the following data types:
secmodel.secmodel_register(sm,
id, name,
behavior, sm_eval,
sm_setinfo)secmodel
framework and stores its description inside sm.
secmodel description.secmodel.secmodel.secmodel_<model>_eval()
callback used by a secmodel to register an
evaluation routine that can be queried later by another security
model.secmodel_<model>_setinfo() callback used
by a secmodel to register a routine that
permits other security models to alter the
secmodel internals. Currently not
implemented.secmodel_deregister(sm)secmodel described by
sm.secmodel_eval(id,
what, arg,
ret)secmodel framework.secmodel.secmodel.secmodel.EEXIST]secmodel is already registered.EFAULT]EINVAL]ENOENT]secmodel does not exist, or it does
not implement an evaluation callback.A security model is based on the kernel module(9) framework, and can be built-in statically inside kernel or loaded dynamically at run-time. It is composed of (code-wise) the following components:
MODULE() declaration and a
secmodel_<model>_modcmd() function used to
start (through MODULE_CMD_INIT) and stop (through
MODULE_CMD_FINI) the
secmodel.secmodel_<model>_init() and
secmodel_<model>_start(), used to initialize
and start the security model, and another function called
secmodel_<model>_stop(), to stop the
security model in case the module is to be unloaded.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.
KAUTH_SCOPE_CRED, is
required.secmodel_register(), and deregister itself
before being stopped using
secmodel_deregister().
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:
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.
secmodel
implementation of the desired policy. Certain rights can grant more
privileges on the system than others, like allowing calls to
chroot(2) or mounting a
file-system.To properly "stack" minor adjustments on-top of an existing security model, one could use one of two approaches:
This requires the security model developer to add an internal scope for every scope the model partly covers, and register 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 is 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_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);
}
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);
}
secmodel implementation is in
sys/secmodel/secmodel.c.
The header file
<secmodel/secmodel.h>
describes the public interface.
To make it easier on developers to write new security models from
scratch, NetBSD maintains an example
secmodel under
share/examples/secmodel/.
suser().The problem with the above is that the interface ("can X do Y?") was tightly coupled with the implementation ("is X Z?"). kauth(9) allows separating them, dispatching requests with highly detailed context using a consistent and clear KPI.
The secmodel framework was extended in
NetBSD 6.0 to implement
secmodel registration and evaluation procedure
calls.
| December 4, 2011 | NetBSD 9.0 |