| SECMODEL(9) | Kernel Developer's Manual | SECMODEL(9) | 
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.
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:
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.
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.
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); 
}
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/.
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 6.1 |