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