freebsd-dev/sys/security/audit/audit_bsm.c

1697 lines
41 KiB
C
Raw Normal View History

/*
* Copyright (c) 1999-2009 Apple Inc.
* All rights reserved.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/vnode.h>
#include <sys/ipc.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/extattr.h>
#include <sys/fcntl.h>
#include <sys/user.h>
#include <sys/systm.h>
#include <bsm/audit.h>
#include <bsm/audit_internal.h>
#include <bsm/audit_record.h>
#include <bsm/audit_kevents.h>
#include <security/audit/audit.h>
#include <security/audit/audit_private.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
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();
}
/*
* 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 { \
2013-02-17 11:57:47 +00:00
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:
kau_write(rec, tok);
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;
Fix the handling of IPv6 addresses for subject and process BSM audit tokens. Currently, we do not support the set{get}audit_addr(2) system calls which allows processes like sshd to set extended or ip6 information for subject tokens. The approach that was taken was to change the process audit state slightly to use an extended terminal ID in the kernel. This allows us to store both IPv4 IPv6 addresses. In the case that an IPv4 address is in use, we convert the terminal ID from an struct auditinfo_addr to a struct auditinfo. If getaudit(2) is called when the subject is bound to an ip6 address, we return E2BIG. - Change the internal audit record to store an extended terminal ID - Introduce ARG_TERMID_ADDR - Change the kaudit <-> BSM conversion process so that we are using the appropriate subject token. If the address associated with the subject is IPv4, we use the standard subject32 token. If the subject has an IPv6 address associated with them, we use an extended subject32 token. - Fix a couple of endian issues where we do a couple of byte swaps when we shouldn't be. IP addresses are already in the correct byte order, so reading the ip6 address 4 bytes at a time and swapping them results in in-correct address data. It should be noted that the same issue was found in the openbsm library and it has been changed there too on the vendor branch - Change A_GETPINFO to use the appropriate structures - Implement A_GETPINFO_ADDR which basically does what A_GETPINFO does, but can also handle ip6 addresses - Adjust get{set}audit(2) syscalls to convert the data auditinfo <-> auditinfo_addr - Fully implement set{get}audit_addr(2) NOTE: This adds the ability for processes to correctly set extended subject information. The appropriate userspace utilities still need to be updated. MFC after: 1 month Reviewed by: rwatson Obtained from: TrustedBSD
2007-04-13 14:55:19 +00:00
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 */
Fix the handling of IPv6 addresses for subject and process BSM audit tokens. Currently, we do not support the set{get}audit_addr(2) system calls which allows processes like sshd to set extended or ip6 information for subject tokens. The approach that was taken was to change the process audit state slightly to use an extended terminal ID in the kernel. This allows us to store both IPv4 IPv6 addresses. In the case that an IPv4 address is in use, we convert the terminal ID from an struct auditinfo_addr to a struct auditinfo. If getaudit(2) is called when the subject is bound to an ip6 address, we return E2BIG. - Change the internal audit record to store an extended terminal ID - Introduce ARG_TERMID_ADDR - Change the kaudit <-> BSM conversion process so that we are using the appropriate subject token. If the address associated with the subject is IPv4, we use the standard subject32 token. If the subject has an IPv6 address associated with them, we use an extended subject32 token. - Fix a couple of endian issues where we do a couple of byte swaps when we shouldn't be. IP addresses are already in the correct byte order, so reading the ip6 address 4 bytes at a time and swapping them results in in-correct address data. It should be noted that the same issue was found in the openbsm library and it has been changed there too on the vendor branch - Change A_GETPINFO to use the appropriate structures - Implement A_GETPINFO_ADDR which basically does what A_GETPINFO does, but can also handle ip6 addresses - Adjust get{set}audit(2) syscalls to convert the data auditinfo <-> auditinfo_addr - Fully implement set{get}audit_addr(2) NOTE: This adds the ability for processes to correctly set extended subject information. The appropriate userspace utilities still need to be updated. MFC after: 1 month Reviewed by: rwatson Obtained from: TrustedBSD
2007-04-13 14:55:19 +00:00
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:
case AUE_BIND:
case AUE_LISTEN:
case AUE_CONNECT:
case AUE_RECV:
case AUE_RECVFROM:
case AUE_RECVMSG:
case AUE_SEND:
case AUE_SENDFILE:
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_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_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_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_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:
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:
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_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);
}
if (ar->ar_arg_cmd == F_GETLK || ar->ar_arg_cmd == F_SETLK ||
ar->ar_arg_cmd == F_SETLKW) {
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_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_TEXT)) {
tok = au_to_text(ar->ar_arg_text);
kau_write(rec, tok);
}
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;
/* AUE_SHMOPEN, AUE_SHMUNLINK, AUE_SEMOPEN, AUE_SEMCLOSE
* and AUE_SEMUNLINK are Posix IPC */
case AUE_SHMOPEN:
if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) {
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:
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_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:
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;
Merge Capsicum overhaul: - Capability is no longer separate descriptor type. Now every descriptor has set of its own capability rights. - The cap_new(2) system call is left, but it is no longer documented and should not be used in new code. - The new syscall cap_rights_limit(2) should be used instead of cap_new(2), which limits capability rights of the given descriptor without creating a new one. - The cap_getrights(2) syscall is renamed to cap_rights_get(2). - If CAP_IOCTL capability right is present we can further reduce allowed ioctls list with the new cap_ioctls_limit(2) syscall. List of allowed ioctls can be retrived with cap_ioctls_get(2) syscall. - If CAP_FCNTL capability right is present we can further reduce fcntls that can be used with the new cap_fcntls_limit(2) syscall and retrive them with cap_fcntls_get(2). - To support ioctl and fcntl white-listing the filedesc structure was heavly modified. - The audit subsystem, kdump and procstat tools were updated to recognize new syscalls. - Capability rights were revised and eventhough I tried hard to provide backward API and ABI compatibility there are some incompatible changes that are described in detail below: CAP_CREATE old behaviour: - Allow for openat(2)+O_CREAT. - Allow for linkat(2). - Allow for symlinkat(2). CAP_CREATE new behaviour: - Allow for openat(2)+O_CREAT. Added CAP_LINKAT: - Allow for linkat(2). ABI: Reuses CAP_RMDIR bit. - Allow to be target for renameat(2). Added CAP_SYMLINKAT: - Allow for symlinkat(2). Removed CAP_DELETE. Old behaviour: - Allow for unlinkat(2) when removing non-directory object. - Allow to be source for renameat(2). Removed CAP_RMDIR. Old behaviour: - Allow for unlinkat(2) when removing directory. Added CAP_RENAMEAT: - Required for source directory for the renameat(2) syscall. Added CAP_UNLINKAT (effectively it replaces CAP_DELETE and CAP_RMDIR): - Allow for unlinkat(2) on any object. - Required if target of renameat(2) exists and will be removed by this call. Removed CAP_MAPEXEC. CAP_MMAP old behaviour: - Allow for mmap(2) with any combination of PROT_NONE, PROT_READ and PROT_WRITE. CAP_MMAP new behaviour: - Allow for mmap(2)+PROT_NONE. Added CAP_MMAP_R: - Allow for mmap(PROT_READ). Added CAP_MMAP_W: - Allow for mmap(PROT_WRITE). Added CAP_MMAP_X: - Allow for mmap(PROT_EXEC). Added CAP_MMAP_RW: - Allow for mmap(PROT_READ | PROT_WRITE). Added CAP_MMAP_RX: - Allow for mmap(PROT_READ | PROT_EXEC). Added CAP_MMAP_WX: - Allow for mmap(PROT_WRITE | PROT_EXEC). Added CAP_MMAP_RWX: - Allow for mmap(PROT_READ | PROT_WRITE | PROT_EXEC). Renamed CAP_MKDIR to CAP_MKDIRAT. Renamed CAP_MKFIFO to CAP_MKFIFOAT. Renamed CAP_MKNODE to CAP_MKNODEAT. CAP_READ old behaviour: - Allow pread(2). - Disallow read(2), readv(2) (if there is no CAP_SEEK). CAP_READ new behaviour: - Allow read(2), readv(2). - Disallow pread(2) (CAP_SEEK was also required). CAP_WRITE old behaviour: - Allow pwrite(2). - Disallow write(2), writev(2) (if there is no CAP_SEEK). CAP_WRITE new behaviour: - Allow write(2), writev(2). - Disallow pwrite(2) (CAP_SEEK was also required). Added convinient defines: #define CAP_PREAD (CAP_SEEK | CAP_READ) #define CAP_PWRITE (CAP_SEEK | CAP_WRITE) #define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ) #define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE) #define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL) #define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W) #define CAP_MMAP_RX (CAP_MMAP_R | CAP_MMAP_X) #define CAP_MMAP_WX (CAP_MMAP_W | CAP_MMAP_X) #define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X) #define CAP_RECV CAP_READ #define CAP_SEND CAP_WRITE #define CAP_SOCK_CLIENT \ (CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \ CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN) #define CAP_SOCK_SERVER \ (CAP_ACCEPT | CAP_BIND | CAP_GETPEERNAME | CAP_GETSOCKNAME | \ CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \ CAP_SETSOCKOPT | CAP_SHUTDOWN) Added defines for backward API compatibility: #define CAP_MAPEXEC CAP_MMAP_X #define CAP_DELETE CAP_UNLINKAT #define CAP_MKDIR CAP_MKDIRAT #define CAP_RMDIR CAP_UNLINKAT #define CAP_MKFIFO CAP_MKFIFOAT #define CAP_MKNOD CAP_MKNODAT #define CAP_SOCK_ALL (CAP_SOCK_CLIENT | CAP_SOCK_SERVER) Sponsored by: The FreeBSD Foundation Reviewed by: Christoph Mallon <christoph.mallon@gmx.de> Many aspects discussed with: rwatson, benl, jonathan ABI compatibility discussed with: kib
2013-03-02 00:53:12 +00:00
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)) {
Change the cap_rights_t type from uint64_t to a structure that we can extend in the future in a backward compatible (API and ABI) way. The cap_rights_t represents capability rights. We used to use one bit to represent one right, but we are running out of spare bits. Currently the new structure provides place for 114 rights (so 50 more than the previous cap_rights_t), but it is possible to grow the structure to hold at least 285 rights, although we can make it even larger if 285 rights won't be enough. The structure definition looks like this: struct cap_rights { uint64_t cr_rights[CAP_RIGHTS_VERSION + 2]; }; The initial CAP_RIGHTS_VERSION is 0. The top two bits in the first element of the cr_rights[] array contain total number of elements in the array - 2. This means if those two bits are equal to 0, we have 2 array elements. The top two bits in all remaining array elements should be 0. The next five bits in all array elements contain array index. Only one bit is used and bit position in this five-bits range defines array index. This means there can be at most five array elements in the future. To define new right the CAPRIGHT() macro must be used. The macro takes two arguments - an array index and a bit to set, eg. #define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL) We still support aliases that combine few rights, but the rights have to belong to the same array element, eg: #define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL) #define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL) #define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP) There is new API to manage the new cap_rights_t structure: cap_rights_t *cap_rights_init(cap_rights_t *rights, ...); void cap_rights_set(cap_rights_t *rights, ...); void cap_rights_clear(cap_rights_t *rights, ...); bool cap_rights_is_set(const cap_rights_t *rights, ...); bool cap_rights_is_valid(const cap_rights_t *rights); void cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); void cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); bool cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little); Capability rights to the cap_rights_init(), cap_rights_set(), cap_rights_clear() and cap_rights_is_set() functions are provided by separating them with commas, eg: cap_rights_t rights; cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FSTAT); There is no need to terminate the list of rights, as those functions are actually macros that take care of the termination, eg: #define cap_rights_set(rights, ...) \ __cap_rights_set((rights), __VA_ARGS__, 0ULL) void __cap_rights_set(cap_rights_t *rights, ...); Thanks to using one bit as an array index we can assert in those functions that there are no two rights belonging to different array elements provided together. For example this is illegal and will be detected, because CAP_LOOKUP belongs to element 0 and CAP_PDKILL to element 1: cap_rights_init(&rights, CAP_LOOKUP | CAP_PDKILL); Providing several rights that belongs to the same array's element this way is correct, but is not advised. It should only be used for aliases definition. This commit also breaks compatibility with some existing Capsicum system calls, but I see no other way to do that. This should be fine as Capsicum is still experimental and this change is not going to 9.x. Sponsored by: The FreeBSD Foundation
2013-09-05 00:09:56 +00:00
tok = au_to_rights(&ar->ar_arg_rights);
kau_write(rec, tok);
}
break;
Merge Capsicum overhaul: - Capability is no longer separate descriptor type. Now every descriptor has set of its own capability rights. - The cap_new(2) system call is left, but it is no longer documented and should not be used in new code. - The new syscall cap_rights_limit(2) should be used instead of cap_new(2), which limits capability rights of the given descriptor without creating a new one. - The cap_getrights(2) syscall is renamed to cap_rights_get(2). - If CAP_IOCTL capability right is present we can further reduce allowed ioctls list with the new cap_ioctls_limit(2) syscall. List of allowed ioctls can be retrived with cap_ioctls_get(2) syscall. - If CAP_FCNTL capability right is present we can further reduce fcntls that can be used with the new cap_fcntls_limit(2) syscall and retrive them with cap_fcntls_get(2). - To support ioctl and fcntl white-listing the filedesc structure was heavly modified. - The audit subsystem, kdump and procstat tools were updated to recognize new syscalls. - Capability rights were revised and eventhough I tried hard to provide backward API and ABI compatibility there are some incompatible changes that are described in detail below: CAP_CREATE old behaviour: - Allow for openat(2)+O_CREAT. - Allow for linkat(2). - Allow for symlinkat(2). CAP_CREATE new behaviour: - Allow for openat(2)+O_CREAT. Added CAP_LINKAT: - Allow for linkat(2). ABI: Reuses CAP_RMDIR bit. - Allow to be target for renameat(2). Added CAP_SYMLINKAT: - Allow for symlinkat(2). Removed CAP_DELETE. Old behaviour: - Allow for unlinkat(2) when removing non-directory object. - Allow to be source for renameat(2). Removed CAP_RMDIR. Old behaviour: - Allow for unlinkat(2) when removing directory. Added CAP_RENAMEAT: - Required for source directory for the renameat(2) syscall. Added CAP_UNLINKAT (effectively it replaces CAP_DELETE and CAP_RMDIR): - Allow for unlinkat(2) on any object. - Required if target of renameat(2) exists and will be removed by this call. Removed CAP_MAPEXEC. CAP_MMAP old behaviour: - Allow for mmap(2) with any combination of PROT_NONE, PROT_READ and PROT_WRITE. CAP_MMAP new behaviour: - Allow for mmap(2)+PROT_NONE. Added CAP_MMAP_R: - Allow for mmap(PROT_READ). Added CAP_MMAP_W: - Allow for mmap(PROT_WRITE). Added CAP_MMAP_X: - Allow for mmap(PROT_EXEC). Added CAP_MMAP_RW: - Allow for mmap(PROT_READ | PROT_WRITE). Added CAP_MMAP_RX: - Allow for mmap(PROT_READ | PROT_EXEC). Added CAP_MMAP_WX: - Allow for mmap(PROT_WRITE | PROT_EXEC). Added CAP_MMAP_RWX: - Allow for mmap(PROT_READ | PROT_WRITE | PROT_EXEC). Renamed CAP_MKDIR to CAP_MKDIRAT. Renamed CAP_MKFIFO to CAP_MKFIFOAT. Renamed CAP_MKNODE to CAP_MKNODEAT. CAP_READ old behaviour: - Allow pread(2). - Disallow read(2), readv(2) (if there is no CAP_SEEK). CAP_READ new behaviour: - Allow read(2), readv(2). - Disallow pread(2) (CAP_SEEK was also required). CAP_WRITE old behaviour: - Allow pwrite(2). - Disallow write(2), writev(2) (if there is no CAP_SEEK). CAP_WRITE new behaviour: - Allow write(2), writev(2). - Disallow pwrite(2) (CAP_SEEK was also required). Added convinient defines: #define CAP_PREAD (CAP_SEEK | CAP_READ) #define CAP_PWRITE (CAP_SEEK | CAP_WRITE) #define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ) #define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE) #define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL) #define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W) #define CAP_MMAP_RX (CAP_MMAP_R | CAP_MMAP_X) #define CAP_MMAP_WX (CAP_MMAP_W | CAP_MMAP_X) #define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X) #define CAP_RECV CAP_READ #define CAP_SEND CAP_WRITE #define CAP_SOCK_CLIENT \ (CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \ CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN) #define CAP_SOCK_SERVER \ (CAP_ACCEPT | CAP_BIND | CAP_GETPEERNAME | CAP_GETSOCKNAME | \ CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \ CAP_SETSOCKOPT | CAP_SHUTDOWN) Added defines for backward API compatibility: #define CAP_MAPEXEC CAP_MMAP_X #define CAP_DELETE CAP_UNLINKAT #define CAP_MKDIR CAP_MKDIRAT #define CAP_RMDIR CAP_UNLINKAT #define CAP_MKFIFO CAP_MKFIFOAT #define CAP_MKNOD CAP_MKNODAT #define CAP_SOCK_ALL (CAP_SOCK_CLIENT | CAP_SOCK_SERVER) Sponsored by: The FreeBSD Foundation Reviewed by: Christoph Mallon <christoph.mallon@gmx.de> Many aspects discussed with: rwatson, benl, jonathan ABI compatibility discussed with: kib
2013-03-02 00:53:12 +00:00
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;
Merge Capsicum overhaul: - Capability is no longer separate descriptor type. Now every descriptor has set of its own capability rights. - The cap_new(2) system call is left, but it is no longer documented and should not be used in new code. - The new syscall cap_rights_limit(2) should be used instead of cap_new(2), which limits capability rights of the given descriptor without creating a new one. - The cap_getrights(2) syscall is renamed to cap_rights_get(2). - If CAP_IOCTL capability right is present we can further reduce allowed ioctls list with the new cap_ioctls_limit(2) syscall. List of allowed ioctls can be retrived with cap_ioctls_get(2) syscall. - If CAP_FCNTL capability right is present we can further reduce fcntls that can be used with the new cap_fcntls_limit(2) syscall and retrive them with cap_fcntls_get(2). - To support ioctl and fcntl white-listing the filedesc structure was heavly modified. - The audit subsystem, kdump and procstat tools were updated to recognize new syscalls. - Capability rights were revised and eventhough I tried hard to provide backward API and ABI compatibility there are some incompatible changes that are described in detail below: CAP_CREATE old behaviour: - Allow for openat(2)+O_CREAT. - Allow for linkat(2). - Allow for symlinkat(2). CAP_CREATE new behaviour: - Allow for openat(2)+O_CREAT. Added CAP_LINKAT: - Allow for linkat(2). ABI: Reuses CAP_RMDIR bit. - Allow to be target for renameat(2). Added CAP_SYMLINKAT: - Allow for symlinkat(2). Removed CAP_DELETE. Old behaviour: - Allow for unlinkat(2) when removing non-directory object. - Allow to be source for renameat(2). Removed CAP_RMDIR. Old behaviour: - Allow for unlinkat(2) when removing directory. Added CAP_RENAMEAT: - Required for source directory for the renameat(2) syscall. Added CAP_UNLINKAT (effectively it replaces CAP_DELETE and CAP_RMDIR): - Allow for unlinkat(2) on any object. - Required if target of renameat(2) exists and will be removed by this call. Removed CAP_MAPEXEC. CAP_MMAP old behaviour: - Allow for mmap(2) with any combination of PROT_NONE, PROT_READ and PROT_WRITE. CAP_MMAP new behaviour: - Allow for mmap(2)+PROT_NONE. Added CAP_MMAP_R: - Allow for mmap(PROT_READ). Added CAP_MMAP_W: - Allow for mmap(PROT_WRITE). Added CAP_MMAP_X: - Allow for mmap(PROT_EXEC). Added CAP_MMAP_RW: - Allow for mmap(PROT_READ | PROT_WRITE). Added CAP_MMAP_RX: - Allow for mmap(PROT_READ | PROT_EXEC). Added CAP_MMAP_WX: - Allow for mmap(PROT_WRITE | PROT_EXEC). Added CAP_MMAP_RWX: - Allow for mmap(PROT_READ | PROT_WRITE | PROT_EXEC). Renamed CAP_MKDIR to CAP_MKDIRAT. Renamed CAP_MKFIFO to CAP_MKFIFOAT. Renamed CAP_MKNODE to CAP_MKNODEAT. CAP_READ old behaviour: - Allow pread(2). - Disallow read(2), readv(2) (if there is no CAP_SEEK). CAP_READ new behaviour: - Allow read(2), readv(2). - Disallow pread(2) (CAP_SEEK was also required). CAP_WRITE old behaviour: - Allow pwrite(2). - Disallow write(2), writev(2) (if there is no CAP_SEEK). CAP_WRITE new behaviour: - Allow write(2), writev(2). - Disallow pwrite(2) (CAP_SEEK was also required). Added convinient defines: #define CAP_PREAD (CAP_SEEK | CAP_READ) #define CAP_PWRITE (CAP_SEEK | CAP_WRITE) #define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ) #define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE) #define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL) #define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W) #define CAP_MMAP_RX (CAP_MMAP_R | CAP_MMAP_X) #define CAP_MMAP_WX (CAP_MMAP_W | CAP_MMAP_X) #define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X) #define CAP_RECV CAP_READ #define CAP_SEND CAP_WRITE #define CAP_SOCK_CLIENT \ (CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \ CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN) #define CAP_SOCK_SERVER \ (CAP_ACCEPT | CAP_BIND | CAP_GETPEERNAME | CAP_GETSOCKNAME | \ CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \ CAP_SETSOCKOPT | CAP_SHUTDOWN) Added defines for backward API compatibility: #define CAP_MAPEXEC CAP_MMAP_X #define CAP_DELETE CAP_UNLINKAT #define CAP_MKDIR CAP_MKDIRAT #define CAP_RMDIR CAP_UNLINKAT #define CAP_MKFIFO CAP_MKFIFOAT #define CAP_MKNOD CAP_MKNODAT #define CAP_SOCK_ALL (CAP_SOCK_CLIENT | CAP_SOCK_SERVER) Sponsored by: The FreeBSD Foundation Reviewed by: Christoph Mallon <christoph.mallon@gmx.de> Many aspects discussed with: rwatson, benl, jonathan ABI compatibility discussed with: kib
2013-03-02 00:53:12 +00:00
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_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);
}