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 resonsibility 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.   A secmodel_start() routine, receiving and returning void.
                This routine registers the various listeners for the security
                model.

           2.   An "init" routine, named secmodel_<model>_init(), receiving
                and returning void.  This routine is used to initialize any
                private data-structures that may be used by the model.  If
                none exist, it can be omitted.  It should be called from
                secmodel_start().

           3.   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.

           4.   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.

           5.   Optionally, internal data-structures used by the model. These
                must all be prefixed with "secmodel_<model<_".

           6.   A set of listeners, attached to various scopes, used to
                enforce the policy the model intends to implement.

     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 4.0                    September 19, 2006                    NetBSD 4.0