Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Using TrustedBSD


Using TrustedBSD July 2003
by Evan Sarmiento

TrustedBSD provides a set of trusted operating system extensions to FreeBSD. Currently, these extensions can be downloaded from http://www.TrustedBSD.org, but most of them (ACL and MAC — Mandatory Access Control) have been integrated into the FreeBSD-current tree. The MAC framework allows security policies to be set dynamically at runtime. The MAC Framework essentially gives the developer the access to define a security policy that works by positioning the developer's code within kernel functions. The developer's security policy can either augment the traditional FreeBSD discretionary access control policy, or replace it entirely. Essentially, the MAC framework allows the systems administrator to give fine-grained privileges to each user through modifying the existing security policy to match the needs of the given system. TrustedBSD comes prepackaged with a few MAC security modules, which augment the security policies of the given system or add new features — Biba Integrity Policy and File System Firewall Policy, among others.

TrustedBSD also provides the concept of labels. Labels are extraneous pieces of information that are grafted onto various kernel structures. These labels can be filled with information that can be used by MAC modules to determine the outcome of a security check. Access Control List (ACL) functionality is also provided. In this article, Ill introduce TrustedBSD's ACL functionality and describes its MAC framework by detailing the use of three previously listed MAC modules. Ill also explain how systems administrators can design their own security policies for a given system.

Installation

The following article presumes that the reader has FreeBSD 5.0-CURRENT or DP2 installed. The TrustedBSD development branch and the FreeBSD 5.0-CURRENT branch are developed at different intervals; however, the functionality from TrustedBSD included in FreeBSD 5.0-CURRENT is adequate for the purposes of this article.

The following options should be added to the kernel and recompiled:

options UFS_ACL
options MAC
To enable ACLs, the following steps must be completed. If you are running a UFS1 filesystem, be sure support for extended attributes is enabled within your kernel using options UFS_EXTATTR and options UFS_EXTATTR_AUTOSTART. If you are running UFS1, you must also complete the following operations after these options have been compiled into the kernel:
mkdir -p /.attribute/system
cd /.attribute/system
extattrctl initattr -p / 388 posix1e.acl_access
extattrctl initattr -p / 388 posix1e.acl_default
If you are running either UFS1 or the UFS2 filesystem, you can enable ACLs after the capability has been compiled into the kernel by using the command tunefs -a enable /dev/ad6s1a (or your respective filesystem) or editing /etc/fstab to include acl within the options column on the desired filesystem. Access Control Lists ACLs allow the administrator to set fine-grained restrictions on files and directories. The manipulation of ACLs in FreeBSD is done by getfacl and setfacl. The setfacl utility sets ACL properties on a file and directory, while getfacl displays them. Using ACLs, an administrator can set permissions for a file that allow only user A and B read and execute access, but, allow group C read, write, and execute access. Usage of setfacl is quite simple. To set access controls on a file using setfacl, execute the following command:
setfacl -m u::r file
This allows the owner of file to read file. You can execute similar commands such as:
setfacl -m u:evms:rwx,g:politburo:rw file2
The above allows the user "evms" read, write, and execute access to file2, while it allows the group politburo' only read and write access to file2. The following command duplicates the ACL entries contained in file1 onto file2:
setfacl -M file1 file2
If you want to delete an entry from the ACL table associated with a specific file, the following command will remove politburo's read and write access to file2:
setfacl -x g:politburo:rw file2
The following removes all ACL entries from file2:
setfacl -bn file2
The command getfacl file2 displays the ACL entries for file2. MAC modules The TrustedBSD system comes with various MAC modules that implement security policies. Two of these modules are Biba and the File System Firewall Policy. I will briefly describe the features of Biba, but will give a more detailed explanation of the File System Fire Wall Policy because it has a powerful mechanism for providing fine-grained access controls to users, groups, and various domains. Biba The Biba Data Integrity Policy can be loaded by issuing the command kldload mac_biba, or placing mac_biba_load="YES" in your loader.conf. The mac_biba policy module protects the integrity of system objects by assigning each system object a specified label, containing a grade. These grades are placed in a hierarchy. The grades are usually in a range between 0 to 65535. There are also special labels that exist — biba/low, biba/equal, biba/high. The biba/high label is assigned to system objects that could potentially affect the security of the entire system. The Biba module protects the integrity of the system by forcing all operations to conform to the following security policy:
  1. A subject and an object with the same integrity level may both read and write to the object.
  2. A subject with a higher integrity level than an object may write to the object, but not read from it.
  3. A subject at a lower integrity level than an object read from the object, but not write to it.
  4. If the labels cannot be compared, all access is restricted.
For more information, view the man page mac_biba(4) and associated documentation. File System Firewall Policy The File System Firewall Policy can be loaded by issuing the command kldload mac_bsdextended or placing mac_bsdextend_load="YES" in loader.conf. This module implements a security policy and a few system calls that are utilized by the userland program ugidfw. The ugidfw utility provides an ipfw-like interface to manage accesses to the file system objects by UID and GID, supported by the mac_bsdextended mac policy." The general syntax for using ugidfw is:
ugidfw set rulenum subject [not] [uid uid] [gid gid] object [not]
            [uid uid] [gid gid] mode arswxn
There are modes that are not typical to Unix systems — n', a', and s'. These modes allow the subject to execute no operations on the object, to gain access to administrative operations regarding the object, and to access its file attributes, respectively. The following example allows the user with the uid of ten' to access all objects whose owner has the uid of twenty':
ugidfw set 1 subject uid 10 object uid 20 rw
The following commands list and remove rules:
ugidfw list
ugidfw remove rulenum
The interface is rather intuitive, but consult the manpage if you want to learn more about ugidfw. Designing MAC Security Modules None of these security policies can adequately meet the specific needs of your system. You may need to customize your system by writing your own MAC security policy, either to replace or augment a previous security policy. The following section will briefly cover how the MAC framework operates, and then describe how to write a module that implements a security policy. Knowledge of the C programming language is necessary to understand the examples in this section. The MAC framework consists of entry points positioned throughout the kernel. These entry points are basically function pointers that execute the user-defined security policy directly from the loaded module. The kernel function cr_cansee() (within sys/kern/kern_prot.c) takes two arguments that are both pointers to ucred structures. The cr_cansee() function determines whether the credentials of process u1 are enough to see the subject specified by u2. Within this function is a peculiar line:
int
cr_cansee(struct ucred *u1, struct ucred *u2)
{
	...
#ifdef MAC
        if ((error = mac_check_cred_visible(u1, u2)))
                return (error);
#endif
	...
}
The function mac_check_cred_visible(u1, u2) calls the function defined within a loaded MAC security policy, previously constructed by the administrator. Now it is evident how the MAC framework can extend the security policy within the kernel. Entry points are placed at various points throughout the kernel code to allow for extensibility. TrustedBSD also introduces the concept of labels'. Every kernel object on the system contains a struct label', which can be filled with any information. There are separate entry points for user-defined code that manipulates labels. A label has the following format:
struct label {
              int     l_flags;
	              union {
		        void    *l_ptr;
	                long     l_long;
	     }       l_perpolicy[MAC_MAX_POLICIES];
      };
For example, within the kernel there is a function vfs_nmount(), which attempts to mount a filesystem. This function contains the MAC entry point mac_init_mount(). When vfs_nmount() executes mac_init_mount() (kern/kern_mac.c), it executes the user-defined code within the module, but also executes a second entry point called init_mount_fs_label'. This entry point calls upon the user-defined function, which initializes the label on the mount structure. Labels are not required to build your own MAC module, but they are useful. A developer can design an access control policy that only uses mandatory access controls and not labels, as will shown in the following example. Building your own MAC Module When constructing a security policy for your system through a MAC module, it's probably easier to start by looking at the source code for the module mac_none (in /usr/src/sys/security/mac_none/mac_none.c). The mac_none module is a bare-bones module that defines all entry points. All the administrator is required to do is rename the module, fill in code where appropriate, and recompile. The example below is trivial; it will increment a static counter whenever a process returns from kernel space to user space.
#include <sys/types.h>
#include <sys/param.h>
#include <sys/acl.h>
#include <sys/conf.h>
#include <sys/extattr.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/pipe.h>
#include <sys/sysctl.h>

#include <vm/vm.h>

#include <sys/mac_policy.h>

int example_counter;

static void
mac_counter_init(struct mac_policy_conf *conf)
{
	example_counter=0;
	return;
}

static void
mac_counter_userret(struct thread *td)
{
	++example_counter;
	return;
}

static int
mac_counter_syscall(struct thread *td, int call, void *arg)
{
	printf("Counter: %d\n", example_counter);
}

static struct mac_policy_ops mac_counter_ops =
{
	.mpo_init = mac_counter_init,
	.mpo_thread_userret = mac_counter_userret,
	.mpo_syscall = mac_counter_syscall,
	
};

MAC_POLICY_SET(&mac_counter_ops, mac_counter, "SysAdmin MAC example",
		MPC_LOADTIME_FLAG_UNLOADOK, NULL);
When the MAC kernel module is loaded, mac_counter_init() is executed first. This function sets the counter to zero. When a process returns from kernel space to user space, mac_counter_userret() increments the counter by one. When a user executes the system call mac_counter_syscall(), it prints the value of the counter. The mac_policy_ops structure is registered with the kernel. Whenever a kernel function reaches an entry point, it finds this structure and executes the appropriate user-defined function. MAC_POLICY_SET() macro are the last instructions that actually load the module. The Makefile for both examples is the following:
KMOD= mac 
SRCS= vnode_if.h \ example1.c OR example2.c
 .include <bsd.kmod.mk> 
This second module will:
  • Initialize a label on a user credential' to NORMAL,
  • Prevent any user with group id five' from connecting to any local sockets,
  • Prevent any users from executing files that are owned by them,
  • And prevent users with the WARNING label from executing any files (effectively preventing users from executing programs they download):
(example1.c)
#include <sys/types.h>
#include <sys/param.h>
#include <sys/acl.h>
#include <sys/conf.h>
#include <sys/extattr.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/pipe.h>
#include <sys/sysctl.h>
#include <sys/imgact.h>
#include <fs/devfs/devfs.h>

#include <net/bpfdesc.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_var.h>

#include <netinet/in.h>
#include <netinet/ip_var.h>

#include <vm/vm.h>

#include <sys/mac_policy.h>

#define	 WARNING 2
#define	 NORMAL  1

static void
mac_example_init(struct mac_policy_conf *conf)
{
	printf("Example MAC module loaded.\n");
}

/* Initailizes the label on the ucred structure */
static void
mac_example_cred_init_label(struct label *label)
{
	label->l_flags = NORMAL;
}

/* Will prevent user with GID 5 of accessing any local sockets.
   This function is called when a socket is being
   connected to. */
static int
mac_example_check_socket_connect(struct ucred *cred, struct socket *socket,
    struct label *socketlabel, struct sockaddr *sockaddr)
{
	if (cred->cr_rgid == (gid_t)5) {
	   if ( sockaddr->sa_family == AF_LOCAL ) {
	      cred->cr_label.l_flags = WARNING;
	      return EPERM;
	   }
	}
	return (0);
}

/* Will prevent user from executing any file owned by them 
   This function is called when a file is being executed. */
static int
mac_example_check_vnode_exec(struct ucred *cred, struct vnode *vp,
    struct label *label, struct image_params *imgp,
    struct label *execlabel)
{
	if (cred->cr_label.l_flags == WARNING) 
		return EPERM;
	if (cred->cr_uid == 0)
	   return (0);
	if (imgp->attr->va_uid == cred->cr_uid)
	   return EPERM;
	return (0);
}

static struct mac_policy_ops mac_example_ops =
{
	.mpo_init = mac_example_init,
	.mpo_check_vnode_exec = mac_example_check_vnode_exec,
	.mpo_check_socket_connect = mac_example_check_socket_connect,
	.mpo_init_cred_label = mac_example_cred_init_label,
};

MAC_POLICY_SET(&mac_example_ops, mac_example, "SysAdmin MAC/Example",
    MPC_LOADTIME_FLAG_UNLOADOK, NULL);
When a new ucred structure is created, mac_example_cred_init_label() is executed and the l_flags element on the label structure is filled with the value of "NORMAL", which is 0. Whenever a user attempts to connect to a socket, the function mac_example_check_socket_connect() is executed. If a user attempts to connect to a socket that is local to the system, the label on his ucred object is changed to "WARNING" and a permissions error (EPERM) is returned. Otherwise, the function returns 0. Whenever a program is executed, mac_example_check_vnode() is executed, which first checks to see whether the current user has a WARNING label. If the user has a WARNING label, he is unable to execute anything. Otherwise, the function checks whether the program about to be executed (the program binary is abstracted with the image_params structure) is owned by the user. If the program about to be executed is owned by the user, then the user is not allowed to execute the program and an EPERM error is returned. There are thousands of places to extract information about a current user from the kernel. When designing a security policy, be sure to look at the definitions for the ucred structure. Most likely, one or more of your functions will deal with examining an element from the ucred structure and determining the outcome of a security check from the value of that element. In the FreeBSD Developer's Handbook there is a list of all the MAC entry points within the kernel: http://www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/mac-entry-point-reference.html This simple module below creates a security policy that does not allow a user to change his directory or open files that are not owned either by the user or group:
(example2.c)
#include <sys/types.h>
#include <sys/param.h>
#include <sys/acl.h>
#include <sys/conf.h>
#include <sys/extattr.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/pipe.h>
#include <sys/sysctl.h>

#include <vm/vm.h>

#include <sys/mac_policy.h>


static void
mac_tres_init(struct mac_policy_conf *conf)
{
	return;
}

static int
mac_tres_check_vnode_chdir(struct ucred *cred, struct vnode *dvp, \
   struct label *dlabel)
{
	struct vattr vap;

        if (cred->cr_uid == 0)
	   return (0); /* Allow root to change directories */
	
        VOP_GETATTR(dvp, &vap, cred, curthread);

	if ((vap.va_uid != cred->cr_uid) && (vap.va_gid != cred->cr_gid))
	   return EPERM;

        return 0;
}

static int 
mac_tres_check_vnode_open(struct ucred *cred, struct vnode *vp, struct \
   label *label, int acc_mode)
{
	struct vattr vap;
	
	if (cred->cr_uid == 0)
	   return (0);

	VOP_GETATTR(vp, &vap, cred, curthread);

	if ((vap.va_uid != cred->cr_uid) && (vap.va_gid != cred->cr_gid))
	   return EPERM;

	return 0;
}

static struct mac_policy_ops mac_tres_ops =
{
	.mpo_init = mac_tres_init,
	.mpo_check_vnode_chdir = mac_tres_check_vnode_chdir,
	.mpo_check_vnode_open = mac_tres_check_vnode_open,
};

MAC_POLICY_SET(&mac_tres_ops, mac_tres, "SysAdmin MAC/Example 3",
    MPC_LOADTIME_FLAG_UNLOADOK, NULL);

Stacking

The most powerful feature of the MAC framework is stacking. Security modules can be stacked to enhance the flexibility of previously loaded modules. For example, suppose that I like the functionality of the LoMAC module, but I want to add extra checks to cr_ucansee() and to init_mount(). I would write a new MAC module that adds these new checks and load the module. When the kernel executes either of these functions, it executes the check from LoMAC and subsequently the check from my module. The return value of the function is the lowest value produced from both checks.

Conclusion

The MAC Framework makes FreeBSD highly extensible. It's very easy (with a simple knowledge of C) to curtail the access of your users and secure your system in a way not possible before. Labels, in particular, are useful to protecting the integrity of objects. An administrator can use labels to prevent backdoors. The administrator can create a database, containing the MD5 or SHA-1 hash of every vital executable of the system, which is loaded into the kernel upon bootup.

Before a file is executed, code can be placed within the check_vnode_exec entry point, which takes the hash on the current vnode. If the hash of the current vnode does not equal the hash within the created database, the administrator can take a variety of options: prevent the user from executing the file; place a label on the user, which severely curtails his operations, etc. In conclusion, the MAC framework allows any administrator to tailor a security policy to meet the needs of his users and provide system integrity.

Evan Sarmiento is a senior at Boston University Academy. He enjoys FreeBSD kernel hacking and network administration. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.