freebsd-dev/sys/security/mac/mac_internal.h
Robert Watson 800c940832 Add a new priv(9) kernel interface for checking the availability of
privilege for threads and credentials.  Unlike the existing suser(9)
interface, priv(9) exposes a named privilege identifier to the privilege
checking code, allowing more complex policies regarding the granting of
privilege to be expressed.  Two interfaces are provided, replacing the
existing suser(9) interface:

suser(td)                 ->   priv_check(td, priv)
suser_cred(cred, flags)   ->   priv_check_cred(cred, priv, flags)

A comprehensive list of currently available kernel privileges may be
found in priv.h.  New privileges are easily added as required, but the
comments on adding privileges found in priv.h and priv(9) should be read
before doing so.

The new privilege interface exposed sufficient information to the
privilege checking routine that it will now be possible for jail to
determine whether a particular privilege is granted in the check routine,
rather than relying on hints from the calling context via the
SUSER_ALLOWJAIL flag.  For now, the flag is maintained, but a new jail
check function, prison_priv_check(), is exposed from kern_jail.c and used
by the privilege check routine to determine if the privilege is permitted
in jail.  As a result, a centralized list of privileges permitted in jail
is now present in kern_jail.c.

The MAC Framework is now also able to instrument privilege checks, both
to deny privileges otherwise granted (mac_priv_check()), and to grant
privileges otherwise denied (mac_priv_grant()), permitting MAC Policy
modules to implement privilege models, as well as control a much broader
range of system behavior in order to constrain processes running with
root privilege.

The suser() and suser_cred() functions remain implemented, now in terms
of priv_check() and the PRIV_ROOT privilege, for use during the transition
and possibly continuing use by third party kernel modules that have not
been updated.  The PRIV_DRIVER privilege exists to allow device drivers to
check privilege without adopting a more specific privilege identifier.

This change does not modify the actual security policy, rather, it
modifies the interface for privilege checks so changes to the security
policy become more feasible.

Sponsored by:		nCircle Network Security, Inc.
Obtained from:		TrustedBSD Project
Discussed on:		arch@
Reviewed (at least in part) by:	mlaier, jmg, pjd, bde, ceri,
			Alex Lyashkov <umka at sevcity dot net>,
			Skip Ford <skip dot ford at verizon dot net>,
			Antoine Brodin <antoine dot brodin at laposte dot net>
2006-11-06 13:37:19 +00:00

305 lines
10 KiB
C

/*-
* Copyright (c) 1999-2002 Robert N. M. Watson
* Copyright (c) 2001 Ilmar S. Habibulin
* Copyright (c) 2001-2004 Networks Associates Technology, Inc.
* Copyright (c) 2006 nCircle Network Security, Inc.
* All rights reserved.
*
* This software was developed by Robert Watson and Ilmar Habibulin for the
* TrustedBSD Project.
*
* This software was developed for the FreeBSD Project in part by Network
* Associates Laboratories, the Security Research Division of Network
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
* as part of the DARPA CHATS research program.
*
* This software was developed by Robert N. M. Watson for the TrustedBSD
* Project under contract to nCircle Network Security, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* MAC Framework sysctl namespace.
*/
#ifdef SYSCTL_DECL
SYSCTL_DECL(_security_mac);
#endif /* SYSCTL_DECL */
/*
* MAC Framework global types and typedefs.
*/
LIST_HEAD(mac_policy_list_head, mac_policy_conf);
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_MACTEMP);
#endif
/*
* MAC Framework global variables.
*/
extern struct mac_policy_list_head mac_policy_list;
extern struct mac_policy_list_head mac_static_policy_list;
extern int mac_late;
extern int mac_enforce_network;
extern int mac_enforce_process;
extern int mac_enforce_socket;
extern int mac_enforce_vm;
#ifndef MAC_ALWAYS_LABEL_MBUF
extern int mac_labelmbufs;
#endif
/*
* MAC Framework infrastructure functions.
*/
int mac_error_select(int error1, int error2);
void mac_policy_grab_exclusive(void);
void mac_policy_assert_exclusive(void);
void mac_policy_release_exclusive(void);
void mac_policy_list_busy(void);
int mac_policy_list_conditional_busy(void);
void mac_policy_list_unbusy(void);
struct label *mac_labelzone_alloc(int flags);
void mac_labelzone_free(struct label *label);
void mac_labelzone_init(void);
void mac_init_label(struct label *label);
void mac_destroy_label(struct label *label);
int mac_check_structmac_consistent(struct mac *mac);
int mac_allocate_slot(void);
/*
* MAC Framework per-object type functions. It's not yet clear how
* the namespaces, etc, should work for these, so for now, sort by
* object type.
*/
struct label *mac_pipe_label_alloc(void);
void mac_pipe_label_free(struct label *label);
struct label *mac_socket_label_alloc(int flag);
void mac_socket_label_free(struct label *label);
int mac_check_cred_relabel(struct ucred *cred, struct label *newlabel);
int mac_externalize_cred_label(struct label *label, char *elements,
char *outbuf, size_t outbuflen);
int mac_internalize_cred_label(struct label *label, char *string);
void mac_relabel_cred(struct ucred *cred, struct label *newlabel);
struct label *mac_mbuf_to_label(struct mbuf *m);
void mac_copy_pipe_label(struct label *src, struct label *dest);
int mac_externalize_pipe_label(struct label *label, char *elements,
char *outbuf, size_t outbuflen);
int mac_internalize_pipe_label(struct label *label, char *string);
int mac_socket_label_set(struct ucred *cred, struct socket *so,
struct label *label);
void mac_copy_socket_label(struct label *src, struct label *dest);
int mac_externalize_socket_label(struct label *label, char *elements,
char *outbuf, size_t outbuflen);
int mac_internalize_socket_label(struct label *label, char *string);
int mac_externalize_vnode_label(struct label *label, char *elements,
char *outbuf, size_t outbuflen);
int mac_internalize_vnode_label(struct label *label, char *string);
void mac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp,
int *prot);
int vn_setlabel(struct vnode *vp, struct label *intlabel,
struct ucred *cred);
/*
* MAC_CHECK performs the designated check by walking the policy module
* list and checking with each as to how it feels about the request.
* Note that it returns its value via 'error' in the scope of the caller.
*/
#define MAC_CHECK(check, args...) do { \
struct mac_policy_conf *mpc; \
int entrycount; \
\
error = 0; \
LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## check != NULL) \
error = mac_error_select( \
mpc->mpc_ops->mpo_ ## check (args), \
error); \
} \
if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \
LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## check != NULL) \
error = mac_error_select( \
mpc->mpc_ops->mpo_ ## check (args), \
error); \
} \
mac_policy_list_unbusy(); \
} \
} while (0)
/*
* MAC_GRANT performs the designated check by walking the policy module
* list and checking with each as to how it feels about the request. Unlike
* MAC_CHECK, it grants if any policies return '0', and otherwise returns
* EPERM. Note that it returns its value via 'error' in the scope of the
* caller.
*/
#define MAC_GRANT(check, args...) do { \
struct mac_policy_conf *mpc; \
int entrycount; \
\
error = EPERM; \
LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## check != NULL) { \
if (mpc->mpc_ops->mpo_ ## check(args) == 0) \
error = 0; \
} \
} \
if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \
LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## check != NULL) { \
if (mpc->mpc_ops->mpo_ ## check (args) \
== 0) \
error = 0; \
} \
} \
mac_policy_list_unbusy(); \
} \
} while (0)
/*
* MAC_BOOLEAN performs the designated boolean composition by walking
* the module list, invoking each instance of the operation, and
* combining the results using the passed C operator. Note that it
* returns its value via 'result' in the scope of the caller, which
* should be initialized by the caller in a meaningful way to get
* a meaningful result.
*/
#define MAC_BOOLEAN(operation, composition, args...) do { \
struct mac_policy_conf *mpc; \
int entrycount; \
\
LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## operation != NULL) \
result = result composition \
mpc->mpc_ops->mpo_ ## operation (args); \
} \
if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \
LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## operation != NULL) \
result = result composition \
mpc->mpc_ops->mpo_ ## operation \
(args); \
} \
mac_policy_list_unbusy(); \
} \
} while (0)
#define MAC_EXTERNALIZE(type, label, elementlist, outbuf, \
outbuflen) do { \
int claimed, first, ignorenotfound, savedlen; \
char *element_name, *element_temp; \
struct sbuf sb; \
\
error = 0; \
first = 1; \
sbuf_new(&sb, outbuf, outbuflen, SBUF_FIXEDLEN); \
element_temp = elementlist; \
while ((element_name = strsep(&element_temp, ",")) != NULL) { \
if (element_name[0] == '?') { \
element_name++; \
ignorenotfound = 1; \
} else \
ignorenotfound = 0; \
savedlen = sbuf_len(&sb); \
if (first) \
error = sbuf_printf(&sb, "%s/", element_name); \
else \
error = sbuf_printf(&sb, ",%s/", element_name); \
if (error == -1) { \
error = EINVAL; /* XXX: E2BIG? */ \
break; \
} \
claimed = 0; \
MAC_CHECK(externalize_ ## type ## _label, label, \
element_name, &sb, &claimed); \
if (error) \
break; \
if (claimed == 0 && ignorenotfound) { \
/* Revert last label name. */ \
sbuf_setpos(&sb, savedlen); \
} else if (claimed != 1) { \
error = EINVAL; /* XXX: ENOLABEL? */ \
break; \
} else { \
first = 0; \
} \
} \
sbuf_finish(&sb); \
} while (0)
#define MAC_INTERNALIZE(type, label, instring) do { \
char *element, *element_name, *element_data; \
int claimed; \
\
error = 0; \
element = instring; \
while ((element_name = strsep(&element, ",")) != NULL) { \
element_data = element_name; \
element_name = strsep(&element_data, "/"); \
if (element_data == NULL) { \
error = EINVAL; \
break; \
} \
claimed = 0; \
MAC_CHECK(internalize_ ## type ## _label, label, \
element_name, element_data, &claimed); \
if (error) \
break; \
if (claimed != 1) { \
/* XXXMAC: Another error here? */ \
error = EINVAL; \
break; \
} \
} \
} while (0)
/*
* MAC_PERFORM performs the designated operation by walking the policy
* module list and invoking that operation for each policy.
*/
#define MAC_PERFORM(operation, args...) do { \
struct mac_policy_conf *mpc; \
int entrycount; \
\
LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## operation != NULL) \
mpc->mpc_ops->mpo_ ## operation (args); \
} \
if ((entrycount = mac_policy_list_conditional_busy()) != 0) { \
LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \
if (mpc->mpc_ops->mpo_ ## operation != NULL) \
mpc->mpc_ops->mpo_ ## operation (args); \
} \
mac_policy_list_unbusy(); \
} \
} while (0)