diff --git a/sys/security/audit/audit_bsm.c b/sys/security/audit/audit_bsm.c index 3094509dbfdd..6742470c9578 100644 --- a/sys/security/audit/audit_bsm.c +++ b/sys/security/audit/audit_bsm.c @@ -1,1834 +1,1833 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1999-2009 Apple Inc. * Copyright (c) 2016-2017 Robert N. M. Watson * All rights reserved. * * Portions of this software were developed by BAE Systems, the University of * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent * Computing (TC) research program. * * 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_AUDITBSM, "audit_bsm", "Audit BSM data"); static void audit_sys_auditon(struct audit_record *ar, struct au_record *rec); /* * Initialize the BSM auditing subsystem. */ void kau_init(void) { au_evclassmap_init(); au_evnamemap_init(); } /* * This call reserves memory for the audit record. Memory must be guaranteed * before any auditable event can be generated. The au_record structure * maintains a reference to the memory allocated above and also the list of * tokens associated with this record. */ static struct au_record * kau_open(void) { struct au_record *rec; rec = malloc(sizeof(*rec), M_AUDITBSM, M_WAITOK); rec->data = NULL; TAILQ_INIT(&rec->token_q); rec->len = 0; rec->used = 1; return (rec); } /* * Store the token with the record descriptor. */ static void kau_write(struct au_record *rec, struct au_token *tok) { KASSERT(tok != NULL, ("kau_write: tok == NULL")); TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens); rec->len += tok->len; } /* * Close out the audit record by adding the header token, identifying any * missing tokens. Write out the tokens to the record memory. */ static void kau_close(struct au_record *rec, struct timespec *ctime, short event) { u_char *dptr; size_t tot_rec_size; token_t *cur, *hdr, *trail; struct timeval tm; size_t hdrsize; struct auditinfo_addr ak; struct in6_addr *ap; audit_get_kinfo(&ak); hdrsize = 0; switch (ak.ai_termid.at_type) { case AU_IPv4: hdrsize = (ak.ai_termid.at_addr[0] == INADDR_ANY) ? AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&ak); break; case AU_IPv6: ap = (struct in6_addr *)&ak.ai_termid.at_addr[0]; hdrsize = (IN6_IS_ADDR_UNSPECIFIED(ap)) ? AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&ak); break; default: panic("kau_close: invalid address family"); } tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE; rec->data = malloc(tot_rec_size, M_AUDITBSM, M_WAITOK | M_ZERO); tm.tv_usec = ctime->tv_nsec / 1000; tm.tv_sec = ctime->tv_sec; if (hdrsize != AUDIT_HEADER_SIZE) hdr = au_to_header32_ex_tm(tot_rec_size, event, 0, tm, &ak); else hdr = au_to_header32_tm(tot_rec_size, event, 0, tm); TAILQ_INSERT_HEAD(&rec->token_q, hdr, tokens); trail = au_to_trailer(tot_rec_size); TAILQ_INSERT_TAIL(&rec->token_q, trail, tokens); rec->len = tot_rec_size; dptr = rec->data; TAILQ_FOREACH(cur, &rec->token_q, tokens) { memcpy(dptr, cur->t_data, cur->len); dptr += cur->len; } } /* * Free a BSM audit record by releasing all the tokens and clearing the audit * record information. */ void kau_free(struct au_record *rec) { struct au_token *tok; /* Free the token list. */ while ((tok = TAILQ_FIRST(&rec->token_q))) { TAILQ_REMOVE(&rec->token_q, tok, tokens); free(tok->t_data, M_AUDITBSM); free(tok, M_AUDITBSM); } rec->used = 0; rec->len = 0; free(rec->data, M_AUDITBSM); free(rec, M_AUDITBSM); } /* * XXX: May want turn some (or all) of these macros into functions in order * to reduce the generated code size. * * XXXAUDIT: These macros assume that 'kar', 'ar', 'rec', and 'tok' in the * caller are OK with this. */ #define ATFD1_TOKENS(argnum) do { \ if (ARG_IS_VALID(kar, ARG_ATFD1)) { \ tok = au_to_arg32(argnum, "at fd 1", ar->ar_arg_atfd1); \ kau_write(rec, tok); \ } \ } while (0) #define ATFD2_TOKENS(argnum) do { \ if (ARG_IS_VALID(kar, ARG_ATFD2)) { \ tok = au_to_arg32(argnum, "at fd 2", ar->ar_arg_atfd2); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_UPATH1)) { \ tok = au_to_path(ar->ar_arg_upath1); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH2_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_UPATH2)) { \ tok = au_to_path(ar->ar_arg_upath2); \ kau_write(rec, tok); \ } \ } while (0) #define VNODE1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_ATFD)) { \ tok = au_to_arg32(1, "at fd", ar->ar_arg_atfd); \ kau_write(rec, tok); \ } \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } \ } while (0) #define UPATH1_VNODE1_TOKENS do { \ UPATH1_TOKENS; \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } \ } while (0) #define VNODE2_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_VNODE2)) { \ tok = au_to_attr32(&ar->ar_arg_vnode2); \ kau_write(rec, tok); \ } \ } while (0) #define FD_VNODE1_TOKENS do { \ if (ARG_IS_VALID(kar, ARG_VNODE1)) { \ if (ARG_IS_VALID(kar, ARG_FD)) { \ tok = au_to_arg32(1, "fd", ar->ar_arg_fd); \ kau_write(rec, tok); \ } \ tok = au_to_attr32(&ar->ar_arg_vnode1); \ kau_write(rec, tok); \ } else { \ if (ARG_IS_VALID(kar, ARG_FD)) { \ tok = au_to_arg32(1, "non-file: fd", \ ar->ar_arg_fd); \ kau_write(rec, tok); \ } \ } \ } while (0) #define PROCESS_PID_TOKENS(argn) do { \ if ((ar->ar_arg_pid > 0) /* Reference a single process */ \ && (ARG_IS_VALID(kar, ARG_PROCESS))) { \ tok = au_to_process32_ex(ar->ar_arg_auid, \ ar->ar_arg_euid, ar->ar_arg_egid, \ ar->ar_arg_ruid, ar->ar_arg_rgid, \ ar->ar_arg_pid, ar->ar_arg_asid, \ &ar->ar_arg_termid_addr); \ kau_write(rec, tok); \ } else if (ARG_IS_VALID(kar, ARG_PID)) { \ tok = au_to_arg32(argn, "process", ar->ar_arg_pid); \ kau_write(rec, tok); \ } \ } while (0) #define EXTATTR_TOKENS(namespace_argnum) do { \ if (ARG_IS_VALID(kar, ARG_VALUE)) { \ switch (ar->ar_arg_value) { \ case EXTATTR_NAMESPACE_USER: \ tok = au_to_text(EXTATTR_NAMESPACE_USER_STRING);\ break; \ case EXTATTR_NAMESPACE_SYSTEM: \ tok = au_to_text(EXTATTR_NAMESPACE_SYSTEM_STRING);\ break; \ default: \ tok = au_to_arg32((namespace_argnum), \ "attrnamespace", ar->ar_arg_value); \ break; \ } \ kau_write(rec, tok); \ } \ /* attrname is in the text field */ \ if (ARG_IS_VALID(kar, ARG_TEXT)) { \ tok = au_to_text(ar->ar_arg_text); \ kau_write(rec, tok); \ } \ } while (0) /* * Not all pointer arguments to system calls are of interest, but in some * cases they reflect delegation of rights, such as mmap(2) followed by * minherit(2) before execve(2), so do the best we can. */ #define ADDR_TOKEN(argnum, argname) do { \ if (ARG_IS_VALID(kar, ARG_ADDR)) { \ if (sizeof(void *) == sizeof(uint32_t)) \ tok = au_to_arg32((argnum), (argname), \ (uint32_t)(uintptr_t)ar->ar_arg_addr); \ else \ tok = au_to_arg64((argnum), (argname), \ (uint64_t)(uintptr_t)ar->ar_arg_addr); \ kau_write(rec, tok); \ } \ } while (0) - /* * Implement auditing for the auditon() system call. The audit tokens that * are generated depend on the command that was sent into the auditon() * system call. */ static void audit_sys_auditon(struct audit_record *ar, struct au_record *rec) { struct au_token *tok; tok = au_to_arg32(3, "length", ar->ar_arg_len); kau_write(rec, tok); switch (ar->ar_arg_cmd) { case A_OLDSETPOLICY: if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { tok = au_to_arg64(2, "policy", ar->ar_arg_auditon.au_policy64); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETPOLICY: tok = au_to_arg32(2, "policy", ar->ar_arg_auditon.au_policy); kau_write(rec, tok); break; case A_SETKMASK: tok = au_to_arg32(2, "setkmask:as_success", ar->ar_arg_auditon.au_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setkmask:as_failure", ar->ar_arg_auditon.au_mask.am_failure); kau_write(rec, tok); break; case A_OLDSETQCTRL: if ((size_t)ar->ar_arg_len == sizeof(au_qctrl64_t)) { tok = au_to_arg64(2, "setqctrl:aq_hiwater", ar->ar_arg_auditon.au_qctrl64.aq64_hiwater); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_lowater", ar->ar_arg_auditon.au_qctrl64.aq64_lowater); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_bufsz", ar->ar_arg_auditon.au_qctrl64.aq64_bufsz); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_delay", ar->ar_arg_auditon.au_qctrl64.aq64_delay); kau_write(rec, tok); tok = au_to_arg64(2, "setqctrl:aq_minfree", ar->ar_arg_auditon.au_qctrl64.aq64_minfree); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETQCTRL: tok = au_to_arg32(2, "setqctrl:aq_hiwater", ar->ar_arg_auditon.au_qctrl.aq_hiwater); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_lowater", ar->ar_arg_auditon.au_qctrl.aq_lowater); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_bufsz", ar->ar_arg_auditon.au_qctrl.aq_bufsz); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_delay", ar->ar_arg_auditon.au_qctrl.aq_delay); kau_write(rec, tok); tok = au_to_arg32(2, "setqctrl:aq_minfree", ar->ar_arg_auditon.au_qctrl.aq_minfree); kau_write(rec, tok); break; case A_SETUMASK: tok = au_to_arg32(2, "setumask:as_success", ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setumask:as_failure", ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); kau_write(rec, tok); break; case A_SETSMASK: tok = au_to_arg32(2, "setsmask:as_success", ar->ar_arg_auditon.au_auinfo.ai_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setsmask:as_failure", ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure); kau_write(rec, tok); break; case A_OLDSETCOND: if ((size_t)ar->ar_arg_len == sizeof(int64_t)) { tok = au_to_arg64(2, "setcond", ar->ar_arg_auditon.au_cond64); kau_write(rec, tok); break; } /* FALLTHROUGH */ case A_SETCOND: tok = au_to_arg32(2, "setcond", ar->ar_arg_auditon.au_cond); kau_write(rec, tok); break; case A_SETCLASS: tok = au_to_arg32(2, "setclass:ec_event", ar->ar_arg_auditon.au_evclass.ec_number); kau_write(rec, tok); tok = au_to_arg32(2, "setclass:ec_class", ar->ar_arg_auditon.au_evclass.ec_class); kau_write(rec, tok); break; case A_SETPMASK: tok = au_to_arg32(2, "setpmask:as_success", ar->ar_arg_auditon.au_aupinfo.ap_mask.am_success); kau_write(rec, tok); tok = au_to_arg32(2, "setpmask:as_failure", ar->ar_arg_auditon.au_aupinfo.ap_mask.am_failure); kau_write(rec, tok); break; case A_SETFSIZE: tok = au_to_arg32(2, "setfsize:filesize", ar->ar_arg_auditon.au_fstat.af_filesz); kau_write(rec, tok); break; default: break; } } /* * Convert an internal kernel audit record to a BSM record and return a * success/failure indicator. The BSM record is passed as an out parameter to * this function. * * Return conditions: * BSM_SUCCESS: The BSM record is valid * BSM_FAILURE: Failure; the BSM record is NULL. * BSM_NOAUDIT: The event is not auditable for BSM; the BSM record is NULL. */ int kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau) { struct au_token *tok, *subj_tok, *jail_tok; struct au_record *rec; au_tid_t tid; struct audit_record *ar; int ctr; KASSERT(kar != NULL, ("kaudit_to_bsm: kar == NULL")); *pau = NULL; ar = &kar->k_ar; rec = kau_open(); /* * Create the subject token. If this credential was jailed be sure to * generate a zonename token. */ if (ar->ar_jailname[0] != '\0') jail_tok = au_to_zonename(ar->ar_jailname); else jail_tok = NULL; switch (ar->ar_subj_term_addr.at_type) { case AU_IPv4: tid.port = ar->ar_subj_term_addr.at_port; tid.machine = ar->ar_subj_term_addr.at_addr[0]; subj_tok = au_to_subject32(ar->ar_subj_auid, /* audit ID */ ar->ar_subj_cred.cr_uid, /* eff uid */ ar->ar_subj_egid, /* eff group id */ ar->ar_subj_ruid, /* real uid */ ar->ar_subj_rgid, /* real group id */ ar->ar_subj_pid, /* process id */ ar->ar_subj_asid, /* session ID */ &tid); break; case AU_IPv6: subj_tok = au_to_subject32_ex(ar->ar_subj_auid, ar->ar_subj_cred.cr_uid, ar->ar_subj_egid, ar->ar_subj_ruid, ar->ar_subj_rgid, ar->ar_subj_pid, ar->ar_subj_asid, &ar->ar_subj_term_addr); break; default: bzero(&tid, sizeof(tid)); subj_tok = au_to_subject32(ar->ar_subj_auid, ar->ar_subj_cred.cr_uid, ar->ar_subj_egid, ar->ar_subj_ruid, ar->ar_subj_rgid, ar->ar_subj_pid, ar->ar_subj_asid, &tid); } /* * The logic inside each case fills in the tokens required for the * event, except for the header, trailer, and return tokens. The * header and trailer tokens are added by the kau_close() function. * The return token is added outside of the switch statement. */ switch(ar->ar_event) { case AUE_ACCEPT: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRINET)) { tok = au_to_sock_inet((struct sockaddr_in *) &ar->ar_arg_sockaddr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } break; case AUE_BIND: case AUE_LISTEN: case AUE_CONNECT: case AUE_RECV: case AUE_RECVFROM: case AUE_RECVMSG: case AUE_SEND: case AUE_SENDMSG: case AUE_SENDTO: /* * Socket-related events. */ if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRINET)) { tok = au_to_sock_inet((struct sockaddr_in *) &ar->ar_arg_sockaddr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } /* XXX Need to handle ARG_SADDRINET6 */ break; case AUE_BINDAT: case AUE_CONNECTAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(2, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } break; case AUE_SENDFILE: FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_SADDRINET)) { tok = au_to_sock_inet((struct sockaddr_in *) &ar->ar_arg_sockaddr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) { tok = au_to_sock_unix((struct sockaddr_un *) &ar->ar_arg_sockaddr); kau_write(rec, tok); UPATH1_TOKENS; } /* XXX Need to handle ARG_SADDRINET6 */ break; case AUE_SOCKET: case AUE_SOCKETPAIR: if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { tok = au_to_arg32(1, "domain", ar->ar_arg_sockinfo.so_domain); kau_write(rec, tok); tok = au_to_arg32(2, "type", ar->ar_arg_sockinfo.so_type); kau_write(rec, tok); tok = au_to_arg32(3, "protocol", ar->ar_arg_sockinfo.so_protocol); kau_write(rec, tok); } break; case AUE_SETSOCKOPT: case AUE_SHUTDOWN: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_ACCT: if (ARG_IS_VALID(kar, ARG_UPATH1)) { UPATH1_VNODE1_TOKENS; } else { tok = au_to_arg32(1, "accounting off", 0); kau_write(rec, tok); } break; case AUE_SETAUID: if (ARG_IS_VALID(kar, ARG_AUID)) { tok = au_to_arg32(2, "setauid", ar->ar_arg_auid); kau_write(rec, tok); } break; case AUE_SETAUDIT: if (ARG_IS_VALID(kar, ARG_AUID) && ARG_IS_VALID(kar, ARG_ASID) && ARG_IS_VALID(kar, ARG_AMASK) && ARG_IS_VALID(kar, ARG_TERMID)) { tok = au_to_arg32(1, "setaudit:auid", ar->ar_arg_auid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:port", ar->ar_arg_termid.port); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:machine", ar->ar_arg_termid.machine); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:as_success", ar->ar_arg_amask.am_success); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:as_failure", ar->ar_arg_amask.am_failure); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit:asid", ar->ar_arg_asid); kau_write(rec, tok); } break; case AUE_SETAUDIT_ADDR: if (ARG_IS_VALID(kar, ARG_AUID) && ARG_IS_VALID(kar, ARG_ASID) && ARG_IS_VALID(kar, ARG_AMASK) && ARG_IS_VALID(kar, ARG_TERMID_ADDR)) { tok = au_to_arg32(1, "setaudit_addr:auid", ar->ar_arg_auid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:as_success", ar->ar_arg_amask.am_success); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:as_failure", ar->ar_arg_amask.am_failure); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:asid", ar->ar_arg_asid); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:type", ar->ar_arg_termid_addr.at_type); kau_write(rec, tok); tok = au_to_arg32(1, "setaudit_addr:port", ar->ar_arg_termid_addr.at_port); kau_write(rec, tok); if (ar->ar_arg_termid_addr.at_type == AU_IPv6) tok = au_to_in_addr_ex((struct in6_addr *) &ar->ar_arg_termid_addr.at_addr[0]); if (ar->ar_arg_termid_addr.at_type == AU_IPv4) tok = au_to_in_addr((struct in_addr *) &ar->ar_arg_termid_addr.at_addr[0]); kau_write(rec, tok); } break; case AUE_AUDITON: /* * For AUDITON commands without own event, audit the cmd. */ if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_AUDITON_GETCAR: case AUE_AUDITON_GETCLASS: case AUE_AUDITON_GETCOND: case AUE_AUDITON_GETCWD: case AUE_AUDITON_GETKMASK: case AUE_AUDITON_GETSTAT: case AUE_AUDITON_GPOLICY: case AUE_AUDITON_GQCTRL: case AUE_AUDITON_SETCLASS: case AUE_AUDITON_SETCOND: case AUE_AUDITON_SETKMASK: case AUE_AUDITON_SETSMASK: case AUE_AUDITON_SETSTAT: case AUE_AUDITON_SETUMASK: case AUE_AUDITON_SPOLICY: case AUE_AUDITON_SQCTRL: if (ARG_IS_VALID(kar, ARG_AUDITON)) audit_sys_auditon(ar, rec); break; case AUE_AUDITCTL: UPATH1_VNODE1_TOKENS; break; case AUE_EXIT: if (ARG_IS_VALID(kar, ARG_EXIT)) { tok = au_to_exit(ar->ar_arg_exitretval, ar->ar_arg_exitstatus); kau_write(rec, tok); } break; case AUE_ADJTIME: case AUE_CLOCK_SETTIME: case AUE_AUDIT: case AUE_DUP2: case AUE_GETAUDIT: case AUE_GETAUDIT_ADDR: case AUE_GETAUID: case AUE_GETCWD: case AUE_GETFSSTAT: case AUE_GETRESUID: case AUE_GETRESGID: case AUE_KQUEUE: case AUE_MODLOAD: case AUE_MODUNLOAD: case AUE_MSGSYS: case AUE_NTP_ADJTIME: case AUE_PIPE: case AUE_POSIX_OPENPT: case AUE_PROFILE: case AUE_RTPRIO: case AUE_SEMSYS: case AUE_SETFIB: case AUE_SHMSYS: case AUE_SETPGRP: case AUE_SETRLIMIT: case AUE_SETSID: case AUE_SETTIMEOFDAY: case AUE_SYSARCH: /* * Header, subject, and return tokens added at end. */ break; case AUE_ACL_DELETE_FD: case AUE_ACL_DELETE_FILE: case AUE_ACL_CHECK_FD: case AUE_ACL_CHECK_FILE: case AUE_ACL_CHECK_LINK: case AUE_ACL_DELETE_LINK: case AUE_ACL_GET_FD: case AUE_ACL_GET_FILE: case AUE_ACL_GET_LINK: case AUE_ACL_SET_FD: case AUE_ACL_SET_FILE: case AUE_ACL_SET_LINK: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(1, "type", ar->ar_arg_value); kau_write(rec, tok); } ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; /* * NB: We may want to verify that the appropriate * audit args are being processed here, but I think * a bit analysis is required. * * Process AUE_JAIL_SET in the next block so we can pickup any path * related tokens that might exist. */ case AUE_JAIL_GET: case AUE_JAIL_ATTACH: case AUE_JAIL_REMOVE: break; case AUE_JAIL_SET: case AUE_CHDIR: case AUE_CHROOT: case AUE_FSTATAT: case AUE_FUTIMESAT: case AUE_GETATTRLIST: case AUE_JAIL: case AUE_LUTIMES: case AUE_NFS_GETFH: case AUE_LGETFH: case AUE_LSTAT: case AUE_LPATHCONF: case AUE_PATHCONF: case AUE_READLINK: case AUE_READLINKAT: case AUE_REVOKE: case AUE_RMDIR: case AUE_SEARCHFS: case AUE_SETATTRLIST: case AUE_STAT: case AUE_STATFS: case AUE_SWAPON: case AUE_SWAPOFF: case AUE_TRUNCATE: case AUE_UNDELETE: case AUE_UNLINK: case AUE_UNLINKAT: case AUE_UTIMES: case AUE_REALPATHAT: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_ACCESS: case AUE_EACCESS: case AUE_FACCESSAT: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_FHSTATFS: case AUE_FHOPEN: case AUE_FHSTAT: /* XXXRW: Need to audit vnode argument. */ break; case AUE_CHFLAGS: case AUE_LCHFLAGS: case AUE_CHFLAGSAT: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CHMOD: case AUE_LCHMOD: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHMODAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CHOWN: case AUE_LCHOWN: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHOWNAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(3, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(4, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_EXCHANGEDATA: UPATH1_VNODE1_TOKENS; UPATH2_TOKENS; break; case AUE_CLOSE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_CLOSEFROM: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_CORE: if (ARG_IS_VALID(kar, ARG_SIGNUM)) { tok = au_to_arg32(1, "signal", ar->ar_arg_signum); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_EXTATTRCTL: UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } /* extattrctl(2) filename parameter is in upath2/vnode2 */ UPATH2_TOKENS; VNODE2_TOKENS; EXTATTR_TOKENS(4); break; case AUE_EXTATTR_GET_FILE: case AUE_EXTATTR_SET_FILE: case AUE_EXTATTR_LIST_FILE: case AUE_EXTATTR_DELETE_FILE: case AUE_EXTATTR_GET_LINK: case AUE_EXTATTR_SET_LINK: case AUE_EXTATTR_LIST_LINK: case AUE_EXTATTR_DELETE_LINK: UPATH1_VNODE1_TOKENS; EXTATTR_TOKENS(2); break; case AUE_EXTATTR_GET_FD: case AUE_EXTATTR_SET_FD: case AUE_EXTATTR_LIST_FD: case AUE_EXTATTR_DELETE_FD: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(2, "fd", ar->ar_arg_fd); kau_write(rec, tok); } EXTATTR_TOKENS(2); break; case AUE_FEXECVE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_EXECVE: case AUE_MAC_EXECVE: if (ARG_IS_VALID(kar, ARG_ARGV)) { tok = au_to_exec_args(ar->ar_arg_argv, ar->ar_arg_argc); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_ENVV)) { tok = au_to_exec_env(ar->ar_arg_envv, ar->ar_arg_envc); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_FCHMOD: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "new file mode", ar->ar_arg_mode); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; /* * XXXRW: Some of these need to handle non-vnode cases as well. */ case AUE_FCHDIR: case AUE_FPATHCONF: case AUE_FSTAT: case AUE_FSTATFS: case AUE_FSYNC: case AUE_FTRUNCATE: case AUE_FUTIMES: case AUE_GETDIRENTRIES: case AUE_GETDIRENTRIESATTR: case AUE_LSEEK: case AUE_POLL: case AUE_POSIX_FALLOCATE: case AUE_PREAD: case AUE_PWRITE: case AUE_READ: case AUE_READV: case AUE_WRITE: case AUE_WRITEV: FD_VNODE1_TOKENS; break; case AUE_FCHOWN: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FCNTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", au_fcntl_cmd_to_bsm(ar->ar_arg_cmd)); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FCHFLAGS: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_FLOCK: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "operation", ar->ar_arg_cmd); kau_write(rec, tok); } FD_VNODE1_TOKENS; break; case AUE_RFORK: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(1, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_FORK: case AUE_VFORK: if (ARG_IS_VALID(kar, ARG_PID)) { tok = au_to_arg32(0, "child PID", ar->ar_arg_pid); kau_write(rec, tok); } break; case AUE_IOCTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VNODE1)) FD_VNODE1_TOKENS; else { if (ARG_IS_VALID(kar, ARG_SOCKINFO)) { tok = kau_to_socket(&ar->ar_arg_sockinfo); kau_write(rec, tok); } else { if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } } } break; case AUE_KILL: case AUE_KILLPG: if (ARG_IS_VALID(kar, ARG_SIGNUM)) { tok = au_to_arg32(2, "signal", ar->ar_arg_signum); kau_write(rec, tok); } PROCESS_PID_TOKENS(1); break; case AUE_KTRACE: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "ops", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "trpoints", ar->ar_arg_value); kau_write(rec, tok); } PROCESS_PID_TOKENS(4); UPATH1_VNODE1_TOKENS; break; case AUE_LINK: case AUE_LINKAT: case AUE_RENAME: case AUE_RENAMEAT: ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; ATFD2_TOKENS(3); UPATH2_TOKENS; break; case AUE_LOADSHFILE: ADDR_TOKEN(4, "base addr"); UPATH1_VNODE1_TOKENS; break; case AUE_MKDIR: case AUE_MKDIRAT: case AUE_MKFIFO: case AUE_MKFIFOAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_mode); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_MKNOD: case AUE_MKNODAT: ATFD1_TOKENS(1); if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(2, "mode", ar->ar_arg_mode); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_DEV)) { tok = au_to_arg32(3, "dev", ar->ar_arg_dev); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_MMAP: case AUE_MUNMAP: case AUE_MPROTECT: case AUE_MLOCK: case AUE_MUNLOCK: case AUE_MINHERIT: ADDR_TOKEN(1, "addr"); if (ARG_IS_VALID(kar, ARG_LEN)) { tok = au_to_arg32(2, "len", ar->ar_arg_len); kau_write(rec, tok); } if (ar->ar_event == AUE_MMAP) FD_VNODE1_TOKENS; if (ar->ar_event == AUE_MPROTECT) { if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "protection", ar->ar_arg_value); kau_write(rec, tok); } } if (ar->ar_event == AUE_MINHERIT) { if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "inherit", ar->ar_arg_value); kau_write(rec, tok); } } break; case AUE_MOUNT: case AUE_NMOUNT: /* XXX Need to handle NFS mounts */ if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(3, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_NFS_SVC: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "flags", ar->ar_arg_cmd); kau_write(rec, tok); } break; case AUE_UMOUNT: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(2, "flags", ar->ar_arg_value); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } break; case AUE_MSGCTL: ar->ar_event = audit_msgctl_to_event(ar->ar_arg_svipc_cmd); /* Fall through */ case AUE_MSGRCV: case AUE_MSGSND: tok = au_to_arg32(1, "msg ID", ar->ar_arg_svipc_id); kau_write(rec, tok); if (ar->ar_errno != EINVAL) { tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id); kau_write(rec, tok); } break; case AUE_MSGGET: if (ar->ar_errno == 0) { if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_RESETSHFILE: ADDR_TOKEN(1, "base addr"); break; case AUE_OPEN_RC: case AUE_OPEN_RTC: case AUE_OPEN_RWC: case AUE_OPEN_RWTC: case AUE_OPEN_WC: case AUE_OPEN_WTC: case AUE_CREAT: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_OPEN_R: case AUE_OPEN_RT: case AUE_OPEN_RW: case AUE_OPEN_RWT: case AUE_OPEN_W: case AUE_OPEN_WT: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_OPENAT_RC: case AUE_OPENAT_RTC: case AUE_OPENAT_RWC: case AUE_OPENAT_RWTC: case AUE_OPENAT_WC: case AUE_OPENAT_WTC: if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_OPENAT_R: case AUE_OPENAT_RT: case AUE_OPENAT_RW: case AUE_OPENAT_RWT: case AUE_OPENAT_W: case AUE_OPENAT_WT: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_PDKILL: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SIGNUM)) { tok = au_to_arg32(2, "signal", ar->ar_arg_signum); kau_write(rec, tok); } PROCESS_PID_TOKENS(1); break; case AUE_PDFORK: if (ARG_IS_VALID(kar, ARG_PID)) { tok = au_to_arg32(0, "child PID", ar->ar_arg_pid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_PDGETPID: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_PROCCTL: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(1, "idtype", ar->ar_arg_value); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "com", ar->ar_arg_cmd); kau_write(rec, tok); } PROCESS_PID_TOKENS(3); break; case AUE_PTRACE: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "request", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(4, "data", ar->ar_arg_value); kau_write(rec, tok); } PROCESS_PID_TOKENS(2); break; case AUE_QUOTACTL: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(2, "command", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(3, "uid", ar->ar_arg_uid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(3, "gid", ar->ar_arg_gid); kau_write(rec, tok); } UPATH1_VNODE1_TOKENS; break; case AUE_REBOOT: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "howto", ar->ar_arg_cmd); kau_write(rec, tok); } break; case AUE_SEMCTL: ar->ar_event = audit_semctl_to_event(ar->ar_arg_svipc_cmd); /* Fall through */ case AUE_SEMOP: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "sem ID", ar->ar_arg_svipc_id); kau_write(rec, tok); if (ar->ar_errno != EINVAL) { tok = au_to_ipc(AT_IPC_SEM, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_SEMGET: if (ar->ar_errno == 0) { if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_ipc(AT_IPC_SEM, ar->ar_arg_svipc_id); kau_write(rec, tok); } } break; case AUE_SETEGID: if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(1, "egid", ar->ar_arg_egid); kau_write(rec, tok); } break; case AUE_SETEUID: if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(1, "euid", ar->ar_arg_euid); kau_write(rec, tok); } break; case AUE_SETREGID: if (ARG_IS_VALID(kar, ARG_RGID)) { tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(2, "egid", ar->ar_arg_egid); kau_write(rec, tok); } break; case AUE_SETREUID: if (ARG_IS_VALID(kar, ARG_RUID)) { tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(2, "euid", ar->ar_arg_euid); kau_write(rec, tok); } break; case AUE_SETRESGID: if (ARG_IS_VALID(kar, ARG_RGID)) { tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EGID)) { tok = au_to_arg32(2, "egid", ar->ar_arg_egid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SGID)) { tok = au_to_arg32(3, "sgid", ar->ar_arg_sgid); kau_write(rec, tok); } break; case AUE_SETRESUID: if (ARG_IS_VALID(kar, ARG_RUID)) { tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_EUID)) { tok = au_to_arg32(2, "euid", ar->ar_arg_euid); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SUID)) { tok = au_to_arg32(3, "suid", ar->ar_arg_suid); kau_write(rec, tok); } break; case AUE_SETGID: if (ARG_IS_VALID(kar, ARG_GID)) { tok = au_to_arg32(1, "gid", ar->ar_arg_gid); kau_write(rec, tok); } break; case AUE_SETUID: if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(1, "uid", ar->ar_arg_uid); kau_write(rec, tok); } break; case AUE_SETGROUPS: if (ARG_IS_VALID(kar, ARG_GROUPSET)) { for(ctr = 0; ctr < ar->ar_arg_groups.gidset_size; ctr++) { tok = au_to_arg32(1, "setgroups", ar->ar_arg_groups.gidset[ctr]); kau_write(rec, tok); } } break; case AUE_SETLOGIN: if (ARG_IS_VALID(kar, ARG_LOGIN)) { tok = au_to_text(ar->ar_arg_login); kau_write(rec, tok); } break; case AUE_SETLOGINCLASS: break; case AUE_SETPRIORITY: if (ARG_IS_VALID(kar, ARG_CMD)) { tok = au_to_arg32(1, "which", ar->ar_arg_cmd); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_UID)) { tok = au_to_arg32(2, "who", ar->ar_arg_uid); kau_write(rec, tok); } PROCESS_PID_TOKENS(2); if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "priority", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_SETPRIVEXEC: if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(1, "flag", ar->ar_arg_value); kau_write(rec, tok); } break; /* AUE_SHMAT, AUE_SHMCTL, AUE_SHMDT and AUE_SHMGET are SysV IPC */ case AUE_SHMAT: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); /* XXXAUDIT: Does having the ipc token make sense? */ tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { tok = au_to_arg32(2, "shmaddr", (int)(uintptr_t)ar->ar_arg_svipc_addr); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; case AUE_SHMCTL: if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); /* XXXAUDIT: Does having the ipc token make sense? */ tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } switch (ar->ar_arg_svipc_cmd) { case IPC_STAT: ar->ar_event = AUE_SHMCTL_STAT; break; case IPC_RMID: ar->ar_event = AUE_SHMCTL_RMID; break; case IPC_SET: ar->ar_event = AUE_SHMCTL_SET; if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; default: break; /* We will audit a bad command */ } break; case AUE_SHMDT: if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) { tok = au_to_arg32(1, "shmaddr", (int)(uintptr_t)ar->ar_arg_svipc_addr); kau_write(rec, tok); } break; case AUE_SHMGET: /* This is unusual; the return value is in an argument token */ if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) { tok = au_to_arg32(0, "shmid", ar->ar_arg_svipc_id); kau_write(rec, tok); tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) { tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm); kau_write(rec, tok); } break; /* shm_rename is a non-Posix extension to the Posix shm implementation */ case AUE_SHMRENAME: UPATH1_TOKENS; UPATH2_TOKENS; if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } break; /* AUE_SHMOPEN, AUE_SHMUNLINK, AUE_SEMOPEN, AUE_SEMCLOSE * and AUE_SEMUNLINK are Posix IPC */ case AUE_SHMOPEN: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_SHMUNLINK: UPATH1_TOKENS; if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { struct ipc_perm perm; perm.uid = ar->ar_arg_pipc_perm.pipc_uid; perm.gid = ar->ar_arg_pipc_perm.pipc_gid; perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; perm.mode = ar->ar_arg_pipc_perm.pipc_mode; perm.seq = 0; perm.key = 0; tok = au_to_ipc_perm(&perm); kau_write(rec, tok); } break; case AUE_SEMOPEN: if (ARG_IS_VALID(kar, ARG_FFLAGS)) { tok = au_to_arg32(2, "flags", ar->ar_arg_fflags); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_MODE)) { tok = au_to_arg32(3, "mode", ar->ar_arg_mode); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(4, "value", ar->ar_arg_value); kau_write(rec, tok); } /* FALLTHROUGH */ case AUE_SEMUNLINK: if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) { struct ipc_perm perm; perm.uid = ar->ar_arg_pipc_perm.pipc_uid; perm.gid = ar->ar_arg_pipc_perm.pipc_gid; perm.cuid = ar->ar_arg_pipc_perm.pipc_uid; perm.cgid = ar->ar_arg_pipc_perm.pipc_gid; perm.mode = ar->ar_arg_pipc_perm.pipc_mode; perm.seq = 0; perm.key = 0; tok = au_to_ipc_perm(&perm); kau_write(rec, tok); } break; case AUE_SEMCLOSE: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "sem", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_SYMLINK: case AUE_SYMLINKAT: if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } ATFD1_TOKENS(1); UPATH1_VNODE1_TOKENS; break; case AUE_SYSCTL: case AUE_SYSCTL_NONADMIN: if (ARG_IS_VALID(kar, ARG_CTLNAME | ARG_LEN)) { for (ctr = 0; ctr < ar->ar_arg_len; ctr++) { tok = au_to_arg32(1, "name", ar->ar_arg_ctlname[ctr]); kau_write(rec, tok); } } if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(5, "newval", ar->ar_arg_value); kau_write(rec, tok); } if (ARG_IS_VALID(kar, ARG_TEXT)) { tok = au_to_text(ar->ar_arg_text); kau_write(rec, tok); } break; case AUE_UMASK: if (ARG_IS_VALID(kar, ARG_MASK)) { tok = au_to_arg32(1, "new mask", ar->ar_arg_mask); kau_write(rec, tok); } tok = au_to_arg32(0, "prev mask", ar->ar_retval); kau_write(rec, tok); break; case AUE_WAIT4: case AUE_WAIT6: PROCESS_PID_TOKENS(1); if (ARG_IS_VALID(kar, ARG_VALUE)) { tok = au_to_arg32(3, "options", ar->ar_arg_value); kau_write(rec, tok); } break; case AUE_CAP_RIGHTS_LIMIT: /* * XXXRW/XXXJA: Would be nice to audit socket/etc information. */ FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_RIGHTS)) { tok = au_to_rights(&ar->ar_arg_rights); kau_write(rec, tok); } break; case AUE_CAP_FCNTLS_GET: case AUE_CAP_IOCTLS_GET: case AUE_CAP_IOCTLS_LIMIT: case AUE_CAP_RIGHTS_GET: if (ARG_IS_VALID(kar, ARG_FD)) { tok = au_to_arg32(1, "fd", ar->ar_arg_fd); kau_write(rec, tok); } break; case AUE_CAP_FCNTLS_LIMIT: FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_FCNTL_RIGHTS)) { tok = au_to_arg32(2, "fcntlrights", ar->ar_arg_fcntl_rights); kau_write(rec, tok); } break; case AUE_CAP_ENTER: case AUE_CAP_GETMODE: break; case AUE_THR_NEW: case AUE_THR_KILL: case AUE_THR_EXIT: break; case AUE_NULL: default: printf("BSM conversion requested for unknown event %d\n", ar->ar_event); /* * Write the subject token so it is properly freed here. */ if (jail_tok != NULL) kau_write(rec, jail_tok); kau_write(rec, subj_tok); kau_free(rec); return (BSM_NOAUDIT); } if (jail_tok != NULL) kau_write(rec, jail_tok); kau_write(rec, subj_tok); tok = au_to_return32(au_errno_to_bsm(ar->ar_errno), ar->ar_retval); kau_write(rec, tok); /* Every record gets a return token */ kau_close(rec, &ar->ar_endtime, ar->ar_event); *pau = rec; return (BSM_SUCCESS); } /* * Verify that a record is a valid BSM record. This verification is simple * now, but may be expanded on sometime in the future. Return 1 if the * record is good, 0 otherwise. */ int bsm_rec_verify(void *rec) { char c = *(char *)rec; /* * Check the token ID of the first token; it has to be a header * token. * * XXXAUDIT There needs to be a token structure to map a token. * XXXAUDIT 'Shouldn't be simply looking at the first char. */ if ((c != AUT_HEADER32) && (c != AUT_HEADER32_EX) && (c != AUT_HEADER64) && (c != AUT_HEADER64_EX)) return (0); return (1); } diff --git a/sys/security/audit/audit_dtrace.c b/sys/security/audit/audit_dtrace.c index 985baf142ab3..21a290217288 100644 --- a/sys/security/audit/audit_dtrace.c +++ b/sys/security/audit/audit_dtrace.c @@ -1,538 +1,536 @@ /*- * Copyright (c) 2016, 2018 Robert N. M. Watson * All rights reserved. * * This software was developed by BAE Systems, the University of Cambridge * Computer Laboratory, and Memorial University under DARPA/AFRL contract * FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent Computing * (TC) research program. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*- * Audit DTrace provider: allow DTrace to request that audit records be * generated for various audit events, and then expose those records (in * various forms) to probes. The model is that each event type has two * probes, which use the event's name to create the probe: * * - "commit" passes the kernel-internal (unserialised) kaudit_record * synchronously (from the originating thread) of the record as we prepare * to "commit" the record to the audit queue. * * - "bsm" also passes generated BSM, and executes asynchronously in the audit * worker thread, once it has been extracted from the audit queue. This is * the point at which an audit record would be enqueued to the trail on * disk, or to pipes. * * These probes support very different goals. The former executes in the * thread originating the record, making it easier to correlate other DTrace * probe activity with the event described in the record. The latter gives * access to BSM-formatted events (at a cost) allowing DTrace to extract BSM * directly an alternative mechanism to the formal audit trail and audit * pipes. * * To generate names for numeric event IDs, userspace will push the contents * of /etc/security/audit_event into the kernel during audit setup, much as it * does /etc/security/audit_class. We then create the probes for each of * those mappings. If one (or both) of the probes are enabled, then we cause * a record to be generated (as both normal audit preselection and audit pipes * do), and catch it on the way out during commit. There are suitable hook * functions in the audit code that this provider can register to catch * various events in the audit-record life cycle. * * Further ponderings: * * - How do we want to handle events for which there are not names -- perhaps * a catch-all probe for those events without mappings? * * - Should the evname code really be present even if DTrace isn't loaded...? * Right now, we arrange that it is so that userspace can usefully maintain * the list in case DTrace is later loaded (and to prevent userspace * confusion). * * - Should we add an additional set of audit:class::commit probes that use * event class names to match broader categories of events as specified in * /etc/security/event_class? * * - If we pursue that last point, we will want to pass the name of the event * into the probe explicitly (e.g., as arg0), since it would no longer be * available as the probe function name. */ static int dtaudit_unload(void); static void dtaudit_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); static void dtaudit_provide(void *, dtrace_probedesc_t *); static void dtaudit_destroy(void *, dtrace_id_t, void *); static void dtaudit_enable(void *, dtrace_id_t, void *); static void dtaudit_disable(void *, dtrace_id_t, void *); static void dtaudit_load(void *); static dtrace_pattr_t dtaudit_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, }; /* * Strings for the "module" and "name" portions of the probe. The name of the * audit event will be the "function" portion of the probe. All dtaudit * probes therefore take the form audit:event::commit. */ static char *dtaudit_module_str = "event"; static char *dtaudit_name_commit_str = "commit"; static char *dtaudit_name_bsm_str = "bsm"; static dtrace_pops_t dtaudit_pops = { .dtps_provide = dtaudit_provide, .dtps_provide_module = NULL, .dtps_enable = dtaudit_enable, .dtps_disable = dtaudit_disable, .dtps_suspend = NULL, .dtps_resume = NULL, .dtps_getargdesc = dtaudit_getargdesc, .dtps_getargval = NULL, .dtps_usermode = NULL, .dtps_destroy = dtaudit_destroy }; static dtrace_provider_id_t dtaudit_id; /* * Because looking up entries in the event-to-name mapping is quite expensive, * maintain a global flag tracking whether any dtaudit probes are enabled. If * not, don't bother doing all that work whenever potential queries about * events turn up during preselection or commit. * * NB: We used to maintain our own variable in dtaudit, but now use the * centralized audit_dtrace_enabled variable imported from the audit code. * * static uint_t dtaudit_probes_enabled; */ /* * Check dtaudit policy for the event to see whether this is an event we would * like to preselect (i.e., cause an audit record to be generated for). To * minimise probe effect when not used at all, we not only check for the probe * on the individual event, but also a global flag indicating that at least * one probe is enabled, before acquiring locks, searching lists, etc. * * If the event is selected, return an evname_elem reference to be stored in * the audit record, which we can use later to avoid further lookups. The * contents of the evname_elem must be sufficiently stable so as to not risk * race conditions here. * * Currently, we take an interest only in the 'event' argument, but in the * future might want to support other types of record selection tied to * additional probe types (e.g., event clases). * * XXXRW: Should we have a catch-all probe here for events without registered * names? */ static void * dtaudit_preselect(au_id_t auid, au_event_t event, au_class_t class) { struct evname_elem *ene; int probe_enabled; /* * NB: Lockless reads here may return a slightly stale value; this is * considered better than acquiring a lock, however. */ if (!audit_dtrace_enabled) return (NULL); ene = au_evnamemap_lookup(event); if (ene == NULL) return (NULL); /* * See if either of the two probes for the audit event are enabled. * * NB: Lock also not acquired here -- but perhaps it wouldn't matter * given that we've already used the list lock above? * * XXXRW: Alternatively, au_evnamemap_lookup() could return these * values while holding the list lock...? */ probe_enabled = ene->ene_commit_probe_enabled || ene->ene_bsm_probe_enabled; if (!probe_enabled) return (NULL); return ((void *)ene); } /* * Commit probe pre-BSM. Fires the probe but also checks to see if we should * ask the audit framework to call us again with BSM arguments in the audit * worker thread. * * XXXRW: Should we have a catch-all probe here for events without registered * names? */ static int dtaudit_commit(struct kaudit_record *kar, au_id_t auid, au_event_t event, au_class_t class, int sorf) { char ene_name_lower[EVNAMEMAP_NAME_SIZE]; struct evname_elem *ene; int i; ene = (struct evname_elem *)kar->k_dtaudit_state; if (ene == NULL) return (0); /* * Process a possibly registered commit probe. */ if (ene->ene_commit_probe_enabled) { /* * XXXRW: Lock ene to provide stability to the name string. A * bit undesirable! We may want another locking strategy * here. At least we don't run the DTrace probe under the * lock. * * XXXRW: We provide the struct audit_record pointer -- but * perhaps should provide the kaudit_record pointer? */ EVNAME_LOCK(ene); for (i = 0; i < sizeof(ene_name_lower); i++) ene_name_lower[i] = tolower(ene->ene_name[i]); EVNAME_UNLOCK(ene); dtrace_probe(ene->ene_commit_probe_id, (uintptr_t)ene_name_lower, (uintptr_t)&kar->k_ar, 0, 0, 0); } /* * Return the state of the BSM probe to the caller. */ return (ene->ene_bsm_probe_enabled); } /* * Commit probe post-BSM. * * XXXRW: Should we have a catch-all probe here for events without registered * names? */ static void dtaudit_bsm(struct kaudit_record *kar, au_id_t auid, au_event_t event, au_class_t class, int sorf, void *bsm_data, size_t bsm_len) { char ene_name_lower[EVNAMEMAP_NAME_SIZE]; struct evname_elem *ene; int i; ene = (struct evname_elem *)kar->k_dtaudit_state; if (ene == NULL) return; if (!(ene->ene_bsm_probe_enabled)) return; /* * XXXRW: Lock ene to provide stability to the name string. A bit * undesirable! We may want another locking strategy here. At least * we don't run the DTrace probe under the lock. * * XXXRW: We provide the struct audit_record pointer -- but perhaps * should provide the kaudit_record pointer? */ EVNAME_LOCK(ene); for (i = 0; i < sizeof(ene_name_lower); i++) ene_name_lower[i] = tolower(ene->ene_name[i]); EVNAME_UNLOCK(ene); dtrace_probe(ene->ene_bsm_probe_id, (uintptr_t)ene_name_lower, (uintptr_t)&kar->k_ar, (uintptr_t)bsm_data, (uintptr_t)bsm_len, 0); } /* * A very simple provider: argument types are identical across all probes: the * kaudit_record, plus a BSM pointer and length. */ static void dtaudit_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) { struct evname_elem *ene; const char *p; ene = (struct evname_elem *)parg; p = NULL; switch (desc->dtargd_ndx) { case 0: /* Audit event name. */ p = "char *"; break; case 1: /* In-kernel audit record. */ p = "struct audit_record *"; break; case 2: /* BSM data, if present. */ if (id == ene->ene_bsm_probe_id) p = "const void *"; else desc->dtargd_ndx = DTRACE_ARGNONE; break; case 3: /* BSM length, if present. */ if (id == ene->ene_bsm_probe_id) p = "size_t"; else desc->dtargd_ndx = DTRACE_ARGNONE; break; default: desc->dtargd_ndx = DTRACE_ARGNONE; break; } if (p != NULL) strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native)); } /* * Callback from the event-to-name mapping code when performing * evname_foreach(). Note that we may update the entry, so the foreach code * must have a write lock. However, as the synchronisation model is private * to the evname code, we cannot easily assert it here. * * XXXRW: How do we want to handle event rename / collision issues here -- * e.g., if userspace was using a name to point to one event number, and then * changes it so that the name points at another? For now, paper over this by * skipping event numbers that are already registered, and likewise skipping * names that are already registered. However, this could lead to confusing * behaviour so possibly needs to be resolved in the longer term. */ static void dtaudit_au_evnamemap_callback(struct evname_elem *ene) { char ene_name_lower[EVNAMEMAP_NAME_SIZE]; int i; /* * DTrace, by convention, has lower-case probe names. However, the * in-kernel event-to-name mapping table must maintain event-name case * as submitted by userspace. Create a temporary lower-case version * here, away from the fast path, to use when exposing the event name * to DTrace as part of the name of a probe. * * NB: Convert the entire array, including the terminating nul, * because these strings are short and it's more work not to. If they * become long, we might feel more guilty about this sloppiness! */ for (i = 0; i < sizeof(ene_name_lower); i++) ene_name_lower[i] = tolower(ene->ene_name[i]); /* * Don't register a new probe if this event number already has an * associated commit probe -- or if another event has already * registered this name. * * XXXRW: There is an argument that if multiple numeric events match * a single name, they should all be exposed to the same named probe. * In particular, we should perhaps use a probe ID returned by this * lookup and just stick that in the saved probe ID? */ if ((ene->ene_commit_probe_id == 0) && (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str, ene_name_lower, dtaudit_name_commit_str) == 0)) { - /* * Create the commit probe. * * NB: We don't declare any extra stack frames because stack() * will just return the path to the audit commit code, which * is not really interesting anyway. * * We pass in the pointer to the evnam_elem entry so that we * can easily change its enabled flag in the probe * enable/disable interface. */ ene->ene_commit_probe_id = dtrace_probe_create(dtaudit_id, dtaudit_module_str, ene_name_lower, dtaudit_name_commit_str, 0, ene); } /* * Don't register a new probe if this event number already has an * associated bsm probe -- or if another event has already * registered this name. * * XXXRW: There is an argument that if multiple numeric events match * a single name, they should all be exposed to the same named probe. * In particular, we should perhaps use a probe ID returned by this * lookup and just stick that in the saved probe ID? */ if ((ene->ene_bsm_probe_id == 0) && (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str, ene_name_lower, dtaudit_name_bsm_str) == 0)) { - /* * Create the bsm probe. * * NB: We don't declare any extra stack frames because stack() * will just return the path to the audit commit code, which * is not really interesting anyway. * * We pass in the pointer to the evnam_elem entry so that we * can easily change its enabled flag in the probe * enable/disable interface. */ ene->ene_bsm_probe_id = dtrace_probe_create(dtaudit_id, dtaudit_module_str, ene_name_lower, dtaudit_name_bsm_str, 0, ene); } } static void dtaudit_provide(void *arg, dtrace_probedesc_t *desc) { /* * Walk all registered number-to-name mapping entries, and ensure each * is properly registered. */ au_evnamemap_foreach(dtaudit_au_evnamemap_callback); } static void dtaudit_destroy(void *arg, dtrace_id_t id, void *parg) { } static void dtaudit_enable(void *arg, dtrace_id_t id, void *parg) { struct evname_elem *ene; ene = parg; KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id, ("%s: probe ID mismatch (%u, %u != %u)", __func__, ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id)); if (id == ene->ene_commit_probe_id) ene->ene_commit_probe_enabled = 1; else ene->ene_bsm_probe_enabled = 1; refcount_acquire(&audit_dtrace_enabled); audit_syscalls_enabled_update(); } static void dtaudit_disable(void *arg, dtrace_id_t id, void *parg) { struct evname_elem *ene; ene = parg; KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id, ("%s: probe ID mismatch (%u, %u != %u)", __func__, ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id)); if (id == ene->ene_commit_probe_id) ene->ene_commit_probe_enabled = 0; else ene->ene_bsm_probe_enabled = 0; (void)refcount_release(&audit_dtrace_enabled); audit_syscalls_enabled_update(); } static void dtaudit_load(void *dummy) { if (dtrace_register("audit", &dtaudit_attr, DTRACE_PRIV_USER, NULL, &dtaudit_pops, NULL, &dtaudit_id) != 0) return; dtaudit_hook_preselect = dtaudit_preselect; dtaudit_hook_commit = dtaudit_commit; dtaudit_hook_bsm = dtaudit_bsm; } static int dtaudit_unload(void) { int error; dtaudit_hook_preselect = NULL; dtaudit_hook_commit = NULL; dtaudit_hook_bsm = NULL; if ((error = dtrace_unregister(dtaudit_id)) != 0) return (error); return (0); } static int dtaudit_modevent(module_t mod __unused, int type, void *data __unused) { int error = 0; switch (type) { case MOD_LOAD: case MOD_UNLOAD: case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } SYSINIT(dtaudit_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtaudit_load, NULL); SYSUNINIT(dtaudit_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtaudit_unload, NULL); DEV_MODULE(dtaudit, dtaudit_modevent, NULL); MODULE_VERSION(dtaudit, 1); MODULE_DEPEND(dtaudit, dtrace, 1, 1, 1); MODULE_DEPEND(dtaudit, opensolaris, 1, 1, 1); diff --git a/sys/security/audit/audit_syscalls.c b/sys/security/audit/audit_syscalls.c index 8548c4e1f74b..5f07241109f1 100644 --- a/sys/security/audit/audit_syscalls.c +++ b/sys/security/audit/audit_syscalls.c @@ -1,906 +1,905 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1999-2009 Apple Inc. * Copyright (c) 2016, 2018 Robert N. M. Watson * All rights reserved. * * Portions of this software were developed by BAE Systems, the University of * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent * Computing (TC) research program. * * 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AUDIT /* * System call to allow a user space application to submit a BSM audit record * to the kernel for inclusion in the audit log. This function does little * verification on the audit record that is submitted. * * XXXAUDIT: Audit preselection for user records does not currently work, * since we pre-select only based on the AUE_audit event type, not the event * type submitted as part of the user audit data. */ /* ARGSUSED */ int sys_audit(struct thread *td, struct audit_args *uap) { int error; void * rec; struct kaudit_record *ar; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_SUBMIT); if (error) return (error); if ((uap->length <= 0) || (uap->length > audit_qctrl.aq_bufsz)) return (EINVAL); ar = currecord(); /* * If there's no current audit record (audit() itself not audited) * commit the user audit record. */ if (ar == NULL) { - /* * This is not very efficient; we're required to allocate a * complete kernel audit record just so the user record can * tag along. * * XXXAUDIT: Maybe AUE_AUDIT in the system call context and * special pre-select handling? */ td->td_ar = audit_new(AUE_NULL, td); if (td->td_ar == NULL) return (ENOTSUP); td->td_pflags |= TDP_AUDITREC; ar = td->td_ar; } if (uap->length > MAX_AUDIT_RECORD_SIZE) return (EINVAL); rec = malloc(uap->length, M_AUDITDATA, M_WAITOK); error = copyin(uap->record, rec, uap->length); if (error) goto free_out; /* Verify the record. */ if (bsm_rec_verify(rec) == 0) { error = EINVAL; goto free_out; } #ifdef MAC error = mac_system_check_audit(td->td_ucred, rec, uap->length); if (error) goto free_out; #endif /* * Attach the user audit record to the kernel audit record. Because * this system call is an auditable event, we will write the user * record along with the record for this audit event. * * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen, * k_ar_commit & AR_COMMIT_USER? */ ar->k_udata = rec; ar->k_ulen = uap->length; ar->k_ar_commit |= AR_COMMIT_USER; /* * Currently we assume that all preselection has been performed in * userspace. We unconditionally set these masks so that the records * get committed both to the trail and pipe. In the future we will * want to setup kernel based preselection. */ ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE); return (0); free_out: /* * audit_syscall_exit() will free the audit record on the thread even * if we allocated it above. */ free(rec, M_AUDITDATA); return (error); } /* * System call to manipulate auditing. */ /* ARGSUSED */ int sys_auditon(struct thread *td, struct auditon_args *uap) { struct ucred *cred, *newcred, *oldcred; int error; union auditon_udata udata; struct proc *tp; if (jailed(td->td_ucred)) return (ENOSYS); AUDIT_ARG_CMD(uap->cmd); #ifdef MAC error = mac_system_check_auditon(td->td_ucred, uap->cmd); if (error) return (error); #endif error = priv_check(td, PRIV_AUDIT_CONTROL); if (error) return (error); if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata))) return (EINVAL); memset((void *)&udata, 0, sizeof(udata)); /* * Some of the GET commands use the arguments too. */ switch (uap->cmd) { case A_SETPOLICY: case A_OLDSETPOLICY: case A_SETKMASK: case A_SETQCTRL: case A_OLDSETQCTRL: case A_SETSTAT: case A_SETUMASK: case A_SETSMASK: case A_SETCOND: case A_OLDSETCOND: case A_SETCLASS: case A_SETEVENT: case A_SETPMASK: case A_SETFSIZE: case A_SETKAUDIT: case A_GETCLASS: case A_GETEVENT: case A_GETPINFO: case A_GETPINFO_ADDR: case A_SENDTRIGGER: error = copyin(uap->data, (void *)&udata, uap->length); if (error) return (error); AUDIT_ARG_AUDITON(&udata); break; } /* * XXXAUDIT: Locking? */ switch (uap->cmd) { case A_OLDGETPOLICY: case A_GETPOLICY: if (uap->length == sizeof(udata.au_policy64)) { if (!audit_fail_stop) udata.au_policy64 |= AUDIT_CNT; if (audit_panic_on_write_fail) udata.au_policy64 |= AUDIT_AHLT; if (audit_argv) udata.au_policy64 |= AUDIT_ARGV; if (audit_arge) udata.au_policy64 |= AUDIT_ARGE; break; } if (uap->length != sizeof(udata.au_policy)) return (EINVAL); if (!audit_fail_stop) udata.au_policy |= AUDIT_CNT; if (audit_panic_on_write_fail) udata.au_policy |= AUDIT_AHLT; if (audit_argv) udata.au_policy |= AUDIT_ARGV; if (audit_arge) udata.au_policy |= AUDIT_ARGE; break; case A_OLDSETPOLICY: case A_SETPOLICY: if (uap->length == sizeof(udata.au_policy64)) { if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT| AUDIT_ARGV|AUDIT_ARGE)) return (EINVAL); audit_fail_stop = ((udata.au_policy64 & AUDIT_CNT) == 0); audit_panic_on_write_fail = (udata.au_policy64 & AUDIT_AHLT); audit_argv = (udata.au_policy64 & AUDIT_ARGV); audit_arge = (udata.au_policy64 & AUDIT_ARGE); break; } if (uap->length != sizeof(udata.au_policy)) return (EINVAL); if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT|AUDIT_ARGV| AUDIT_ARGE)) return (EINVAL); /* * XXX - Need to wake up waiters if the policy relaxes? */ audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0); audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT); audit_argv = (udata.au_policy & AUDIT_ARGV); audit_arge = (udata.au_policy & AUDIT_ARGE); break; case A_GETKMASK: if (uap->length != sizeof(udata.au_mask)) return (EINVAL); udata.au_mask = audit_nae_mask; break; case A_SETKMASK: if (uap->length != sizeof(udata.au_mask)) return (EINVAL); audit_nae_mask = udata.au_mask; break; case A_OLDGETQCTRL: case A_GETQCTRL: if (uap->length == sizeof(udata.au_qctrl64)) { udata.au_qctrl64.aq64_hiwater = (u_int64_t)audit_qctrl.aq_hiwater; udata.au_qctrl64.aq64_lowater = (u_int64_t)audit_qctrl.aq_lowater; udata.au_qctrl64.aq64_bufsz = (u_int64_t)audit_qctrl.aq_bufsz; udata.au_qctrl64.aq64_minfree = (u_int64_t)audit_qctrl.aq_minfree; break; } if (uap->length != sizeof(udata.au_qctrl)) return (EINVAL); udata.au_qctrl = audit_qctrl; break; case A_OLDSETQCTRL: case A_SETQCTRL: if (uap->length == sizeof(udata.au_qctrl64)) { /* NB: aq64_minfree is unsigned unlike aq_minfree. */ if ((udata.au_qctrl64.aq64_hiwater > AQ_MAXHIGH) || (udata.au_qctrl64.aq64_lowater >= udata.au_qctrl.aq_hiwater) || (udata.au_qctrl64.aq64_bufsz > AQ_MAXBUFSZ) || (udata.au_qctrl64.aq64_minfree > 100)) return (EINVAL); audit_qctrl.aq_hiwater = (int)udata.au_qctrl64.aq64_hiwater; audit_qctrl.aq_lowater = (int)udata.au_qctrl64.aq64_lowater; audit_qctrl.aq_bufsz = (int)udata.au_qctrl64.aq64_bufsz; audit_qctrl.aq_minfree = (int)udata.au_qctrl64.aq64_minfree; audit_qctrl.aq_delay = -1; /* Not used. */ break; } if (uap->length != sizeof(udata.au_qctrl)) return (EINVAL); if ((udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) || (udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) || (udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) || (udata.au_qctrl.aq_minfree < 0) || (udata.au_qctrl.aq_minfree > 100)) return (EINVAL); audit_qctrl = udata.au_qctrl; /* XXX The queue delay value isn't used with the kernel. */ audit_qctrl.aq_delay = -1; break; case A_GETCWD: return (ENOSYS); break; case A_GETCAR: return (ENOSYS); break; case A_GETSTAT: return (ENOSYS); break; case A_SETSTAT: return (ENOSYS); break; case A_SETUMASK: return (ENOSYS); break; case A_SETSMASK: return (ENOSYS); break; case A_OLDGETCOND: case A_GETCOND: if (uap->length == sizeof(udata.au_cond64)) { if (audit_trail_enabled && !audit_trail_suspended) udata.au_cond64 = AUC_AUDITING; else udata.au_cond64 = AUC_NOAUDIT; break; } if (uap->length != sizeof(udata.au_cond)) return (EINVAL); if (audit_trail_enabled && !audit_trail_suspended) udata.au_cond = AUC_AUDITING; else udata.au_cond = AUC_NOAUDIT; break; case A_OLDSETCOND: case A_SETCOND: if (uap->length == sizeof(udata.au_cond64)) { if (udata.au_cond64 == AUC_NOAUDIT) audit_trail_suspended = 1; if (udata.au_cond64 == AUC_AUDITING) audit_trail_suspended = 0; if (udata.au_cond64 == AUC_DISABLED) { audit_trail_suspended = 1; audit_shutdown(NULL, 0); } audit_syscalls_enabled_update(); break; } if (uap->length != sizeof(udata.au_cond)) return (EINVAL); if (udata.au_cond == AUC_NOAUDIT) audit_trail_suspended = 1; if (udata.au_cond == AUC_AUDITING) audit_trail_suspended = 0; if (udata.au_cond == AUC_DISABLED) { audit_trail_suspended = 1; audit_shutdown(NULL, 0); } audit_syscalls_enabled_update(); break; case A_GETCLASS: if (uap->length != sizeof(udata.au_evclass)) return (EINVAL); udata.au_evclass.ec_class = au_event_class( udata.au_evclass.ec_number); break; case A_GETEVENT: if (uap->length != sizeof(udata.au_evname)) return (EINVAL); error = au_event_name(udata.au_evname.en_number, udata.au_evname.en_name); if (error != 0) return (error); break; case A_SETCLASS: if (uap->length != sizeof(udata.au_evclass)) return (EINVAL); au_evclassmap_insert(udata.au_evclass.ec_number, udata.au_evclass.ec_class); break; case A_SETEVENT: if (uap->length != sizeof(udata.au_evname)) return (EINVAL); /* Ensure nul termination from userspace. */ udata.au_evname.en_name[sizeof(udata.au_evname.en_name) - 1] = 0; au_evnamemap_insert(udata.au_evname.en_number, udata.au_evname.en_name); break; case A_GETPINFO: if (uap->length != sizeof(udata.au_aupinfo)) return (EINVAL); if (udata.au_aupinfo.ap_pid < 1) return (ESRCH); if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) return (ESRCH); if ((error = p_cansee(td, tp)) != 0) { PROC_UNLOCK(tp); return (error); } cred = tp->p_ucred; if (cred->cr_audit.ai_termid.at_type == AU_IPv6) { PROC_UNLOCK(tp); return (EINVAL); } udata.au_aupinfo.ap_auid = cred->cr_audit.ai_auid; udata.au_aupinfo.ap_mask.am_success = cred->cr_audit.ai_mask.am_success; udata.au_aupinfo.ap_mask.am_failure = cred->cr_audit.ai_mask.am_failure; udata.au_aupinfo.ap_termid.machine = cred->cr_audit.ai_termid.at_addr[0]; udata.au_aupinfo.ap_termid.port = (dev_t)cred->cr_audit.ai_termid.at_port; udata.au_aupinfo.ap_asid = cred->cr_audit.ai_asid; PROC_UNLOCK(tp); break; case A_SETPMASK: if (uap->length != sizeof(udata.au_aupinfo)) return (EINVAL); if (udata.au_aupinfo.ap_pid < 1) return (ESRCH); newcred = crget(); if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) { crfree(newcred); return (ESRCH); } if ((error = p_cansee(td, tp)) != 0) { PROC_UNLOCK(tp); crfree(newcred); return (error); } oldcred = tp->p_ucred; crcopy(newcred, oldcred); newcred->cr_audit.ai_mask.am_success = udata.au_aupinfo.ap_mask.am_success; newcred->cr_audit.ai_mask.am_failure = udata.au_aupinfo.ap_mask.am_failure; proc_set_cred(tp, newcred); PROC_UNLOCK(tp); crfree(oldcred); break; case A_SETFSIZE: if (uap->length != sizeof(udata.au_fstat)) return (EINVAL); if ((udata.au_fstat.af_filesz != 0) && (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) return (EINVAL); audit_fstat.af_filesz = udata.au_fstat.af_filesz; break; case A_GETFSIZE: if (uap->length != sizeof(udata.au_fstat)) return (EINVAL); udata.au_fstat.af_filesz = audit_fstat.af_filesz; udata.au_fstat.af_currsz = audit_fstat.af_currsz; break; case A_GETPINFO_ADDR: if (uap->length != sizeof(udata.au_aupinfo_addr)) return (EINVAL); if (udata.au_aupinfo_addr.ap_pid < 1) return (ESRCH); if ((tp = pfind(udata.au_aupinfo_addr.ap_pid)) == NULL) return (ESRCH); cred = tp->p_ucred; udata.au_aupinfo_addr.ap_auid = cred->cr_audit.ai_auid; udata.au_aupinfo_addr.ap_mask.am_success = cred->cr_audit.ai_mask.am_success; udata.au_aupinfo_addr.ap_mask.am_failure = cred->cr_audit.ai_mask.am_failure; udata.au_aupinfo_addr.ap_termid = cred->cr_audit.ai_termid; udata.au_aupinfo_addr.ap_asid = cred->cr_audit.ai_asid; PROC_UNLOCK(tp); break; case A_GETKAUDIT: if (uap->length != sizeof(udata.au_kau_info)) return (EINVAL); audit_get_kinfo(&udata.au_kau_info); break; case A_SETKAUDIT: if (uap->length != sizeof(udata.au_kau_info)) return (EINVAL); if (udata.au_kau_info.ai_termid.at_type != AU_IPv4 && udata.au_kau_info.ai_termid.at_type != AU_IPv6) return (EINVAL); audit_set_kinfo(&udata.au_kau_info); break; case A_SENDTRIGGER: if (uap->length != sizeof(udata.au_trigger)) return (EINVAL); if ((udata.au_trigger < AUDIT_TRIGGER_MIN) || (udata.au_trigger > AUDIT_TRIGGER_MAX)) return (EINVAL); return (audit_send_trigger(udata.au_trigger)); default: return (EINVAL); } /* * Copy data back to userspace for the GET comands. */ switch (uap->cmd) { case A_GETPOLICY: case A_OLDGETPOLICY: case A_GETKMASK: case A_GETQCTRL: case A_OLDGETQCTRL: case A_GETCWD: case A_GETCAR: case A_GETSTAT: case A_GETCOND: case A_OLDGETCOND: case A_GETCLASS: case A_GETPINFO: case A_GETFSIZE: case A_GETPINFO_ADDR: case A_GETKAUDIT: error = copyout((void *)&udata, uap->data, uap->length); if (error) return (error); break; } return (0); } /* * System calls to manage the user audit information. */ /* ARGSUSED */ int sys_getauid(struct thread *td, struct getauid_args *uap) { int error; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit.ai_auid, uap->auid, sizeof(td->td_ucred->cr_audit.ai_auid))); } /* ARGSUSED */ int sys_setauid(struct thread *td, struct setauid_args *uap) { struct ucred *newcred, *oldcred; au_id_t id; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auid, &id, sizeof(id)); if (error) return (error); audit_arg_auid(id); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setauid(oldcred, id); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; newcred->cr_audit.ai_auid = id; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* * System calls to get and set process audit information. */ /* ARGSUSED */ int sys_getaudit(struct thread *td, struct getaudit_args *uap) { struct auditinfo ai; struct ucred *cred; int error; cred = td->td_ucred; if (jailed(cred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); if (cred->cr_audit.ai_termid.at_type == AU_IPv6) return (E2BIG); bzero(&ai, sizeof(ai)); ai.ai_auid = cred->cr_audit.ai_auid; ai.ai_mask = cred->cr_audit.ai_mask; ai.ai_asid = cred->cr_audit.ai_asid; ai.ai_termid.machine = cred->cr_audit.ai_termid.at_addr[0]; ai.ai_termid.port = cred->cr_audit.ai_termid.at_port; return (copyout(&ai, uap->auditinfo, sizeof(ai))); } /* ARGSUSED */ int sys_setaudit(struct thread *td, struct setaudit_args *uap) { struct ucred *newcred, *oldcred; struct auditinfo ai; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auditinfo, &ai, sizeof(ai)); if (error) return (error); audit_arg_auditinfo(&ai); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setaudit(oldcred, &ai); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; bzero(&newcred->cr_audit, sizeof(newcred->cr_audit)); newcred->cr_audit.ai_auid = ai.ai_auid; newcred->cr_audit.ai_mask = ai.ai_mask; newcred->cr_audit.ai_asid = ai.ai_asid; newcred->cr_audit.ai_termid.at_addr[0] = ai.ai_termid.machine; newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port; newcred->cr_audit.ai_termid.at_type = AU_IPv4; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* ARGSUSED */ int sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) { int error; if (jailed(td->td_ucred)) return (ENOSYS); if (uap->length < sizeof(*uap->auditinfo_addr)) return (EOVERFLOW); error = priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit, uap->auditinfo_addr, sizeof(*uap->auditinfo_addr))); } /* ARGSUSED */ int sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) { struct ucred *newcred, *oldcred; struct auditinfo_addr aia; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auditinfo_addr, &aia, sizeof(aia)); if (error) return (error); audit_arg_auditinfo_addr(&aia); if (aia.ai_termid.at_type != AU_IPv6 && aia.ai_termid.at_type != AU_IPv4) return (EINVAL); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setaudit_addr(oldcred, &aia); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; newcred->cr_audit = aia; proc_set_cred(td->td_proc, newcred); PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); } /* * Syscall to manage audit files. */ /* ARGSUSED */ int sys_auditctl(struct thread *td, struct auditctl_args *uap) { struct nameidata nd; struct ucred *cred; struct vnode *vp; int error = 0; int flags; if (jailed(td->td_ucred)) return (ENOSYS); error = priv_check(td, PRIV_AUDIT_CONTROL); if (error) return (error); vp = NULL; cred = NULL; /* * If a path is specified, open the replacement vnode, perform * validity checks, and grab another reference to the current * credential. * * On Darwin, a NULL path argument is also used to disable audit. */ if (uap->path == NULL) return (EINVAL); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); flags = AUDIT_OPEN_FLAGS; error = vn_open(&nd, &flags, 0, NULL); if (error) return (error); vp = nd.ni_vp; #ifdef MAC error = mac_system_check_auditctl(td->td_ucred, vp); VOP_UNLOCK(vp); if (error) { vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); return (error); } #else VOP_UNLOCK(vp); #endif NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VREG) { vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); return (EINVAL); } cred = td->td_ucred; crhold(cred); /* * XXXAUDIT: Should audit_trail_suspended actually be cleared by * audit_worker? */ audit_trail_suspended = 0; audit_syscalls_enabled_update(); audit_rotate_vnode(cred, vp); return (error); } #else /* !AUDIT */ int sys_audit(struct thread *td, struct audit_args *uap) { return (ENOSYS); } int sys_auditon(struct thread *td, struct auditon_args *uap) { return (ENOSYS); } int sys_getauid(struct thread *td, struct getauid_args *uap) { return (ENOSYS); } int sys_setauid(struct thread *td, struct setauid_args *uap) { return (ENOSYS); } int sys_getaudit(struct thread *td, struct getaudit_args *uap) { return (ENOSYS); } int sys_setaudit(struct thread *td, struct setaudit_args *uap) { return (ENOSYS); } int sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) { return (ENOSYS); } int sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) { return (ENOSYS); } int sys_auditctl(struct thread *td, struct auditctl_args *uap) { return (ENOSYS); } #endif /* AUDIT */ diff --git a/sys/security/audit/bsm_token.c b/sys/security/audit/bsm_token.c index 05a1dcff93f7..2dc4df2aa1b8 100644 --- a/sys/security/audit/bsm_token.c +++ b/sys/security/audit/bsm_token.c @@ -1,1613 +1,1611 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2004-2009 Apple Inc. * Copyright (c) 2005 SPARTA, Inc. * All rights reserved. * * This code was developed in part by Robert N. M. Watson, Senior Principal * Scientist, SPARTA, 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include - #include #include #include #include #include #define GET_TOKEN_AREA(t, dptr, length) do { \ t = malloc(sizeof(token_t), M_AUDITBSM, M_WAITOK); \ t->t_data = malloc(length, M_AUDITBSM, M_WAITOK | M_ZERO); \ t->len = length; \ dptr = t->t_data; \ } while (0) /* * token ID 1 byte * success/failure 1 byte * privstrlen 2 bytes * privstr N bytes + 1 (\0 byte) */ token_t * au_to_upriv(char sorf, char *priv) { u_int16_t textlen; u_char *dptr; token_t *t; textlen = strlen(priv) + 1; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_char) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_UPRIV); ADD_U_CHAR(dptr, sorf); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, priv, textlen); return (t); } /* * token ID 1 byte * privtstrlen 2 bytes * privtstr N bytes + 1 * privstrlen 2 bytes * privstr N bytes + 1 */ token_t * au_to_privset(char *privtypestr, char *privstr) { u_int16_t type_len, priv_len; u_char *dptr; token_t *t; type_len = strlen(privtypestr) + 1; priv_len = strlen(privstr) + 1; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + sizeof(u_int16_t) + type_len + priv_len); ADD_U_CHAR(dptr, AUT_PRIV); ADD_U_INT16(dptr, type_len); ADD_STRING(dptr, privtypestr, type_len); ADD_U_INT16(dptr, priv_len); ADD_STRING(dptr, privstr, priv_len); return (t); } /* * token ID 1 byte * argument # 1 byte * argument value 4 bytes/8 bytes (32-bit/64-bit value) * text length 2 bytes * text N bytes + 1 terminating NULL byte */ token_t * au_to_arg32(char n, const char *text, u_int32_t v) { token_t *t; u_char *dptr = NULL; u_int16_t textlen; textlen = strlen(text); textlen += 1; GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_ARG32); ADD_U_CHAR(dptr, n); ADD_U_INT32(dptr, v); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, text, textlen); return (t); } token_t * au_to_arg64(char n, const char *text, u_int64_t v) { token_t *t; u_char *dptr = NULL; u_int16_t textlen; textlen = strlen(text); textlen += 1; GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_ARG64); ADD_U_CHAR(dptr, n); ADD_U_INT64(dptr, v); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, text, textlen); return (t); } token_t * au_to_arg(char n, const char *text, u_int32_t v) { return (au_to_arg32(n, text, v)); } #if defined(_KERNEL) || defined(KERNEL) /* * token ID 1 byte * file access mode 4 bytes * owner user ID 4 bytes * owner group ID 4 bytes * file system ID 4 bytes * node ID 8 bytes * device 4 bytes/8 bytes (32-bit/64-bit) */ token_t * au_to_attr32(struct vnode_au_info *vni) { token_t *t; u_char *dptr = NULL; u_int16_t pad0_16 = 0; u_int32_t pad0_32 = 0; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + 3 * sizeof(u_int32_t) + sizeof(u_int64_t) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_ATTR32); /* * BSD defines the size for the file mode as 2 bytes; BSM defines 4 * so pad with 0. * * XXXRW: Possibly should be conditionally compiled. * * XXXRW: Should any conversions take place on the mode? */ ADD_U_INT16(dptr, pad0_16); ADD_U_INT16(dptr, vni->vn_mode); ADD_U_INT32(dptr, vni->vn_uid); ADD_U_INT32(dptr, vni->vn_gid); ADD_U_INT32(dptr, vni->vn_fsid); /* * Some systems use 32-bit file ID's, others use 64-bit file IDs. * Attempt to handle both, and let the compiler sort it out. If we * could pick this out at compile-time, it would be better, so as to * avoid the else case below. */ if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) { ADD_U_INT32(dptr, pad0_32); ADD_U_INT32(dptr, vni->vn_fileid); } else if (sizeof(vni->vn_fileid) == sizeof(uint64_t)) ADD_U_INT64(dptr, vni->vn_fileid); else ADD_U_INT64(dptr, 0LL); ADD_U_INT32(dptr, vni->vn_dev); return (t); } token_t * au_to_attr64(struct vnode_au_info *vni) { token_t *t; u_char *dptr = NULL; u_int16_t pad0_16 = 0; u_int32_t pad0_32 = 0; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + 3 * sizeof(u_int32_t) + sizeof(u_int64_t) * 2); ADD_U_CHAR(dptr, AUT_ATTR64); /* * BSD defines the size for the file mode as 2 bytes; BSM defines 4 * so pad with 0. * * XXXRW: Possibly should be conditionally compiled. * * XXXRW: Should any conversions take place on the mode? */ ADD_U_INT16(dptr, pad0_16); ADD_U_INT16(dptr, vni->vn_mode); ADD_U_INT32(dptr, vni->vn_uid); ADD_U_INT32(dptr, vni->vn_gid); ADD_U_INT32(dptr, vni->vn_fsid); /* * Some systems use 32-bit file ID's, other's use 64-bit file IDs. * Attempt to handle both, and let the compiler sort it out. If we * could pick this out at compile-time, it would be better, so as to * avoid the else case below. */ if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) { ADD_U_INT32(dptr, pad0_32); ADD_U_INT32(dptr, vni->vn_fileid); } else if (sizeof(vni->vn_fileid) == sizeof(uint64_t)) ADD_U_INT64(dptr, vni->vn_fileid); else ADD_U_INT64(dptr, 0LL); ADD_U_INT64(dptr, vni->vn_dev); return (t); } token_t * au_to_attr(struct vnode_au_info *vni) { return (au_to_attr32(vni)); } #endif /* !(defined(_KERNEL) || defined(KERNEL) */ /* * token ID 1 byte * how to print 1 byte * basic unit 1 byte * unit count 1 byte * data items (depends on basic unit) */ token_t * au_to_data(char unit_print, char unit_type, char unit_count, const char *p) { token_t *t; u_char *dptr = NULL; size_t datasize, totdata; /* Determine the size of the basic unit. */ switch (unit_type) { case AUR_BYTE: /* case AUR_CHAR: */ datasize = AUR_BYTE_SIZE; break; case AUR_SHORT: datasize = AUR_SHORT_SIZE; break; case AUR_INT32: /* case AUR_INT: */ datasize = AUR_INT32_SIZE; break; case AUR_INT64: datasize = AUR_INT64_SIZE; break; default: return (NULL); } totdata = datasize * unit_count; GET_TOKEN_AREA(t, dptr, 4 * sizeof(u_char) + totdata); /* * XXXRW: We should be byte-swapping each data item for multi-byte * types. */ ADD_U_CHAR(dptr, AUT_DATA); ADD_U_CHAR(dptr, unit_print); ADD_U_CHAR(dptr, unit_type); ADD_U_CHAR(dptr, unit_count); ADD_MEM(dptr, p, totdata); return (t); } - /* * token ID 1 byte * status 4 bytes * return value 4 bytes */ token_t * au_to_exit(int retval, int err) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_EXIT); ADD_U_INT32(dptr, err); ADD_U_INT32(dptr, retval); return (t); } /* */ token_t * au_to_groups(int *groups) { return (au_to_newgroups(AUDIT_MAX_GROUPS, (gid_t *)groups)); } /* * token ID 1 byte * number groups 2 bytes * group list count * 4 bytes */ token_t * au_to_newgroups(u_int16_t n, gid_t *groups) { token_t *t; u_char *dptr = NULL; int i; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + n * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_NEWGROUPS); ADD_U_INT16(dptr, n); for (i = 0; i < n; i++) ADD_U_INT32(dptr, groups[i]); return (t); } /* * token ID 1 byte * internet address 4 bytes */ token_t * au_to_in_addr(struct in_addr *internet_addr) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(uint32_t)); ADD_U_CHAR(dptr, AUT_IN_ADDR); ADD_MEM(dptr, &internet_addr->s_addr, sizeof(uint32_t)); return (t); } /* * token ID 1 byte * address type/length 4 bytes * address 16 bytes */ token_t * au_to_in_addr_ex(struct in6_addr *internet_addr) { token_t *t; u_char *dptr = NULL; u_int32_t type = AU_IPv6; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(uint32_t)); ADD_U_CHAR(dptr, AUT_IN_ADDR_EX); ADD_U_INT32(dptr, type); ADD_MEM(dptr, internet_addr, 4 * sizeof(uint32_t)); return (t); } /* * token ID 1 byte * ip header 20 bytes * * The IP header should be submitted in network byte order. */ token_t * au_to_ip(struct ip *ip) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(struct ip)); ADD_U_CHAR(dptr, AUT_IP); ADD_MEM(dptr, ip, sizeof(struct ip)); return (t); } /* * token ID 1 byte * object ID type 1 byte * object ID 4 bytes */ token_t * au_to_ipc(char type, int id) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_IPC); ADD_U_CHAR(dptr, type); ADD_U_INT32(dptr, id); return (t); } /* * token ID 1 byte * owner user ID 4 bytes * owner group ID 4 bytes * creator user ID 4 bytes * creator group ID 4 bytes * access mode 4 bytes * slot sequence # 4 bytes * key 4 bytes */ token_t * au_to_ipc_perm(struct ipc_perm *perm) { token_t *t; u_char *dptr = NULL; u_int16_t pad0 = 0; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 12 * sizeof(u_int16_t) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_IPC_PERM); /* * Systems vary significantly in what types they use in struct * ipc_perm; at least a few still use 16-bit uid's and gid's, so * allow for that, as BSM define 32-bit values here. * Some systems define the sizes for ipc_perm members as 2 bytes; * BSM defines 4 so pad with 0. * * XXXRW: Possibly shoulid be conditionally compiled, and more cases * need to be handled. */ if (sizeof(perm->uid) != sizeof(u_int32_t)) { ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->uid); ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->gid); ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->cuid); ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->cgid); } else { ADD_U_INT32(dptr, perm->uid); ADD_U_INT32(dptr, perm->gid); ADD_U_INT32(dptr, perm->cuid); ADD_U_INT32(dptr, perm->cgid); } ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->mode); ADD_U_INT16(dptr, pad0); ADD_U_INT16(dptr, perm->seq); ADD_U_INT32(dptr, perm->key); return (t); } /* * token ID 1 byte * port IP address 2 bytes */ token_t * au_to_iport(u_int16_t iport) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t)); ADD_U_CHAR(dptr, AUT_IPORT); ADD_U_INT16(dptr, iport); return (t); } /* * token ID 1 byte * size 2 bytes * data size bytes */ token_t * au_to_opaque(const char *data, u_int16_t bytes) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + bytes); ADD_U_CHAR(dptr, AUT_OPAQUE); ADD_U_INT16(dptr, bytes); ADD_MEM(dptr, data, bytes); return (t); } /* * token ID 1 byte * seconds of time 4 bytes * milliseconds of time 4 bytes * file name len 2 bytes * file pathname N bytes + 1 terminating NULL byte */ token_t * au_to_file(const char *file, struct timeval tm) { token_t *t; u_char *dptr = NULL; u_int16_t filelen; u_int32_t timems; filelen = strlen(file); filelen += 1; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t) + sizeof(u_int16_t) + filelen); timems = tm.tv_usec/1000; ADD_U_CHAR(dptr, AUT_OTHER_FILE32); ADD_U_INT32(dptr, tm.tv_sec); ADD_U_INT32(dptr, timems); /* We need time in ms. */ ADD_U_INT16(dptr, filelen); ADD_STRING(dptr, file, filelen); return (t); } /* * token ID 1 byte * text length 2 bytes * text N bytes + 1 terminating NULL byte */ token_t * au_to_text(const char *text) { token_t *t; u_char *dptr = NULL; u_int16_t textlen; textlen = strlen(text); textlen += 1; /* XXXRW: Should validate length against token size limit. */ GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_TEXT); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, text, textlen); return (t); } /* * token ID 1 byte * path length 2 bytes * path N bytes + 1 terminating NULL byte */ token_t * au_to_path(const char *text) { token_t *t; u_char *dptr = NULL; u_int16_t textlen; textlen = strlen(text); textlen += 1; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_PATH); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, text, textlen); return (t); } /* * token ID 1 byte * audit ID 4 bytes * effective user ID 4 bytes * effective group ID 4 bytes * real user ID 4 bytes * real group ID 4 bytes * process ID 4 bytes * session ID 4 bytes * terminal ID * port ID 4 bytes/8 bytes (32-bit/64-bit value) * machine address 4 bytes */ token_t * au_to_process32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_PROCESS32); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT32(dptr, tid->port); /* * Note: Solaris will write out IPv6 addresses here as a 32-bit * address type and 16 bytes of address, but for IPv4 addresses it * simply writes the 4-byte address directly. We support only IPv4 * addresses for process32 tokens. */ ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); return (t); } token_t * au_to_process64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 8 * sizeof(u_int32_t) + sizeof(u_int64_t)); ADD_U_CHAR(dptr, AUT_PROCESS64); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT64(dptr, tid->port); /* * Note: Solaris will write out IPv6 addresses here as a 32-bit * address type and 16 bytes of address, but for IPv4 addresses it * simply writes the 4-byte address directly. We support only IPv4 * addresses for process64 tokens. */ ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); return (t); } token_t * au_to_process(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { return (au_to_process32(auid, euid, egid, ruid, rgid, pid, sid, tid)); } /* * token ID 1 byte * audit ID 4 bytes * effective user ID 4 bytes * effective group ID 4 bytes * real user ID 4 bytes * real group ID 4 bytes * process ID 4 bytes * session ID 4 bytes * terminal ID * port ID 4 bytes/8 bytes (32-bit/64-bit value) * address type-len 4 bytes * machine address 16 bytes */ token_t * au_to_process32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { token_t *t; u_char *dptr = NULL; KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), ("au_to_process32_ex: type %u", (unsigned int)tid->at_type)); if (tid->at_type == AU_IPv4) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 10 * sizeof(u_int32_t)); else GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_PROCESS32_EX); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT32(dptr, tid->at_port); ADD_U_INT32(dptr, tid->at_type); ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); if (tid->at_type == AU_IPv6) { ADD_MEM(dptr, &tid->at_addr[1], sizeof(u_int32_t)); ADD_MEM(dptr, &tid->at_addr[2], sizeof(u_int32_t)); ADD_MEM(dptr, &tid->at_addr[3], sizeof(u_int32_t)); } return (t); } token_t * au_to_process64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { token_t *t; u_char *dptr = NULL; if (tid->at_type == AU_IPv4) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + 2 * sizeof(u_int32_t)); else if (tid->at_type == AU_IPv6) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + 5 * sizeof(u_int32_t)); else panic("au_to_process64_ex: invalidate at_type (%d)", tid->at_type); ADD_U_CHAR(dptr, AUT_PROCESS64_EX); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT64(dptr, tid->at_port); ADD_U_INT32(dptr, tid->at_type); ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); if (tid->at_type == AU_IPv6) { ADD_MEM(dptr, &tid->at_addr[1], sizeof(u_int32_t)); ADD_MEM(dptr, &tid->at_addr[2], sizeof(u_int32_t)); ADD_MEM(dptr, &tid->at_addr[3], sizeof(u_int32_t)); } return (t); } token_t * au_to_process_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { return (au_to_process32_ex(auid, euid, egid, ruid, rgid, pid, sid, tid)); } token_t * au_to_rights(cap_rights_t *rightsp) { token_t *t; u_char *dptr; int i; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(*rightsp)); ADD_U_CHAR(dptr, AUT_RIGHTS); for (i = 0; i < nitems(rightsp->cr_rights); i++) ADD_U_INT64(dptr, rightsp->cr_rights[i]); return (t); } /* * token ID 1 byte * error status 1 byte * return value 4 bytes/8 bytes (32-bit/64-bit value) */ token_t * au_to_return32(char status, u_int32_t ret) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_RETURN32); ADD_U_CHAR(dptr, status); ADD_U_INT32(dptr, ret); return (t); } token_t * au_to_return64(char status, u_int64_t ret) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t)); ADD_U_CHAR(dptr, AUT_RETURN64); ADD_U_CHAR(dptr, status); ADD_U_INT64(dptr, ret); return (t); } token_t * au_to_return(char status, u_int32_t ret) { return (au_to_return32(status, ret)); } /* * token ID 1 byte * sequence number 4 bytes */ token_t * au_to_seq(long audit_count) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SEQ); ADD_U_INT32(dptr, audit_count); return (t); } /* * token ID 1 byte * socket domain 2 bytes * socket type 2 bytes * address type 2 byte * local port 2 bytes * local address 4 bytes/16 bytes (IPv4/IPv6 address) * remote port 2 bytes * remote address 4 bytes/16 bytes (IPv4/IPv6 address) * * Domain and type arguments to this routine are assumed to already have been * converted to the BSM constant space, so we don't do that here. */ token_t * au_to_socket_ex(u_short so_domain, u_short so_type, struct sockaddr *sa_local, struct sockaddr *sa_remote) { token_t *t; u_char *dptr = NULL; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (so_domain == AF_INET) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t)); else if (so_domain == AF_INET6) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(u_int16_t) + 8 * sizeof(u_int32_t)); else return (NULL); ADD_U_CHAR(dptr, AUT_SOCKET_EX); ADD_U_INT16(dptr, au_domain_to_bsm(so_domain)); ADD_U_INT16(dptr, au_socket_type_to_bsm(so_type)); if (so_domain == AF_INET) { ADD_U_INT16(dptr, AU_IPv4); sin = (struct sockaddr_in *)sa_local; ADD_MEM(dptr, &sin->sin_port, sizeof(uint16_t)); ADD_MEM(dptr, &sin->sin_addr.s_addr, sizeof(uint32_t)); sin = (struct sockaddr_in *)sa_remote; ADD_MEM(dptr, &sin->sin_port, sizeof(uint16_t)); ADD_MEM(dptr, &sin->sin_addr.s_addr, sizeof(uint32_t)); } else { ADD_U_INT16(dptr, AU_IPv6); sin6 = (struct sockaddr_in6 *)sa_local; ADD_MEM(dptr, &sin6->sin6_port, sizeof(uint16_t)); ADD_MEM(dptr, &sin6->sin6_addr, 4 * sizeof(uint32_t)); sin6 = (struct sockaddr_in6 *)sa_remote; ADD_MEM(dptr, &sin6->sin6_port, sizeof(uint16_t)); ADD_MEM(dptr, &sin6->sin6_addr, 4 * sizeof(uint32_t)); } return (t); } /* * Kernel-specific version of the above function. * * XXXRW: Should now use au_to_socket_ex() here. */ #ifdef _KERNEL token_t * kau_to_socket(struct socket_au_info *soi) { token_t *t; u_char *dptr; u_int16_t so_type; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) + sizeof(u_int32_t) + sizeof(u_int16_t) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SOCKET); /* Coerce the socket type into a short value */ so_type = soi->so_type; ADD_U_INT16(dptr, so_type); ADD_U_INT16(dptr, soi->so_lport); ADD_U_INT32(dptr, soi->so_laddr); ADD_U_INT16(dptr, soi->so_rport); ADD_U_INT32(dptr, soi->so_raddr); return (t); } #endif /* * token ID 1 byte * socket family 2 bytes * path (up to) 104 bytes + NULL (NULL terminated string) */ token_t * au_to_sock_unix(struct sockaddr_un *so) { token_t *t; u_char *dptr; GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + strlen(so->sun_path) + 1); ADD_U_CHAR(dptr, AUT_SOCKUNIX); /* BSM token has two bytes for family */ ADD_U_CHAR(dptr, 0); ADD_U_CHAR(dptr, so->sun_family); ADD_STRING(dptr, so->sun_path, strlen(so->sun_path) + 1); return (t); } /* * token ID 1 byte * socket family 2 bytes * local port 2 bytes * socket address 4 bytes */ token_t * au_to_sock_inet32(struct sockaddr_in *so) { token_t *t; u_char *dptr = NULL; uint16_t family; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(uint16_t) + sizeof(uint32_t)); ADD_U_CHAR(dptr, AUT_SOCKINET32); /* * BSM defines the family field as 16 bits, but many operating * systems have an 8-bit sin_family field. Extend to 16 bits before * writing into the token. Assume that both the port and the address * in the sockaddr_in are already in network byte order, but family * is in local byte order. * * XXXRW: Should a name space conversion be taking place on the value * of sin_family? */ family = so->sin_family; ADD_U_INT16(dptr, family); ADD_MEM(dptr, &so->sin_port, sizeof(uint16_t)); ADD_MEM(dptr, &so->sin_addr.s_addr, sizeof(uint32_t)); return (t); } token_t * au_to_sock_inet128(struct sockaddr_in6 *so) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + sizeof(u_int16_t) + 4 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SOCKINET128); /* * In BSD, sin6_family is one octet, but BSM defines the token to * store two. So we copy in a 0 first. XXXRW: Possibly should be * conditionally compiled. */ ADD_U_CHAR(dptr, 0); ADD_U_CHAR(dptr, so->sin6_family); ADD_U_INT16(dptr, so->sin6_port); ADD_MEM(dptr, &so->sin6_addr, 4 * sizeof(uint32_t)); return (t); } token_t * au_to_sock_inet(struct sockaddr_in *so) { return (au_to_sock_inet32(so)); } /* * token ID 1 byte * audit ID 4 bytes * effective user ID 4 bytes * effective group ID 4 bytes * real user ID 4 bytes * real group ID 4 bytes * process ID 4 bytes * session ID 4 bytes * terminal ID * port ID 4 bytes/8 bytes (32-bit/64-bit value) * machine address 4 bytes */ token_t * au_to_subject32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SUBJECT32); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT32(dptr, tid->port); ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); return (t); } token_t * au_to_subject64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { token_t *t; u_char *dptr = NULL; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SUBJECT64); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT64(dptr, tid->port); ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t)); return (t); } token_t * au_to_subject(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_t *tid) { return (au_to_subject32(auid, euid, egid, ruid, rgid, pid, sid, tid)); } /* * token ID 1 byte * audit ID 4 bytes * effective user ID 4 bytes * effective group ID 4 bytes * real user ID 4 bytes * real group ID 4 bytes * process ID 4 bytes * session ID 4 bytes * terminal ID * port ID 4 bytes/8 bytes (32-bit/64-bit value) * address type/length 4 bytes * machine address 16 bytes */ token_t * au_to_subject32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { token_t *t; u_char *dptr = NULL; KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), ("au_to_subject32_ex: type %u", (unsigned int)tid->at_type)); if (tid->at_type == AU_IPv4) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 10 * sizeof(u_int32_t)); else GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SUBJECT32_EX); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT32(dptr, tid->at_port); ADD_U_INT32(dptr, tid->at_type); if (tid->at_type == AU_IPv6) ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); else ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); return (t); } token_t * au_to_subject64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { token_t *t; u_char *dptr = NULL; KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6), ("au_to_subject64_ex: type %u", (unsigned int)tid->at_type)); if (tid->at_type == AU_IPv4) GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + 2 * sizeof(u_int32_t)); else GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) + sizeof(u_int64_t) + 5 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_SUBJECT64_EX); ADD_U_INT32(dptr, auid); ADD_U_INT32(dptr, euid); ADD_U_INT32(dptr, egid); ADD_U_INT32(dptr, ruid); ADD_U_INT32(dptr, rgid); ADD_U_INT32(dptr, pid); ADD_U_INT32(dptr, sid); ADD_U_INT64(dptr, tid->at_port); ADD_U_INT32(dptr, tid->at_type); if (tid->at_type == AU_IPv6) ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); else ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); return (t); } token_t * au_to_subject_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid) { return (au_to_subject32_ex(auid, euid, egid, ruid, rgid, pid, sid, tid)); } #if !defined(_KERNEL) && !defined(KERNEL) && defined(HAVE_AUDIT_SYSCALLS) /* * Collects audit information for the current process and creates a subject * token from it. */ token_t * au_to_me(void) { auditinfo_t auinfo; auditinfo_addr_t aia; /* * Try to use getaudit_addr(2) first. If this kernel does not support * it, then fall back on to getaudit(2). */ if (getaudit_addr(&aia, sizeof(aia)) != 0) { if (errno == ENOSYS) { if (getaudit(&auinfo) != 0) return (NULL); return (au_to_subject32(auinfo.ai_auid, geteuid(), getegid(), getuid(), getgid(), getpid(), auinfo.ai_asid, &auinfo.ai_termid)); } else { /* getaudit_addr(2) failed for some other reason. */ return (NULL); } } return (au_to_subject32_ex(aia.ai_auid, geteuid(), getegid(), getuid(), getgid(), getpid(), aia.ai_asid, &aia.ai_termid)); } #endif #if defined(_KERNEL) || defined(KERNEL) static token_t * au_to_exec_strings(char *strs, int count, u_char type) { token_t *t; u_char *dptr = NULL; u_int32_t totlen; int ctr; char *p; totlen = 0; ctr = count; p = strs; while (ctr-- > 0) { totlen += strlen(p) + 1; p = strs + totlen; } GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); ADD_U_CHAR(dptr, type); ADD_U_INT32(dptr, count); ADD_STRING(dptr, strs, totlen); return (t); } /* * token ID 1 byte * count 4 bytes * text count null-terminated strings */ token_t * au_to_exec_args(char *args, int argc) { return (au_to_exec_strings(args, argc, AUT_EXEC_ARGS)); } /* * token ID 1 byte * count 4 bytes * text count null-terminated strings */ token_t * au_to_exec_env(char *envs, int envc) { return (au_to_exec_strings(envs, envc, AUT_EXEC_ENV)); } #else /* * token ID 1 byte * count 4 bytes * text count null-terminated strings */ token_t * au_to_exec_args(char **argv) { token_t *t; u_char *dptr = NULL; const char *nextarg; int i, count = 0; size_t totlen = 0; nextarg = *argv; while (nextarg != NULL) { int nextlen; nextlen = strlen(nextarg); totlen += nextlen + 1; count++; nextarg = *(argv + count); } GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); ADD_U_CHAR(dptr, AUT_EXEC_ARGS); ADD_U_INT32(dptr, count); for (i = 0; i < count; i++) { nextarg = *(argv + i); ADD_MEM(dptr, nextarg, strlen(nextarg) + 1); } return (t); } /* * token ID 1 byte * count 4 bytes * text count null-terminated strings */ token_t * au_to_exec_env(char **envp) { token_t *t; u_char *dptr = NULL; int i, count = 0; size_t totlen = 0; const char *nextenv; nextenv = *envp; while (nextenv != NULL) { int nextlen; nextlen = strlen(nextenv); totlen += nextlen + 1; count++; nextenv = *(envp + count); } GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen); ADD_U_CHAR(dptr, AUT_EXEC_ENV); ADD_U_INT32(dptr, count); for (i = 0; i < count; i++) { nextenv = *(envp + i); ADD_MEM(dptr, nextenv, strlen(nextenv) + 1); } return (t); } #endif /* * token ID 1 byte * zonename length 2 bytes * zonename N bytes + 1 terminating NULL byte */ token_t * au_to_zonename(const char *zonename) { u_char *dptr = NULL; u_int16_t textlen; token_t *t; textlen = strlen(zonename) + 1; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen); ADD_U_CHAR(dptr, AUT_ZONENAME); ADD_U_INT16(dptr, textlen); ADD_STRING(dptr, zonename, textlen); return (t); } /* * token ID 1 byte * record byte count 4 bytes * version # 1 byte [2] * event type 2 bytes * event modifier 2 bytes * seconds of time 4 bytes/8 bytes (32-bit/64-bit value) * milliseconds of time 4 bytes/8 bytes (32-bit/64-bit value) */ token_t * au_to_header32_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, struct timeval tm) { token_t *t; u_char *dptr = NULL; u_int32_t timems; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_HEADER32); ADD_U_INT32(dptr, rec_size); ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); ADD_U_INT16(dptr, e_type); ADD_U_INT16(dptr, e_mod); timems = tm.tv_usec/1000; /* Add the timestamp */ ADD_U_INT32(dptr, tm.tv_sec); ADD_U_INT32(dptr, timems); /* We need time in ms. */ return (t); } /* * token ID 1 byte * record byte count 4 bytes * version # 1 byte [2] * event type 2 bytes * event modifier 2 bytes * address type/length 4 bytes * machine address 4 bytes/16 bytes (IPv4/IPv6 address) * seconds of time 4 bytes/8 bytes (32-bit/64-bit value) * milliseconds of time 4 bytes/8 bytes (32-bit/64-bit value) */ token_t * au_to_header32_ex_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, struct timeval tm, struct auditinfo_addr *aia) { token_t *t; u_char *dptr = NULL; u_int32_t timems; au_tid_addr_t *tid; tid = &aia->ai_termid; KASSERT(tid->at_type == AU_IPv4 || tid->at_type == AU_IPv6, ("au_to_header32_ex_tm: invalid address family")); GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + sizeof(u_char) + 2 * sizeof(u_int16_t) + 3 * sizeof(u_int32_t) + tid->at_type); ADD_U_CHAR(dptr, AUT_HEADER32_EX); ADD_U_INT32(dptr, rec_size); ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); ADD_U_INT16(dptr, e_type); ADD_U_INT16(dptr, e_mod); ADD_U_INT32(dptr, tid->at_type); if (tid->at_type == AU_IPv6) ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t)); else ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t)); timems = tm.tv_usec/1000; /* Add the timestamp */ ADD_U_INT32(dptr, tm.tv_sec); ADD_U_INT32(dptr, timems); /* We need time in ms. */ return (t); } token_t * au_to_header64_tm(int rec_size, au_event_t e_type, au_emod_t e_mod, struct timeval tm) { token_t *t; u_char *dptr = NULL; u_int32_t timems; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int64_t)); ADD_U_CHAR(dptr, AUT_HEADER64); ADD_U_INT32(dptr, rec_size); ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM); ADD_U_INT16(dptr, e_type); ADD_U_INT16(dptr, e_mod); timems = tm.tv_usec/1000; /* Add the timestamp */ ADD_U_INT64(dptr, tm.tv_sec); ADD_U_INT64(dptr, timems); /* We need time in ms. */ return (t); } #if !defined(KERNEL) && !defined(_KERNEL) #ifdef HAVE_AUDIT_SYSCALLS token_t * au_to_header32_ex(int rec_size, au_event_t e_type, au_emod_t e_mod) { struct timeval tm; struct auditinfo_addr aia; if (gettimeofday(&tm, NULL) == -1) return (NULL); if (audit_get_kaudit(&aia, sizeof(aia)) != 0) { if (errno != ENOSYS) return (NULL); return (au_to_header32_tm(rec_size, e_type, e_mod, tm)); } return (au_to_header32_ex_tm(rec_size, e_type, e_mod, tm, &aia)); } #endif /* HAVE_AUDIT_SYSCALLS */ token_t * au_to_header32(int rec_size, au_event_t e_type, au_emod_t e_mod) { struct timeval tm; if (gettimeofday(&tm, NULL) == -1) return (NULL); return (au_to_header32_tm(rec_size, e_type, e_mod, tm)); } token_t * au_to_header64(__unused int rec_size, __unused au_event_t e_type, __unused au_emod_t e_mod) { struct timeval tm; if (gettimeofday(&tm, NULL) == -1) return (NULL); return (au_to_header64_tm(rec_size, e_type, e_mod, tm)); } token_t * au_to_header(int rec_size, au_event_t e_type, au_emod_t e_mod) { return (au_to_header32(rec_size, e_type, e_mod)); } #ifdef HAVE_AUDIT_SYSCALLS token_t * au_to_header_ex(int rec_size, au_event_t e_type, au_emod_t e_mod) { return (au_to_header32_ex(rec_size, e_type, e_mod)); } #endif /* HAVE_AUDIT_SYSCALLS */ #endif /* !defined(KERNEL) && !defined(_KERNEL) */ /* * token ID 1 byte * trailer magic number 2 bytes * record byte count 4 bytes */ token_t * au_to_trailer(int rec_size) { token_t *t; u_char *dptr = NULL; u_int16_t magic = AUT_TRAILER_MAGIC; GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + sizeof(u_int32_t)); ADD_U_CHAR(dptr, AUT_TRAILER); ADD_U_INT16(dptr, magic); ADD_U_INT32(dptr, rec_size); return (t); } diff --git a/sys/security/mac/mac_internal.h b/sys/security/mac/mac_internal.h index 52d4a681486a..c5437f78b029 100644 --- a/sys/security/mac/mac_internal.h +++ b/sys/security/mac/mac_internal.h @@ -1,536 +1,535 @@ /*- * Copyright (c) 1999-2002, 2006, 2009, 2019 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. * Copyright (c) 2006 SPARTA, Inc. * Copyright (c) 2009 Apple, 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. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, 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$ */ #ifndef _SECURITY_MAC_MAC_INTERNAL_H_ #define _SECURITY_MAC_MAC_INTERNAL_H_ #ifndef _KERNEL #error "no user-serviceable parts inside" #endif #include #include /* * MAC Framework sysctl namespace. */ #ifdef SYSCTL_DECL SYSCTL_DECL(_security_mac); #endif /* SYSCTL_DECL */ /* * MAC Framework SDT DTrace probe namespace, macros for declaring entry * point probes, macros for invoking them. */ #ifdef SDT_PROVIDER_DECLARE SDT_PROVIDER_DECLARE(mac); /* MAC Framework-level events. */ SDT_PROVIDER_DECLARE(mac_framework); /* Entry points to MAC. */ #define MAC_CHECK_PROBE_DEFINE4(name, arg0, arg1, arg2, arg3) \ SDT_PROBE_DEFINE5(mac_framework, , name, mac__check__err, \ "int", arg0, arg1, arg2, arg3); \ SDT_PROBE_DEFINE5(mac_framework, , name, mac__check__ok, \ "int", arg0, arg1, arg2, arg3); #define MAC_CHECK_PROBE_DEFINE3(name, arg0, arg1, arg2) \ SDT_PROBE_DEFINE4(mac_framework, , name, mac__check__err, \ "int", arg0, arg1, arg2); \ SDT_PROBE_DEFINE4(mac_framework, , name, mac__check__ok, \ "int", arg0, arg1, arg2); #define MAC_CHECK_PROBE_DEFINE2(name, arg0, arg1) \ SDT_PROBE_DEFINE3(mac_framework, , name, mac__check__err, \ "int", arg0, arg1); \ SDT_PROBE_DEFINE3(mac_framework, , name, mac__check__ok, \ "int", arg0, arg1); #define MAC_CHECK_PROBE_DEFINE1(name, arg0) \ SDT_PROBE_DEFINE2(mac_framework, , name, mac__check__err, \ "int", arg0); \ SDT_PROBE_DEFINE2(mac_framework, , name, mac__check__ok, \ "int", arg0); #define MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, arg3) do { \ if (SDT_PROBES_ENABLED()) { \ if (error) { \ SDT_PROBE5(mac_framework, , name, mac__check__err,\ error, arg0, arg1, arg2, arg3); \ } else { \ SDT_PROBE5(mac_framework, , name, mac__check__ok,\ 0, arg0, arg1, arg2, arg3); \ } \ } \ } while (0) #define MAC_CHECK_PROBE3(name, error, arg0, arg1, arg2) \ MAC_CHECK_PROBE4(name, error, arg0, arg1, arg2, 0) #define MAC_CHECK_PROBE2(name, error, arg0, arg1) \ MAC_CHECK_PROBE3(name, error, arg0, arg1, 0) #define MAC_CHECK_PROBE1(name, error, arg0) \ MAC_CHECK_PROBE2(name, error, arg0, 0) #endif #define MAC_GRANT_PROBE_DEFINE2(name, arg0, arg1) \ SDT_PROBE_DEFINE3(mac_framework, , name, mac__grant__err, \ "int", arg0, arg1); \ SDT_PROBE_DEFINE3(mac_framework, , name, mac__grant__ok, \ "int", arg0, arg1); #define MAC_GRANT_PROBE2(name, error, arg0, arg1) do { \ if (SDT_PROBES_ENABLED()) { \ if (error) { \ SDT_PROBE3(mac_framework, , name, mac__grant__err,\ error, arg0, arg1); \ } else { \ SDT_PROBE3(mac_framework, , name, mac__grant__ok,\ error, arg0, arg1); \ } \ } \ } while (0) /* * MAC Framework global types and typedefs. */ LIST_HEAD(mac_policy_list_head, mac_policy_conf); #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_MACTEMP); #endif /* * MAC labels -- in-kernel storage format. * * In general, struct label pointers are embedded in kernel data structures * representing objects that may be labeled (and protected). Struct label is * opaque to both kernel services that invoke the MAC Framework and MAC * policy modules. In particular, we do not wish to encode the layout of the * label structure into any ABIs. Historically, the slot array contained * unions of {long, void} but now contains uintptr_t. */ #define MAC_MAX_SLOTS 4 #define MAC_FLAG_INITIALIZED 0x0000001 /* Is initialized for use. */ struct label { int l_flags; intptr_t l_perpolicy[MAC_MAX_SLOTS]; }; - /* * Flags for mac_labeled, a bitmask of object types need across the union of * all policies currently registered with the MAC Framework, used to key * whether or not labels are allocated and constructors for the type are * invoked. */ #define MPC_OBJECT_CRED 0x0000000000000001 #define MPC_OBJECT_PROC 0x0000000000000002 #define MPC_OBJECT_VNODE 0x0000000000000004 #define MPC_OBJECT_INPCB 0x0000000000000008 #define MPC_OBJECT_SOCKET 0x0000000000000010 #define MPC_OBJECT_DEVFS 0x0000000000000020 #define MPC_OBJECT_MBUF 0x0000000000000040 #define MPC_OBJECT_IPQ 0x0000000000000080 #define MPC_OBJECT_IFNET 0x0000000000000100 #define MPC_OBJECT_BPFDESC 0x0000000000000200 #define MPC_OBJECT_PIPE 0x0000000000000400 #define MPC_OBJECT_MOUNT 0x0000000000000800 #define MPC_OBJECT_POSIXSEM 0x0000000000001000 #define MPC_OBJECT_POSIXSHM 0x0000000000002000 #define MPC_OBJECT_SYSVMSG 0x0000000000004000 #define MPC_OBJECT_SYSVMSQ 0x0000000000008000 #define MPC_OBJECT_SYSVSEM 0x0000000000010000 #define MPC_OBJECT_SYSVSHM 0x0000000000020000 #define MPC_OBJECT_SYNCACHE 0x0000000000040000 #define MPC_OBJECT_IP6Q 0x0000000000080000 /* * MAC Framework global variables. */ extern struct mac_policy_list_head mac_policy_list; extern struct mac_policy_list_head mac_static_policy_list; extern u_int mac_policy_count; extern uint64_t mac_labeled; extern struct mtx mac_ifnet_mtx; /* * MAC Framework infrastructure functions. */ int mac_error_select(int error1, int error2); void mac_policy_slock_nosleep(struct rm_priotracker *tracker); void mac_policy_slock_sleep(void); void mac_policy_sunlock_nosleep(struct rm_priotracker *tracker); void mac_policy_sunlock_sleep(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); /* * Lock ifnets to protect labels only if ifnet labels are in use. */ #define MAC_IFNET_LOCK(ifp, locked) do { \ if (mac_labeled & MPC_OBJECT_IFNET) { \ mtx_lock(&mac_ifnet_mtx); \ locked = 1; \ } else { \ locked = 0; \ } \ } while (0) #define MAC_IFNET_UNLOCK(ifp, locked) do { \ if (locked) { \ mtx_unlock(&mac_ifnet_mtx); \ locked = 0; \ } \ } while (0) /* * 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_cred_label_alloc(void); void mac_cred_label_free(struct label *label); 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); struct label *mac_vnode_label_alloc(void); void mac_vnode_label_free(struct label *label); int mac_cred_check_relabel(struct ucred *cred, struct label *newlabel); int mac_cred_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen); int mac_cred_internalize_label(struct label *label, char *string); void mac_cred_relabel(struct ucred *cred, struct label *newlabel); struct label *mac_mbuf_to_label(struct mbuf *m); void mac_pipe_copy_label(struct label *src, struct label *dest); int mac_pipe_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen); int mac_pipe_internalize_label(struct label *label, char *string); int mac_socket_label_set(struct ucred *cred, struct socket *so, struct label *label); void mac_socket_copy_label(struct label *src, struct label *dest); int mac_socket_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen); int mac_socket_internalize_label(struct label *label, char *string); int mac_vnode_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen); int mac_vnode_internalize_label(struct label *label, char *string); void mac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, int *prot); int vn_setlabel(struct vnode *vp, struct label *intlabel, struct ucred *cred); /* * MAC Framework composition macros invoke all registered MAC policies for a * specific entry point. They come in two forms: one which permits policies * to sleep/block, and another that does not. * * MAC_POLICY_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_POLICY_CHECK(check, args...) do { \ struct mac_policy_conf *mpc; \ \ 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 (!LIST_EMPTY(&mac_policy_list)) { \ mac_policy_slock_sleep(); \ 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_sunlock_sleep(); \ } \ } while (0) #define MAC_POLICY_CHECK_NOSLEEP(check, args...) do { \ struct mac_policy_conf *mpc; \ \ 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 (!LIST_EMPTY(&mac_policy_list)) { \ struct rm_priotracker tracker; \ \ mac_policy_slock_nosleep(&tracker); \ 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_sunlock_nosleep(&tracker); \ } \ } while (0) /* * MAC_POLICY_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_POLICY_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_POLICY_GRANT_NOSLEEP(check, args...) do { \ struct mac_policy_conf *mpc; \ \ 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 (!LIST_EMPTY(&mac_policy_list)) { \ struct rm_priotracker tracker; \ \ mac_policy_slock_nosleep(&tracker); \ 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_sunlock_nosleep(&tracker); \ } \ } while (0) /* * MAC_POLICY_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_POLICY_BOOLEAN(operation, composition, args...) do { \ struct mac_policy_conf *mpc; \ \ 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 (!LIST_EMPTY(&mac_policy_list)) { \ mac_policy_slock_sleep(); \ 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_sunlock_sleep(); \ } \ } while (0) #define MAC_POLICY_BOOLEAN_NOSLEEP(operation, composition, args...) do {\ struct mac_policy_conf *mpc; \ \ 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 (!LIST_EMPTY(&mac_policy_list)) { \ struct rm_priotracker tracker; \ \ mac_policy_slock_nosleep(&tracker); \ 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_sunlock_nosleep(&tracker); \ } \ } while (0) /* * MAC_POLICY_EXTERNALIZE queries each policy to see if it can generate an * externalized version of a label element by name. Policies declare whether * they have matched a particular element name, parsed from the string by * MAC_POLICY_EXTERNALIZE, and an error is returned if any element is matched * by no policy. */ #define MAC_POLICY_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_POLICY_CHECK(type ## _externalize_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) /* * MAC_POLICY_INTERNALIZE presents parsed element names and data to each * policy to see if any is willing to claim it and internalize the label * data. If no policies match, an error is returned. */ #define MAC_POLICY_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_POLICY_CHECK(type ## _internalize_label, label, \ element_name, element_data, &claimed); \ if (error) \ break; \ if (claimed != 1) { \ /* XXXMAC: Another error here? */ \ error = EINVAL; \ break; \ } \ } \ } while (0) /* * MAC_POLICY_PERFORM performs the designated operation by walking the policy * module list and invoking that operation for each policy. */ #define MAC_POLICY_PERFORM(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ if (!LIST_EMPTY(&mac_policy_list)) { \ mac_policy_slock_sleep(); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ mac_policy_sunlock_sleep(); \ } \ } while (0) #define MAC_POLICY_PERFORM_NOSLEEP(operation, args...) do { \ struct mac_policy_conf *mpc; \ \ LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ if (!LIST_EMPTY(&mac_policy_list)) { \ struct rm_priotracker tracker; \ \ mac_policy_slock_nosleep(&tracker); \ LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { \ if (mpc->mpc_ops->mpo_ ## operation != NULL) \ mpc->mpc_ops->mpo_ ## operation (args); \ } \ mac_policy_sunlock_nosleep(&tracker); \ } \ } while (0) #endif /* !_SECURITY_MAC_MAC_INTERNAL_H_ */ diff --git a/sys/security/mac/mac_label.c b/sys/security/mac/mac_label.c index c05865316373..86796f25cd04 100644 --- a/sys/security/mac/mac_label.c +++ b/sys/security/mac/mac_label.c @@ -1,151 +1,150 @@ /*- * Copyright (c) 2003-2004 Networks Associates Technology, Inc. * Copyright (c) 2007 Robert N. M. Watson * All rights reserved. * * 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. * * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include /* * zone_label is the UMA zone from which most labels are allocated. Label * structures are initialized to zero bytes so that policies see a NULL/0 * slot on first use, even if the policy is loaded after the label is * allocated for an object. */ static uma_zone_t zone_label; static int mac_labelzone_ctor(void *mem, int size, void *arg, int flags); static void mac_labelzone_dtor(void *mem, int size, void *arg); void mac_labelzone_init(void) { zone_label = uma_zcreate("MAC labels", sizeof(struct label), mac_labelzone_ctor, mac_labelzone_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * mac_init_label() and mac_destroy_label() are exported so that they can be * used in mbuf tag initialization, where labels are not slab allocated from * the zone_label zone. */ void mac_init_label(struct label *label) { bzero(label, sizeof(*label)); label->l_flags = MAC_FLAG_INITIALIZED; } void mac_destroy_label(struct label *label) { KASSERT(label->l_flags & MAC_FLAG_INITIALIZED, ("destroying uninitialized label")); #ifdef DIAGNOSTIC bzero(label, sizeof(*label)); #else label->l_flags &= ~MAC_FLAG_INITIALIZED; #endif } - static int mac_labelzone_ctor(void *mem, int size, void *arg, int flags) { struct label *label; KASSERT(size == sizeof(*label), ("mac_labelzone_ctor: wrong size\n")); label = mem; mac_init_label(label); return (0); } static void mac_labelzone_dtor(void *mem, int size, void *arg) { struct label *label; KASSERT(size == sizeof(*label), ("mac_labelzone_dtor: wrong size\n")); label = mem; mac_destroy_label(label); } struct label * mac_labelzone_alloc(int flags) { return (uma_zalloc(zone_label, flags)); } void mac_labelzone_free(struct label *label) { uma_zfree(zone_label, label); } /* * Functions used by policy modules to get and set label values. */ intptr_t mac_label_get(struct label *l, int slot) { KASSERT(l != NULL, ("mac_label_get: NULL label")); return (l->l_perpolicy[slot]); } void mac_label_set(struct label *l, int slot, intptr_t v) { KASSERT(l != NULL, ("mac_label_set: NULL label")); l->l_perpolicy[slot] = v; } diff --git a/sys/security/mac/mac_socket.c b/sys/security/mac/mac_socket.c index 95cfc6b06023..d55487897522 100644 --- a/sys/security/mac/mac_socket.c +++ b/sys/security/mac/mac_socket.c @@ -1,633 +1,633 @@ /*- * Copyright (c) 1999-2002, 2009 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * Copyright (c) 2005-2006 SPARTA, Inc. * Copyright (c) 2008 Apple 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 McAfee * Research, the Technology 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 enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Currently, sockets hold two labels: the label of the socket itself, and a * peer label, which may be used by policies to hold a copy of the label of * any remote endpoint. * * Possibly, this peer label should be maintained at the protocol layer * (inpcb, unpcb, etc), as this would allow protocol-aware code to maintain * the label consistently. For example, it might be copied live from a * remote socket for UNIX domain sockets rather than keeping a local copy on * this endpoint, but be cached and updated based on packets received for * TCP/IP. * * Unlike with many other object types, the lock protecting MAC labels on * sockets (the socket lock) is not frequently held at the points in code * where socket-related checks are called. The MAC Framework acquires the * lock over some entry points in order to enforce atomicity (such as label * copies) but in other cases the policy modules will have to acquire the * lock themselves if they use labels. This approach (a) avoids lock * acquisitions when policies don't require labels and (b) solves a number of * potential lock order issues when multiple sockets are used in the same * entry point. */ struct label * mac_socket_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); if (flag & M_WAITOK) MAC_POLICY_CHECK(socket_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(socket_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(socket_destroy_label, label); mac_labelzone_free(label); return (NULL); } return (label); } static struct label * mac_socketpeer_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); if (flag & M_WAITOK) MAC_POLICY_CHECK(socketpeer_init_label, label, flag); else MAC_POLICY_CHECK_NOSLEEP(socketpeer_init_label, label, flag); if (error) { MAC_POLICY_PERFORM_NOSLEEP(socketpeer_destroy_label, label); mac_labelzone_free(label); return (NULL); } return (label); } int mac_socket_init(struct socket *so, int flag) { if (mac_labeled & MPC_OBJECT_SOCKET) { so->so_label = mac_socket_label_alloc(flag); if (so->so_label == NULL) return (ENOMEM); so->so_peerlabel = mac_socketpeer_label_alloc(flag); if (so->so_peerlabel == NULL) { mac_socket_label_free(so->so_label); so->so_label = NULL; return (ENOMEM); } } else { so->so_label = NULL; so->so_peerlabel = NULL; } return (0); } void mac_socket_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(socket_destroy_label, label); mac_labelzone_free(label); } static void mac_socketpeer_label_free(struct label *label) { MAC_POLICY_PERFORM_NOSLEEP(socketpeer_destroy_label, label); mac_labelzone_free(label); } void mac_socket_destroy(struct socket *so) { if (so->so_label != NULL) { mac_socket_label_free(so->so_label); so->so_label = NULL; mac_socketpeer_label_free(so->so_peerlabel); so->so_peerlabel = NULL; } } void mac_socket_copy_label(struct label *src, struct label *dest) { MAC_POLICY_PERFORM_NOSLEEP(socket_copy_label, src, dest); } int mac_socket_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen) { int error; MAC_POLICY_EXTERNALIZE(socket, label, elements, outbuf, outbuflen); return (error); } static int mac_socketpeer_externalize_label(struct label *label, char *elements, char *outbuf, size_t outbuflen) { int error; MAC_POLICY_EXTERNALIZE(socketpeer, label, elements, outbuf, outbuflen); return (error); } int mac_socket_internalize_label(struct label *label, char *string) { int error; MAC_POLICY_INTERNALIZE(socket, label, string); return (error); } void mac_socket_create(struct ucred *cred, struct socket *so) { MAC_POLICY_PERFORM_NOSLEEP(socket_create, cred, so, so->so_label); } void mac_socket_newconn(struct socket *oldso, struct socket *newso) { MAC_POLICY_PERFORM_NOSLEEP(socket_newconn, oldso, oldso->so_label, newso, newso->so_label); } static void mac_socket_relabel(struct ucred *cred, struct socket *so, struct label *newlabel) { SOCK_LOCK_ASSERT(so); MAC_POLICY_PERFORM_NOSLEEP(socket_relabel, cred, so, so->so_label, newlabel); } void mac_socketpeer_set_from_mbuf(struct mbuf *m, struct socket *so) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(socketpeer_set_from_mbuf, m, label, so, so->so_peerlabel); } void mac_socketpeer_set_from_socket(struct socket *oldso, struct socket *newso) { - + if (mac_policy_count == 0) return; MAC_POLICY_PERFORM_NOSLEEP(socketpeer_set_from_socket, oldso, oldso->so_label, newso, newso->so_peerlabel); } void mac_socket_create_mbuf(struct socket *so, struct mbuf *m) { struct label *label; if (mac_policy_count == 0) return; label = mac_mbuf_to_label(m); MAC_POLICY_PERFORM_NOSLEEP(socket_create_mbuf, so, so->so_label, m, label); } MAC_CHECK_PROBE_DEFINE2(socket_check_accept, "struct ucred *", "struct socket *"); int mac_socket_check_accept(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_accept, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_accept, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE3(socket_check_bind, "struct ucred *", "struct socket *", "struct sockaddr *"); int mac_socket_check_bind(struct ucred *cred, struct socket *so, struct sockaddr *sa) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_bind, cred, so, so->so_label, sa); MAC_CHECK_PROBE3(socket_check_bind, error, cred, so, sa); return (error); } MAC_CHECK_PROBE_DEFINE3(socket_check_connect, "struct ucred *", "struct socket *", "struct sockaddr *"); int mac_socket_check_connect(struct ucred *cred, struct socket *so, struct sockaddr *sa) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_connect, cred, so, so->so_label, sa); MAC_CHECK_PROBE3(socket_check_connect, error, cred, so, sa); return (error); } MAC_CHECK_PROBE_DEFINE4(socket_check_create, "struct ucred *", "int", "int", "int"); int mac_socket_check_create(struct ucred *cred, int domain, int type, int proto) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_create, cred, domain, type, proto); MAC_CHECK_PROBE4(socket_check_create, error, cred, domain, type, proto); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_deliver, "struct socket *", "struct mbuf *"); int mac_socket_check_deliver(struct socket *so, struct mbuf *m) { struct label *label; int error; if (mac_policy_count == 0) return (0); label = mac_mbuf_to_label(m); MAC_POLICY_CHECK_NOSLEEP(socket_check_deliver, so, so->so_label, m, label); MAC_CHECK_PROBE2(socket_check_deliver, error, so, m); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_listen, "struct ucred *", "struct socket *"); int mac_socket_check_listen(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_listen, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_listen, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_poll, "struct ucred *", "struct socket *"); int mac_socket_check_poll(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_poll, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_poll, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_receive, "struct ucred *", "struct socket *"); int mac_socket_check_receive(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_receive, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_receive, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE3(socket_check_relabel, "struct ucred *", "struct socket *", "struct label *"); static int mac_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *newlabel) { int error; SOCK_LOCK_ASSERT(so); MAC_POLICY_CHECK_NOSLEEP(socket_check_relabel, cred, so, so->so_label, newlabel); MAC_CHECK_PROBE3(socket_check_relabel, error, cred, so, newlabel); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_send, "struct ucred *", "struct socket *"); int mac_socket_check_send(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_send, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_send, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_stat, "struct ucred *", "struct socket *"); int mac_socket_check_stat(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_stat, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_stat, error, cred, so); return (error); } MAC_CHECK_PROBE_DEFINE2(socket_check_visible, "struct ucred *", "struct socket *"); int mac_socket_check_visible(struct ucred *cred, struct socket *so) { int error; MAC_POLICY_CHECK_NOSLEEP(socket_check_visible, cred, so, so->so_label); MAC_CHECK_PROBE2(socket_check_visible, error, cred, so); return (error); } int mac_socket_label_set(struct ucred *cred, struct socket *so, struct label *label) { int error; /* * We acquire the socket lock when we perform the test and set, but * have to release it as the pcb code needs to acquire the pcb lock, * which will precede the socket lock in the lock order. However, * this is fine, as any race will simply result in the inpcb being * refreshed twice, but still consistently, as the inpcb code will * acquire the socket lock before refreshing, holding both locks. */ SOCK_LOCK(so); error = mac_socket_check_relabel(cred, so, label); if (error) { SOCK_UNLOCK(so); return (error); } mac_socket_relabel(cred, so, label); SOCK_UNLOCK(so); /* * If the protocol has expressed interest in socket layer changes, * such as if it needs to propagate changes to a cached pcb label * from the socket, notify it of the label change while holding the * socket lock. */ if (so->so_proto->pr_usrreqs->pru_sosetlabel != NULL) (so->so_proto->pr_usrreqs->pru_sosetlabel)(so); return (0); } int mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac) { struct label *intlabel; char *buffer; int error; if (!(mac_labeled & MPC_OBJECT_SOCKET)) return (EINVAL); error = mac_check_structmac_consistent(mac); if (error) return (error); buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL); if (error) { free(buffer, M_MACTEMP); return (error); } intlabel = mac_socket_label_alloc(M_WAITOK); error = mac_socket_internalize_label(intlabel, buffer); free(buffer, M_MACTEMP); if (error) goto out; error = mac_socket_label_set(cred, so, intlabel); out: mac_socket_label_free(intlabel); return (error); } int mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *mac) { char *buffer, *elements; struct label *intlabel; int error; if (!(mac_labeled & MPC_OBJECT_SOCKET)) return (EINVAL); error = mac_check_structmac_consistent(mac); if (error) return (error); elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); intlabel = mac_socket_label_alloc(M_WAITOK); SOCK_LOCK(so); mac_socket_copy_label(so->so_label, intlabel); SOCK_UNLOCK(so); error = mac_socket_externalize_label(intlabel, elements, buffer, mac->m_buflen); mac_socket_label_free(intlabel); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } int mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so, struct mac *mac) { char *elements, *buffer; struct label *intlabel; int error; if (!(mac_labeled & MPC_OBJECT_SOCKET)) return (EINVAL); error = mac_check_structmac_consistent(mac); if (error) return (error); elements = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac->m_string, elements, mac->m_buflen, NULL); if (error) { free(elements, M_MACTEMP); return (error); } buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); intlabel = mac_socket_label_alloc(M_WAITOK); SOCK_LOCK(so); mac_socket_copy_label(so->so_peerlabel, intlabel); SOCK_UNLOCK(so); error = mac_socketpeer_externalize_label(intlabel, elements, buffer, mac->m_buflen); mac_socket_label_free(intlabel); if (error == 0) error = copyout(buffer, mac->m_string, strlen(buffer)+1); free(buffer, M_MACTEMP); free(elements, M_MACTEMP); return (error); } diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c index 84e38fcc5218..296d4a4e25b8 100644 --- a/sys/security/mac_biba/mac_biba.c +++ b/sys/security/mac_biba/mac_biba.c @@ -1,3798 +1,3798 @@ /*- * Copyright (c) 1999-2002, 2007-2011 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, 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$ */ /* * Developed by the TrustedBSD Project. * * Biba fixed label mandatory integrity policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, biba, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_biba policy controls"); static int biba_label_size = sizeof(struct mac_biba); SYSCTL_INT(_security_mac_biba, OID_AUTO, label_size, CTLFLAG_RD, &biba_label_size, 0, "Size of struct mac_biba"); static int biba_enabled = 1; SYSCTL_INT(_security_mac_biba, OID_AUTO, enabled, CTLFLAG_RWTUN, &biba_enabled, 0, "Enforce MAC/Biba policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_biba, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, trust_all_interfaces, CTLFLAG_RDTUN, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/Biba"); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_biba, OID_AUTO, trusted_interfaces, CTLFLAG_RDTUN, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/Biba"); static int max_compartments = MAC_BIBA_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_biba, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum supported compartments"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as biba/equal on create"); static int interfaces_equal = 1; SYSCTL_INT(_security_mac_biba, OID_AUTO, interfaces_equal, CTLFLAG_RWTUN, &interfaces_equal, 0, "Label network interfaces as biba/equal on create"); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int biba_slot; #define SLOT(l) ((struct mac_biba *)mac_label_get((l), biba_slot)) #define SLOT_SET(l, val) mac_label_set((l), biba_slot, (uintptr_t)(val)) static uma_zone_t zone_biba; static __inline int biba_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_BIBA_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_biba * biba_alloc(int flag) { return (uma_zalloc(zone_biba, flag | M_ZERO)); } static void biba_free(struct mac_biba *mb) { if (mb != NULL) uma_zfree(zone_biba, mb); else atomic_add_int(&destroyed_not_inited, 1); } static int biba_atmostflags(struct mac_biba *mb, int flags) { if ((mb->mb_flags & flags) != mb->mb_flags) return (EINVAL); return (0); } static int biba_dominate_element(struct mac_biba_element *a, struct mac_biba_element *b) { int bit; switch (a->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: return (1); case MAC_BIBA_TYPE_LOW: switch (b->mbe_type) { case MAC_BIBA_TYPE_GRADE: case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); default: panic("biba_dominate_element: b->mbe_type invalid"); } case MAC_BIBA_TYPE_GRADE: switch (b->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_GRADE: for (bit = 1; bit <= MAC_BIBA_MAX_COMPARTMENTS; bit++) if (!MAC_BIBA_BIT_TEST(bit, a->mbe_compartments) && MAC_BIBA_BIT_TEST(bit, b->mbe_compartments)) return (0); return (a->mbe_grade >= b->mbe_grade); default: panic("biba_dominate_element: b->mbe_type invalid"); } default: panic("biba_dominate_element: a->mbe_type invalid"); } return (0); } static int biba_subject_dominate_high(struct mac_biba *mb) { struct mac_biba_element *element; KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_effective_in_range: mb not effective")); element = &mb->mb_effective; return (element->mbe_type == MAC_BIBA_TYPE_EQUAL || element->mbe_type == MAC_BIBA_TYPE_HIGH); } static int biba_range_in_range(struct mac_biba *rangea, struct mac_biba *rangeb) { return (biba_dominate_element(&rangeb->mb_rangehigh, &rangea->mb_rangehigh) && biba_dominate_element(&rangea->mb_rangelow, &rangeb->mb_rangelow)); } static int biba_effective_in_range(struct mac_biba *effective, struct mac_biba *range) { KASSERT((effective->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_effective_in_range: a not effective")); KASSERT((range->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("biba_effective_in_range: b not range")); return (biba_dominate_element(&range->mb_rangehigh, &effective->mb_effective) && biba_dominate_element(&effective->mb_effective, &range->mb_rangelow)); return (1); } static int biba_dominate_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_dominate_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_dominate_effective: b not effective")); return (biba_dominate_element(&a->mb_effective, &b->mb_effective)); } static int biba_equal_element(struct mac_biba_element *a, struct mac_biba_element *b) { if (a->mbe_type == MAC_BIBA_TYPE_EQUAL || b->mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); return (a->mbe_type == b->mbe_type && a->mbe_grade == b->mbe_grade); } static int biba_equal_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: b not effective")); return (biba_equal_element(&a->mb_effective, &b->mb_effective)); } static int biba_contains_equal(struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); if (mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); } return (0); } static int biba_subject_privileged(struct mac_biba *mb) { KASSERT((mb->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH, ("biba_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL || mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mb->mb_rangelow.mbe_type == MAC_BIBA_TYPE_LOW && mb->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int biba_high_effective(struct mac_biba *mb) { KASSERT((mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_equal_effective: mb not effective")); return (mb->mb_effective.mbe_type == MAC_BIBA_TYPE_HIGH); } static int biba_valid(struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { switch (mb->mb_effective.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_effective.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_effective.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mb->mb_effective.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { switch (mb->mb_rangelow.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_rangelow.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_rangelow.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mb->mb_rangehigh.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mb->mb_rangehigh.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mb->mb_rangehigh.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!biba_dominate_element(&mb->mb_rangehigh, &mb->mb_rangelow)) return (EINVAL); } else { if (mb->mb_rangelow.mbe_type != MAC_BIBA_TYPE_UNDEF || mb->mb_rangehigh.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } return (0); } static void biba_set_range(struct mac_biba *mb, u_short typelow, u_short gradelow, u_char *compartmentslow, u_short typehigh, u_short gradehigh, u_char *compartmentshigh) { mb->mb_rangelow.mbe_type = typelow; mb->mb_rangelow.mbe_grade = gradelow; if (compartmentslow != NULL) memcpy(mb->mb_rangelow.mbe_compartments, compartmentslow, sizeof(mb->mb_rangelow.mbe_compartments)); mb->mb_rangehigh.mbe_type = typehigh; mb->mb_rangehigh.mbe_grade = gradehigh; if (compartmentshigh != NULL) memcpy(mb->mb_rangehigh.mbe_compartments, compartmentshigh, sizeof(mb->mb_rangehigh.mbe_compartments)); mb->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void biba_set_effective(struct mac_biba *mb, u_short type, u_short grade, u_char *compartments) { mb->mb_effective.mbe_type = type; mb->mb_effective.mbe_grade = grade; if (compartments != NULL) memcpy(mb->mb_effective.mbe_compartments, compartments, sizeof(mb->mb_effective.mbe_compartments)); mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void biba_copy_range(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("biba_copy_range: labelfrom not range")); labelto->mb_rangelow = labelfrom->mb_rangelow; labelto->mb_rangehigh = labelfrom->mb_rangehigh; labelto->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void biba_copy_effective(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("biba_copy_effective: labelfrom not effective")); labelto->mb_effective = labelfrom->mb_effective; labelto->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void biba_copy(struct mac_biba *source, struct mac_biba *dest) { if (source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) biba_copy_effective(source, dest); if (source->mb_flags & MAC_BIBA_FLAG_RANGE) biba_copy_range(source, dest); } /* * Policy module operations. */ static void biba_init(struct mac_policy_conf *conf) { zone_biba = uma_zcreate("mac_biba", sizeof(struct mac_biba), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void biba_init_label(struct label *label) { SLOT_SET(label, biba_alloc(M_WAITOK)); } static int biba_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, biba_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void biba_destroy_label(struct label *label) { biba_free(SLOT(label)); SLOT_SET(label, NULL); } /* * biba_element_to_string() accepts an sbuf and Biba element. It converts * the Biba element to a string and stores the result in the sbuf; if there * isn't space in the sbuf, -1 is returned. */ static int biba_element_to_string(struct sbuf *sb, struct mac_biba_element *element) { int i, first; switch (element->mbe_type) { case MAC_BIBA_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_BIBA_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_BIBA_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_BIBA_TYPE_GRADE: if (sbuf_printf(sb, "%d", element->mbe_grade) == -1) return (-1); first = 1; for (i = 1; i <= MAC_BIBA_MAX_COMPARTMENTS; i++) { if (MAC_BIBA_BIT_TEST(i, element->mbe_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("biba_element_to_string: invalid type (%d)", element->mbe_type); } } /* * biba_to_string() converts a Biba label to a string, and places the results * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't * room in the sbuf. Note: the sbuf will be modified even in a failure case, * so the caller may need to revert the sbuf by restoring the offset if * that's undesired. */ static int biba_to_string(struct sbuf *sb, struct mac_biba *mb) { if (mb->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { if (biba_element_to_string(sb, &mb->mb_effective) == -1) return (EINVAL); } if (mb->mb_flags & MAC_BIBA_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (biba_element_to_string(sb, &mb->mb_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (biba_element_to_string(sb, &mb->mb_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int biba_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_biba *mb; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mb = SLOT(label); return (biba_to_string(sb, mb)); } static int biba_parse_element(struct mac_biba_element *element, char *string) { char *compartment, *end, *grade; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mbe_type = MAC_BIBA_TYPE_HIGH; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mbe_type = MAC_BIBA_TYPE_LOW; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mbe_type = MAC_BIBA_TYPE_EQUAL; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else { element->mbe_type = MAC_BIBA_TYPE_GRADE; /* * Numeric grade piece of the element. */ grade = strsep(&string, ":"); value = strtol(grade, &end, 10); if (end == grade || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mbe_grade = value; /* * Optional compartment piece of the element. If none are * included, we assume that the label has no compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_BIBA_MAX_COMPARTMENTS) return (EINVAL); MAC_BIBA_BIT_SET(value, element->mbe_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int biba_parse(struct mac_biba *mb, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("biba_parse: range mismatch")); bzero(mb, sizeof(*mb)); if (effective != NULL) { error = biba_parse_element(&mb->mb_effective, effective); if (error) return (error); mb->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = biba_parse_element(&mb->mb_rangelow, rangelow); if (error) return (error); error = biba_parse_element(&mb->mb_rangehigh, rangehigh); if (error) return (error); mb->mb_flags |= MAC_BIBA_FLAG_RANGE; } error = biba_valid(mb); if (error) return (error); return (0); } static int biba_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_biba *mb, mb_temp; int error; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = biba_parse(&mb_temp, element_data); if (error) return (error); mb = SLOT(label); *mb = mb_temp; return (0); } static void biba_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int biba_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_biba *a, *b; if (!biba_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (biba_equal_effective(a, b)) return (0); return (EACCES); } static void biba_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); biba_copy_effective(source, dest); } static void biba_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_cred_associate_nfsd(struct ucred *cred) { struct mac_biba *label; label = SLOT(cred->cr_label); biba_set_effective(label, MAC_BIBA_TYPE_LOW, 0, NULL); biba_set_range(label, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static int biba_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the credential, it may * be an update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { /* * If the change request modifies both the Biba label * effective and range, check that the new effective will be * in the new range. */ if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH && !biba_effective_in_range(new, new)) return (EINVAL); /* * To change the Biba effective label on a credential, the * new effective label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE && !biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba range on a credential, the new range * label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_RANGE && !biba_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential Biba * label, the subject must already have EQUAL in their label. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_cred_check_visible(struct ucred *u1, struct ucred *u2) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(u1->cr_label); obj = SLOT(u2->cr_label); /* XXX: range */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); return (0); } static void biba_cred_create_init(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); biba_set_effective(dest, MAC_BIBA_TYPE_HIGH, 0, NULL); biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_cred_create_swapper(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); biba_copy(source, dest); } static void biba_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *mb; const char *dn; int biba_type; mb = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0) biba_type = MAC_BIBA_TYPE_EQUAL; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) biba_type = MAC_BIBA_TYPE_EQUAL; else biba_type = MAC_BIBA_TYPE_HIGH; biba_set_effective(mb, biba_type, 0, NULL); } static void biba_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *mb; mb = SLOT(delabel); biba_set_effective(mb, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void biba_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); biba_copy_effective(source, dest); } static void biba_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); biba_copy(source, dest); } static void biba_devfs_vnode_associate(struct mount *mp, struct label *mntlabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); biba_copy_effective(source, dest); } static int biba_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the interface, it may be an * update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires Biba privilege. */ error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *i; if (!biba_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (biba_effective_in_range(p, i) ? 0 : EACCES); } static void biba_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_biba *dest; int len, type; dest = SLOT(ifplabel); if (ifp->if_type == IFT_LOOP || interfaces_equal != 0) { type = MAC_BIBA_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { type = MAC_BIBA_TYPE_HIGH; goto set; } type = MAC_BIBA_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); if (strcmp(tifname, ifp->if_xname) == 0) { type = MAC_BIBA_TYPE_HIGH; break; } } else { *p = '\0'; printf("mac_biba warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: biba_set_effective(dest, type, 0, NULL); biba_set_range(dest, type, 0, NULL, type, 0, NULL); } static void biba_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); biba_copy(source, dest); } static int biba_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *i; if (!biba_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (biba_equal_effective(p, i) ? 0 : EACCES); } static int biba_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!biba_dominate_effective(obj, subj)) return (ENOENT); return (0); } static void biba_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static void biba_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); biba_copy(source, dest); } static void biba_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); biba_copy_effective(source, dest); } static int biba_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_biba *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (biba_equal_effective(a, b)); } static void biba_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ biba_copy_effective(source, dest); } static void biba_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void biba_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); biba_copy_effective(source, dest); } static int biba_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_biba *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (biba_equal_effective(a, b)); } static void biba_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ biba_copy_effective(source, dest); } static void biba_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int biba_kld_check_load(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_mount_check_stat(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); biba_copy_effective(source, dest); } static void biba_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_biba *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); biba_copy_effective(source, dest); } static void biba_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); biba_copy_effective(source, dest); } static void biba_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_biba *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); biba_copy_effective(source, dest); } static void biba_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void biba_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *dest; dest = SLOT(mlabel); biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static int biba_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if(!biba_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int biba_pipe_check_poll(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is a Biba label update for a pipe, it must be a effective * update. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (Biba label or not), Biba must * authorize the relabel. */ if (!biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a pipe, the new pipe label * must be in the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_pipe_check_stat(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static void biba_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); biba_copy_effective(source, dest); } static void biba_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); biba_copy(source, dest); } static int biba_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, struct label *kslabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, struct label *kslabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_posixsem_create(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(kslabel); biba_copy_effective(source, dest); } static int biba_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, int prot, int flags) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *vp, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *vp, struct label *shmlabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); biba_copy_effective(source, dest); } /* * Some system privileges are allowed regardless of integrity grade; others * are allowed only when running with privilege with respect to the Biba * policy as they might otherwise allow bypassing of the integrity policy. */ static int biba_priv_check(struct ucred *cred, int priv) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); /* * Exempt only specific privileges from the Biba integrity policy. */ switch (priv) { case PRIV_KTRACE: case PRIV_MSGBUF: /* * Allow processes to manipulate basic process audit properties, and * to submit audit records. */ case PRIV_AUDIT_GETAUDIT: case PRIV_AUDIT_SETAUDIT: case PRIV_AUDIT_SUBMIT: /* * Allow processes to manipulate their regular UNIX credentials. */ case PRIV_CRED_SETUID: case PRIV_CRED_SETEUID: case PRIV_CRED_SETGID: case PRIV_CRED_SETEGID: case PRIV_CRED_SETGROUPS: case PRIV_CRED_SETREUID: case PRIV_CRED_SETREGID: case PRIV_CRED_SETRESUID: case PRIV_CRED_SETRESGID: /* * Allow processes to perform system monitoring. */ case PRIV_SEEOTHERGIDS: case PRIV_SEEOTHERUIDS: break; /* * Allow access to general process debugging facilities. We * separately control debugging based on MAC label. */ case PRIV_DEBUG_DIFFCRED: case PRIV_DEBUG_SUGID: case PRIV_DEBUG_UNPRIV: /* * Allow manipulating jails. */ case PRIV_JAIL_ATTACH: /* * Allow privilege with respect to the Partition policy, but not the * Privs policy. */ case PRIV_MAC_PARTITION: /* * Allow privilege with respect to process resource limits and login * context. */ case PRIV_PROC_LIMIT: case PRIV_PROC_SETLOGIN: case PRIV_PROC_SETRLIMIT: /* * Allow System V and POSIX IPC privileges. */ case PRIV_IPC_READ: case PRIV_IPC_WRITE: case PRIV_IPC_ADMIN: case PRIV_IPC_MSGSIZE: case PRIV_MQ_ADMIN: /* * Allow certain scheduler manipulations -- possibly this should be * controlled by more fine-grained policy, as potentially low * integrity processes can deny CPU to higher integrity ones. */ case PRIV_SCHED_DIFFCRED: case PRIV_SCHED_SETPRIORITY: case PRIV_SCHED_RTPRIO: case PRIV_SCHED_SETPOLICY: case PRIV_SCHED_SET: case PRIV_SCHED_SETPARAM: /* * More IPC privileges. */ case PRIV_SEM_WRITE: /* * Allow signaling privileges subject to integrity policy. */ case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: /* * Allow access to only limited sysctls from lower integrity levels; * piggy-back on the Jail definition. */ case PRIV_SYSCTL_WRITEJAIL: /* * Allow TTY-based privileges, subject to general device access using * labels on TTY device nodes, but not console privilege. */ case PRIV_TTY_DRAINWAIT: case PRIV_TTY_DTRWAIT: case PRIV_TTY_EXCLUSIVE: case PRIV_TTY_STI: case PRIV_TTY_SETA: /* * Grant most VFS privileges, as almost all are in practice bounded * by more specific checks using labels. */ case PRIV_VFS_READ: case PRIV_VFS_WRITE: case PRIV_VFS_ADMIN: case PRIV_VFS_EXEC: case PRIV_VFS_LOOKUP: case PRIV_VFS_CHFLAGS_DEV: case PRIV_VFS_CHOWN: case PRIV_VFS_CHROOT: case PRIV_VFS_RETAINSUGID: case PRIV_VFS_EXCEEDQUOTA: case PRIV_VFS_FCHROOT: case PRIV_VFS_FHOPEN: case PRIV_VFS_FHSTATFS: case PRIV_VFS_GENERATION: case PRIV_VFS_GETFH: case PRIV_VFS_GETQUOTA: case PRIV_VFS_LINK: case PRIV_VFS_MOUNT: case PRIV_VFS_MOUNT_OWNER: case PRIV_VFS_MOUNT_PERM: case PRIV_VFS_MOUNT_SUIDDIR: case PRIV_VFS_MOUNT_NONUSER: case PRIV_VFS_SETGID: case PRIV_VFS_STICKYFILE: case PRIV_VFS_SYSFLAGS: case PRIV_VFS_UNMOUNT: /* * Allow VM privileges; it would be nice if these were subject to * resource limits. */ case PRIV_VM_MADV_PROTECT: case PRIV_VM_MLOCK: case PRIV_VM_MUNLOCK: case PRIV_VM_SWAP_NOQUOTA: case PRIV_VM_SWAP_NORLIMIT: /* * Allow some but not all network privileges. In general, dont allow * reconfiguring the network stack, just normal use. */ case PRIV_NETINET_RESERVEDPORT: case PRIV_NETINET_RAW: case PRIV_NETINET_REUSEPORT: break; /* * All remaining system privileges are allow only if the process * holds privilege with respect to the Biba policy. */ default: subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); } return (0); } static int biba_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!biba_dominate_effective(obj, subj)) return (ESRCH); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *s; int error; if (!biba_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = biba_equal_effective(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int biba_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is a Biba label update for the socket, it may be an * update of effective. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the * subject range. */ if (!biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!biba_dominate_effective(obj, subj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void biba_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); biba_copy_effective(source, dest); } static void biba_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_biba source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); biba_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void biba_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_biba *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); biba_copy(source, dest); } static void biba_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_biba *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); biba_copy_effective(source, dest); SOCK_UNLOCK(so); } static void biba_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_biba source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); biba_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void biba_syncache_create(struct label *label, struct inpcb *inp) { struct mac_biba *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); biba_copy_effective(source, dest); } static void biba_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); biba_copy_effective(source, dest); } static int biba_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); if (vplabel == NULL) return (0); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); if (vplabel == NULL) return (0); obj = SLOT(vplabel); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_auditon(struct ucred *cred, int cmd) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_system_check_swapoff(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); error = biba_subject_privileged(subj); if (error) return (error); return (0); } static int biba_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); error = biba_subject_privileged(subj); if (error) return (error); if (!biba_high_effective(obj)) return (EACCES); return (0); } static int biba_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_biba *subj; int error; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as biba/high, * but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { if (!biba_subject_dominate_high(subj)) return (EACCES); error = biba_subject_privileged(subj); if (error) return (error); } return (0); } static void biba_sysvmsg_cleanup(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_biba)); } static void biba_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_biba *source, *dest; /* Ignore the msgq label */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); biba_copy_effective(source, dest); } static int biba_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static void biba_sysvmsq_cleanup(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_biba)); } static void biba_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); biba_copy_effective(source, dest); } static int biba_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int biba_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (accesstype & SEM_R) if (!biba_dominate_effective(obj, subj)) return (EACCES); if (accesstype & SEM_A) if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static void biba_sysvsem_cleanup(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_biba)); } static void biba_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); biba_copy_effective(source, dest); } static int biba_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } - + return (0); } static int biba_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int biba_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static void biba_sysvshm_cleanup(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_biba)); } static void biba_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); biba_copy_effective(source, dest); } static int biba_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_biba mb_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, &buflen, (char *) &mb_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ biba_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(mb_temp)) { printf("biba_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } if (biba_valid(&mb_temp) != 0) { printf("biba_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((mb_temp.mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_EFFECTIVE) { printf("biba_vnode_associate_extattr: not effective\n"); return (EPERM); } biba_copy_effective(&mb_temp, dest); return (0); } static void biba_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_biba *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); biba_copy_effective(source, dest); } static int biba_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel) { struct mac_biba *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of Biba, so disallow non-NULL Biba label * elements in the execlabel. */ exec = SLOT(execlabel); error = biba_atmostflags(exec, 0); if (error) return (error); } if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_biba *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_readlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_biba *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a Biba label update for the vnode, it must be a * effective label. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (Biba label or not), Biba must * authorize the relabel. */ if (!biba_effective_in_range(old, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a vnode, the new vnode label * must be in the subject range. */ if (!biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (biba_contains_equal(new)) { error = biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int biba_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int biba_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int biba_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int biba_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_biba *subj, *obj; if (!biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int biba_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_biba *source, *dest, mb_temp; size_t buflen; int error; buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); biba_copy_effective(source, &mb_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); if (error == 0) biba_copy_effective(source, dest); return (error); } static void biba_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(vplabel); biba_copy(source, dest); } static int biba_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_biba *source, mb_temp; size_t buflen; int error; buflen = sizeof(mb_temp); bzero(&mb_temp, buflen); source = SLOT(intlabel); if ((source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) == 0) return (0); biba_copy_effective(source, &mb_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &mb_temp, curthread); return (error); } static struct mac_policy_ops mac_biba_ops = { .mpo_init = biba_init, .mpo_bpfdesc_check_receive = biba_bpfdesc_check_receive, .mpo_bpfdesc_create = biba_bpfdesc_create, .mpo_bpfdesc_create_mbuf = biba_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = biba_destroy_label, .mpo_bpfdesc_init_label = biba_init_label, .mpo_cred_associate_nfsd = biba_cred_associate_nfsd, .mpo_cred_check_relabel = biba_cred_check_relabel, .mpo_cred_check_visible = biba_cred_check_visible, .mpo_cred_copy_label = biba_copy_label, .mpo_cred_create_init = biba_cred_create_init, .mpo_cred_create_swapper = biba_cred_create_swapper, .mpo_cred_destroy_label = biba_destroy_label, .mpo_cred_externalize_label = biba_externalize_label, .mpo_cred_init_label = biba_init_label, .mpo_cred_internalize_label = biba_internalize_label, .mpo_cred_relabel = biba_cred_relabel, .mpo_devfs_create_device = biba_devfs_create_device, .mpo_devfs_create_directory = biba_devfs_create_directory, .mpo_devfs_create_symlink = biba_devfs_create_symlink, .mpo_devfs_destroy_label = biba_destroy_label, .mpo_devfs_init_label = biba_init_label, .mpo_devfs_update = biba_devfs_update, .mpo_devfs_vnode_associate = biba_devfs_vnode_associate, .mpo_ifnet_check_relabel = biba_ifnet_check_relabel, .mpo_ifnet_check_transmit = biba_ifnet_check_transmit, .mpo_ifnet_copy_label = biba_copy_label, .mpo_ifnet_create = biba_ifnet_create, .mpo_ifnet_create_mbuf = biba_ifnet_create_mbuf, .mpo_ifnet_destroy_label = biba_destroy_label, .mpo_ifnet_externalize_label = biba_externalize_label, .mpo_ifnet_init_label = biba_init_label, .mpo_ifnet_internalize_label = biba_internalize_label, .mpo_ifnet_relabel = biba_ifnet_relabel, .mpo_inpcb_check_deliver = biba_inpcb_check_deliver, .mpo_inpcb_check_visible = biba_inpcb_check_visible, .mpo_inpcb_create = biba_inpcb_create, .mpo_inpcb_create_mbuf = biba_inpcb_create_mbuf, .mpo_inpcb_destroy_label = biba_destroy_label, .mpo_inpcb_init_label = biba_init_label_waitcheck, .mpo_inpcb_sosetlabel = biba_inpcb_sosetlabel, .mpo_ip6q_create = biba_ip6q_create, .mpo_ip6q_destroy_label = biba_destroy_label, .mpo_ip6q_init_label = biba_init_label_waitcheck, .mpo_ip6q_match = biba_ip6q_match, .mpo_ip6q_reassemble = biba_ip6q_reassemble, .mpo_ip6q_update = biba_ip6q_update, .mpo_ipq_create = biba_ipq_create, .mpo_ipq_destroy_label = biba_destroy_label, .mpo_ipq_init_label = biba_init_label_waitcheck, .mpo_ipq_match = biba_ipq_match, .mpo_ipq_reassemble = biba_ipq_reassemble, .mpo_ipq_update = biba_ipq_update, .mpo_kld_check_load = biba_kld_check_load, .mpo_mbuf_copy_label = biba_copy_label, .mpo_mbuf_destroy_label = biba_destroy_label, .mpo_mbuf_init_label = biba_init_label_waitcheck, .mpo_mount_check_stat = biba_mount_check_stat, .mpo_mount_create = biba_mount_create, .mpo_mount_destroy_label = biba_destroy_label, .mpo_mount_init_label = biba_init_label, .mpo_netinet_arp_send = biba_netinet_arp_send, .mpo_netinet_firewall_reply = biba_netinet_firewall_reply, .mpo_netinet_firewall_send = biba_netinet_firewall_send, .mpo_netinet_fragment = biba_netinet_fragment, .mpo_netinet_icmp_reply = biba_netinet_icmp_reply, .mpo_netinet_igmp_send = biba_netinet_igmp_send, .mpo_netinet6_nd6_send = biba_netinet6_nd6_send, .mpo_pipe_check_ioctl = biba_pipe_check_ioctl, .mpo_pipe_check_poll = biba_pipe_check_poll, .mpo_pipe_check_read = biba_pipe_check_read, .mpo_pipe_check_relabel = biba_pipe_check_relabel, .mpo_pipe_check_stat = biba_pipe_check_stat, .mpo_pipe_check_write = biba_pipe_check_write, .mpo_pipe_copy_label = biba_copy_label, .mpo_pipe_create = biba_pipe_create, .mpo_pipe_destroy_label = biba_destroy_label, .mpo_pipe_externalize_label = biba_externalize_label, .mpo_pipe_init_label = biba_init_label, .mpo_pipe_internalize_label = biba_internalize_label, .mpo_pipe_relabel = biba_pipe_relabel, .mpo_posixsem_check_getvalue = biba_posixsem_check_rdonly, .mpo_posixsem_check_open = biba_posixsem_check_openunlink, .mpo_posixsem_check_post = biba_posixsem_check_write, .mpo_posixsem_check_setmode = biba_posixsem_check_setmode, .mpo_posixsem_check_setowner = biba_posixsem_check_setowner, .mpo_posixsem_check_stat = biba_posixsem_check_rdonly, .mpo_posixsem_check_unlink = biba_posixsem_check_openunlink, .mpo_posixsem_check_wait = biba_posixsem_check_write, .mpo_posixsem_create = biba_posixsem_create, .mpo_posixsem_destroy_label = biba_destroy_label, .mpo_posixsem_init_label = biba_init_label, .mpo_posixshm_check_mmap = biba_posixshm_check_mmap, .mpo_posixshm_check_open = biba_posixshm_check_open, .mpo_posixshm_check_read = biba_posixshm_check_read, .mpo_posixshm_check_setmode = biba_posixshm_check_setmode, .mpo_posixshm_check_setowner = biba_posixshm_check_setowner, .mpo_posixshm_check_stat = biba_posixshm_check_stat, .mpo_posixshm_check_truncate = biba_posixshm_check_truncate, .mpo_posixshm_check_unlink = biba_posixshm_check_unlink, .mpo_posixshm_check_write = biba_posixshm_check_write, .mpo_posixshm_create = biba_posixshm_create, .mpo_posixshm_destroy_label = biba_destroy_label, .mpo_posixshm_init_label = biba_init_label, .mpo_priv_check = biba_priv_check, .mpo_proc_check_debug = biba_proc_check_debug, .mpo_proc_check_sched = biba_proc_check_sched, .mpo_proc_check_signal = biba_proc_check_signal, .mpo_socket_check_deliver = biba_socket_check_deliver, .mpo_socket_check_relabel = biba_socket_check_relabel, .mpo_socket_check_visible = biba_socket_check_visible, .mpo_socket_copy_label = biba_copy_label, .mpo_socket_create = biba_socket_create, .mpo_socket_create_mbuf = biba_socket_create_mbuf, .mpo_socket_destroy_label = biba_destroy_label, .mpo_socket_externalize_label = biba_externalize_label, .mpo_socket_init_label = biba_init_label_waitcheck, .mpo_socket_internalize_label = biba_internalize_label, .mpo_socket_newconn = biba_socket_newconn, .mpo_socket_relabel = biba_socket_relabel, .mpo_socketpeer_destroy_label = biba_destroy_label, .mpo_socketpeer_externalize_label = biba_externalize_label, .mpo_socketpeer_init_label = biba_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = biba_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = biba_socketpeer_set_from_socket, .mpo_syncache_create = biba_syncache_create, .mpo_syncache_create_mbuf = biba_syncache_create_mbuf, .mpo_syncache_destroy_label = biba_destroy_label, .mpo_syncache_init_label = biba_init_label_waitcheck, .mpo_system_check_acct = biba_system_check_acct, .mpo_system_check_auditctl = biba_system_check_auditctl, .mpo_system_check_auditon = biba_system_check_auditon, .mpo_system_check_swapoff = biba_system_check_swapoff, .mpo_system_check_swapon = biba_system_check_swapon, .mpo_system_check_sysctl = biba_system_check_sysctl, .mpo_sysvmsg_cleanup = biba_sysvmsg_cleanup, .mpo_sysvmsg_create = biba_sysvmsg_create, .mpo_sysvmsg_destroy_label = biba_destroy_label, .mpo_sysvmsg_init_label = biba_init_label, .mpo_sysvmsq_check_msgrcv = biba_sysvmsq_check_msgrcv, .mpo_sysvmsq_check_msgrmid = biba_sysvmsq_check_msgrmid, .mpo_sysvmsq_check_msqget = biba_sysvmsq_check_msqget, .mpo_sysvmsq_check_msqsnd = biba_sysvmsq_check_msqsnd, .mpo_sysvmsq_check_msqrcv = biba_sysvmsq_check_msqrcv, .mpo_sysvmsq_check_msqctl = biba_sysvmsq_check_msqctl, .mpo_sysvmsq_cleanup = biba_sysvmsq_cleanup, .mpo_sysvmsq_create = biba_sysvmsq_create, .mpo_sysvmsq_destroy_label = biba_destroy_label, .mpo_sysvmsq_init_label = biba_init_label, .mpo_sysvsem_check_semctl = biba_sysvsem_check_semctl, .mpo_sysvsem_check_semget = biba_sysvsem_check_semget, .mpo_sysvsem_check_semop = biba_sysvsem_check_semop, .mpo_sysvsem_cleanup = biba_sysvsem_cleanup, .mpo_sysvsem_create = biba_sysvsem_create, .mpo_sysvsem_destroy_label = biba_destroy_label, .mpo_sysvsem_init_label = biba_init_label, .mpo_sysvshm_check_shmat = biba_sysvshm_check_shmat, .mpo_sysvshm_check_shmctl = biba_sysvshm_check_shmctl, .mpo_sysvshm_check_shmget = biba_sysvshm_check_shmget, .mpo_sysvshm_cleanup = biba_sysvshm_cleanup, .mpo_sysvshm_create = biba_sysvshm_create, .mpo_sysvshm_destroy_label = biba_destroy_label, .mpo_sysvshm_init_label = biba_init_label, .mpo_vnode_associate_extattr = biba_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = biba_vnode_associate_singlelabel, .mpo_vnode_check_access = biba_vnode_check_open, .mpo_vnode_check_chdir = biba_vnode_check_chdir, .mpo_vnode_check_chroot = biba_vnode_check_chroot, .mpo_vnode_check_create = biba_vnode_check_create, .mpo_vnode_check_deleteacl = biba_vnode_check_deleteacl, .mpo_vnode_check_deleteextattr = biba_vnode_check_deleteextattr, .mpo_vnode_check_exec = biba_vnode_check_exec, .mpo_vnode_check_getacl = biba_vnode_check_getacl, .mpo_vnode_check_getextattr = biba_vnode_check_getextattr, .mpo_vnode_check_link = biba_vnode_check_link, .mpo_vnode_check_listextattr = biba_vnode_check_listextattr, .mpo_vnode_check_lookup = biba_vnode_check_lookup, .mpo_vnode_check_mmap = biba_vnode_check_mmap, .mpo_vnode_check_open = biba_vnode_check_open, .mpo_vnode_check_poll = biba_vnode_check_poll, .mpo_vnode_check_read = biba_vnode_check_read, .mpo_vnode_check_readdir = biba_vnode_check_readdir, .mpo_vnode_check_readlink = biba_vnode_check_readlink, .mpo_vnode_check_relabel = biba_vnode_check_relabel, .mpo_vnode_check_rename_from = biba_vnode_check_rename_from, .mpo_vnode_check_rename_to = biba_vnode_check_rename_to, .mpo_vnode_check_revoke = biba_vnode_check_revoke, .mpo_vnode_check_setacl = biba_vnode_check_setacl, .mpo_vnode_check_setextattr = biba_vnode_check_setextattr, .mpo_vnode_check_setflags = biba_vnode_check_setflags, .mpo_vnode_check_setmode = biba_vnode_check_setmode, .mpo_vnode_check_setowner = biba_vnode_check_setowner, .mpo_vnode_check_setutimes = biba_vnode_check_setutimes, .mpo_vnode_check_stat = biba_vnode_check_stat, .mpo_vnode_check_unlink = biba_vnode_check_unlink, .mpo_vnode_check_write = biba_vnode_check_write, .mpo_vnode_create_extattr = biba_vnode_create_extattr, .mpo_vnode_copy_label = biba_copy_label, .mpo_vnode_destroy_label = biba_destroy_label, .mpo_vnode_externalize_label = biba_externalize_label, .mpo_vnode_init_label = biba_init_label, .mpo_vnode_internalize_label = biba_internalize_label, .mpo_vnode_relabel = biba_vnode_relabel, .mpo_vnode_setlabel_extattr = biba_vnode_setlabel_extattr, }; MAC_POLICY_SET(&mac_biba_ops, mac_biba, "TrustedBSD MAC/Biba", MPC_LOADTIME_FLAG_NOTLATE, &biba_slot); diff --git a/sys/security/mac_lomac/mac_lomac.c b/sys/security/mac_lomac/mac_lomac.c index dca68b4877d8..821b0faf68d0 100644 --- a/sys/security/mac_lomac/mac_lomac.c +++ b/sys/security/mac_lomac/mac_lomac.c @@ -1,3066 +1,3066 @@ /*- * Copyright (c) 1999-2002, 2007-2009 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by NAI Labs, * 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 enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * 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$ */ /* * Developed by the TrustedBSD Project. * * Low-watermark floating label mandatory integrity policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct mac_lomac_proc { struct mac_lomac mac_lomac; struct mtx mtx; }; SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, lomac, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_lomac policy controls"); static int lomac_label_size = sizeof(struct mac_lomac); SYSCTL_INT(_security_mac_lomac, OID_AUTO, label_size, CTLFLAG_RD, &lomac_label_size, 0, "Size of struct mac_lomac"); static int lomac_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, enabled, CTLFLAG_RWTUN, &lomac_enabled, 0, "Enforce MAC/LOMAC policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_lomac, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, trust_all_interfaces, CTLFLAG_RDTUN, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/LOMAC"); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_lomac, OID_AUTO, trusted_interfaces, CTLFLAG_RDTUN, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/LOMAC"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as lomac/equal on create"); static int revocation_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int lomac_slot; #define SLOT(l) ((struct mac_lomac *)mac_label_get((l), lomac_slot)) #define SLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) #define PSLOT(l) ((struct mac_lomac_proc *) \ mac_label_get((l), lomac_slot)) #define PSLOT_SET(l, val) mac_label_set((l), lomac_slot, (uintptr_t)(val)) static MALLOC_DEFINE(M_LOMAC, "mac_lomac_label", "MAC/LOMAC labels"); static struct mac_lomac * lomac_alloc(int flag) { struct mac_lomac *ml; ml = malloc(sizeof(*ml), M_LOMAC, M_ZERO | flag); return (ml); } static void lomac_free(struct mac_lomac *ml) { if (ml != NULL) free(ml, M_LOMAC); else atomic_add_int(&destroyed_not_inited, 1); } static int lomac_atmostflags(struct mac_lomac *ml, int flags) { if ((ml->ml_flags & flags) != ml->ml_flags) return (EINVAL); return (0); } static int lomac_dominate_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { switch (a->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: return (1); case MAC_LOMAC_TYPE_LOW: switch (b->mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); default: panic("lomac_dominate_element: b->mle_type invalid"); } case MAC_LOMAC_TYPE_GRADE: switch (b->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_GRADE: return (a->mle_grade >= b->mle_grade); default: panic("lomac_dominate_element: b->mle_type invalid"); } default: panic("lomac_dominate_element: a->mle_type invalid"); } } static int lomac_range_in_range(struct mac_lomac *rangea, struct mac_lomac *rangeb) { return (lomac_dominate_element(&rangeb->ml_rangehigh, &rangea->ml_rangehigh) && lomac_dominate_element(&rangea->ml_rangelow, &rangeb->ml_rangelow)); } static int lomac_single_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_single_in_range: a not single")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_single_in_range: b not range")); return (lomac_dominate_element(&range->ml_rangehigh, &single->ml_single) && lomac_dominate_element(&single->ml_single, &range->ml_rangelow)); } static int lomac_auxsingle_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("lomac_single_in_range: a not auxsingle")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_single_in_range: b not range")); return (lomac_dominate_element(&range->ml_rangehigh, &single->ml_auxsingle) && lomac_dominate_element(&single->ml_auxsingle, &range->ml_rangelow)); } static int lomac_dominate_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: b not single")); return (lomac_dominate_element(&a->ml_single, &b->ml_single)); } static int lomac_subject_dominate(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((~a->ml_flags & (MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_RANGE)) == 0, ("lomac_dominate_single: a not subject")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_dominate_single: b not single")); return (lomac_dominate_element(&a->ml_rangehigh, &b->ml_single)); } static int lomac_equal_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { if (a->mle_type == MAC_LOMAC_TYPE_EQUAL || b->mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); return (a->mle_type == b->mle_type && a->mle_grade == b->mle_grade); } static int lomac_equal_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_equal_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_equal_single: b not single")); return (lomac_equal_element(&a->ml_single, &b->ml_single)); } static int lomac_contains_equal(struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) if (ml->ml_auxsingle.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); } return (0); } static int lomac_subject_privileged(struct mac_lomac *ml) { KASSERT((ml->ml_flags & MAC_LOMAC_FLAGS_BOTH) == MAC_LOMAC_FLAGS_BOTH, ("lomac_subject_privileged: subject doesn't have both labels")); /* If the single is EQUAL, it's ok. */ if (ml->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL || ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (ml->ml_rangelow.mle_type == MAC_LOMAC_TYPE_LOW && ml->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int lomac_high_single(struct mac_lomac *ml) { KASSERT((ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_high_single: mac_lomac not single")); return (ml->ml_single.mle_type == MAC_LOMAC_TYPE_HIGH); } static int lomac_valid(struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { switch (ml->ml_single.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (ml->ml_single.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { switch (ml->ml_auxsingle.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (ml->ml_auxsingle.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { switch (ml->ml_rangelow.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } switch (ml->ml_rangehigh.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } if (!lomac_dominate_element(&ml->ml_rangehigh, &ml->ml_rangelow)) return (EINVAL); } else { if (ml->ml_rangelow.mle_type != MAC_LOMAC_TYPE_UNDEF || ml->ml_rangehigh.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } return (0); } static void lomac_set_range(struct mac_lomac *ml, u_short typelow, u_short gradelow, u_short typehigh, u_short gradehigh) { ml->ml_rangelow.mle_type = typelow; ml->ml_rangelow.mle_grade = gradelow; ml->ml_rangehigh.mle_type = typehigh; ml->ml_rangehigh.mle_grade = gradehigh; ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void lomac_set_single(struct mac_lomac *ml, u_short type, u_short grade) { ml->ml_single.mle_type = type; ml->ml_single.mle_grade = grade; ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void lomac_copy_range(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("lomac_copy_range: labelfrom not range")); labelto->ml_rangelow = labelfrom->ml_rangelow; labelto->ml_rangehigh = labelfrom->ml_rangehigh; labelto->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void lomac_copy_single(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("lomac_copy_single: labelfrom not single")); labelto->ml_single = labelfrom->ml_single; labelto->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void lomac_copy_auxsingle(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("lomac_copy_auxsingle: labelfrom not auxsingle")); labelto->ml_auxsingle = labelfrom->ml_auxsingle; labelto->ml_flags |= MAC_LOMAC_FLAG_AUX; } static void lomac_copy(struct mac_lomac *source, struct mac_lomac *dest) { if (source->ml_flags & MAC_LOMAC_FLAG_SINGLE) lomac_copy_single(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_AUX) lomac_copy_auxsingle(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_RANGE) lomac_copy_range(source, dest); } static int lomac_to_string(struct sbuf *sb, struct mac_lomac *ml); static int maybe_demote(struct mac_lomac *subjlabel, struct mac_lomac *objlabel, const char *actionname, const char *objname, struct vnode *vp) { struct sbuf subjlabel_sb, subjtext_sb, objlabel_sb; char *subjlabeltext, *objlabeltext, *subjtext; struct mac_lomac cached_subjlabel; struct mac_lomac_proc *subj; struct vattr va; struct proc *p; pid_t pgid; subj = PSLOT(curthread->td_proc->p_label); p = curthread->td_proc; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { /* * Check to see if the pending demotion would be more or less * severe than this one, and keep the more severe. This can * only happen for a multi-threaded application. */ if (lomac_dominate_single(objlabel, &subj->mac_lomac)) { mtx_unlock(&subj->mtx); return (0); } } bzero(&subj->mac_lomac, sizeof(subj->mac_lomac)); /* * Always demote the single label. */ lomac_copy_single(objlabel, &subj->mac_lomac); /* * Start with the original range, then minimize each side of the * range to the point of not dominating the object. The high side * will always be demoted, of course. */ lomac_copy_range(subjlabel, &subj->mac_lomac); if (!lomac_dominate_element(&objlabel->ml_single, &subj->mac_lomac.ml_rangelow)) subj->mac_lomac.ml_rangelow = objlabel->ml_single; subj->mac_lomac.ml_rangehigh = objlabel->ml_single; subj->mac_lomac.ml_flags |= MAC_LOMAC_FLAG_UPDATE; thread_lock(curthread); curthread->td_flags |= TDF_ASTPENDING | TDF_MACPEND; thread_unlock(curthread); /* * Avoid memory allocation while holding a mutex; cache the label. */ lomac_copy_single(&subj->mac_lomac, &cached_subjlabel); mtx_unlock(&subj->mtx); sbuf_new(&subjlabel_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&subjlabel_sb, subjlabel); sbuf_finish(&subjlabel_sb); subjlabeltext = sbuf_data(&subjlabel_sb); sbuf_new(&subjtext_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&subjtext_sb, &subj->mac_lomac); sbuf_finish(&subjtext_sb); subjtext = sbuf_data(&subjtext_sb); sbuf_new(&objlabel_sb, NULL, 0, SBUF_AUTOEXTEND); lomac_to_string(&objlabel_sb, objlabel); sbuf_finish(&objlabel_sb); objlabeltext = sbuf_data(&objlabel_sb); pgid = p->p_pgrp->pg_id; /* XXX could be stale? */ if (vp != NULL && VOP_GETATTR(vp, &va, curthread->td_ucred) == 0) { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s (inode=%ju, " "mountpount=%s)\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname, (uintmax_t)va.va_fileid, vp->v_mount->mnt_stat.f_mntonname); } else { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname); } sbuf_delete(&subjlabel_sb); sbuf_delete(&subjtext_sb); sbuf_delete(&objlabel_sb); return (0); } /* * Relabel "to" to "from" only if "from" is a valid label (contains at least * a single), as for a relabel operation which may or may not involve a * relevant label. */ static void try_relabel(struct mac_lomac *from, struct mac_lomac *to) { if (from->ml_flags & MAC_LOMAC_FLAG_SINGLE) { bzero(to, sizeof(*to)); lomac_copy(from, to); } } /* * Policy module operations. */ static void lomac_init(struct mac_policy_conf *conf) { } /* * Label operations. */ static void lomac_init_label(struct label *label) { SLOT_SET(label, lomac_alloc(M_WAITOK)); } static int lomac_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, lomac_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void lomac_destroy_label(struct label *label) { lomac_free(SLOT(label)); SLOT_SET(label, NULL); } static int lomac_element_to_string(struct sbuf *sb, struct mac_lomac_element *element) { switch (element->mle_type) { case MAC_LOMAC_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_LOMAC_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_LOMAC_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_LOMAC_TYPE_GRADE: return (sbuf_printf(sb, "%d", element->mle_grade)); default: panic("lomac_element_to_string: invalid type (%d)", element->mle_type); } } static int lomac_to_string(struct sbuf *sb, struct mac_lomac *ml) { if (ml->ml_flags & MAC_LOMAC_FLAG_SINGLE) { if (lomac_element_to_string(sb, &ml->ml_single) == -1) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_AUX) { if (sbuf_putc(sb, '[') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_auxsingle) == -1) return (EINVAL); if (sbuf_putc(sb, ']') == -1) return (EINVAL); } if (ml->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (lomac_element_to_string(sb, &ml->ml_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int lomac_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_lomac *ml; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; ml = SLOT(label); return (lomac_to_string(sb, ml)); } static int lomac_parse_element(struct mac_lomac_element *element, char *string) { if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mle_type = MAC_LOMAC_TYPE_HIGH; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mle_type = MAC_LOMAC_TYPE_LOW; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mle_type = MAC_LOMAC_TYPE_EQUAL; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else { char *p0, *p1; int d; p0 = string; d = strtol(p0, &p1, 10); - + if (d < 0 || d > 65535) return (EINVAL); element->mle_type = MAC_LOMAC_TYPE_GRADE; element->mle_grade = d; if (p1 == p0 || *p1 != '\0') return (EINVAL); } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int lomac_parse(struct mac_lomac *ml, char *string) { char *range, *rangeend, *rangehigh, *rangelow, *single, *auxsingle, *auxsingleend; int error; /* Do we have a range? */ single = string; range = strchr(string, '('); if (range == single) single = NULL; auxsingle = strchr(string, '['); if (auxsingle == single) single = NULL; if (range != NULL && auxsingle != NULL) return (EINVAL); rangelow = rangehigh = NULL; if (range != NULL) { /* Nul terminate the end of the single string. */ *range = '\0'; range++; rangelow = range; rangehigh = strchr(rangelow, '-'); if (rangehigh == NULL) return (EINVAL); rangehigh++; if (*rangelow == '\0' || *rangehigh == '\0') return (EINVAL); rangeend = strchr(rangehigh, ')'); if (rangeend == NULL) return (EINVAL); if (*(rangeend + 1) != '\0') return (EINVAL); /* Nul terminate the ends of the ranges. */ *(rangehigh - 1) = '\0'; *rangeend = '\0'; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("lomac_internalize_label: range mismatch")); if (auxsingle != NULL) { /* Nul terminate the end of the single string. */ *auxsingle = '\0'; auxsingle++; auxsingleend = strchr(auxsingle, ']'); if (auxsingleend == NULL) return (EINVAL); if (*(auxsingleend + 1) != '\0') return (EINVAL); /* Nul terminate the end of the auxsingle. */ *auxsingleend = '\0'; } bzero(ml, sizeof(*ml)); if (single != NULL) { error = lomac_parse_element(&ml->ml_single, single); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } if (auxsingle != NULL) { error = lomac_parse_element(&ml->ml_auxsingle, auxsingle); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_AUX; } if (rangelow != NULL) { error = lomac_parse_element(&ml->ml_rangelow, rangelow); if (error) return (error); error = lomac_parse_element(&ml->ml_rangehigh, rangehigh); if (error) return (error); ml->ml_flags |= MAC_LOMAC_FLAG_RANGE; } error = lomac_valid(ml); if (error) return (error); return (0); } static int lomac_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_lomac *ml, ml_temp; int error; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = lomac_parse(&ml_temp, element_data); if (error) return (error); ml = SLOT(label); *ml = ml_temp; return (0); } static void lomac_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int lomac_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_lomac *a, *b; if (!lomac_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (lomac_equal_single(a, b)) return (0); return (EACCES); } static void lomac_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); lomac_copy_single(source, dest); } static void lomac_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static int lomac_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the credential, it may be an * update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) lomac_copy_range(subj, new); /* * To change the LOMAC range on a credential, the new range * label must be in the current range. */ if (!lomac_range_in_range(new, subj)) return (EPERM); /* * To change the LOMAC single label on a credential, the new * single label must be in the new range. Implicitly from * the previous check, the new single is in the old range. */ if (!lomac_single_in_range(new, new)) return (EPERM); /* * To have EQUAL in any component of the new credential LOMAC * label, the subject must already have EQUAL in their label. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } /* * XXXMAC: Additional consistency tests regarding the single * and range of the new label might be performed here. */ } return (0); } static int lomac_cred_check_visible(struct ucred *cr1, struct ucred *cr2) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cr1->cr_label); obj = SLOT(cr2->cr_label); /* XXX: range */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); return (0); } static void lomac_cred_create_init(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); lomac_set_single(dest, MAC_LOMAC_TYPE_HIGH, 0); lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_cred_create_swapper(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); try_relabel(source, dest); } static void lomac_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *ml; const char *dn; int lomac_type; ml = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0 || strncmp(dn, "ttyv", strlen("ttyv")) == 0) lomac_type = MAC_LOMAC_TYPE_EQUAL; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) lomac_type = MAC_LOMAC_TYPE_EQUAL; else lomac_type = MAC_LOMAC_TYPE_HIGH; lomac_set_single(ml, lomac_type, 0); } static void lomac_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *ml; ml = SLOT(delabel); lomac_set_single(ml, MAC_LOMAC_TYPE_HIGH, 0); } static void lomac_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); lomac_copy_single(source, dest); } static void lomac_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); lomac_copy(source, dest); } static void lomac_devfs_vnode_associate(struct mount *mp, struct label *mplabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); lomac_copy_single(source, dest); } static int lomac_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the interface, it may be an * update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires LOMAC privilege. */ error = lomac_subject_privileged(subj); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) lomac_copy_range(subj, new); /* * Rely on the traditional superuser status for the LOMAC * interface relabel requirements. XXXMAC: This will go * away. * * XXXRW: This is also redundant to a higher layer check. */ error = priv_check_cred(cred, PRIV_NET_SETIFMAC); if (error) return (EPERM); /* * XXXMAC: Additional consistency tests regarding the single * and the range of the new label might be performed here. */ } return (0); } static int lomac_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *i; if (!lomac_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (lomac_single_in_range(p, i) ? 0 : EACCES); } static void lomac_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_lomac *dest; int len, grade; dest = SLOT(ifplabel); if (ifp->if_type == IFT_LOOP) { grade = MAC_LOMAC_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { grade = MAC_LOMAC_TYPE_HIGH; goto set; } grade = MAC_LOMAC_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); if (strcmp(tifname, ifp->if_xname) == 0) { grade = MAC_LOMAC_TYPE_HIGH; break; } } else { *p = '\0'; printf("MAC/LOMAC warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: lomac_set_single(dest, grade, 0); lomac_set_range(dest, grade, 0, grade, 0); } static void lomac_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static void lomac_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); try_relabel(source, dest); } static int lomac_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *i; if (!lomac_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (lomac_equal_single(p, i) ? 0 : EACCES); } static int lomac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!lomac_dominate_single(obj, subj)) return (ENOENT); return (0); } static void lomac_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); lomac_copy_single(source, dest); } static void lomac_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); lomac_copy_single(source, dest); } static void lomac_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); lomac_copy_single(source, dest); } static void lomac_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); lomac_copy_single(source, dest); } static int lomac_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_lomac *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (lomac_equal_single(a, b)); } static void lomac_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ lomac_copy_single(source, dest); } static void lomac_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void lomac_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); lomac_copy_single(source, dest); } static int lomac_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_lomac *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (lomac_equal_single(a, b)); } static void lomac_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ lomac_copy_single(source, dest); } static void lomac_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int lomac_kld_check_load(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static void lomac_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); lomac_copy_single(source, dest); } static void lomac_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_lomac *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); lomac_copy_single(source, dest); } static void lomac_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); lomac_copy_single(source, dest); } static void lomac_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_lomac *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); lomac_copy_single(source, dest); } static void lomac_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void lomac_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *dest; dest = SLOT(mlabel); lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static int lomac_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if (!lomac_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int lomac_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "pipe", NULL)); return (0); } static int lomac_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is a LOMAC label update for a pipe, it must be a single * update. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To perform a relabel of a pipe (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a pipe, the new pipe label * must be in the subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static void lomac_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); lomac_copy_single(source, dest); } static void lomac_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); try_relabel(source, dest); } /* * Some system privileges are allowed regardless of integrity grade; others * are allowed only when running with privilege with respect to the LOMAC * policy as they might otherwise allow bypassing of the integrity policy. */ static int lomac_priv_check(struct ucred *cred, int priv) { struct mac_lomac *subj; int error; if (!lomac_enabled) return (0); /* * Exempt only specific privileges from the LOMAC integrity policy. */ switch (priv) { case PRIV_KTRACE: case PRIV_MSGBUF: /* * Allow processes to manipulate basic process audit properties, and * to submit audit records. */ case PRIV_AUDIT_GETAUDIT: case PRIV_AUDIT_SETAUDIT: case PRIV_AUDIT_SUBMIT: /* * Allow processes to manipulate their regular UNIX credentials. */ case PRIV_CRED_SETUID: case PRIV_CRED_SETEUID: case PRIV_CRED_SETGID: case PRIV_CRED_SETEGID: case PRIV_CRED_SETGROUPS: case PRIV_CRED_SETREUID: case PRIV_CRED_SETREGID: case PRIV_CRED_SETRESUID: case PRIV_CRED_SETRESGID: /* * Allow processes to perform system monitoring. */ case PRIV_SEEOTHERGIDS: case PRIV_SEEOTHERUIDS: break; /* * Allow access to general process debugging facilities. We * separately control debugging based on MAC label. */ case PRIV_DEBUG_DIFFCRED: case PRIV_DEBUG_SUGID: case PRIV_DEBUG_UNPRIV: /* * Allow manipulating jails. */ case PRIV_JAIL_ATTACH: /* * Allow privilege with respect to the Partition policy, but not the * Privs policy. */ case PRIV_MAC_PARTITION: /* * Allow privilege with respect to process resource limits and login * context. */ case PRIV_PROC_LIMIT: case PRIV_PROC_SETLOGIN: case PRIV_PROC_SETRLIMIT: /* * Allow System V and POSIX IPC privileges. */ case PRIV_IPC_READ: case PRIV_IPC_WRITE: case PRIV_IPC_ADMIN: case PRIV_IPC_MSGSIZE: case PRIV_MQ_ADMIN: /* * Allow certain scheduler manipulations -- possibly this should be * controlled by more fine-grained policy, as potentially low * integrity processes can deny CPU to higher integrity ones. */ case PRIV_SCHED_DIFFCRED: case PRIV_SCHED_SETPRIORITY: case PRIV_SCHED_RTPRIO: case PRIV_SCHED_SETPOLICY: case PRIV_SCHED_SET: case PRIV_SCHED_SETPARAM: /* * More IPC privileges. */ case PRIV_SEM_WRITE: /* * Allow signaling privileges subject to integrity policy. */ case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: /* * Allow access to only limited sysctls from lower integrity levels; * piggy-back on the Jail definition. */ case PRIV_SYSCTL_WRITEJAIL: /* * Allow TTY-based privileges, subject to general device access using * labels on TTY device nodes, but not console privilege. */ case PRIV_TTY_DRAINWAIT: case PRIV_TTY_DTRWAIT: case PRIV_TTY_EXCLUSIVE: case PRIV_TTY_STI: case PRIV_TTY_SETA: /* * Grant most VFS privileges, as almost all are in practice bounded * by more specific checks using labels. */ case PRIV_VFS_READ: case PRIV_VFS_WRITE: case PRIV_VFS_ADMIN: case PRIV_VFS_EXEC: case PRIV_VFS_LOOKUP: case PRIV_VFS_CHFLAGS_DEV: case PRIV_VFS_CHOWN: case PRIV_VFS_CHROOT: case PRIV_VFS_RETAINSUGID: case PRIV_VFS_EXCEEDQUOTA: case PRIV_VFS_FCHROOT: case PRIV_VFS_FHOPEN: case PRIV_VFS_FHSTATFS: case PRIV_VFS_GENERATION: case PRIV_VFS_GETFH: case PRIV_VFS_GETQUOTA: case PRIV_VFS_LINK: case PRIV_VFS_MOUNT: case PRIV_VFS_MOUNT_OWNER: case PRIV_VFS_MOUNT_PERM: case PRIV_VFS_MOUNT_SUIDDIR: case PRIV_VFS_MOUNT_NONUSER: case PRIV_VFS_SETGID: case PRIV_VFS_STICKYFILE: case PRIV_VFS_SYSFLAGS: case PRIV_VFS_UNMOUNT: /* * Allow VM privileges; it would be nice if these were subject to * resource limits. */ case PRIV_VM_MADV_PROTECT: case PRIV_VM_MLOCK: case PRIV_VM_MUNLOCK: case PRIV_VM_SWAP_NOQUOTA: case PRIV_VM_SWAP_NORLIMIT: /* * Allow some but not all network privileges. In general, dont allow * reconfiguring the network stack, just normal use. */ case PRIV_NETINET_RESERVEDPORT: case PRIV_NETINET_RAW: case PRIV_NETINET_REUSEPORT: break; /* * All remaining system privileges are allow only if the process * holds privilege with respect to the LOMAC policy. */ default: subj = SLOT(cred->cr_label); error = lomac_subject_privileged(subj); if (error) return (error); } return (0); } static int lomac_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!lomac_dominate_single(obj, subj)) return (ESRCH); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static void lomac_proc_destroy_label(struct label *label) { mtx_destroy(&PSLOT(label)->mtx); free(PSLOT(label), M_LOMAC); PSLOT_SET(label, NULL); } static void lomac_proc_init_label(struct label *label) { PSLOT_SET(label, malloc(sizeof(struct mac_lomac_proc), M_LOMAC, M_ZERO | M_WAITOK)); mtx_init(&PSLOT(label)->mtx, "MAC/Lomac proc lock", NULL, MTX_DEF); } static int lomac_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *s; int error; if (!lomac_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = lomac_equal_single(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int lomac_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is a LOMAC label update for the socket, it may be an * update of single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To relabel a socket, the old socket single must be in the subject * range. */ if (!lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To relabel a socket, the new socket single must be in the * subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!lomac_dominate_single(obj, subj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void lomac_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); lomac_copy_single(source, dest); } static void lomac_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); lomac_copy_single(source, dest); SOCK_UNLOCK(so); } static void lomac_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_lomac source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); lomac_copy_single(&source, dest); SOCK_UNLOCK(newso); } static void lomac_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_lomac *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); try_relabel(source, dest); } static void lomac_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_lomac *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); lomac_copy_single(source, dest); SOCK_UNLOCK(so); } static void lomac_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_lomac source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); lomac_copy_single(&source, dest); SOCK_UNLOCK(newso); } static void lomac_syncache_create(struct label *label, struct inpcb *inp) { struct mac_lomac *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); lomac_copy(source, dest); } static void lomac_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); lomac_copy(source, dest); } static int lomac_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_swapoff(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); if (lomac_subject_privileged(subj)) return (EPERM); return (0); } static int lomac_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (lomac_subject_privileged(subj)) return (EPERM); if (!lomac_high_single(obj)) return (EACCES); return (0); } static int lomac_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_lomac *subj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as lomac/high, * but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { #ifdef notdef if (!lomac_subject_dominate_high(subj)) return (EACCES); #endif if (lomac_subject_privileged(subj)) return (EPERM); } return (0); } static void lomac_thread_userret(struct thread *td) { struct proc *p = td->td_proc; struct mac_lomac_proc *subj = PSLOT(p->p_label); struct ucred *newcred, *oldcred; int dodrop; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { dodrop = 0; mtx_unlock(&subj->mtx); newcred = crget(); /* * Prevent a lock order reversal in mac_proc_vm_revoke; * ideally, the other user of subj->mtx wouldn't be holding * Giant. */ mtx_lock(&Giant); PROC_LOCK(p); mtx_lock(&subj->mtx); /* * Check if we lost the race while allocating the cred. */ if ((subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) == 0) { crfree(newcred); goto out; } oldcred = p->p_ucred; crcopy(newcred, oldcred); crhold(newcred); lomac_copy(&subj->mac_lomac, SLOT(newcred->cr_label)); proc_set_cred(p, newcred); crfree(oldcred); dodrop = 1; out: mtx_unlock(&subj->mtx); PROC_UNLOCK(p); if (dodrop) mac_proc_vm_revoke(curthread); mtx_unlock(&Giant); } else { mtx_unlock(&subj->mtx); } } static int lomac_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac ml_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(ml_temp); bzero(&ml_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, &buflen, (char *)&ml_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ lomac_copy_single(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(ml_temp)) { if (buflen != sizeof(ml_temp) - sizeof(ml_temp.ml_auxsingle)) { printf("lomac_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } bzero(&ml_temp.ml_auxsingle, sizeof(ml_temp.ml_auxsingle)); buflen = sizeof(ml_temp); (void)vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&ml_temp, curthread); } if (lomac_valid(&ml_temp) != 0) { printf("lomac_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((ml_temp.ml_flags & MAC_LOMAC_FLAGS_BOTH) != MAC_LOMAC_FLAG_SINGLE) { printf("lomac_vnode_associate_extattr: not single\n"); return (EPERM); } lomac_copy_single(&ml_temp, dest); return (0); } static void lomac_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_lomac *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); lomac_copy_single(source, dest); } static int lomac_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); if (obj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&subj->ml_single, &obj->ml_auxsingle)) return (EACCES); return (0); } static int lomac_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "mapping", "file", vp)); } return (0); } static void lomac_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, struct label *vplabel, /* XXX vm_prot_t */ int *prot) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!lomac_enabled || !revocation_enabled) return; subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) *prot &= ~VM_PROT_WRITE; } static int lomac_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & VMODIFY_PERMS) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int lomac_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "file", vp)); return (0); } static int lomac_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_lomac *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a LOMAC label update for the vnode, it must be a * single label, with an optional explicit auxiliary single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_AUX); if (error) return (error); /* * To perform a relabel of the vnode (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!lomac_single_in_range(old, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a vnode, the new vnode label * must be in the subject range. */ if (!lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } if (new->ml_flags & MAC_LOMAC_FLAG_AUX) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) lomac_copy_single(subj, new); /* * To change the auxiliary LOMAC label on a vnode, the new * vnode label must be in the subject range. */ if (!lomac_auxsingle_in_range(new, subj)) return (EPERM); /* * To change the auxiliary LOMAC label on the vnode to be * EQUAL, the subject must have appropriate privilege. */ if (lomac_contains_equal(new)) { error = lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int lomac_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int lomac_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int lomac_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_lomac *subj, *obj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int lomac_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_lomac *source, *dest, *dir, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); dir = SLOT(dvplabel); if (dir->ml_flags & MAC_LOMAC_FLAG_AUX) { lomac_copy_auxsingle(dir, &temp); lomac_set_single(&temp, dir->ml_auxsingle.mle_type, dir->ml_auxsingle.mle_grade); } else { lomac_copy_single(source, &temp); } error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); if (error == 0) lomac_copy(&temp, dest); return (error); } static void lomac_vnode_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *source, *dest, *obj, *robj; source = SLOT(old->cr_label); dest = SLOT(new->cr_label); obj = SLOT(vplabel); robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; lomac_copy(source, dest); /* * If there's an auxiliary label on the real object, respect it and * assume that this level should be assumed immediately if a higher * level is currently in place. */ if (robj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&robj->ml_auxsingle, &dest->ml_single) && lomac_auxsingle_in_range(robj, dest)) lomac_set_single(dest, robj->ml_auxsingle.mle_type, robj->ml_auxsingle.mle_grade); /* * Restructuring to use the execve transitioning mechanism instead of * the normal demotion mechanism here would be difficult, so just * copy the label over and perform standard demotion. This is also * non-optimal because it will result in the intermediate label "new" * being created and immediately recycled. */ if (lomac_enabled && revocation_enabled && !lomac_dominate_single(obj, source)) (void)maybe_demote(source, obj, "executing", "file", vp); } static int lomac_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *subj, *obj, *robj; if (!lomac_enabled || !revocation_enabled) return (0); subj = SLOT(old->cr_label); obj = SLOT(vplabel); robj = interpvplabel != NULL ? SLOT(interpvplabel) : obj; return ((robj->ml_flags & MAC_LOMAC_FLAG_AUX && !lomac_dominate_element(&robj->ml_auxsingle, &subj->ml_single) && lomac_auxsingle_in_range(robj, subj)) || !lomac_dominate_single(obj, subj)); } static void lomac_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(vplabel); try_relabel(source, dest); } static int lomac_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_lomac *source, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(intlabel); if ((source->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) return (0); lomac_copy_single(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); return (error); } static struct mac_policy_ops lomac_ops = { .mpo_init = lomac_init, .mpo_bpfdesc_check_receive = lomac_bpfdesc_check_receive, .mpo_bpfdesc_create = lomac_bpfdesc_create, .mpo_bpfdesc_create_mbuf = lomac_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = lomac_destroy_label, .mpo_bpfdesc_init_label = lomac_init_label, .mpo_cred_check_relabel = lomac_cred_check_relabel, .mpo_cred_check_visible = lomac_cred_check_visible, .mpo_cred_copy_label = lomac_copy_label, .mpo_cred_create_swapper = lomac_cred_create_swapper, .mpo_cred_create_init = lomac_cred_create_init, .mpo_cred_destroy_label = lomac_destroy_label, .mpo_cred_externalize_label = lomac_externalize_label, .mpo_cred_init_label = lomac_init_label, .mpo_cred_internalize_label = lomac_internalize_label, .mpo_cred_relabel = lomac_cred_relabel, .mpo_devfs_create_device = lomac_devfs_create_device, .mpo_devfs_create_directory = lomac_devfs_create_directory, .mpo_devfs_create_symlink = lomac_devfs_create_symlink, .mpo_devfs_destroy_label = lomac_destroy_label, .mpo_devfs_init_label = lomac_init_label, .mpo_devfs_update = lomac_devfs_update, .mpo_devfs_vnode_associate = lomac_devfs_vnode_associate, .mpo_ifnet_check_relabel = lomac_ifnet_check_relabel, .mpo_ifnet_check_transmit = lomac_ifnet_check_transmit, .mpo_ifnet_copy_label = lomac_copy_label, .mpo_ifnet_create = lomac_ifnet_create, .mpo_ifnet_create_mbuf = lomac_ifnet_create_mbuf, .mpo_ifnet_destroy_label = lomac_destroy_label, .mpo_ifnet_externalize_label = lomac_externalize_label, .mpo_ifnet_init_label = lomac_init_label, .mpo_ifnet_internalize_label = lomac_internalize_label, .mpo_ifnet_relabel = lomac_ifnet_relabel, .mpo_syncache_create = lomac_syncache_create, .mpo_syncache_destroy_label = lomac_destroy_label, .mpo_syncache_init_label = lomac_init_label_waitcheck, .mpo_inpcb_check_deliver = lomac_inpcb_check_deliver, .mpo_inpcb_check_visible = lomac_inpcb_check_visible, .mpo_inpcb_create = lomac_inpcb_create, .mpo_inpcb_create_mbuf = lomac_inpcb_create_mbuf, .mpo_inpcb_destroy_label = lomac_destroy_label, .mpo_inpcb_init_label = lomac_init_label_waitcheck, .mpo_inpcb_sosetlabel = lomac_inpcb_sosetlabel, .mpo_ip6q_create = lomac_ip6q_create, .mpo_ip6q_destroy_label = lomac_destroy_label, .mpo_ip6q_init_label = lomac_init_label_waitcheck, .mpo_ip6q_match = lomac_ip6q_match, .mpo_ip6q_reassemble = lomac_ip6q_reassemble, .mpo_ip6q_update = lomac_ip6q_update, .mpo_ipq_create = lomac_ipq_create, .mpo_ipq_destroy_label = lomac_destroy_label, .mpo_ipq_init_label = lomac_init_label_waitcheck, .mpo_ipq_match = lomac_ipq_match, .mpo_ipq_reassemble = lomac_ipq_reassemble, .mpo_ipq_update = lomac_ipq_update, .mpo_kld_check_load = lomac_kld_check_load, .mpo_mbuf_copy_label = lomac_copy_label, .mpo_mbuf_destroy_label = lomac_destroy_label, .mpo_mbuf_init_label = lomac_init_label_waitcheck, .mpo_mount_create = lomac_mount_create, .mpo_mount_destroy_label = lomac_destroy_label, .mpo_mount_init_label = lomac_init_label, .mpo_netinet_arp_send = lomac_netinet_arp_send, .mpo_netinet_firewall_reply = lomac_netinet_firewall_reply, .mpo_netinet_firewall_send = lomac_netinet_firewall_send, .mpo_netinet_fragment = lomac_netinet_fragment, .mpo_netinet_icmp_reply = lomac_netinet_icmp_reply, .mpo_netinet_igmp_send = lomac_netinet_igmp_send, .mpo_netinet6_nd6_send = lomac_netinet6_nd6_send, .mpo_pipe_check_ioctl = lomac_pipe_check_ioctl, .mpo_pipe_check_read = lomac_pipe_check_read, .mpo_pipe_check_relabel = lomac_pipe_check_relabel, .mpo_pipe_check_write = lomac_pipe_check_write, .mpo_pipe_copy_label = lomac_copy_label, .mpo_pipe_create = lomac_pipe_create, .mpo_pipe_destroy_label = lomac_destroy_label, .mpo_pipe_externalize_label = lomac_externalize_label, .mpo_pipe_init_label = lomac_init_label, .mpo_pipe_internalize_label = lomac_internalize_label, .mpo_pipe_relabel = lomac_pipe_relabel, .mpo_priv_check = lomac_priv_check, .mpo_proc_check_debug = lomac_proc_check_debug, .mpo_proc_check_sched = lomac_proc_check_sched, .mpo_proc_check_signal = lomac_proc_check_signal, .mpo_proc_destroy_label = lomac_proc_destroy_label, .mpo_proc_init_label = lomac_proc_init_label, .mpo_socket_check_deliver = lomac_socket_check_deliver, .mpo_socket_check_relabel = lomac_socket_check_relabel, .mpo_socket_check_visible = lomac_socket_check_visible, .mpo_socket_copy_label = lomac_copy_label, .mpo_socket_create = lomac_socket_create, .mpo_socket_create_mbuf = lomac_socket_create_mbuf, .mpo_socket_destroy_label = lomac_destroy_label, .mpo_socket_externalize_label = lomac_externalize_label, .mpo_socket_init_label = lomac_init_label_waitcheck, .mpo_socket_internalize_label = lomac_internalize_label, .mpo_socket_newconn = lomac_socket_newconn, .mpo_socket_relabel = lomac_socket_relabel, .mpo_socketpeer_destroy_label = lomac_destroy_label, .mpo_socketpeer_externalize_label = lomac_externalize_label, .mpo_socketpeer_init_label = lomac_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = lomac_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = lomac_socketpeer_set_from_socket, .mpo_syncache_create_mbuf = lomac_syncache_create_mbuf, .mpo_system_check_acct = lomac_system_check_acct, .mpo_system_check_auditctl = lomac_system_check_auditctl, .mpo_system_check_swapoff = lomac_system_check_swapoff, .mpo_system_check_swapon = lomac_system_check_swapon, .mpo_system_check_sysctl = lomac_system_check_sysctl, .mpo_thread_userret = lomac_thread_userret, .mpo_vnode_associate_extattr = lomac_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = lomac_vnode_associate_singlelabel, .mpo_vnode_check_access = lomac_vnode_check_open, .mpo_vnode_check_create = lomac_vnode_check_create, .mpo_vnode_check_deleteacl = lomac_vnode_check_deleteacl, .mpo_vnode_check_link = lomac_vnode_check_link, .mpo_vnode_check_mmap = lomac_vnode_check_mmap, .mpo_vnode_check_mmap_downgrade = lomac_vnode_check_mmap_downgrade, .mpo_vnode_check_open = lomac_vnode_check_open, .mpo_vnode_check_read = lomac_vnode_check_read, .mpo_vnode_check_relabel = lomac_vnode_check_relabel, .mpo_vnode_check_rename_from = lomac_vnode_check_rename_from, .mpo_vnode_check_rename_to = lomac_vnode_check_rename_to, .mpo_vnode_check_revoke = lomac_vnode_check_revoke, .mpo_vnode_check_setacl = lomac_vnode_check_setacl, .mpo_vnode_check_setextattr = lomac_vnode_check_setextattr, .mpo_vnode_check_setflags = lomac_vnode_check_setflags, .mpo_vnode_check_setmode = lomac_vnode_check_setmode, .mpo_vnode_check_setowner = lomac_vnode_check_setowner, .mpo_vnode_check_setutimes = lomac_vnode_check_setutimes, .mpo_vnode_check_unlink = lomac_vnode_check_unlink, .mpo_vnode_check_write = lomac_vnode_check_write, .mpo_vnode_copy_label = lomac_copy_label, .mpo_vnode_create_extattr = lomac_vnode_create_extattr, .mpo_vnode_destroy_label = lomac_destroy_label, .mpo_vnode_execve_transition = lomac_vnode_execve_transition, .mpo_vnode_execve_will_transition = lomac_vnode_execve_will_transition, .mpo_vnode_externalize_label = lomac_externalize_label, .mpo_vnode_init_label = lomac_init_label, .mpo_vnode_internalize_label = lomac_internalize_label, .mpo_vnode_relabel = lomac_vnode_relabel, .mpo_vnode_setlabel_extattr = lomac_vnode_setlabel_extattr, }; MAC_POLICY_SET(&lomac_ops, mac_lomac, "TrustedBSD MAC/LOMAC", MPC_LOADTIME_FLAG_NOTLATE, &lomac_slot); diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c index a0e71403b802..da9ed8a3e141 100644 --- a/sys/security/mac_mls/mac_mls.c +++ b/sys/security/mac_mls/mac_mls.c @@ -1,3424 +1,3423 @@ /*- * Copyright (c) 1999-2002, 2007-2011 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, 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$ */ /* * Developed by the TrustedBSD Project. * * MLS fixed label mandatory confidentiality policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, mls, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_mls policy controls"); static int mls_label_size = sizeof(struct mac_mls); SYSCTL_INT(_security_mac_mls, OID_AUTO, label_size, CTLFLAG_RD, &mls_label_size, 0, "Size of struct mac_mls"); static int mls_enabled = 1; SYSCTL_INT(_security_mac_mls, OID_AUTO, enabled, CTLFLAG_RWTUN, &mls_enabled, 0, "Enforce MAC/MLS policy"); static int destroyed_not_inited; SYSCTL_INT(_security_mac_mls, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, ptys_equal, CTLFLAG_RWTUN, &ptys_equal, 0, "Label pty devices as mls/equal on create"); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, revocation_enabled, CTLFLAG_RWTUN, &revocation_enabled, 0, "Revoke access to objects on relabel"); static int max_compartments = MAC_MLS_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_mls, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum compartments the policy supports"); static int mls_slot; #define SLOT(l) ((struct mac_mls *)mac_label_get((l), mls_slot)) #define SLOT_SET(l, val) mac_label_set((l), mls_slot, (uintptr_t)(val)) static uma_zone_t zone_mls; static __inline int mls_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_MLS_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_mls * mls_alloc(int flag) { return (uma_zalloc(zone_mls, flag | M_ZERO)); } static void mls_free(struct mac_mls *mm) { if (mm != NULL) uma_zfree(zone_mls, mm); else atomic_add_int(&destroyed_not_inited, 1); } static int mls_atmostflags(struct mac_mls *mm, int flags) { if ((mm->mm_flags & flags) != mm->mm_flags) return (EINVAL); return (0); } static int mls_dominate_element(struct mac_mls_element *a, struct mac_mls_element *b) { int bit; switch (a->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: return (1); case MAC_MLS_TYPE_LOW: switch (b->mme_type) { case MAC_MLS_TYPE_LEVEL: case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); default: panic("mls_dominate_element: b->mme_type invalid"); } case MAC_MLS_TYPE_LEVEL: switch (b->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_LEVEL: for (bit = 1; bit <= MAC_MLS_MAX_COMPARTMENTS; bit++) if (!MAC_MLS_BIT_TEST(bit, a->mme_compartments) && MAC_MLS_BIT_TEST(bit, b->mme_compartments)) return (0); return (a->mme_level >= b->mme_level); default: panic("mls_dominate_element: b->mme_type invalid"); } default: panic("mls_dominate_element: a->mme_type invalid"); } return (0); } static int mls_range_in_range(struct mac_mls *rangea, struct mac_mls *rangeb) { return (mls_dominate_element(&rangeb->mm_rangehigh, &rangea->mm_rangehigh) && mls_dominate_element(&rangea->mm_rangelow, &rangeb->mm_rangelow)); } static int mls_effective_in_range(struct mac_mls *effective, struct mac_mls *range) { KASSERT((effective->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_effective_in_range: a not effective")); KASSERT((range->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mls_effective_in_range: b not range")); return (mls_dominate_element(&range->mm_rangehigh, &effective->mm_effective) && mls_dominate_element(&effective->mm_effective, &range->mm_rangelow)); return (1); } static int mls_dominate_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_dominate_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_dominate_effective: b not effective")); return (mls_dominate_element(&a->mm_effective, &b->mm_effective)); } static int mls_equal_element(struct mac_mls_element *a, struct mac_mls_element *b) { if (a->mme_type == MAC_MLS_TYPE_EQUAL || b->mme_type == MAC_MLS_TYPE_EQUAL) return (1); return (a->mme_type == b->mme_type && a->mme_level == b->mme_level); } static int mls_equal_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_equal_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_equal_effective: b not effective")); return (mls_equal_element(&a->mm_effective, &b->mm_effective)); } static int mls_contains_equal(struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (1); } return (0); } static int mls_subject_privileged(struct mac_mls *mm) { KASSERT((mm->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH, ("mls_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mm->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL || mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mm->mm_rangelow.mme_type == MAC_MLS_TYPE_LOW && mm->mm_rangehigh.mme_type == MAC_MLS_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int mls_valid(struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { switch (mm->mm_effective.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_effective.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_effective.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mm->mm_effective.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { switch (mm->mm_rangelow.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_rangelow.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_rangelow.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mm->mm_rangehigh.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mm->mm_rangehigh.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mm->mm_rangehigh.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!mls_dominate_element(&mm->mm_rangehigh, &mm->mm_rangelow)) return (EINVAL); } else { if (mm->mm_rangelow.mme_type != MAC_MLS_TYPE_UNDEF || mm->mm_rangehigh.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } return (0); } static void mls_set_range(struct mac_mls *mm, u_short typelow, u_short levellow, u_char *compartmentslow, u_short typehigh, u_short levelhigh, u_char *compartmentshigh) { mm->mm_rangelow.mme_type = typelow; mm->mm_rangelow.mme_level = levellow; if (compartmentslow != NULL) memcpy(mm->mm_rangelow.mme_compartments, compartmentslow, sizeof(mm->mm_rangelow.mme_compartments)); mm->mm_rangehigh.mme_type = typehigh; mm->mm_rangehigh.mme_level = levelhigh; if (compartmentshigh != NULL) memcpy(mm->mm_rangehigh.mme_compartments, compartmentshigh, sizeof(mm->mm_rangehigh.mme_compartments)); mm->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mls_set_effective(struct mac_mls *mm, u_short type, u_short level, u_char *compartments) { mm->mm_effective.mme_type = type; mm->mm_effective.mme_level = level; if (compartments != NULL) memcpy(mm->mm_effective.mme_compartments, compartments, sizeof(mm->mm_effective.mme_compartments)); mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mls_copy_range(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mls_copy_range: labelfrom not range")); labelto->mm_rangelow = labelfrom->mm_rangelow; labelto->mm_rangehigh = labelfrom->mm_rangehigh; labelto->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mls_copy_effective(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mls_copy_effective: labelfrom not effective")); labelto->mm_effective = labelfrom->mm_effective; labelto->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mls_copy(struct mac_mls *source, struct mac_mls *dest) { if (source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) mls_copy_effective(source, dest); if (source->mm_flags & MAC_MLS_FLAG_RANGE) mls_copy_range(source, dest); } /* * Policy module operations. */ static void mls_init(struct mac_policy_conf *conf) { zone_mls = uma_zcreate("mac_mls", sizeof(struct mac_mls), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void mls_init_label(struct label *label) { SLOT_SET(label, mls_alloc(M_WAITOK)); } static int mls_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, mls_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void mls_destroy_label(struct label *label) { mls_free(SLOT(label)); SLOT_SET(label, NULL); } /* * mls_element_to_string() accepts an sbuf and MLS element. It converts the * MLS element to a string and stores the result in the sbuf; if there isn't * space in the sbuf, -1 is returned. */ static int mls_element_to_string(struct sbuf *sb, struct mac_mls_element *element) { int i, first; switch (element->mme_type) { case MAC_MLS_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_MLS_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_MLS_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_MLS_TYPE_LEVEL: if (sbuf_printf(sb, "%d", element->mme_level) == -1) return (-1); first = 1; for (i = 1; i <= MAC_MLS_MAX_COMPARTMENTS; i++) { if (MAC_MLS_BIT_TEST(i, element->mme_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("mls_element_to_string: invalid type (%d)", element->mme_type); } } /* * mls_to_string() converts an MLS label to a string, and places the results * in the passed sbuf. It returns 0 on success, or EINVAL if there isn't * room in the sbuf. Note: the sbuf will be modified even in a failure case, * so the caller may need to revert the sbuf by restoring the offset if * that's undesired. */ static int mls_to_string(struct sbuf *sb, struct mac_mls *mm) { if (mm->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { if (mls_element_to_string(sb, &mm->mm_effective) == -1) return (EINVAL); } if (mm->mm_flags & MAC_MLS_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (mls_element_to_string(sb, &mm->mm_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (mls_element_to_string(sb, &mm->mm_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int mls_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_mls *mm; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mm = SLOT(label); return (mls_to_string(sb, mm)); } static int mls_parse_element(struct mac_mls_element *element, char *string) { char *compartment, *end, *level; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mme_type = MAC_MLS_TYPE_HIGH; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mme_type = MAC_MLS_TYPE_LOW; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mme_type = MAC_MLS_TYPE_EQUAL; element->mme_level = MAC_MLS_TYPE_UNDEF; } else { element->mme_type = MAC_MLS_TYPE_LEVEL; /* * Numeric level piece of the element. */ level = strsep(&string, ":"); value = strtol(level, &end, 10); if (end == level || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mme_level = value; /* * Optional compartment piece of the element. If none are * included, we assume that the label has no compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_MLS_MAX_COMPARTMENTS) return (EINVAL); MAC_MLS_BIT_SET(value, element->mme_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before calling * if that's a problem. */ static int mls_parse(struct mac_mls *mm, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("mls_parse: range mismatch")); bzero(mm, sizeof(*mm)); if (effective != NULL) { error = mls_parse_element(&mm->mm_effective, effective); if (error) return (error); mm->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = mls_parse_element(&mm->mm_rangelow, rangelow); if (error) return (error); error = mls_parse_element(&mm->mm_rangehigh, rangehigh); if (error) return (error); mm->mm_flags |= MAC_MLS_FLAG_RANGE; } error = mls_valid(mm); if (error) return (error); return (0); } static int mls_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_mls *mm, mm_temp; int error; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = mls_parse(&mm_temp, element_data); if (error) return (error); mm = SLOT(label); *mm = mm_temp; return (0); } static void mls_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Object-specific entry point implementations are sorted alphabetically by * object type name and then by operation. */ static int mls_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { struct mac_mls *a, *b; if (!mls_enabled) return (0); a = SLOT(dlabel); b = SLOT(ifplabel); if (mls_equal_effective(a, b)) return (0); return (EACCES); } static void mls_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(dlabel); mls_copy_effective(source, dest); } static void mls_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(dlabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_cred_associate_nfsd(struct ucred *cred) { struct mac_mls *label; label = SLOT(cred->cr_label); mls_set_effective(label, MAC_MLS_TYPE_LOW, 0, NULL); mls_set_range(label, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static int mls_cred_check_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the credential, it may be an * update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { /* * If the change request modifies both the MLS label * effective and range, check that the new effective will be * in the new range. */ if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH && !mls_effective_in_range(new, new)) return (EINVAL); /* * To change the MLS effective label on a credential, the new * effective label must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE && !mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS range label on a credential, the new * range must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_RANGE && !mls_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential MLS * label, the subject must already have EQUAL in their label. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_cred_check_visible(struct ucred *cr1, struct ucred *cr2) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cr1->cr_label); obj = SLOT(cr2->cr_label); /* XXX: range */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); return (0); } static void mls_cred_create_init(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mls_set_effective(dest, MAC_MLS_TYPE_LOW, 0, NULL); mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mls_cred_create_swapper(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mls_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); mls_copy(source, dest); } static void mls_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *mm; const char *dn; int mls_type; mm = SLOT(delabel); dn = devtoname(dev); if (strcmp(dn, "null") == 0 || strcmp(dn, "zero") == 0 || strcmp(dn, "random") == 0 || strncmp(dn, "fd/", strlen("fd/")) == 0) mls_type = MAC_MLS_TYPE_EQUAL; else if (strcmp(dn, "kmem") == 0 || strcmp(dn, "mem") == 0) mls_type = MAC_MLS_TYPE_HIGH; else if (ptys_equal && (strncmp(dn, "ttyp", strlen("ttyp")) == 0 || strncmp(dn, "pts/", strlen("pts/")) == 0 || strncmp(dn, "ptyp", strlen("ptyp")) == 0)) mls_type = MAC_MLS_TYPE_EQUAL; else mls_type = MAC_MLS_TYPE_LOW; mls_set_effective(mm, mls_type, 0, NULL); } static void mls_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *mm; mm = SLOT(delabel); mls_set_effective(mm, MAC_MLS_TYPE_LOW, 0, NULL); } static void mls_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); mls_copy_effective(source, dest); } static void mls_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(vplabel); dest = SLOT(delabel); mls_copy_effective(source, dest); } static void mls_devfs_vnode_associate(struct mount *mp, struct label *mplabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(delabel); dest = SLOT(vplabel); mls_copy_effective(source, dest); } static int mls_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the interface, it may be an * update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * Relabeling network interfaces requires MLS privilege. */ return (mls_subject_privileged(subj)); } static int mls_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *i; if (!mls_enabled) return (0); p = SLOT(mlabel); i = SLOT(ifplabel); return (mls_effective_in_range(p, i) ? 0 : EACCES); } static void mls_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { struct mac_mls *dest; int type; dest = SLOT(ifplabel); if (ifp->if_type == IFT_LOOP) type = MAC_MLS_TYPE_EQUAL; else type = MAC_MLS_TYPE_LOW; mls_set_effective(dest, type, 0, NULL); mls_set_range(dest, type, 0, NULL, type, 0, NULL); } static void mls_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(ifplabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(ifplabel); mls_copy(source, dest); } static int mls_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *i; if (!mls_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (mls_equal_effective(p, i) ? 0 : EACCES); } static int mls_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(inplabel); if (!mls_dominate_effective(subj, obj)) return (ENOENT); return (0); } static void mls_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mls_copy_effective(source, dest); } static void mls_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static void mls_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(solabel); dest = SLOT(inplabel); mls_copy(source, dest); } static void mls_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(q6label); mls_copy_effective(source, dest); } static int mls_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { struct mac_mls *a, *b; a = SLOT(q6label); b = SLOT(mlabel); return (mls_equal_effective(a, b)); } static void mls_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(q6label); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ mls_copy_effective(source, dest); } static void mls_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { /* NOOP: we only accept matching labels, so no need to update */ } static void mls_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(qlabel); mls_copy_effective(source, dest); } static int mls_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { struct mac_mls *a, *b; a = SLOT(qlabel); b = SLOT(mlabel); return (mls_equal_effective(a, b)); } static void mls_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(qlabel); dest = SLOT(mlabel); /* Just use the head, since we require them all to match. */ mls_copy_effective(source, dest); } static void mls_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static int mls_mount_check_stat(struct ucred *cred, struct mount *mp, struct label *mntlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mntlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mplabel); mls_copy_effective(source, dest); } static void mls_netinet_arp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_mls *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); mls_copy_effective(source, dest); } static void mls_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); /* XXX: where is the label for the firewall really coming from? */ mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(fraglabel); mls_copy_effective(source, dest); } static void mls_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { struct mac_mls *source, *dest; source = SLOT(mrecvlabel); dest = SLOT(msendlabel); mls_copy_effective(source, dest); } static void mls_netinet_igmp_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mls_netinet6_nd6_send(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *dest; dest = SLOT(mlabel); mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static int mls_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { if (!mls_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int mls_pipe_check_poll(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); /* * If there is an MLS label update for a pipe, it must be a effective * update. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (MLS label or not), MLS must * authorize the relabel. */ if (!mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a pipe, the new pipe label must * be in the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on a pipe to be EQUAL, the subject * must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_pipe_check_stat(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(pplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pplabel); mls_copy_effective(source, dest); } static void mls_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(pplabel); mls_copy(source, dest); } static int mls_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, struct label *shmlabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(kslabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_posixsem_create(struct ucred *cred, struct ksem *ks, struct label *kslabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(kslabel); mls_copy_effective(source, dest); } static int mls_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, int prot, int flags) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(shmlabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mls_copy_effective(source, dest); } static int mls_proc_check_debug(struct ucred *cred, struct proc *p) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_proc_check_sched(struct ucred *cred, struct proc *p) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(p->p_ucred->cr_label); /* XXX: range checks */ if (!mls_dominate_effective(subj, obj)) return (ESRCH); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *s; int error; if (!mls_enabled) return (0); p = SLOT(mlabel); s = SLOT(solabel); SOCK_LOCK(so); error = mls_equal_effective(p, s) ? 0 : EACCES; SOCK_UNLOCK(so); return (error); } static int mls_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; SOCK_LOCK_ASSERT(so); new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(solabel); /* * If there is an MLS label update for the socket, it may be an * update of effective. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the * subject range. */ if (!mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(solabel); SOCK_LOCK(so); if (!mls_dominate_effective(subj, obj)) { SOCK_UNLOCK(so); return (ENOENT); } SOCK_UNLOCK(so); return (0); } static void mls_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(solabel); mls_copy_effective(source, dest); } static void mls_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(mlabel); SOCK_LOCK(so); mls_copy_effective(source, dest); SOCK_UNLOCK(so); } static void mls_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { struct mac_mls source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsolabel); SOCK_LOCK(newso); mls_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void mls_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { struct mac_mls *source, *dest; SOCK_LOCK_ASSERT(so); source = SLOT(newlabel); dest = SLOT(solabel); mls_copy(source, dest); } static void mls_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { struct mac_mls *source, *dest; source = SLOT(mlabel); dest = SLOT(sopeerlabel); SOCK_LOCK(so); mls_copy_effective(source, dest); SOCK_UNLOCK(so); } static void mls_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { struct mac_mls source, *dest; SOCK_LOCK(oldso); source = *SLOT(oldsolabel); SOCK_UNLOCK(oldso); dest = SLOT(newsopeerlabel); SOCK_LOCK(newso); mls_copy_effective(&source, dest); SOCK_UNLOCK(newso); } static void mls_syncache_create(struct label *label, struct inpcb *inp) { struct mac_mls *source, *dest; source = SLOT(inp->inp_label); dest = SLOT(label); mls_copy_effective(source, dest); } static void mls_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(sc_label); dest = SLOT(mlabel); mls_copy_effective(source, dest); } static int mls_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); if (vplabel == NULL) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj) || !mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mls_sysvmsg_cleanup(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_mls)); } static void mls_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_mls *source, *dest; /* Ignore the msgq label. */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); mls_copy_effective(source, dest); } static int mls_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static void mls_sysvmsq_cleanup(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_mls)); } static void mls_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); mls_copy_effective(source, dest); } static int mls_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mls_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if( accesstype & SEM_R ) if (!mls_dominate_effective(subj, obj)) return (EACCES); if( accesstype & SEM_A ) if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_sysvsem_cleanup(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_mls)); } static void mls_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); mls_copy_effective(source, dest); } static int mls_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } - + return (0); } static int mls_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mls_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mls_sysvshm_cleanup(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_mls)); } static void mls_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mls_copy_effective(source, dest); } static int mls_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_mls mm_temp, *source, *dest; int buflen, error; source = SLOT(mplabel); dest = SLOT(vplabel); buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, &buflen, (char *) &mm_temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the mntlabel. */ mls_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(mm_temp)) { printf("mls_vnode_associate_extattr: bad size %d\n", buflen); return (EPERM); } if (mls_valid(&mm_temp) != 0) { printf("mls_vnode_associate_extattr: invalid\n"); return (EPERM); } if ((mm_temp.mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_EFFECTIVE) { printf("mls_associated_vnode_extattr: not effective\n"); return (EPERM); } mls_copy_effective(&mm_temp, dest); return (0); } static void mls_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { struct mac_mls *source, *dest; source = SLOT(mplabel); dest = SLOT(vplabel); mls_copy_effective(source, dest); } static int mls_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel) { struct mac_mls *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of MLS, so disallow non-NULL MLS label * elements in the execlabel. */ exec = SLOT(execlabel); error = mls_atmostflags(exec, 0); if (error) return (error); } if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { struct mac_mls *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); /* XXX privilege override for admin? */ if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } if (accmode & VMODIFY_PERMS) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_readdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_readlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { struct mac_mls *old, *new, *subj; int error; old = SLOT(vplabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is an MLS label update for the vnode, it must be a * effective label. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (MLS label or not), MLS must * authorize the relabel. */ if (!mls_effective_in_range(old, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a vnode, the new vnode label * must be in the subject range. */ if (!mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the vnode to be EQUAL, the * subject must have appropriate privilege. */ if (mls_contains_equal(new)) { error = mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mls_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); if (vp != NULL) { obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mls_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int mls_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mls_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dvplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { struct mac_mls *subj, *obj; if (!mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vplabel); if (!mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mls_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mplabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { struct mac_mls *source, *dest, mm_temp; size_t buflen; int error; buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vplabel); mls_copy_effective(source, &mm_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); if (error == 0) mls_copy_effective(source, dest); return (error); } static void mls_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *label) { struct mac_mls *source, *dest; source = SLOT(label); dest = SLOT(vplabel); mls_copy(source, dest); } static int mls_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { struct mac_mls *source, mm_temp; size_t buflen; int error; buflen = sizeof(mm_temp); bzero(&mm_temp, buflen); source = SLOT(intlabel); if ((source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) == 0) return (0); mls_copy_effective(source, &mm_temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &mm_temp, curthread); return (error); } static struct mac_policy_ops mls_ops = { .mpo_init = mls_init, .mpo_bpfdesc_check_receive = mls_bpfdesc_check_receive, .mpo_bpfdesc_create = mls_bpfdesc_create, .mpo_bpfdesc_create_mbuf = mls_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = mls_destroy_label, .mpo_bpfdesc_init_label = mls_init_label, .mpo_cred_associate_nfsd = mls_cred_associate_nfsd, .mpo_cred_check_relabel = mls_cred_check_relabel, .mpo_cred_check_visible = mls_cred_check_visible, .mpo_cred_copy_label = mls_copy_label, .mpo_cred_create_init = mls_cred_create_init, .mpo_cred_create_swapper = mls_cred_create_swapper, .mpo_cred_destroy_label = mls_destroy_label, .mpo_cred_externalize_label = mls_externalize_label, .mpo_cred_init_label = mls_init_label, .mpo_cred_internalize_label = mls_internalize_label, .mpo_cred_relabel = mls_cred_relabel, .mpo_devfs_create_device = mls_devfs_create_device, .mpo_devfs_create_directory = mls_devfs_create_directory, .mpo_devfs_create_symlink = mls_devfs_create_symlink, .mpo_devfs_destroy_label = mls_destroy_label, .mpo_devfs_init_label = mls_init_label, .mpo_devfs_update = mls_devfs_update, .mpo_devfs_vnode_associate = mls_devfs_vnode_associate, .mpo_ifnet_check_relabel = mls_ifnet_check_relabel, .mpo_ifnet_check_transmit = mls_ifnet_check_transmit, .mpo_ifnet_copy_label = mls_copy_label, .mpo_ifnet_create = mls_ifnet_create, .mpo_ifnet_create_mbuf = mls_ifnet_create_mbuf, .mpo_ifnet_destroy_label = mls_destroy_label, .mpo_ifnet_externalize_label = mls_externalize_label, .mpo_ifnet_init_label = mls_init_label, .mpo_ifnet_internalize_label = mls_internalize_label, .mpo_ifnet_relabel = mls_ifnet_relabel, .mpo_inpcb_check_deliver = mls_inpcb_check_deliver, .mpo_inpcb_check_visible = mls_inpcb_check_visible, .mpo_inpcb_create = mls_inpcb_create, .mpo_inpcb_create_mbuf = mls_inpcb_create_mbuf, .mpo_inpcb_destroy_label = mls_destroy_label, .mpo_inpcb_init_label = mls_init_label_waitcheck, .mpo_inpcb_sosetlabel = mls_inpcb_sosetlabel, .mpo_ip6q_create = mls_ip6q_create, .mpo_ip6q_destroy_label = mls_destroy_label, .mpo_ip6q_init_label = mls_init_label_waitcheck, .mpo_ip6q_match = mls_ip6q_match, .mpo_ip6q_reassemble = mls_ip6q_reassemble, .mpo_ip6q_update = mls_ip6q_update, .mpo_ipq_create = mls_ipq_create, .mpo_ipq_destroy_label = mls_destroy_label, .mpo_ipq_init_label = mls_init_label_waitcheck, .mpo_ipq_match = mls_ipq_match, .mpo_ipq_reassemble = mls_ipq_reassemble, .mpo_ipq_update = mls_ipq_update, .mpo_mbuf_copy_label = mls_copy_label, .mpo_mbuf_destroy_label = mls_destroy_label, .mpo_mbuf_init_label = mls_init_label_waitcheck, .mpo_mount_check_stat = mls_mount_check_stat, .mpo_mount_create = mls_mount_create, .mpo_mount_destroy_label = mls_destroy_label, .mpo_mount_init_label = mls_init_label, .mpo_netinet_arp_send = mls_netinet_arp_send, .mpo_netinet_firewall_reply = mls_netinet_firewall_reply, .mpo_netinet_firewall_send = mls_netinet_firewall_send, .mpo_netinet_fragment = mls_netinet_fragment, .mpo_netinet_icmp_reply = mls_netinet_icmp_reply, .mpo_netinet_igmp_send = mls_netinet_igmp_send, .mpo_netinet6_nd6_send = mls_netinet6_nd6_send, .mpo_pipe_check_ioctl = mls_pipe_check_ioctl, .mpo_pipe_check_poll = mls_pipe_check_poll, .mpo_pipe_check_read = mls_pipe_check_read, .mpo_pipe_check_relabel = mls_pipe_check_relabel, .mpo_pipe_check_stat = mls_pipe_check_stat, .mpo_pipe_check_write = mls_pipe_check_write, .mpo_pipe_copy_label = mls_copy_label, .mpo_pipe_create = mls_pipe_create, .mpo_pipe_destroy_label = mls_destroy_label, .mpo_pipe_externalize_label = mls_externalize_label, .mpo_pipe_init_label = mls_init_label, .mpo_pipe_internalize_label = mls_internalize_label, .mpo_pipe_relabel = mls_pipe_relabel, .mpo_posixsem_check_getvalue = mls_posixsem_check_rdonly, .mpo_posixsem_check_open = mls_posixsem_check_openunlink, .mpo_posixsem_check_post = mls_posixsem_check_write, .mpo_posixsem_check_setmode = mls_posixsem_check_setmode, .mpo_posixsem_check_setowner = mls_posixsem_check_setowner, .mpo_posixsem_check_stat = mls_posixsem_check_rdonly, .mpo_posixsem_check_unlink = mls_posixsem_check_openunlink, .mpo_posixsem_check_wait = mls_posixsem_check_write, .mpo_posixsem_create = mls_posixsem_create, .mpo_posixsem_destroy_label = mls_destroy_label, .mpo_posixsem_init_label = mls_init_label, .mpo_posixshm_check_mmap = mls_posixshm_check_mmap, .mpo_posixshm_check_open = mls_posixshm_check_open, .mpo_posixshm_check_read = mls_posixshm_check_read, .mpo_posixshm_check_setmode = mls_posixshm_check_setmode, .mpo_posixshm_check_setowner = mls_posixshm_check_setowner, .mpo_posixshm_check_stat = mls_posixshm_check_stat, .mpo_posixshm_check_truncate = mls_posixshm_check_truncate, .mpo_posixshm_check_unlink = mls_posixshm_check_unlink, .mpo_posixshm_check_write = mls_posixshm_check_write, .mpo_posixshm_create = mls_posixshm_create, .mpo_posixshm_destroy_label = mls_destroy_label, .mpo_posixshm_init_label = mls_init_label, .mpo_proc_check_debug = mls_proc_check_debug, .mpo_proc_check_sched = mls_proc_check_sched, .mpo_proc_check_signal = mls_proc_check_signal, .mpo_socket_check_deliver = mls_socket_check_deliver, .mpo_socket_check_relabel = mls_socket_check_relabel, .mpo_socket_check_visible = mls_socket_check_visible, .mpo_socket_copy_label = mls_copy_label, .mpo_socket_create = mls_socket_create, .mpo_socket_create_mbuf = mls_socket_create_mbuf, .mpo_socket_destroy_label = mls_destroy_label, .mpo_socket_externalize_label = mls_externalize_label, .mpo_socket_init_label = mls_init_label_waitcheck, .mpo_socket_internalize_label = mls_internalize_label, .mpo_socket_newconn = mls_socket_newconn, .mpo_socket_relabel = mls_socket_relabel, .mpo_socketpeer_destroy_label = mls_destroy_label, .mpo_socketpeer_externalize_label = mls_externalize_label, .mpo_socketpeer_init_label = mls_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = mls_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = mls_socketpeer_set_from_socket, .mpo_syncache_create = mls_syncache_create, .mpo_syncache_create_mbuf = mls_syncache_create_mbuf, .mpo_syncache_destroy_label = mls_destroy_label, .mpo_syncache_init_label = mls_init_label_waitcheck, .mpo_sysvmsg_cleanup = mls_sysvmsg_cleanup, .mpo_sysvmsg_create = mls_sysvmsg_create, .mpo_sysvmsg_destroy_label = mls_destroy_label, .mpo_sysvmsg_init_label = mls_init_label, .mpo_sysvmsq_check_msgrcv = mls_sysvmsq_check_msgrcv, .mpo_sysvmsq_check_msgrmid = mls_sysvmsq_check_msgrmid, .mpo_sysvmsq_check_msqget = mls_sysvmsq_check_msqget, .mpo_sysvmsq_check_msqsnd = mls_sysvmsq_check_msqsnd, .mpo_sysvmsq_check_msqrcv = mls_sysvmsq_check_msqrcv, .mpo_sysvmsq_check_msqctl = mls_sysvmsq_check_msqctl, .mpo_sysvmsq_cleanup = mls_sysvmsq_cleanup, .mpo_sysvmsq_destroy_label = mls_destroy_label, .mpo_sysvmsq_init_label = mls_init_label, .mpo_sysvmsq_create = mls_sysvmsq_create, .mpo_sysvsem_check_semctl = mls_sysvsem_check_semctl, .mpo_sysvsem_check_semget = mls_sysvsem_check_semget, .mpo_sysvsem_check_semop = mls_sysvsem_check_semop, .mpo_sysvsem_cleanup = mls_sysvsem_cleanup, .mpo_sysvsem_create = mls_sysvsem_create, .mpo_sysvsem_destroy_label = mls_destroy_label, .mpo_sysvsem_init_label = mls_init_label, .mpo_sysvshm_check_shmat = mls_sysvshm_check_shmat, .mpo_sysvshm_check_shmctl = mls_sysvshm_check_shmctl, .mpo_sysvshm_check_shmget = mls_sysvshm_check_shmget, .mpo_sysvshm_cleanup = mls_sysvshm_cleanup, .mpo_sysvshm_create = mls_sysvshm_create, .mpo_sysvshm_destroy_label = mls_destroy_label, .mpo_sysvshm_init_label = mls_init_label, - .mpo_system_check_acct = mls_system_check_acct, .mpo_system_check_auditctl = mls_system_check_auditctl, .mpo_system_check_swapon = mls_system_check_swapon, .mpo_vnode_associate_extattr = mls_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = mls_vnode_associate_singlelabel, .mpo_vnode_check_access = mls_vnode_check_open, .mpo_vnode_check_chdir = mls_vnode_check_chdir, .mpo_vnode_check_chroot = mls_vnode_check_chroot, .mpo_vnode_check_create = mls_vnode_check_create, .mpo_vnode_check_deleteacl = mls_vnode_check_deleteacl, .mpo_vnode_check_deleteextattr = mls_vnode_check_deleteextattr, .mpo_vnode_check_exec = mls_vnode_check_exec, .mpo_vnode_check_getacl = mls_vnode_check_getacl, .mpo_vnode_check_getextattr = mls_vnode_check_getextattr, .mpo_vnode_check_link = mls_vnode_check_link, .mpo_vnode_check_listextattr = mls_vnode_check_listextattr, .mpo_vnode_check_lookup = mls_vnode_check_lookup, .mpo_vnode_check_mmap = mls_vnode_check_mmap, .mpo_vnode_check_open = mls_vnode_check_open, .mpo_vnode_check_poll = mls_vnode_check_poll, .mpo_vnode_check_read = mls_vnode_check_read, .mpo_vnode_check_readdir = mls_vnode_check_readdir, .mpo_vnode_check_readlink = mls_vnode_check_readlink, .mpo_vnode_check_relabel = mls_vnode_check_relabel, .mpo_vnode_check_rename_from = mls_vnode_check_rename_from, .mpo_vnode_check_rename_to = mls_vnode_check_rename_to, .mpo_vnode_check_revoke = mls_vnode_check_revoke, .mpo_vnode_check_setacl = mls_vnode_check_setacl, .mpo_vnode_check_setextattr = mls_vnode_check_setextattr, .mpo_vnode_check_setflags = mls_vnode_check_setflags, .mpo_vnode_check_setmode = mls_vnode_check_setmode, .mpo_vnode_check_setowner = mls_vnode_check_setowner, .mpo_vnode_check_setutimes = mls_vnode_check_setutimes, .mpo_vnode_check_stat = mls_vnode_check_stat, .mpo_vnode_check_unlink = mls_vnode_check_unlink, .mpo_vnode_check_write = mls_vnode_check_write, .mpo_vnode_copy_label = mls_copy_label, .mpo_vnode_create_extattr = mls_vnode_create_extattr, .mpo_vnode_destroy_label = mls_destroy_label, .mpo_vnode_externalize_label = mls_externalize_label, .mpo_vnode_init_label = mls_init_label, .mpo_vnode_internalize_label = mls_internalize_label, .mpo_vnode_relabel = mls_vnode_relabel, .mpo_vnode_setlabel_extattr = mls_vnode_setlabel_extattr, }; MAC_POLICY_SET(&mls_ops, mac_mls, "TrustedBSD MAC/MLS", MPC_LOADTIME_FLAG_NOTLATE, &mls_slot); diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c index 6ee3c34dcc94..8174345f073a 100644 --- a/sys/security/mac_stub/mac_stub.c +++ b/sys/security/mac_stub/mac_stub.c @@ -1,1938 +1,1931 @@ /*- * Copyright (c) 1999-2002, 2007-2011 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * Copyright (c) 2005-2006 SPARTA, Inc. * Copyright (c) 2008 Apple Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, 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$ */ /* * Developed by the TrustedBSD Project. * * Stub module that implements a NOOP for most (if not all) MAC Framework * policy entry points. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); static SYSCTL_NODE(_security_mac, OID_AUTO, stub, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "TrustedBSD mac_stub policy controls"); static int stub_enabled = 1; SYSCTL_INT(_security_mac_stub, OID_AUTO, enabled, CTLFLAG_RW, &stub_enabled, 0, "Enforce mac_stub policy"); /* * Policy module operations. */ static void stub_destroy(struct mac_policy_conf *conf) { } static void stub_init(struct mac_policy_conf *conf) { } static int stub_syscall(struct thread *td, int call, void *arg) { return (0); } /* * Label operations. */ static void stub_init_label(struct label *label) { } static int stub_init_label_waitcheck(struct label *label, int flag) { return (0); } static void stub_destroy_label(struct label *label) { } static void stub_copy_label(struct label *src, struct label *dest) { } static int stub_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { return (0); } static int stub_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { return (0); } /* * Object-specific entry point imeplementations are sorted alphabetically by * object type name and then by operation. */ static int stub_bpfdesc_check_receive(struct bpf_d *d, struct label *dlabel, struct ifnet *ifp, struct label *ifplabel) { return (0); } static void stub_bpfdesc_create(struct ucred *cred, struct bpf_d *d, struct label *dlabel) { } static void stub_bpfdesc_create_mbuf(struct bpf_d *d, struct label *dlabel, struct mbuf *m, struct label *mlabel) { } static void stub_cred_associate_nfsd(struct ucred *cred) { } static int stub_cred_check_relabel(struct ucred *cred, struct label *newlabel) { return (0); } static int stub_cred_check_setaudit(struct ucred *cred, struct auditinfo *ai) { return (0); } static int stub_cred_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia) { return (0); } static int stub_cred_check_setauid(struct ucred *cred, uid_t auid) { return (0); } static int stub_cred_check_setegid(struct ucred *cred, gid_t egid) { return (0); } static int stub_cred_check_seteuid(struct ucred *cred, uid_t euid) { return (0); } static int stub_cred_check_setgid(struct ucred *cred, gid_t gid) { return (0); } static int stub_cred_check_setgroups(struct ucred *cred, int ngroups, gid_t *gidset) { return (0); } static int stub_cred_check_setregid(struct ucred *cred, gid_t rgid, gid_t egid) { return (0); } static int stub_cred_check_setresgid(struct ucred *cred, gid_t rgid, gid_t egid, gid_t sgid) { return (0); } static int stub_cred_check_setresuid(struct ucred *cred, uid_t ruid, uid_t euid, uid_t suid) { return (0); } static int stub_cred_check_setreuid(struct ucred *cred, uid_t ruid, uid_t euid) { return (0); } static int stub_cred_check_setuid(struct ucred *cred, uid_t uid) { return (0); } static int stub_cred_check_visible(struct ucred *cr1, struct ucred *cr2) { return (0); } static void stub_cred_create_init(struct ucred *cred) { } static void stub_cred_create_swapper(struct ucred *cred) { } static void stub_cred_relabel(struct ucred *cred, struct label *newlabel) { } static void stub_devfs_create_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *delabel) { } static void stub_devfs_create_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *delabel) { } static void stub_devfs_create_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { } static void stub_devfs_update(struct mount *mp, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { } static void stub_devfs_vnode_associate(struct mount *mp, struct label *mplabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vplabel) { } static int stub_ifnet_check_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { return (0); } static int stub_ifnet_check_transmit(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { return (0); } static void stub_ifnet_create(struct ifnet *ifp, struct label *ifplabel) { } static void stub_ifnet_create_mbuf(struct ifnet *ifp, struct label *ifplabel, struct mbuf *m, struct label *mlabel) { } static void stub_ifnet_relabel(struct ucred *cred, struct ifnet *ifp, struct label *ifplabel, struct label *newlabel) { } static int stub_inpcb_check_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { return (0); } static void stub_inpcb_create(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { } static void stub_inpcb_create_mbuf(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { } static void stub_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { SOCK_LOCK_ASSERT(so); } static void stub_ip6q_create(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { } static int stub_ip6q_match(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { return (1); } static void stub_ip6q_reassemble(struct ip6q *q6, struct label *q6label, struct mbuf *m, struct label *mlabel) { } static void stub_ip6q_update(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label) { } static void stub_ipq_create(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { } static int stub_ipq_match(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { return (1); } static void stub_ipq_reassemble(struct ipq *q, struct label *qlabel, struct mbuf *m, struct label *mlabel) { } static void stub_ipq_update(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel) { } static int stub_kenv_check_dump(struct ucred *cred) { return (0); } static int stub_kenv_check_get(struct ucred *cred, char *name) { return (0); } static int stub_kenv_check_set(struct ucred *cred, char *name, char *value) { return (0); } static int stub_kenv_check_unset(struct ucred *cred, char *name) { return (0); } static int stub_kld_check_load(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_kld_check_stat(struct ucred *cred) { return (0); } static int stub_mount_check_stat(struct ucred *cred, struct mount *mp, struct label *mplabel) { return (0); } static void stub_mount_create(struct ucred *cred, struct mount *mp, struct label *mplabel) { } static void stub_netinet_arp_send(struct ifnet *ifp, struct label *iflpabel, struct mbuf *m, struct label *mlabel) { } static void stub_netinet_firewall_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { } static void stub_netinet_firewall_send(struct mbuf *m, struct label *mlabel) { } static void stub_netinet_fragment(struct mbuf *m, struct label *mlabel, struct mbuf *frag, struct label *fraglabel) { } static void stub_netinet_icmp_reply(struct mbuf *mrecv, struct label *mrecvlabel, struct mbuf *msend, struct label *msendlabel) { } static void stub_netinet_icmp_replyinplace(struct mbuf *m, struct label *mlabel) { } static void stub_netinet_igmp_send(struct ifnet *ifp, struct label *iflpabel, struct mbuf *m, struct label *mlabel) { } static void stub_netinet_tcp_reply(struct mbuf *m, struct label *mlabel) { } static void stub_netinet6_nd6_send(struct ifnet *ifp, struct label *iflpabel, struct mbuf *m, struct label *mlabel) { } static int stub_pipe_check_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pplabel, unsigned long cmd, void /* caddr_t */ *data) { return (0); } static int stub_pipe_check_poll(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { return (0); } static int stub_pipe_check_read(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { return (0); } static int stub_pipe_check_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { return (0); } static int stub_pipe_check_stat(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { return (0); } static int stub_pipe_check_write(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { return (0); } static void stub_pipe_create(struct ucred *cred, struct pipepair *pp, struct label *pplabel) { } static void stub_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pplabel, struct label *newlabel) { } static int stub_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { return (0); } static int stub_posixsem_check_open(struct ucred *cred, struct ksem *ks, struct label *kslabel) { return (0); } static int stub_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { return (0); } static int stub_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, struct label *kslabel, mode_t mode) { return (0); } static int stub_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, struct label *kslabel, uid_t uid, gid_t gid) { return (0); } static int stub_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { return (0); } static int stub_posixsem_check_unlink(struct ucred *cred, struct ksem *ks, struct label *kslabel) { return (0); } static int stub_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred, struct ksem *ks, struct label *kslabel) { return (0); } static void stub_posixsem_create(struct ucred *cred, struct ksem *ks, struct label *kslabel) { } static int stub_posixshm_check_create(struct ucred *cred, const char *path) { return (0); } static int stub_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, int prot, int flags) { return (0); } static int stub_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode) { return (0); } static int stub_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { return (0); } static int stub_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) { return (0); } static int stub_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, uid_t uid, gid_t gid) { return (0); } static int stub_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { return (0); } static int stub_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd, struct label *shmlabel) { return (0); } static int stub_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { return (0); } static int stub_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) { return (0); } static void stub_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) { } static int stub_priv_check(struct ucred *cred, int priv) { return (0); } static int stub_priv_grant(struct ucred *cred, int priv) { return (EPERM); } static int stub_proc_check_debug(struct ucred *cred, struct proc *p) { return (0); } static int stub_proc_check_sched(struct ucred *cred, struct proc *p) { return (0); } static int stub_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { return (0); } static int stub_proc_check_wait(struct ucred *cred, struct proc *p) { return (0); } static int stub_socket_check_accept(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_bind(struct ucred *cred, struct socket *so, struct label *solabel, struct sockaddr *sa) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_connect(struct ucred *cred, struct socket *so, struct label *solabel, struct sockaddr *sa) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_create(struct ucred *cred, int domain, int type, int proto) { return (0); } static int stub_socket_check_deliver(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_listen(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_poll(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_receive(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { SOCK_LOCK_ASSERT(so); return (0); } static int stub_socket_check_send(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_socket_check_stat(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static int stub_inpcb_check_visible(struct ucred *cred, struct inpcb *inp, struct label *inplabel) { return (0); } static int stub_socket_check_visible(struct ucred *cred, struct socket *so, struct label *solabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif return (0); } static void stub_socket_create(struct ucred *cred, struct socket *so, struct label *solabel) { } static void stub_socket_create_mbuf(struct socket *so, struct label *solabel, struct mbuf *m, struct label *mlabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif } static void stub_socket_newconn(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsolabel) { #if 0 SOCK_LOCK(oldso); SOCK_UNLOCK(oldso); #endif #if 0 SOCK_LOCK(newso); SOCK_UNLOCK(newso); #endif } static void stub_socket_relabel(struct ucred *cred, struct socket *so, struct label *solabel, struct label *newlabel) { SOCK_LOCK_ASSERT(so); } static void stub_socketpeer_set_from_mbuf(struct mbuf *m, struct label *mlabel, struct socket *so, struct label *sopeerlabel) { #if 0 SOCK_LOCK(so); SOCK_UNLOCK(so); #endif } static void stub_socketpeer_set_from_socket(struct socket *oldso, struct label *oldsolabel, struct socket *newso, struct label *newsopeerlabel) { #if 0 SOCK_LOCK(oldso); SOCK_UNLOCK(oldso); #endif #if 0 SOCK_LOCK(newso); SOCK_UNLOCK(newso); #endif } static void stub_syncache_create(struct label *label, struct inpcb *inp) { } static void stub_syncache_create_mbuf(struct label *sc_label, struct mbuf *m, struct label *mlabel) { } static int stub_system_check_acct(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_system_check_audit(struct ucred *cred, void *record, int length) { return (0); } static int stub_system_check_auditctl(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_system_check_auditon(struct ucred *cred, int cmd) { return (0); } static int stub_system_check_reboot(struct ucred *cred, int how) { return (0); } static int stub_system_check_swapoff(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_system_check_swapon(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_system_check_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { return (0); } static void stub_sysvmsg_cleanup(struct label *msglabel) { } static void stub_sysvmsg_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { } static int stub_sysvmsq_check_msgmsq(struct ucred *cred, struct msg *msgptr, struct label *msglabel, struct msqid_kernel *msqkptr, struct label *msqklabel) { return (0); } static int stub_sysvmsq_check_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { return (0); } - static int stub_sysvmsq_check_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { return (0); } - static int stub_sysvmsq_check_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { return (0); } - static int stub_sysvmsq_check_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { return (0); } static int stub_sysvmsq_check_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { return (0); } - static int stub_sysvmsq_check_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { return (0); } - static void stub_sysvmsq_cleanup(struct label *msqlabel) { } static void stub_sysvmsq_create(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { } static int stub_sysvsem_check_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { return (0); } static int stub_sysvsem_check_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { return (0); } - static int stub_sysvsem_check_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { return (0); } static void stub_sysvsem_cleanup(struct label *semalabel) { } static void stub_sysvsem_create(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { } static int stub_sysvshm_check_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { return (0); } static int stub_sysvshm_check_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { return (0); } static int stub_sysvshm_check_shmdt(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel) { return (0); } - static int stub_sysvshm_check_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { return (0); } static void stub_sysvshm_cleanup(struct label *shmlabel) { } static void stub_sysvshm_create(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmalabel) { } static void stub_thread_userret(struct thread *td) { } static int stub_vnode_associate_extattr(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { return (0); } static void stub_vnode_associate_singlelabel(struct mount *mp, struct label *mplabel, struct vnode *vp, struct label *vplabel) { } static int stub_vnode_check_access(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { return (0); } static int stub_vnode_check_chdir(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { return (0); } static int stub_vnode_check_chroot(struct ucred *cred, struct vnode *dvp, struct label *dvplabel) { return (0); } static int stub_vnode_check_create(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp, struct vattr *vap) { return (0); } static int stub_vnode_check_deleteacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { return (0); } static int stub_vnode_check_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { return (0); } static int stub_vnode_check_exec(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct image_params *imgp, struct label *execlabel) { return (0); } static int stub_vnode_check_getacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type) { return (0); } static int stub_vnode_check_getextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { return (0); } static int stub_vnode_check_link(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { return (0); } static int stub_vnode_check_listextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace) { return (0); } static int stub_vnode_check_lookup(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct componentname *cnp) { return (0); } static int stub_vnode_check_mmap(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot, int flags) { return (0); } static void stub_vnode_check_mmap_downgrade(struct ucred *cred, struct vnode *vp, struct label *vplabel, int *prot) { } static int stub_vnode_check_mprotect(struct ucred *cred, struct vnode *vp, struct label *vplabel, int prot) { return (0); } static int stub_vnode_check_open(struct ucred *cred, struct vnode *vp, struct label *vplabel, accmode_t accmode) { return (0); } static int stub_vnode_check_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_check_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_check_readdir(struct ucred *cred, struct vnode *vp, struct label *dvplabel) { return (0); } static int stub_vnode_check_readlink(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_check_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *newlabel) { return (0); } static int stub_vnode_check_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { return (0); } static int stub_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, int samedir, struct componentname *cnp) { return (0); } static int stub_vnode_check_revoke(struct ucred *cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_check_setacl(struct ucred *cred, struct vnode *vp, struct label *vplabel, acl_type_t type, struct acl *acl) { return (0); } static int stub_vnode_check_setextattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, int attrnamespace, const char *name) { return (0); } static int stub_vnode_check_setflags(struct ucred *cred, struct vnode *vp, struct label *vplabel, u_long flags) { return (0); } static int stub_vnode_check_setmode(struct ucred *cred, struct vnode *vp, struct label *vplabel, mode_t mode) { return (0); } static int stub_vnode_check_setowner(struct ucred *cred, struct vnode *vp, struct label *vplabel, uid_t uid, gid_t gid) { return (0); } static int stub_vnode_check_setutimes(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct timespec atime, struct timespec mtime) { return (0); } static int stub_vnode_check_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_check_unlink(struct ucred *cred, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { return (0); } static int stub_vnode_check_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vplabel) { return (0); } static int stub_vnode_create_extattr(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct vnode *dvp, struct label *dvplabel, struct vnode *vp, struct label *vplabel, struct componentname *cnp) { return (0); } static void stub_vnode_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { } static int stub_vnode_execve_will_transition(struct ucred *old, struct vnode *vp, struct label *vplabel, struct label *interpvplabel, struct image_params *imgp, struct label *execlabel) { return (0); } static void stub_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *label) { } static int stub_vnode_setlabel_extattr(struct ucred *cred, struct vnode *vp, struct label *vplabel, struct label *intlabel) { return (0); } /* * Register functions with MAC Framework policy entry points. */ static struct mac_policy_ops stub_ops = { .mpo_destroy = stub_destroy, .mpo_init = stub_init, .mpo_syscall = stub_syscall, .mpo_bpfdesc_check_receive = stub_bpfdesc_check_receive, .mpo_bpfdesc_create = stub_bpfdesc_create, .mpo_bpfdesc_create_mbuf = stub_bpfdesc_create_mbuf, .mpo_bpfdesc_destroy_label = stub_destroy_label, .mpo_bpfdesc_init_label = stub_init_label, .mpo_cred_associate_nfsd = stub_cred_associate_nfsd, .mpo_cred_check_relabel = stub_cred_check_relabel, .mpo_cred_check_setaudit = stub_cred_check_setaudit, .mpo_cred_check_setaudit_addr = stub_cred_check_setaudit_addr, .mpo_cred_check_setauid = stub_cred_check_setauid, .mpo_cred_check_setegid = stub_cred_check_setegid, .mpo_cred_check_seteuid = stub_cred_check_seteuid, .mpo_cred_check_setgid = stub_cred_check_setgid, .mpo_cred_check_setgroups = stub_cred_check_setgroups, .mpo_cred_check_setregid = stub_cred_check_setregid, .mpo_cred_check_setresgid = stub_cred_check_setresgid, .mpo_cred_check_setresuid = stub_cred_check_setresuid, .mpo_cred_check_setreuid = stub_cred_check_setreuid, .mpo_cred_check_setuid = stub_cred_check_setuid, .mpo_cred_check_visible = stub_cred_check_visible, .mpo_cred_copy_label = stub_copy_label, .mpo_cred_create_init = stub_cred_create_init, .mpo_cred_create_swapper = stub_cred_create_swapper, .mpo_cred_destroy_label = stub_destroy_label, .mpo_cred_externalize_label = stub_externalize_label, .mpo_cred_init_label = stub_init_label, .mpo_cred_internalize_label = stub_internalize_label, .mpo_cred_relabel= stub_cred_relabel, .mpo_devfs_create_device = stub_devfs_create_device, .mpo_devfs_create_directory = stub_devfs_create_directory, .mpo_devfs_create_symlink = stub_devfs_create_symlink, .mpo_devfs_destroy_label = stub_destroy_label, .mpo_devfs_init_label = stub_init_label, .mpo_devfs_update = stub_devfs_update, .mpo_devfs_vnode_associate = stub_devfs_vnode_associate, .mpo_ifnet_check_relabel = stub_ifnet_check_relabel, .mpo_ifnet_check_transmit = stub_ifnet_check_transmit, .mpo_ifnet_copy_label = stub_copy_label, .mpo_ifnet_create = stub_ifnet_create, .mpo_ifnet_create_mbuf = stub_ifnet_create_mbuf, .mpo_ifnet_destroy_label = stub_destroy_label, .mpo_ifnet_externalize_label = stub_externalize_label, .mpo_ifnet_init_label = stub_init_label, .mpo_ifnet_internalize_label = stub_internalize_label, .mpo_ifnet_relabel = stub_ifnet_relabel, .mpo_inpcb_check_deliver = stub_inpcb_check_deliver, .mpo_inpcb_check_visible = stub_inpcb_check_visible, .mpo_inpcb_create = stub_inpcb_create, .mpo_inpcb_create_mbuf = stub_inpcb_create_mbuf, .mpo_inpcb_destroy_label = stub_destroy_label, .mpo_inpcb_init_label = stub_init_label_waitcheck, .mpo_inpcb_sosetlabel = stub_inpcb_sosetlabel, .mpo_ip6q_create = stub_ip6q_create, .mpo_ip6q_destroy_label = stub_destroy_label, .mpo_ip6q_init_label = stub_init_label_waitcheck, .mpo_ip6q_match = stub_ip6q_match, .mpo_ip6q_update = stub_ip6q_update, .mpo_ip6q_reassemble = stub_ip6q_reassemble, .mpo_ipq_create = stub_ipq_create, .mpo_ipq_destroy_label = stub_destroy_label, .mpo_ipq_init_label = stub_init_label_waitcheck, .mpo_ipq_match = stub_ipq_match, .mpo_ipq_update = stub_ipq_update, .mpo_ipq_reassemble = stub_ipq_reassemble, .mpo_kenv_check_dump = stub_kenv_check_dump, .mpo_kenv_check_get = stub_kenv_check_get, .mpo_kenv_check_set = stub_kenv_check_set, .mpo_kenv_check_unset = stub_kenv_check_unset, .mpo_kld_check_load = stub_kld_check_load, .mpo_kld_check_stat = stub_kld_check_stat, .mpo_mbuf_copy_label = stub_copy_label, .mpo_mbuf_destroy_label = stub_destroy_label, .mpo_mbuf_init_label = stub_init_label_waitcheck, .mpo_mount_check_stat = stub_mount_check_stat, .mpo_mount_create = stub_mount_create, .mpo_mount_destroy_label = stub_destroy_label, .mpo_mount_init_label = stub_init_label, .mpo_netinet_arp_send = stub_netinet_arp_send, .mpo_netinet_firewall_reply = stub_netinet_firewall_reply, .mpo_netinet_firewall_send = stub_netinet_firewall_send, .mpo_netinet_fragment = stub_netinet_fragment, .mpo_netinet_icmp_reply = stub_netinet_icmp_reply, .mpo_netinet_icmp_replyinplace = stub_netinet_icmp_replyinplace, .mpo_netinet_tcp_reply = stub_netinet_tcp_reply, .mpo_netinet_igmp_send = stub_netinet_igmp_send, .mpo_netinet6_nd6_send = stub_netinet6_nd6_send, .mpo_pipe_check_ioctl = stub_pipe_check_ioctl, .mpo_pipe_check_poll = stub_pipe_check_poll, .mpo_pipe_check_read = stub_pipe_check_read, .mpo_pipe_check_relabel = stub_pipe_check_relabel, .mpo_pipe_check_stat = stub_pipe_check_stat, .mpo_pipe_check_write = stub_pipe_check_write, .mpo_pipe_copy_label = stub_copy_label, .mpo_pipe_create = stub_pipe_create, .mpo_pipe_destroy_label = stub_destroy_label, .mpo_pipe_externalize_label = stub_externalize_label, .mpo_pipe_init_label = stub_init_label, .mpo_pipe_internalize_label = stub_internalize_label, .mpo_pipe_relabel = stub_pipe_relabel, .mpo_posixsem_check_getvalue = stub_posixsem_check_getvalue, .mpo_posixsem_check_open = stub_posixsem_check_open, .mpo_posixsem_check_post = stub_posixsem_check_post, .mpo_posixsem_check_setmode = stub_posixsem_check_setmode, .mpo_posixsem_check_setowner = stub_posixsem_check_setowner, .mpo_posixsem_check_stat = stub_posixsem_check_stat, .mpo_posixsem_check_unlink = stub_posixsem_check_unlink, .mpo_posixsem_check_wait = stub_posixsem_check_wait, .mpo_posixsem_create = stub_posixsem_create, .mpo_posixsem_destroy_label = stub_destroy_label, .mpo_posixsem_init_label = stub_init_label, .mpo_posixshm_check_create = stub_posixshm_check_create, .mpo_posixshm_check_mmap = stub_posixshm_check_mmap, .mpo_posixshm_check_open = stub_posixshm_check_open, .mpo_posixshm_check_read = stub_posixshm_check_read, .mpo_posixshm_check_setmode = stub_posixshm_check_setmode, .mpo_posixshm_check_setowner = stub_posixshm_check_setowner, .mpo_posixshm_check_stat = stub_posixshm_check_stat, .mpo_posixshm_check_truncate = stub_posixshm_check_truncate, .mpo_posixshm_check_unlink = stub_posixshm_check_unlink, .mpo_posixshm_check_write = stub_posixshm_check_write, .mpo_posixshm_create = stub_posixshm_create, .mpo_posixshm_destroy_label = stub_destroy_label, .mpo_posixshm_init_label = stub_init_label, .mpo_priv_check = stub_priv_check, .mpo_priv_grant = stub_priv_grant, .mpo_proc_check_debug = stub_proc_check_debug, .mpo_proc_check_sched = stub_proc_check_sched, .mpo_proc_check_signal = stub_proc_check_signal, .mpo_proc_check_wait = stub_proc_check_wait, .mpo_socket_check_accept = stub_socket_check_accept, .mpo_socket_check_bind = stub_socket_check_bind, .mpo_socket_check_connect = stub_socket_check_connect, .mpo_socket_check_create = stub_socket_check_create, .mpo_socket_check_deliver = stub_socket_check_deliver, .mpo_socket_check_listen = stub_socket_check_listen, .mpo_socket_check_poll = stub_socket_check_poll, .mpo_socket_check_receive = stub_socket_check_receive, .mpo_socket_check_relabel = stub_socket_check_relabel, .mpo_socket_check_send = stub_socket_check_send, .mpo_socket_check_stat = stub_socket_check_stat, .mpo_socket_check_visible = stub_socket_check_visible, .mpo_socket_copy_label = stub_copy_label, .mpo_socket_create = stub_socket_create, .mpo_socket_create_mbuf = stub_socket_create_mbuf, .mpo_socket_destroy_label = stub_destroy_label, .mpo_socket_externalize_label = stub_externalize_label, .mpo_socket_init_label = stub_init_label_waitcheck, .mpo_socket_internalize_label = stub_internalize_label, .mpo_socket_newconn = stub_socket_newconn, .mpo_socket_relabel = stub_socket_relabel, .mpo_socketpeer_destroy_label = stub_destroy_label, .mpo_socketpeer_externalize_label = stub_externalize_label, .mpo_socketpeer_init_label = stub_init_label_waitcheck, .mpo_socketpeer_set_from_mbuf = stub_socketpeer_set_from_mbuf, .mpo_socketpeer_set_from_socket = stub_socketpeer_set_from_socket, .mpo_syncache_init_label = stub_init_label_waitcheck, .mpo_syncache_destroy_label = stub_destroy_label, .mpo_syncache_create = stub_syncache_create, .mpo_syncache_create_mbuf= stub_syncache_create_mbuf, .mpo_sysvmsg_cleanup = stub_sysvmsg_cleanup, .mpo_sysvmsg_create = stub_sysvmsg_create, .mpo_sysvmsg_destroy_label = stub_destroy_label, .mpo_sysvmsg_init_label = stub_init_label, .mpo_sysvmsq_check_msgmsq = stub_sysvmsq_check_msgmsq, .mpo_sysvmsq_check_msgrcv = stub_sysvmsq_check_msgrcv, .mpo_sysvmsq_check_msgrmid = stub_sysvmsq_check_msgrmid, .mpo_sysvmsq_check_msqget = stub_sysvmsq_check_msqget, .mpo_sysvmsq_check_msqsnd = stub_sysvmsq_check_msqsnd, .mpo_sysvmsq_check_msqrcv = stub_sysvmsq_check_msqrcv, .mpo_sysvmsq_check_msqctl = stub_sysvmsq_check_msqctl, .mpo_sysvmsq_cleanup = stub_sysvmsq_cleanup, .mpo_sysvmsq_create = stub_sysvmsq_create, .mpo_sysvmsq_destroy_label = stub_destroy_label, .mpo_sysvmsq_init_label = stub_init_label, .mpo_sysvsem_check_semctl = stub_sysvsem_check_semctl, .mpo_sysvsem_check_semget = stub_sysvsem_check_semget, .mpo_sysvsem_check_semop = stub_sysvsem_check_semop, .mpo_sysvsem_cleanup = stub_sysvsem_cleanup, .mpo_sysvsem_create = stub_sysvsem_create, .mpo_sysvsem_destroy_label = stub_destroy_label, .mpo_sysvsem_init_label = stub_init_label, .mpo_sysvshm_check_shmat = stub_sysvshm_check_shmat, .mpo_sysvshm_check_shmctl = stub_sysvshm_check_shmctl, .mpo_sysvshm_check_shmdt = stub_sysvshm_check_shmdt, .mpo_sysvshm_check_shmget = stub_sysvshm_check_shmget, .mpo_sysvshm_cleanup = stub_sysvshm_cleanup, .mpo_sysvshm_create = stub_sysvshm_create, .mpo_sysvshm_destroy_label = stub_destroy_label, .mpo_sysvshm_init_label = stub_init_label, .mpo_system_check_acct = stub_system_check_acct, .mpo_system_check_audit = stub_system_check_audit, .mpo_system_check_auditctl = stub_system_check_auditctl, .mpo_system_check_auditon = stub_system_check_auditon, .mpo_system_check_reboot = stub_system_check_reboot, .mpo_system_check_swapoff = stub_system_check_swapoff, .mpo_system_check_swapon = stub_system_check_swapon, .mpo_system_check_sysctl = stub_system_check_sysctl, .mpo_thread_userret = stub_thread_userret, .mpo_vnode_associate_extattr = stub_vnode_associate_extattr, .mpo_vnode_associate_singlelabel = stub_vnode_associate_singlelabel, .mpo_vnode_check_access = stub_vnode_check_access, .mpo_vnode_check_chdir = stub_vnode_check_chdir, .mpo_vnode_check_chroot = stub_vnode_check_chroot, .mpo_vnode_check_create = stub_vnode_check_create, .mpo_vnode_check_deleteacl = stub_vnode_check_deleteacl, .mpo_vnode_check_deleteextattr = stub_vnode_check_deleteextattr, .mpo_vnode_check_exec = stub_vnode_check_exec, .mpo_vnode_check_getacl = stub_vnode_check_getacl, .mpo_vnode_check_getextattr = stub_vnode_check_getextattr, .mpo_vnode_check_link = stub_vnode_check_link, .mpo_vnode_check_listextattr = stub_vnode_check_listextattr, .mpo_vnode_check_lookup = stub_vnode_check_lookup, .mpo_vnode_check_mmap = stub_vnode_check_mmap, .mpo_vnode_check_mmap_downgrade = stub_vnode_check_mmap_downgrade, .mpo_vnode_check_mprotect = stub_vnode_check_mprotect, .mpo_vnode_check_open = stub_vnode_check_open, .mpo_vnode_check_poll = stub_vnode_check_poll, .mpo_vnode_check_read = stub_vnode_check_read, .mpo_vnode_check_readdir = stub_vnode_check_readdir, .mpo_vnode_check_readlink = stub_vnode_check_readlink, .mpo_vnode_check_relabel = stub_vnode_check_relabel, .mpo_vnode_check_rename_from = stub_vnode_check_rename_from, .mpo_vnode_check_rename_to = stub_vnode_check_rename_to, .mpo_vnode_check_revoke = stub_vnode_check_revoke, .mpo_vnode_check_setacl = stub_vnode_check_setacl, .mpo_vnode_check_setextattr = stub_vnode_check_setextattr, .mpo_vnode_check_setflags = stub_vnode_check_setflags, .mpo_vnode_check_setmode = stub_vnode_check_setmode, .mpo_vnode_check_setowner = stub_vnode_check_setowner, .mpo_vnode_check_setutimes = stub_vnode_check_setutimes, .mpo_vnode_check_stat = stub_vnode_check_stat, .mpo_vnode_check_unlink = stub_vnode_check_unlink, .mpo_vnode_check_write = stub_vnode_check_write, .mpo_vnode_copy_label = stub_copy_label, .mpo_vnode_create_extattr = stub_vnode_create_extattr, .mpo_vnode_destroy_label = stub_destroy_label, .mpo_vnode_execve_transition = stub_vnode_execve_transition, .mpo_vnode_execve_will_transition = stub_vnode_execve_will_transition, .mpo_vnode_externalize_label = stub_externalize_label, .mpo_vnode_init_label = stub_init_label, .mpo_vnode_internalize_label = stub_internalize_label, .mpo_vnode_relabel = stub_vnode_relabel, .mpo_vnode_setlabel_extattr = stub_vnode_setlabel_extattr, }; MAC_POLICY_SET(&stub_ops, mac_stub, "TrustedBSD MAC/Stub", MPC_LOADTIME_FLAG_UNLOADOK, NULL); diff --git a/sys/security/mac_veriexec/veriexec_metadata.c b/sys/security/mac_veriexec/veriexec_metadata.c index 8961835b84dc..0a8505f5d951 100644 --- a/sys/security/mac_veriexec/veriexec_metadata.c +++ b/sys/security/mac_veriexec/veriexec_metadata.c @@ -1,787 +1,786 @@ /* * $FreeBSD$ * * Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc. * All rights reserved. * * Originally derived from: * $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $ * * 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 ``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 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. */ #include #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include "mac_veriexec.h" #include "mac_veriexec_internal.h" /** * @brief per-device meta-data storage */ struct veriexec_dev_list { dev_t fsid; /**< file system identifier of the mount point */ LIST_HEAD(filehead, mac_veriexec_file_info) file_head; /**< list of per-file meta-data information */ LIST_ENTRY(veriexec_dev_list) entries; /**< next entries in the device list */ }; typedef LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_devhead_t; /** * @brief Mutex to protect the meta-data store lists */ struct mtx ve_mutex; /** * @brief Executables meta-data storage * * This is used to store the fingerprints for potentially-executable files. */ veriexec_devhead_t veriexec_dev_head; /** * @brief Plain file meta-data storage * * This is used for files that are not allowed to be executed, but should * have fingerprint validation available. */ veriexec_devhead_t veriexec_file_dev_head; /** * @internal * @brief Search the @p head meta-data list for the specified file identifier * @p fileid in the file system identified by @p fsid * * If meta-data exists for file system identified by @p fsid, it has a * fingerprint list, and @p found_dev is not @c NULL then store true in the * location pointed to by @p found_dev * * @param head meta-data list to search * @param fsid file system identifier to look for * @param fileid file to look for * @param gen generation of file * @param found_dev indicator that an entry for the file system was found * * @return A pointer to the meta-data inforation if meta-data exists for * the specified file identifier, otherwise @c NULL */ static struct mac_veriexec_file_info * get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid, unsigned long gen, int *found_dev) { struct veriexec_dev_list *lp; struct mac_veriexec_file_info *ip, *tip; ip = NULL; /* Initialize the value found_dev, if non-NULL */ if (found_dev != NULL) *found_dev = 0; VERIEXEC_DEBUG(3, ("searching for file %ju.%lu on device %ju," " files=%d\n", (uintmax_t)fileid, gen, (uintmax_t)fsid, (head == &veriexec_file_dev_head))); /* Get a lock to access the list */ mtx_lock(&ve_mutex); /* First, look for the file system */ for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) if (lp->fsid == fsid) break; /* We found the file system in the list */ if (lp != NULL) { VERIEXEC_DEBUG(3, ("found matching dev number %ju\n", (uintmax_t)lp->fsid)); /* If found_dev is non-NULL, store true there */ if (found_dev != NULL) *found_dev = 1; /* Next, look for the meta-data information for the file */ LIST_FOREACH_SAFE(ip, &(lp->file_head), entries, tip) { if (ip->fileid == fileid) { if (ip->gen == gen) break; /* we need to garbage collect */ LIST_REMOVE(ip, entries); if (ip->label) free(ip->label, M_VERIEXEC); free(ip, M_VERIEXEC); } } } /* Release the lock we obtained earlier */ mtx_unlock(&ve_mutex); /* Return the meta-data information we found, if anything */ return (ip); } /** * @internal * @brief Display the fingerprint for each entry in the device list * * @param sbp sbuf to write output to * @param lp pointer to device list */ static void mac_veriexec_print_db_dev_list(struct sbuf *sbp, struct veriexec_dev_list *lp) { struct mac_veriexec_file_info *ip; #define FPB(i) (ip->fingerprint[i]) for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = LIST_NEXT(ip, entries)) sbuf_printf(sbp, " %ld: %u %ld [%02x %02x %02x %02x %02x " "%02x %02x %02x...]\n", ip->fileid, ip->flags, ip->gen, FPB(0), FPB(1), FPB(2), FPB(3), FPB(4), FPB(5), FPB(6), FPB(7)); } /** * @internal * @brief Display the device list * * @param sbp sbuf to write output to * @param head pointer to head of the device list */ static void mac_veriexec_print_db_head(struct sbuf *sbp, struct veriexec_devhead *head) { struct veriexec_dev_list *lp; for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) { sbuf_printf(sbp, " FS id: %ju\n", (uintmax_t)lp->fsid); mac_veriexec_print_db_dev_list(sbp, lp); } } /** * @internal * @brief Generate human-readable output for the current fingerprint database * * @param sbp sbuf to write output to */ void mac_veriexec_metadata_print_db(struct sbuf *sbp) { struct { struct veriexec_devhead *h; const char *name; } fpdbs[] = { { &veriexec_file_dev_head, "regular files" }, { &veriexec_dev_head, "executable files" }, }; int i; mtx_lock(&ve_mutex); for (i = 0; i < sizeof(fpdbs)/sizeof(fpdbs[0]); i++) { sbuf_printf(sbp, "%s fingerprint db:\n", fpdbs[i].name); mac_veriexec_print_db_head(sbp, fpdbs[i].h); } mtx_unlock(&ve_mutex); } /** * @brief Determine if the meta-data store has an entry for the specified file. * * @param fsid file system identifier to look for * @param fileid file to look for * @param gen generation of file * * @return 1 if there is an entry in the meta-data store, 0 otherwise. */ int mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen) { return (mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL, VERIEXEC_FILES_FIRST) != NULL); } /** * @brief Search the list of devices looking for the one given, in order to * release the resources used by it. * * If found, free all file entries for it, and remove it from the list. * * @note Called with @a ve_mutex held * * @param fsid file system identifier to look for * @param head meta-data list to search * * @return 0 if the device entry was freed, otherwise an error code */ static int free_veriexec_dev(dev_t fsid, struct veriexec_devhead *head) { struct veriexec_dev_list *lp; struct mac_veriexec_file_info *ip, *nip; /* Look for the file system */ for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) if (lp->fsid == fsid) break; /* If lp is NULL, we did not find it */ if (lp == NULL) return ENOENT; /* Unhook lp, before we free it and its content */ LIST_REMOVE(lp, entries); /* Release the lock */ mtx_unlock(&ve_mutex); /* Free the file entries in the list */ for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = nip) { nip = LIST_NEXT(ip, entries); LIST_REMOVE(ip, entries); if (ip->label) free(ip->label, M_VERIEXEC); free(ip, M_VERIEXEC); } /* Free the meta-data entry for the device */ free(lp, M_VERIEXEC); /* Re-acquire the lock */ mtx_lock(&ve_mutex); return 0; } /** * @brief Search the list of devices looking for the one given. * * If it is not in the list then add it. * * @note Called with @a ve_mutex held * * @param fsid file system identifier to look for * @param head meta-data list to search * * @return A pointer to the meta-data entry for the device, if found or added, * otherwise @c NULL */ static struct veriexec_dev_list * find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head) { struct veriexec_dev_list *lp; struct veriexec_dev_list *np = NULL; search: /* Look for the file system */ for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) if (lp->fsid == fsid) break; if (lp == NULL) { if (np == NULL) { /* * If pointer is null then entry not there, * add a new one, first try to malloc while * we hold mutex - should work most of the time. */ np = malloc(sizeof(struct veriexec_dev_list), M_VERIEXEC, M_NOWAIT); if (np == NULL) { /* * So much for that plan, dop the mutex * and repeat... */ mtx_unlock(&ve_mutex); np = malloc(sizeof(struct veriexec_dev_list), M_VERIEXEC, M_WAITOK); mtx_lock(&ve_mutex); /* * Repeat the seach, in case someone * added this while we slept. */ goto search; } } if (np) { /* Add the entry to the list */ lp = np; LIST_INIT(&(lp->file_head)); lp->fsid = fsid; LIST_INSERT_HEAD(head, lp, entries); } } else if (np) { /* * Someone else did it while we slept. */ mtx_unlock(&ve_mutex); free(np, M_VERIEXEC); mtx_lock(&ve_mutex); } return (lp); } /** * @internal * @brief Allocate and initialize label record with the provided data. * * @param labelp Location to store the initialized label * @param src Pointer to label string to copy * @param srclen Length of label string to copy * * @return Length of resulting label * * @note Called with ve_mutex locked. */ static size_t mac_veriexec_init_label(char **labelp, size_t labellen, char *src, size_t srclen) { char *label; label = *labelp; if (labellen < srclen) { mtx_unlock(&ve_mutex); if (label != NULL) free(label, M_VERIEXEC); label = malloc(srclen, M_VERIEXEC, M_WAITOK); mtx_lock(&ve_mutex); labellen = srclen; *labelp = label; } memcpy(label, src, srclen); return labellen; } /** * @brief When a device is unmounted, we want to toss the signatures recorded * against it. * * We are being called from unmount() with the root vnode just before it is * freed. * * @param fsid file system identifier to look for * @param td calling thread * * @return 0 on success, otherwise an error code. */ int mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td) { int error; /* * The device can have entries on both lists. */ mtx_lock(&ve_mutex); error = free_veriexec_dev(fsid, &veriexec_dev_head); if (error && error != ENOENT) { mtx_unlock(&ve_mutex); return error; } error = free_veriexec_dev(fsid, &veriexec_file_dev_head); mtx_unlock(&ve_mutex); if (error && error != ENOENT) { return error; } return 0; } /** * @brief Return the flags assigned to the file identified by file system * identifier @p fsid and file identifier @p fileid. * * @param fsid file system identifier * @param fileid file identifier within the file system * @param gen generation of file * @param flags pointer to location to store the flags * @param check_files if 1, check the files list first, otherwise check the * exectuables list first * * @return 0 on success, otherwise an error code. */ int mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen, int *flags, int check_files) { struct mac_veriexec_file_info *ip; int found_dev; ip = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, &found_dev, check_files); if (ip == NULL) return (ENOENT); *flags = ip->flags; return (0); } /** * @brief get the files for the specified process * * @param cred credentials to use * @param p process to get the flags for * @param flags where to store the flags * @param check_files if 1, check the files list first, otherwise check the * exectuables list first * * @return 0 if the process has an entry in the meta-data store, otherwise an * error code */ int mac_veriexec_metadata_get_executable_flags(struct ucred *cred, struct proc *p, int *flags, int check_files) { struct vnode *proc_vn; struct vattr vap; int error; /* Get the text vnode for the process */ proc_vn = p->p_textvp; if (proc_vn == NULL) return EINVAL; /* Get vnode attributes */ error = VOP_GETATTR(proc_vn, &vap, cred); if (error) return error; error = mac_veriexec_metadata_get_file_flags(vap.va_fsid, vap.va_fileid, vap.va_gen, flags, (check_files == VERIEXEC_FILES_FIRST)); return (error); } /** * @brief Ensure the fingerprint status for the vnode @p vp is assigned to its * MAC label. * * @param vp vnode to check * @param vap vnode attributes to use * @param td calling thread * @param check_files if 1, check the files list first, otherwise check the * exectuables list first * * @return 0 on success, otherwise an error code. */ int mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp, struct vattr *vap, struct thread *td, int check_files) { unsigned char digest[MAXFINGERPRINTLEN]; struct mac_veriexec_file_info *ip; int error, found_dev; fingerprint_status_t status; error = 0; ip = NULL; status = mac_veriexec_get_fingerprint_status(vp); if (status == FINGERPRINT_INVALID || status == FINGERPRINT_NODEV) { found_dev = 0; ip = mac_veriexec_metadata_get_file_info(vap->va_fsid, vap->va_fileid, vap->va_gen, &found_dev, check_files); if (ip == NULL) { status = (found_dev) ? FINGERPRINT_NOENTRY : FINGERPRINT_NODEV; VERIEXEC_DEBUG(3, ("fingerprint status is %d for dev %ju, file " "%ju.%lu\n", status, (uintmax_t)vap->va_fsid, (uintmax_t)vap->va_fileid, vap->va_gen)); } else { /* * evaluate and compare fingerprint */ error = mac_veriexec_fingerprint_check_vnode(vp, ip, td, vap->va_size, digest); switch (error) { case 0: /* Process flags */ if ((ip->flags & VERIEXEC_INDIRECT)) status = FINGERPRINT_INDIRECT; else if ((ip->flags & VERIEXEC_FILE)) status = FINGERPRINT_FILE; else status = FINGERPRINT_VALID; VERIEXEC_DEBUG(2, ("%sfingerprint matches for dev %ju, file " "%ju.%lu\n", (status == FINGERPRINT_INDIRECT) ? "indirect " : (status == FINGERPRINT_FILE) ? "file " : "", (uintmax_t)vap->va_fsid, (uintmax_t)vap->va_fileid, vap->va_gen)); break; case EAUTH: #ifdef VERIFIED_EXEC_DEBUG_VERBOSE { char have[MAXFINGERPRINTLEN * 2 + 1]; char want[MAXFINGERPRINTLEN * 2 + 1]; int i, len; len = ip->ops->digest_len; for (i = 0; i < len; i++) { sprintf(&want[i * 2], "%02x", ip->fingerprint[i]); sprintf(&have[i * 2], "%02x", digest[i]); } log(LOG_ERR, MAC_VERIEXEC_FULLNAME ": fingerprint for dev %ju, file " "%ju.%lu %s != %s\n", (uintmax_t)vap->va_fsid, (uintmax_t)vap->va_fileid, vap->va_gen, have, want); } #endif status = FINGERPRINT_NOMATCH; break; default: VERIEXEC_DEBUG(2, ("fingerprint status error %d\n", error)); break; } } mac_veriexec_set_fingerprint_status(vp, status); } return (error); } /** * Add a file and its fingerprint to the list of files attached * to the device @p fsid. * * Only add the entry if it is not already on the list. * * @note Called with @a ve_mutex held * * @param file_dev if 1, the entry should be added on the file list, * otherwise it should be added on the executable list * @param fsid file system identifier of device * @param fileid file to add * @param gen generation of file * @param fingerprint fingerprint to add to the store * @param flags flags to set in the store * @param fp_type digest type * @param override if 1, override any values already stored * * @return 0 on success, otherwise an error code. */ int mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid, unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN], char *label, size_t labellen, int flags, const char *fp_type, int override) { struct mac_veriexec_fpops *fpops; struct veriexec_dev_list *lp; struct veriexec_devhead *head; struct mac_veriexec_file_info *ip; struct mac_veriexec_file_info *np = NULL; /* Label and labellen must be set if VERIEXEC_LABEL is set */ if ((flags & VERIEXEC_LABEL) != 0 && (label == NULL || labellen == 0)) return (EINVAL); /* Look up the device entry */ if (file_dev) head = &veriexec_file_dev_head; else head = &veriexec_dev_head; lp = find_veriexec_dev(fsid, head); /* Look up the fingerprint operations for the digest type */ fpops = mac_veriexec_fingerprint_lookup_ops(fp_type); if (fpops == NULL) return (EOPNOTSUPP); search: for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = LIST_NEXT(ip, entries)) { /* check for a dupe file in the list, skip if an entry * exists for this file except for when the flags contains * VERIEXEC_INDIRECT, always set the flags when it is so * we don't get a hole caused by conflicting flags on * hardlinked files. XXX maybe we should validate * fingerprint is same and complain if it is not... */ if (ip->fileid == fileid && ip->gen == gen) { if (override) { /* * for a signed load we allow overrides, * otherwise fingerpints needed for pkg loads * can fail (the files are on temp device). */ ip->flags = flags; ip->ops = fpops; memcpy(ip->fingerprint, fingerprint, fpops->digest_len); if (flags & VERIEXEC_LABEL) { ip->labellen = mac_veriexec_init_label( &ip->label, ip->labellen, label, labellen); } else if (ip->labellen > 0) { free(ip->label, M_VERIEXEC); ip->labellen = 0; ip->label = NULL; } } else if ((flags & (VERIEXEC_INDIRECT|VERIEXEC_FILE))) ip->flags |= flags; if (np) { /* unlikely but... we don't need it now. */ mtx_unlock(&ve_mutex); free(np, M_VERIEXEC); mtx_lock(&ve_mutex); } return (0); } } /* * We may have been past here before... */ if (np == NULL) { /* * We first try with mutex held and nowait. */ np = malloc(sizeof(struct mac_veriexec_file_info), M_VERIEXEC, M_NOWAIT); if (np == NULL) { /* * It was worth a try, now * drop mutex while we malloc. */ mtx_unlock(&ve_mutex); np = malloc(sizeof(struct mac_veriexec_file_info), M_VERIEXEC, M_WAITOK); mtx_lock(&ve_mutex); /* * We now have to repeat our search! */ goto search; } } /* Set up the meta-data entry */ ip = np; ip->flags = flags; ip->ops = fpops; ip->fileid = fileid; ip->gen = gen; memcpy(ip->fingerprint, fingerprint, fpops->digest_len); if (flags & VERIEXEC_LABEL) ip->labellen = mac_veriexec_init_label(&ip->label, ip->labellen, label, labellen); else { ip->label = NULL; ip->labellen = 0; } VERIEXEC_DEBUG(3, ("add file %ju.%lu (files=%d)\n", (uintmax_t)ip->fileid, ip->gen, file_dev)); /* Add the entry to the list */ LIST_INSERT_HEAD(&(lp->file_head), ip, entries); #ifdef DEBUG_VERIEXEC_FINGERPRINT { off_t offset; printf("Stored %s fingerprint:\n", fp_type); for (offset = 0; offset < fpops->digest_len; offset++) printf("%02x", fingerprint[offset]); printf("\n"); } #endif return (0); } /** * @brief Search the meta-data store for information on the specified file. * * @param fsid file system identifier to look for * @param fileid file to look for * @param gen generation of file * @param found_dev indicator that an entry for the file system was found * @param check_files if 1, check the files list first, otherwise check the * exectuables list first * * @return A pointer to the meta-data inforation if meta-data exists for * the specified file identifier, otherwise @c NULL */ struct mac_veriexec_file_info * mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, unsigned long gen, int *found_dev, int check_files) { struct veriexec_devhead *search[3]; struct mac_veriexec_file_info *ip; int x; /* Determine the order of the lists to search */ if (check_files) { search[0] = &veriexec_file_dev_head; search[1] = &veriexec_dev_head; } else { search[0] = &veriexec_dev_head; search[1] = &veriexec_file_dev_head; } search[2] = NULL; VERIEXEC_DEBUG(3, ("%s: searching for dev %ju, file %lu\n", __func__, (uintmax_t)fsid, fileid)); /* Search for the specified file */ for (ip = NULL, x = 0; ip == NULL && search[x]; x++) ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev); return (ip); } - /** * @brief Intialize the meta-data store */ void mac_veriexec_metadata_init(void) { mtx_init(&ve_mutex, "veriexec lock", NULL, MTX_DEF); LIST_INIT(&veriexec_dev_head); LIST_INIT(&veriexec_file_dev_head); } diff --git a/sys/security/mac_veriexec_parser/mac_veriexec_parser.c b/sys/security/mac_veriexec_parser/mac_veriexec_parser.c index dc3b1301f9ee..41f70bb7074a 100644 --- a/sys/security/mac_veriexec_parser/mac_veriexec_parser.c +++ b/sys/security/mac_veriexec_parser/mac_veriexec_parser.c @@ -1,476 +1,475 @@ /*- * Copyright (c) 2019 Stormshield. * Copyright (c) 2019 Semihalf. * * 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 ``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 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* The following are based on sbin/veriexec */ struct fingerprint_type { const char *fp_type; int fp_size; }; struct fp_flag { const char *flag_name; int flag; }; static const struct fingerprint_type fp_table[] = { {"sha256=", SHA256_DIGEST_LENGTH}, #if MAXFINGERPRINTLEN >= SHA384_DIGEST_LENGTH {"sha384=", SHA384_DIGEST_LENGTH}, #endif #if MAXFINGERPRINTLEN >= SHA512_DIGEST_LENGTH {"sha512=", SHA512_DIGEST_LENGTH}, #endif {NULL, 0} }; static const struct fp_flag flags_table[] = { {"indirect", VERIEXEC_INDIRECT}, {"no_ptrace", VERIEXEC_NOTRACE}, {"trusted", VERIEXEC_TRUSTED}, {"no_fips", VERIEXEC_NOFIPS}, {NULL, 0} }; extern struct mtx ve_mutex; static unsigned char hexchar_to_byte(unsigned char c); static int hexstring_to_bin(unsigned char *buf); static int get_flags(const char *entry); static int get_fp(const char *entry, char **type, unsigned char **digest, int *flags); static int verify_digest(const char *data, size_t len, const unsigned char *expected_hash); static int open_file(const char *path, struct nameidata *nid); static char *read_manifest(char *path, unsigned char *digest); static int parse_entry(char *entry, char *prefix); static int parse_manifest(char *path, unsigned char *hash, char *prefix); static unsigned char hexchar_to_byte(unsigned char c) { if (isdigit(c)) return (c - '0'); return (isupper(c) ? c - 'A' + 10 : c - 'a' + 10); } static int hexstring_to_bin(unsigned char *buf) { size_t i, len; unsigned char byte; len = strlen(buf); for (i = 0; i < len / 2; i++) { if (!isxdigit(buf[2 * i]) || !isxdigit(buf[2 * i + 1])) return (EINVAL); byte = hexchar_to_byte(buf[2 * i]) << 4; byte += hexchar_to_byte(buf[2 * i + 1]); buf[i] = byte; } return (0); } static int get_flags(const char *entry) { int i; int result = 0; for (i = 0; flags_table[i].flag_name != NULL; i++) if (strstr(entry, flags_table[i].flag_name) != NULL) result |= flags_table[i].flag; return (result); } /* * Parse a single line of manifest looking for a digest and its type. * We expect it to be in form of "path shaX=hash". * The line will be split into path, hash type and hash value. */ static int get_fp(const char *entry, char **type, unsigned char **digest, int *flags) { char *delimiter; char *local_digest; char *fp_type; char *prev_fp_type; size_t min_len; int i; delimiter = NULL; fp_type = NULL; prev_fp_type = NULL; for (i = 0; fp_table[i].fp_type != NULL; i++) { fp_type = strstr(entry, fp_table[i].fp_type); /* Look for the last "shaX=hash" in line */ while (fp_type != NULL) { prev_fp_type = fp_type; fp_type++; fp_type = strstr(fp_type, fp_table[i].fp_type); } fp_type = prev_fp_type; if (fp_type != NULL) { if (fp_type == entry || fp_type[-1] != ' ') return (EINVAL); /* * The entry should contain at least * fp_type and digest in hexadecimal form. */ min_len = strlen(fp_table[i].fp_type) + 2 * fp_table[i].fp_size; if (strnlen(fp_type, min_len) < min_len) return (EINVAL); local_digest = &fp_type[strlen(fp_table[i].fp_type)]; delimiter = &local_digest[2 * fp_table[i].fp_size]; /* * Make sure that digest is followed by * some kind of delimiter. */ if (*delimiter != '\n' && *delimiter != '\0' && *delimiter != ' ') return (EINVAL); /* * Does the entry contain flags we need to parse? */ if (*delimiter == ' ' && flags != NULL) *flags = get_flags(delimiter); /* * Split entry into three parts: * path, fp_type and digest. */ local_digest[-1] = '\0'; *delimiter = '\0'; fp_type[-1] = '\0'; break; } } if (fp_type == NULL) return (EINVAL); if (type != NULL) *type = fp_type; if (digest != NULL) *digest = local_digest; return (0); } /* * Currently we verify manifest using sha256. * In future another env with hash type could be introduced. */ static int verify_digest(const char *data, size_t len, const unsigned char *expected_hash) { SHA256_CTX ctx; unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256_Init(&ctx); SHA256_Update(&ctx, data, len); SHA256_Final(hash, &ctx); return (memcmp(expected_hash, hash, SHA256_DIGEST_LENGTH)); } - static int open_file(const char *path, struct nameidata *nid) { int flags, rc; flags = FREAD; pwd_ensure_dirs(); NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path, curthread); rc = vn_open(nid, &flags, 0, NULL); NDFREE(nid, NDF_ONLY_PNBUF); if (rc != 0) return (rc); return (0); } /* * Read the manifest from location specified in path and verify its digest. */ static char* read_manifest(char *path, unsigned char *digest) { struct nameidata nid; struct vattr va; char *data; ssize_t bytes_read, resid; int rc; data = NULL; bytes_read = 0; rc = open_file(path, &nid); if (rc != 0) goto fail; rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); if (rc != 0) goto fail; data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK); while (bytes_read < va.va_size) { rc = vn_rdwr( UIO_READ, nid.ni_vp, data, va.va_size - bytes_read, bytes_read, UIO_SYSSPACE, IO_NODELOCKED, curthread->td_ucred, NOCRED, &resid, curthread); if (rc != 0) goto fail; bytes_read = va.va_size - resid; } data[bytes_read] = '\0'; VOP_UNLOCK(nid.ni_vp); (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); /* * If digest is wrong someone might be trying to fool us. */ if (verify_digest(data, va.va_size, digest)) panic("Manifest hash doesn't match expected value!"); return (data); fail: if (data != NULL) free(data, M_VERIEXEC); return (NULL); } /* * Process single line. * First split it into path, digest_type and digest. * Then try to open the file and insert its fingerprint into metadata store. */ static int parse_entry(char *entry, char *prefix) { struct nameidata nid; struct vattr va; char path[MAXPATHLEN]; char *fp_type; unsigned char *digest; int rc, is_exec, flags; fp_type = NULL; digest = NULL; flags = 0; rc = get_fp(entry, &fp_type, &digest, &flags); if (rc != 0) return (rc); rc = hexstring_to_bin(digest); if (rc != 0) return (rc); if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN) return (EINVAL); /* If the path is not absolute prepend it with a prefix */ if (prefix != NULL && entry[0] != '/') { rc = snprintf(path, MAXPATHLEN, "%s/%s", prefix, entry); if (rc < 0) return (-rc); } else { strcpy(path, entry); } rc = open_file(path, &nid); NDFREE(&nid, NDF_ONLY_PNBUF); if (rc != 0) return (rc); rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); if (rc != 0) goto out; is_exec = (va.va_mode & VEXEC); mtx_lock(&ve_mutex); rc = mac_veriexec_metadata_add_file( is_exec == 0, va.va_fsid, va.va_fileid, va.va_gen, digest, NULL, 0, flags, fp_type, 1); mtx_unlock(&ve_mutex); out: VOP_UNLOCK(nid.ni_vp); vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); return (rc); } /* * Look for manifest in env that have beed passed by loader. * This routine should be called right after the rootfs is mounted. */ static int parse_manifest(char *path, unsigned char *hash, char *prefix) { char *data; char *entry; char *next_entry; int rc, success_count; data = NULL; success_count = 0; rc = 0; data = read_manifest(path, hash); if (data == NULL) { rc = EIO; goto out; } entry = data; while (entry != NULL) { next_entry = strchr(entry, '\n'); if (next_entry != NULL) { *next_entry = '\0'; next_entry++; } if (entry[0] == '\n' || entry[0] == '\0') { entry = next_entry; continue; } if ((rc = parse_entry(entry, prefix))) printf("mac_veriexec_parser: Warning: Failed to parse" " entry with rc:%d, entry:\"%s\"\n", rc, entry); else success_count++; entry = next_entry; } rc = 0; out: if (data != NULL) free(data, M_VERIEXEC); if (success_count == 0) rc = EINVAL; return (rc); } static void parse_manifest_event(void *dummy) { char *manifest_path; char *manifest_prefix; unsigned char *manifest_hash; int rc; /* If the envs are not set fail silently */ manifest_path = kern_getenv("veriexec.manifest_path"); if (manifest_path == NULL) return; manifest_hash = kern_getenv("veriexec.manifest_hash"); if (manifest_hash == NULL) { freeenv(manifest_path); return; } manifest_prefix = kern_getenv("veriexec.manifest_prefix"); if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH) panic("veriexec.manifest_hash has incorrect size"); rc = hexstring_to_bin(manifest_hash); if (rc != 0) panic("mac_veriexec: veriexec.loader.manifest_hash" " doesn't contain a hash in hexadecimal form"); rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix); if (rc != 0) panic("mac_veriexec: Failed to parse manifest err=%d", rc); mtx_lock(&ve_mutex); mac_veriexec_set_state( VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE | VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE); mtx_unlock(&ve_mutex); freeenv(manifest_path); freeenv(manifest_hash); if (manifest_prefix != NULL) freeenv(manifest_prefix); } EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0);