diff options
Diffstat (limited to 'usr/src/uts/common/syscall/auditsys.c')
-rw-r--r-- | usr/src/uts/common/syscall/auditsys.c | 1386 |
1 files changed, 1372 insertions, 14 deletions
diff --git a/usr/src/uts/common/syscall/auditsys.c b/usr/src/uts/common/syscall/auditsys.c index a940bd7d18..538709e2d2 100644 --- a/usr/src/uts/common/syscall/auditsys.c +++ b/usr/src/uts/common/syscall/auditsys.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,35 +28,63 @@ #include <sys/policy.h> #include <c2/audit.h> +#include <c2/audit_kernel.h> +#include <c2/audit_record.h> + +#define CLEAR_VAL -1 + +extern kmutex_t pidlock; + +int audit_policy; /* global audit policies in force */ + /*ARGSUSED1*/ int auditsys(struct auditcalls *uap, rval_t *rvp) { int err; + int result = 0; - /* - * this ugly hack is because auditsys returns - * 0 for all cases except audit_active == 0 - * and uap->code == BSM_AUDITCTL || default) - */ - - if (!audit_active) + if (audit_active == C2AUDIT_DISABLED) return (ENOTSUP); switch (uap->code) { case BSM_GETAUID: + result = getauid((caddr_t)uap->a1); + break; case BSM_SETAUID: + result = setauid((caddr_t)uap->a1); + break; case BSM_GETAUDIT: + result = getaudit((caddr_t)uap->a1); + break; + case BSM_GETAUDIT_ADDR: + result = getaudit_addr((caddr_t)uap->a1, (int)uap->a2); + break; case BSM_SETAUDIT: - case BSM_AUDIT: - return (0); + result = setaudit((caddr_t)uap->a1); + break; + case BSM_SETAUDIT_ADDR: + result = setaudit_addr((caddr_t)uap->a1, (int)uap->a2); + break; case BSM_AUDITCTL: - if ((int)uap->a1 == A_GETCOND) - err = secpolicy_audit_getattr(CRED()); - else - /* FALLTHROUGH */ + result = auditctl((int)uap->a1, (caddr_t)uap->a2, (int)uap->a3); + break; + case BSM_AUDIT: + if (audit_active == C2AUDIT_UNLOADED) + return (0); + result = audit((caddr_t)uap->a1, (int)uap->a2); + break; + case BSM_AUDITDOOR: + if (audit_active == C2AUDIT_LOADED) { + result = auditdoor((int)uap->a1); + break; + } default: + if (audit_active == C2AUDIT_LOADED) { + result = EINVAL; + break; + } /* Return a different error when not privileged */ err = secpolicy_audit_config(CRED()); if (err == 0) @@ -64,4 +92,1334 @@ auditsys(struct auditcalls *uap, rval_t *rvp) else return (err); } + rvp->r_vals = result; + return (result); +} + +/* + * Return the audit user ID for the current process. Currently only + * the privileged processes may see the audit id. That may change. + * If copyout is unsucessful return EFAULT. + */ +int +getauid(caddr_t auid_p) +{ + const auditinfo_addr_t *ainfo; + + if (secpolicy_audit_getattr(CRED()) != 0) + return (EPERM); + + ainfo = crgetauinfo(CRED()); + if (ainfo == NULL) + return (EINVAL); + + if (copyout(&ainfo->ai_auid, auid_p, sizeof (au_id_t))) + return (EFAULT); + + return (0); +} + +/* + * Set the audit userid, for a process. This can only be changed by + * privileged processes. The audit userid is inherited across forks & execs. + * Passed in is a pointer to the au_id_t; if copyin unsuccessful return EFAULT. + */ +int +setauid(caddr_t auid_p) +{ + proc_t *p; + au_id_t auid; + cred_t *newcred; + auditinfo_addr_t *auinfo; + + if (secpolicy_audit_config(CRED()) != 0) + return (EPERM); + + if (copyin(auid_p, &auid, sizeof (au_id_t))) { + return (EFAULT); + } + + newcred = cralloc(); + if ((auinfo = crgetauinfo_modifiable(newcred)) == NULL) { + crfree(newcred); + return (EINVAL); + } + + /* grab p_crlock and switch to new cred */ + p = curproc; + mutex_enter(&p->p_crlock); + crcopy_to(p->p_cred, newcred); + p->p_cred = newcred; + + auinfo->ai_auid = auid; /* update the auid */ + + /* unlock and broadcast the cred changes */ + mutex_exit(&p->p_crlock); + crset(p, newcred); + + return (0); +} + +/* + * Get the audit state information from the current process. + * Return EFAULT if copyout fails. + */ +int +getaudit(caddr_t info_p) +{ + STRUCT_DECL(auditinfo, info); + const auditinfo_addr_t *ainfo; + model_t model; + + if (secpolicy_audit_getattr(CRED()) != 0) + return (EPERM); + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + ainfo = crgetauinfo(CRED()); + if (ainfo == NULL) + return (EINVAL); + + /* trying to read a process with an IPv6 address? */ + if (ainfo->ai_termid.at_type == AU_IPv6) + return (EOVERFLOW); + + STRUCT_FSET(info, ai_auid, ainfo->ai_auid); + STRUCT_FSET(info, ai_mask, ainfo->ai_mask); +#ifdef _LP64 + if (model == DATAMODEL_ILP32) { + dev32_t dev; + /* convert internal 64 bit form to 32 bit version */ + if (cmpldev(&dev, ainfo->ai_termid.at_port) == 0) { + return (EOVERFLOW); + } + STRUCT_FSET(info, ai_termid.port, dev); + } else + STRUCT_FSET(info, ai_termid.port, ainfo->ai_termid.at_port); +#else + STRUCT_FSET(info, ai_termid.port, ainfo->ai_termid.at_port); +#endif + STRUCT_FSET(info, ai_termid.machine, ainfo->ai_termid.at_addr[0]); + STRUCT_FSET(info, ai_asid, ainfo->ai_asid); + + if (copyout(STRUCT_BUF(info), info_p, STRUCT_SIZE(info))) + return (EFAULT); + + return (0); +} + +/* + * Get the audit state information from the current process. + * Return EFAULT if copyout fails. + */ +int +getaudit_addr(caddr_t info_p, int len) +{ + STRUCT_DECL(auditinfo_addr, info); + const auditinfo_addr_t *ainfo; + model_t model; + + if (secpolicy_audit_getattr(CRED()) != 0) + return (EPERM); + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + if (len < STRUCT_SIZE(info)) + return (EOVERFLOW); + + ainfo = crgetauinfo(CRED()); + + if (ainfo == NULL) + return (EINVAL); + + STRUCT_FSET(info, ai_auid, ainfo->ai_auid); + STRUCT_FSET(info, ai_mask, ainfo->ai_mask); +#ifdef _LP64 + if (model == DATAMODEL_ILP32) { + dev32_t dev; + /* convert internal 64 bit form to 32 bit version */ + if (cmpldev(&dev, ainfo->ai_termid.at_port) == 0) { + return (EOVERFLOW); + } + STRUCT_FSET(info, ai_termid.at_port, dev); + } else + STRUCT_FSET(info, ai_termid.at_port, ainfo->ai_termid.at_port); +#else + STRUCT_FSET(info, ai_termid.at_port, ainfo->ai_termid.at_port); +#endif + STRUCT_FSET(info, ai_termid.at_type, ainfo->ai_termid.at_type); + STRUCT_FSET(info, ai_termid.at_addr[0], ainfo->ai_termid.at_addr[0]); + STRUCT_FSET(info, ai_termid.at_addr[1], ainfo->ai_termid.at_addr[1]); + STRUCT_FSET(info, ai_termid.at_addr[2], ainfo->ai_termid.at_addr[2]); + STRUCT_FSET(info, ai_termid.at_addr[3], ainfo->ai_termid.at_addr[3]); + STRUCT_FSET(info, ai_asid, ainfo->ai_asid); + + if (copyout(STRUCT_BUF(info), info_p, STRUCT_SIZE(info))) + return (EFAULT); + + return (0); +} + +/* + * Set the audit state information for the current process. + * Return EFAULT if copyout fails. + */ +int +setaudit(caddr_t info_p) +{ + STRUCT_DECL(auditinfo, info); + proc_t *p; + cred_t *newcred; + model_t model; + auditinfo_addr_t *ainfo; + + if (secpolicy_audit_config(CRED()) != 0) + return (EPERM); + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + if (copyin(info_p, STRUCT_BUF(info), STRUCT_SIZE(info))) + return (EFAULT); + + newcred = cralloc(); + if ((ainfo = crgetauinfo_modifiable(newcred)) == NULL) { + crfree(newcred); + return (EINVAL); + } + + /* grab p_crlock and switch to new cred */ + p = curproc; + mutex_enter(&p->p_crlock); + crcopy_to(p->p_cred, newcred); + p->p_cred = newcred; + + /* Set audit mask, id, termid and session id as specified */ + ainfo->ai_auid = STRUCT_FGET(info, ai_auid); +#ifdef _LP64 + /* only convert to 64 bit if coming from a 32 bit binary */ + if (model == DATAMODEL_ILP32) + ainfo->ai_termid.at_port = + DEVEXPL(STRUCT_FGET(info, ai_termid.port)); + else + ainfo->ai_termid.at_port = STRUCT_FGET(info, ai_termid.port); +#else + ainfo->ai_termid.at_port = STRUCT_FGET(info, ai_termid.port); +#endif + ainfo->ai_termid.at_type = AU_IPv4; + ainfo->ai_termid.at_addr[0] = STRUCT_FGET(info, ai_termid.machine); + ainfo->ai_asid = STRUCT_FGET(info, ai_asid); + ainfo->ai_mask = STRUCT_FGET(info, ai_mask); + + /* unlock and broadcast the cred changes */ + mutex_exit(&p->p_crlock); + crset(p, newcred); + + return (0); +} + +/* + * Set the audit state information for the current process. + * Return EFAULT if copyin fails. + */ +int +setaudit_addr(caddr_t info_p, int len) +{ + STRUCT_DECL(auditinfo_addr, info); + proc_t *p; + cred_t *newcred; + model_t model; + int i; + int type; + auditinfo_addr_t *ainfo; + + if (secpolicy_audit_config(CRED()) != 0) + return (EPERM); + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + if (len < STRUCT_SIZE(info)) + return (EOVERFLOW); + + if (copyin(info_p, STRUCT_BUF(info), STRUCT_SIZE(info))) + return (EFAULT); + + type = STRUCT_FGET(info, ai_termid.at_type); + if ((type != AU_IPv4) && (type != AU_IPv6)) + return (EINVAL); + + newcred = cralloc(); + if ((ainfo = crgetauinfo_modifiable(newcred)) == NULL) { + crfree(newcred); + return (EINVAL); + } + + /* grab p_crlock and switch to new cred */ + p = curproc; + mutex_enter(&p->p_crlock); + crcopy_to(p->p_cred, newcred); + p->p_cred = newcred; + + /* Set audit mask, id, termid and session id as specified */ + ainfo->ai_auid = STRUCT_FGET(info, ai_auid); + ainfo->ai_mask = STRUCT_FGET(info, ai_mask); +#ifdef _LP64 + /* only convert to 64 bit if coming from a 32 bit binary */ + if (model == DATAMODEL_ILP32) + ainfo->ai_termid.at_port = + DEVEXPL(STRUCT_FGET(info, ai_termid.at_port)); + else + ainfo->ai_termid.at_port = STRUCT_FGET(info, ai_termid.at_port); +#else + ainfo->ai_termid.at_port = STRUCT_FGET(info, ai_termid.at_port); +#endif + ainfo->ai_termid.at_type = type; + bzero(&ainfo->ai_termid.at_addr[0], sizeof (ainfo->ai_termid.at_addr)); + for (i = 0; i < (type/sizeof (int)); i++) + ainfo->ai_termid.at_addr[i] = + STRUCT_FGET(info, ai_termid.at_addr[i]); + + if (ainfo->ai_termid.at_type == AU_IPv6 && + IN6_IS_ADDR_V4MAPPED(((in6_addr_t *)ainfo->ai_termid.at_addr))) { + ainfo->ai_termid.at_type = AU_IPv4; + ainfo->ai_termid.at_addr[0] = ainfo->ai_termid.at_addr[3]; + ainfo->ai_termid.at_addr[1] = 0; + ainfo->ai_termid.at_addr[2] = 0; + ainfo->ai_termid.at_addr[3] = 0; + } + + ainfo->ai_asid = STRUCT_FGET(info, ai_asid); + + /* unlock and broadcast the cred changes */ + mutex_exit(&p->p_crlock); + crset(p, newcred); + + return (0); +} + +/* + * Get the global policy flag + */ +static int +getpolicy(caddr_t data) +{ + int policy; + au_kcontext_t *kctx = GET_KCTX_PZ; + + policy = audit_policy | kctx->auk_policy; + + if (copyout(&policy, data, sizeof (int))) + return (EFAULT); + return (0); +} + +/* + * Set the global and local policy flags + * + * The global flags only make sense from the global zone; + * the local flags depend on the AUDIT_PERZONE policy: + * if the perzone policy is set, then policy is set separately + * per zone, else held only in the global zone. + * + * The initial value of a local zone's policy flag is determined + * by the value of the global zone's flags at the time the + * local zone is created. + * + * While auditconfig(1M) allows setting and unsetting policies one bit + * at a time, the mask passed in from auditconfig() is created by a + * syscall to getpolicy and then modified based on the auditconfig() + * cmd line, so the input policy value is used to replace the existing + * policy. + */ +static int +setpolicy(caddr_t data) +{ + int policy; + au_kcontext_t *kctx; + + if (copyin(data, &policy, sizeof (int))) + return (EFAULT); + + kctx = GET_KCTX_NGZ; + + if (INGLOBALZONE(curproc)) { + if (policy & ~(AUDIT_GLOBAL | AUDIT_LOCAL)) + return (EINVAL); + + audit_policy = policy & AUDIT_GLOBAL; + } else { + if (!(audit_policy & AUDIT_PERZONE)) + return (EINVAL); + + if (policy & ~AUDIT_LOCAL) /* global bits are a no-no */ + return (EINVAL); + } + kctx->auk_policy = policy & AUDIT_LOCAL; + + /* + * auk_current_vp is NULL before auditd starts (or during early + * auditd starup) or if auditd is halted; in either case, + * notification of a policy change is not needed, since auditd + * reads policy as it comes up. The error return from au_doormsg() + * is ignored to avoid a race condition -- for example if auditd + * segv's, the audit state may be "auditing" but the door may + * be closed. Returning an error if the door is open makes it + * impossible for Greenline to restart auditd. + */ + if (kctx->auk_current_vp != NULL) + (void) au_doormsg(kctx, AU_DBUF_POLICY, &policy); + + /* + * Wake up anyone who might have blocked on full audit + * partitions. audit daemons need to set AUDIT_FULL when no + * space so we can tell if we should start dropping records. + */ + mutex_enter(&(kctx->auk_queue.lock)); + + if ((policy & (AUDIT_CNT | AUDIT_SCNT) && + (kctx->auk_queue.cnt >= kctx->auk_queue.hiwater))) + cv_broadcast(&(kctx->auk_queue.write_cv)); + + mutex_exit(&(kctx->auk_queue.lock)); + + return (0); +} + +static int +getkmask(caddr_t data) +{ + au_kcontext_t *kctx; + + kctx = GET_KCTX_PZ; + + if (copyout(&kctx->auk_info.ai_mask, data, sizeof (au_mask_t))) + return (EFAULT); + return (0); +} + +static int +setkmask(caddr_t data) +{ + au_mask_t mask; + au_kcontext_t *kctx; + + if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc)) + return (EINVAL); + + kctx = GET_KCTX_NGZ; + + if (copyin(data, &mask, sizeof (au_mask_t))) + return (EFAULT); + + kctx->auk_info.ai_mask = mask; + return (0); +} + +static int +getkaudit(caddr_t info_p, int len) +{ + STRUCT_DECL(auditinfo_addr, info); + model_t model; + au_kcontext_t *kctx = GET_KCTX_PZ; + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + if (len < STRUCT_SIZE(info)) + return (EOVERFLOW); + + STRUCT_FSET(info, ai_auid, kctx->auk_info.ai_auid); + STRUCT_FSET(info, ai_mask, kctx->auk_info.ai_mask); +#ifdef _LP64 + if (model == DATAMODEL_ILP32) { + dev32_t dev; + /* convert internal 64 bit form to 32 bit version */ + if (cmpldev(&dev, kctx->auk_info.ai_termid.at_port) == 0) { + return (EOVERFLOW); + } + STRUCT_FSET(info, ai_termid.at_port, dev); + } else { + STRUCT_FSET(info, ai_termid.at_port, + kctx->auk_info.ai_termid.at_port); + } +#else + STRUCT_FSET(info, ai_termid.at_port, + kctx->auk_info.ai_termid.at_port); +#endif + STRUCT_FSET(info, ai_termid.at_type, + kctx->auk_info.ai_termid.at_type); + STRUCT_FSET(info, ai_termid.at_addr[0], + kctx->auk_info.ai_termid.at_addr[0]); + STRUCT_FSET(info, ai_termid.at_addr[1], + kctx->auk_info.ai_termid.at_addr[1]); + STRUCT_FSET(info, ai_termid.at_addr[2], + kctx->auk_info.ai_termid.at_addr[2]); + STRUCT_FSET(info, ai_termid.at_addr[3], + kctx->auk_info.ai_termid.at_addr[3]); + STRUCT_FSET(info, ai_asid, kctx->auk_info.ai_asid); + + if (copyout(STRUCT_BUF(info), info_p, STRUCT_SIZE(info))) + return (EFAULT); + + return (0); +} + +/* + * the host address for AUDIT_PERZONE == 0 is that of the global + * zone and for local zones it is of the current zone. + */ +static int +setkaudit(caddr_t info_p, int len) +{ + STRUCT_DECL(auditinfo_addr, info); + model_t model; + au_kcontext_t *kctx; + + if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc)) + return (EINVAL); + + kctx = GET_KCTX_NGZ; + + model = get_udatamodel(); + STRUCT_INIT(info, model); + + if (len < STRUCT_SIZE(info)) + return (EOVERFLOW); + + if (copyin(info_p, STRUCT_BUF(info), STRUCT_SIZE(info))) + return (EFAULT); + + if ((STRUCT_FGET(info, ai_termid.at_type) != AU_IPv4) && + (STRUCT_FGET(info, ai_termid.at_type) != AU_IPv6)) + return (EINVAL); + + /* Set audit mask, termid and session id as specified */ + kctx->auk_info.ai_auid = STRUCT_FGET(info, ai_auid); + kctx->auk_info.ai_mask = STRUCT_FGET(info, ai_mask); +#ifdef _LP64 + /* only convert to 64 bit if coming from a 32 bit binary */ + if (model == DATAMODEL_ILP32) + kctx->auk_info.ai_termid.at_port = + DEVEXPL(STRUCT_FGET(info, ai_termid.at_port)); + else + kctx->auk_info.ai_termid.at_port = + STRUCT_FGET(info, ai_termid.at_port); +#else + kctx->auk_info.ai_termid.at_port = STRUCT_FGET(info, ai_termid.at_port); +#endif + kctx->auk_info.ai_termid.at_type = STRUCT_FGET(info, ai_termid.at_type); + bzero(&kctx->auk_info.ai_termid.at_addr[0], + sizeof (kctx->auk_info.ai_termid.at_addr)); + kctx->auk_info.ai_termid.at_addr[0] = + STRUCT_FGET(info, ai_termid.at_addr[0]); + kctx->auk_info.ai_termid.at_addr[1] = + STRUCT_FGET(info, ai_termid.at_addr[1]); + kctx->auk_info.ai_termid.at_addr[2] = + STRUCT_FGET(info, ai_termid.at_addr[2]); + kctx->auk_info.ai_termid.at_addr[3] = + STRUCT_FGET(info, ai_termid.at_addr[3]); + kctx->auk_info.ai_asid = STRUCT_FGET(info, ai_asid); + + if (kctx->auk_info.ai_termid.at_type == AU_IPv6 && + IN6_IS_ADDR_V4MAPPED( + ((in6_addr_t *)kctx->auk_info.ai_termid.at_addr))) { + kctx->auk_info.ai_termid.at_type = AU_IPv4; + kctx->auk_info.ai_termid.at_addr[0] = + kctx->auk_info.ai_termid.at_addr[3]; + kctx->auk_info.ai_termid.at_addr[1] = 0; + kctx->auk_info.ai_termid.at_addr[2] = 0; + kctx->auk_info.ai_termid.at_addr[3] = 0; + } + if (kctx->auk_info.ai_termid.at_type == AU_IPv6) + kctx->auk_hostaddr_valid = IN6_IS_ADDR_UNSPECIFIED( + (in6_addr_t *)kctx->auk_info.ai_termid.at_addr) ? 0 : 1; + else + kctx->auk_hostaddr_valid = + (kctx->auk_info.ai_termid.at_addr[0] == + htonl(INADDR_ANY)) ? 0 : 1; + + return (0); +} + +static int +getqctrl(caddr_t data) +{ + au_kcontext_t *kctx = GET_KCTX_PZ; + STRUCT_DECL(au_qctrl, qctrl); + STRUCT_INIT(qctrl, get_udatamodel()); + + mutex_enter(&(kctx->auk_queue.lock)); + STRUCT_FSET(qctrl, aq_hiwater, kctx->auk_queue.hiwater); + STRUCT_FSET(qctrl, aq_lowater, kctx->auk_queue.lowater); + STRUCT_FSET(qctrl, aq_bufsz, kctx->auk_queue.bufsz); + STRUCT_FSET(qctrl, aq_delay, kctx->auk_queue.delay); + mutex_exit(&(kctx->auk_queue.lock)); + + if (copyout(STRUCT_BUF(qctrl), data, STRUCT_SIZE(qctrl))) + return (EFAULT); + + return (0); +} + +static int +setqctrl(caddr_t data) +{ + au_kcontext_t *kctx; + struct au_qctrl qctrl_tmp; + STRUCT_DECL(au_qctrl, qctrl); + STRUCT_INIT(qctrl, get_udatamodel()); + + if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc)) + return (EINVAL); + kctx = GET_KCTX_NGZ; + + if (copyin(data, STRUCT_BUF(qctrl), STRUCT_SIZE(qctrl))) + return (EFAULT); + + qctrl_tmp.aq_hiwater = (size_t)STRUCT_FGET(qctrl, aq_hiwater); + qctrl_tmp.aq_lowater = (size_t)STRUCT_FGET(qctrl, aq_lowater); + qctrl_tmp.aq_bufsz = (size_t)STRUCT_FGET(qctrl, aq_bufsz); + qctrl_tmp.aq_delay = (clock_t)STRUCT_FGET(qctrl, aq_delay); + + /* enforce sane values */ + + if (qctrl_tmp.aq_hiwater <= qctrl_tmp.aq_lowater) + return (EINVAL); + + if (qctrl_tmp.aq_hiwater < AQ_LOWATER) + return (EINVAL); + + if (qctrl_tmp.aq_hiwater > AQ_MAXHIGH) + return (EINVAL); + + if (qctrl_tmp.aq_bufsz < AQ_BUFSZ) + return (EINVAL); + + if (qctrl_tmp.aq_bufsz > AQ_MAXBUFSZ) + return (EINVAL); + + if (qctrl_tmp.aq_delay == 0) + return (EINVAL); + + if (qctrl_tmp.aq_delay > AQ_MAXDELAY) + return (EINVAL); + + /* update everything at once so things are consistant */ + mutex_enter(&(kctx->auk_queue.lock)); + kctx->auk_queue.hiwater = qctrl_tmp.aq_hiwater; + kctx->auk_queue.lowater = qctrl_tmp.aq_lowater; + kctx->auk_queue.bufsz = qctrl_tmp.aq_bufsz; + kctx->auk_queue.delay = qctrl_tmp.aq_delay; + + if (kctx->auk_queue.rd_block && + kctx->auk_queue.cnt > kctx->auk_queue.lowater) + cv_broadcast(&(kctx->auk_queue.read_cv)); + + if (kctx->auk_queue.wt_block && + kctx->auk_queue.cnt < kctx->auk_queue.hiwater) + cv_broadcast(&(kctx->auk_queue.write_cv)); + + mutex_exit(&(kctx->auk_queue.lock)); + + return (0); +} + +static int +getcwd(caddr_t data, int length) +{ + struct p_audit_data *pad; + struct audit_path *app; + int pathlen; + + pad = P2A(curproc); + ASSERT(pad != NULL); + + mutex_enter(&(pad->pad_lock)); + app = pad->pad_cwd; + au_pathhold(app); + mutex_exit(&(pad->pad_lock)); + + pathlen = app->audp_sect[1] - app->audp_sect[0]; + if (pathlen > length) { + au_pathrele(app); + return (E2BIG); + } + + if (copyout(app->audp_sect[0], data, pathlen)) { + au_pathrele(app); + return (EFAULT); + } + + au_pathrele(app); + return (0); +} + +static int +getcar(caddr_t data, int length) +{ + struct p_audit_data *pad; + struct audit_path *app; + int pathlen; + + pad = P2A(curproc); + ASSERT(pad != NULL); + + mutex_enter(&(pad->pad_lock)); + app = pad->pad_root; + au_pathhold(app); + mutex_exit(&(pad->pad_lock)); + + pathlen = app->audp_sect[1] - app->audp_sect[0]; + if (pathlen > length) { + au_pathrele(app); + return (E2BIG); + } + + if (copyout(app->audp_sect[0], data, pathlen)) { + au_pathrele(app); + return (EFAULT); + } + + au_pathrele(app); + return (0); +} + +static int +getstat(caddr_t data) +{ + au_kcontext_t *kctx = GET_KCTX_PZ; + + membar_consumer(); + + if (copyout((caddr_t)&(kctx->auk_statistics), data, sizeof (au_stat_t))) + return (EFAULT); + return (0); +} + +static int +setstat(caddr_t data) +{ + au_kcontext_t *kctx = GET_KCTX_PZ; + au_stat_t au_stat; + + if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc)) + return (EINVAL); + + if (copyin(data, &au_stat, sizeof (au_stat_t))) + return (EFAULT); + + if (au_stat.as_generated == CLEAR_VAL) + kctx->auk_statistics.as_generated = 0; + if (au_stat.as_nonattrib == CLEAR_VAL) + kctx->auk_statistics.as_nonattrib = 0; + if (au_stat.as_kernel == CLEAR_VAL) + kctx->auk_statistics.as_kernel = 0; + if (au_stat.as_audit == CLEAR_VAL) + kctx->auk_statistics.as_audit = 0; + if (au_stat.as_auditctl == CLEAR_VAL) + kctx->auk_statistics.as_auditctl = 0; + if (au_stat.as_enqueue == CLEAR_VAL) + kctx->auk_statistics.as_enqueue = 0; + if (au_stat.as_written == CLEAR_VAL) + kctx->auk_statistics.as_written = 0; + if (au_stat.as_wblocked == CLEAR_VAL) + kctx->auk_statistics.as_wblocked = 0; + if (au_stat.as_rblocked == CLEAR_VAL) + kctx->auk_statistics.as_rblocked = 0; + if (au_stat.as_dropped == CLEAR_VAL) + kctx->auk_statistics.as_dropped = 0; + if (au_stat.as_totalsize == CLEAR_VAL) + kctx->auk_statistics.as_totalsize = 0; + + membar_producer(); + + return (0); + +} + +static int +setumask(caddr_t data) +{ + STRUCT_DECL(auditinfo, user_info); + struct proc *p; + const auditinfo_addr_t *ainfo; + model_t model; + + /* setumask not applicable in non-global zones without perzone policy */ + if (!(audit_policy & AUDIT_PERZONE) && (!INGLOBALZONE(curproc))) + return (EINVAL); + + model = get_udatamodel(); + STRUCT_INIT(user_info, model); + + if (copyin(data, STRUCT_BUF(user_info), STRUCT_SIZE(user_info))) + return (EFAULT); + + mutex_enter(&pidlock); /* lock the process queue against updates */ + for (p = practive; p != NULL; p = p->p_next) { + cred_t *cr; + + /* if in non-global zone only modify processes in same zone */ + if (!HASZONEACCESS(curproc, p->p_zone->zone_id)) + continue; + + mutex_enter(&p->p_lock); /* so process doesn't go away */ + + /* skip system processes and ones being created or going away */ + if (p->p_stat == SIDL || p->p_stat == SZOMB || + (p->p_flag & (SSYS | SEXITING | SEXITLWPS))) { + mutex_exit(&p->p_lock); + continue; + } + + mutex_enter(&p->p_crlock); + crhold(cr = p->p_cred); + mutex_exit(&p->p_crlock); + ainfo = crgetauinfo(cr); + if (ainfo == NULL) { + mutex_exit(&p->p_lock); + crfree(cr); + continue; + } + + if (ainfo->ai_auid == STRUCT_FGET(user_info, ai_auid)) { + au_mask_t mask; + int err; + + /* + * Here's a process which matches the specified auid. + * If its mask doesn't already match the new mask, + * save the new mask in the pad, to be picked up + * next syscall. + */ + mask = STRUCT_FGET(user_info, ai_mask); + err = bcmp(&mask, &ainfo->ai_mask, sizeof (au_mask_t)); + crfree(cr); + if (err != 0) { + struct p_audit_data *pad = P2A(p); + ASSERT(pad != NULL); + + mutex_enter(&(pad->pad_lock)); + pad->pad_flags |= PAD_SETMASK; + pad->pad_newmask = mask; + mutex_exit(&(pad->pad_lock)); + + /* + * No need to call set_proc_pre_sys(), since + * t_pre_sys is ALWAYS on when audit is + * enabled...due to syscall auditing. + */ + } + } else { + crfree(cr); + } + mutex_exit(&p->p_lock); + } + mutex_exit(&pidlock); + + return (0); +} + +static int +setsmask(caddr_t data) +{ + STRUCT_DECL(auditinfo, user_info); + struct proc *p; + const auditinfo_addr_t *ainfo; + model_t model; + + /* setsmask not applicable in non-global zones without perzone policy */ + if (!(audit_policy & AUDIT_PERZONE) && (!INGLOBALZONE(curproc))) + return (EINVAL); + + model = get_udatamodel(); + STRUCT_INIT(user_info, model); + + if (copyin(data, STRUCT_BUF(user_info), STRUCT_SIZE(user_info))) + return (EFAULT); + + mutex_enter(&pidlock); /* lock the process queue against updates */ + for (p = practive; p != NULL; p = p->p_next) { + cred_t *cr; + + /* if in non-global zone only modify processes in same zone */ + if (!HASZONEACCESS(curproc, p->p_zone->zone_id)) + continue; + + mutex_enter(&p->p_lock); /* so process doesn't go away */ + + /* skip system processes and ones being created or going away */ + if (p->p_stat == SIDL || p->p_stat == SZOMB || + (p->p_flag & (SSYS | SEXITING | SEXITLWPS))) { + mutex_exit(&p->p_lock); + continue; + } + + mutex_enter(&p->p_crlock); + crhold(cr = p->p_cred); + mutex_exit(&p->p_crlock); + ainfo = crgetauinfo(cr); + if (ainfo == NULL) { + mutex_exit(&p->p_lock); + crfree(cr); + continue; + } + + if (ainfo->ai_asid == STRUCT_FGET(user_info, ai_asid)) { + au_mask_t mask; + int err; + + /* + * Here's a process which matches the specified asid. + * If its mask doesn't already match the new mask, + * save the new mask in the pad, to be picked up + * next syscall. + */ + mask = STRUCT_FGET(user_info, ai_mask); + err = bcmp(&mask, &ainfo->ai_mask, sizeof (au_mask_t)); + crfree(cr); + if (err != 0) { + struct p_audit_data *pad = P2A(p); + ASSERT(pad != NULL); + + mutex_enter(&(pad->pad_lock)); + pad->pad_flags |= PAD_SETMASK; + pad->pad_newmask = mask; + mutex_exit(&(pad->pad_lock)); + + /* + * No need to call set_proc_pre_sys(), since + * t_pre_sys is ALWAYS on when audit is + * enabled...due to syscall auditing. + */ + } + } else { + crfree(cr); + } + mutex_exit(&p->p_lock); + } + mutex_exit(&pidlock); + + return (0); +} + +/* + * Get the current audit state of the system + */ +static int +getcond(caddr_t data) +{ + au_kcontext_t *kctx = GET_KCTX_PZ; + + if (copyout(&(kctx->auk_auditstate), data, sizeof (int))) + return (EFAULT); + + return (0); +} + +/* + * Set the current audit state of the system to on (AUC_AUDITING) or + * off (AUC_NOAUDIT). + */ +/* ARGSUSED */ +static int +setcond(caddr_t data) +{ + int auditstate; + au_kcontext_t *kctx; + + if (!(audit_policy & AUDIT_PERZONE) && (!INGLOBALZONE(curproc))) + return (EINVAL); + + kctx = GET_KCTX_NGZ; + + if (copyin(data, &auditstate, sizeof (int))) + return (EFAULT); + + switch (auditstate) { + case AUC_AUDITING: /* Turn auditing on */ + if (audit_active == C2AUDIT_UNLOADED) + audit_init_module(); + kctx->auk_auditstate = AUC_AUDITING; + if (!(audit_policy & AUDIT_PERZONE) && INGLOBALZONE(curproc)) + set_all_zone_usr_proc_sys(ALL_ZONES); + else + set_all_zone_usr_proc_sys(curproc->p_zone->zone_id); + break; + + case AUC_NOAUDIT: /* Turn auditing off */ + if (kctx->auk_auditstate == AUC_NOAUDIT) + break; + kctx->auk_auditstate = AUC_NOAUDIT; + + /* clear out the audit queue */ + + mutex_enter(&(kctx->auk_queue.lock)); + if (kctx->auk_queue.wt_block) + cv_broadcast(&(kctx->auk_queue.write_cv)); + + /* unblock au_output_thread */ + cv_broadcast(&(kctx->auk_queue.read_cv)); + + mutex_exit(&(kctx->auk_queue.lock)); + break; + + default: + return (EINVAL); + } + + return (0); +} + +static int +getclass(caddr_t data) +{ + au_evclass_map_t event; + au_kcontext_t *kctx = GET_KCTX_PZ; + + if (copyin(data, &event, sizeof (au_evclass_map_t))) + return (EFAULT); + + if (event.ec_number > MAX_KEVENTS) + return (EINVAL); + + event.ec_class = kctx->auk_ets[event.ec_number]; + + if (copyout(&event, data, sizeof (au_evclass_map_t))) + return (EFAULT); + + return (0); +} + +static int +setclass(caddr_t data) +{ + au_evclass_map_t event; + au_kcontext_t *kctx; + + if (!(audit_policy & AUDIT_PERZONE) && !INGLOBALZONE(curproc)) + return (EINVAL); + + kctx = GET_KCTX_NGZ; + + if (copyin(data, &event, sizeof (au_evclass_map_t))) + return (EFAULT); + + if (event.ec_number > MAX_KEVENTS) + return (EINVAL); + + kctx->auk_ets[event.ec_number] = event.ec_class; + + return (0); +} + +static int +getpinfo(caddr_t data) +{ + STRUCT_DECL(auditpinfo, apinfo); + proc_t *proc; + const auditinfo_addr_t *ainfo; + model_t model; + cred_t *cr, *newcred; + + model = get_udatamodel(); + STRUCT_INIT(apinfo, model); + + if (copyin(data, STRUCT_BUF(apinfo), STRUCT_SIZE(apinfo))) + return (EFAULT); + + newcred = cralloc(); + + mutex_enter(&pidlock); + if ((proc = prfind(STRUCT_FGET(apinfo, ap_pid))) == NULL) { + mutex_exit(&pidlock); + crfree(newcred); + return (ESRCH); /* no such process */ + } + mutex_enter(&proc->p_lock); /* so process doesn't go away */ + mutex_exit(&pidlock); + + audit_update_context(proc, newcred); /* make sure it's up-to-date */ + + mutex_enter(&proc->p_crlock); + crhold(cr = proc->p_cred); + mutex_exit(&proc->p_crlock); + mutex_exit(&proc->p_lock); + + ainfo = crgetauinfo(cr); + if (ainfo == NULL) { + crfree(cr); + return (EINVAL); + } + + /* designated process has an ipv6 address? */ + if (ainfo->ai_termid.at_type == AU_IPv6) { + crfree(cr); + return (EOVERFLOW); + } + + STRUCT_FSET(apinfo, ap_auid, ainfo->ai_auid); + STRUCT_FSET(apinfo, ap_asid, ainfo->ai_asid); +#ifdef _LP64 + if (model == DATAMODEL_ILP32) { + dev32_t dev; + /* convert internal 64 bit form to 32 bit version */ + if (cmpldev(&dev, ainfo->ai_termid.at_port) == 0) { + crfree(cr); + return (EOVERFLOW); + } + STRUCT_FSET(apinfo, ap_termid.port, dev); + } else + STRUCT_FSET(apinfo, ap_termid.port, ainfo->ai_termid.at_port); +#else + STRUCT_FSET(apinfo, ap_termid.port, ainfo->ai_termid.at_port); +#endif + STRUCT_FSET(apinfo, ap_termid.machine, ainfo->ai_termid.at_addr[0]); + STRUCT_FSET(apinfo, ap_mask, ainfo->ai_mask); + + crfree(cr); + + if (copyout(STRUCT_BUF(apinfo), data, STRUCT_SIZE(apinfo))) + return (EFAULT); + + return (0); +} + +static int +getpinfo_addr(caddr_t data, int len) +{ + STRUCT_DECL(auditpinfo_addr, apinfo); + proc_t *proc; + const auditinfo_addr_t *ainfo; + model_t model; + cred_t *cr, *newcred; + + model = get_udatamodel(); + STRUCT_INIT(apinfo, model); + + if (len < STRUCT_SIZE(apinfo)) + return (EOVERFLOW); + + if (copyin(data, STRUCT_BUF(apinfo), STRUCT_SIZE(apinfo))) + return (EFAULT); + + newcred = cralloc(); + + mutex_enter(&pidlock); + if ((proc = prfind(STRUCT_FGET(apinfo, ap_pid))) == NULL) { + mutex_exit(&pidlock); + crfree(newcred); + return (ESRCH); + } + mutex_enter(&proc->p_lock); /* so process doesn't go away */ + mutex_exit(&pidlock); + + audit_update_context(proc, newcred); /* make sure it's up-to-date */ + + mutex_enter(&proc->p_crlock); + crhold(cr = proc->p_cred); + mutex_exit(&proc->p_crlock); + mutex_exit(&proc->p_lock); + + ainfo = crgetauinfo(cr); + if (ainfo == NULL) { + crfree(cr); + return (EINVAL); + } + + STRUCT_FSET(apinfo, ap_auid, ainfo->ai_auid); + STRUCT_FSET(apinfo, ap_asid, ainfo->ai_asid); +#ifdef _LP64 + if (model == DATAMODEL_ILP32) { + dev32_t dev; + /* convert internal 64 bit form to 32 bit version */ + if (cmpldev(&dev, ainfo->ai_termid.at_port) == 0) { + crfree(cr); + return (EOVERFLOW); + } + STRUCT_FSET(apinfo, ap_termid.at_port, dev); + } else + STRUCT_FSET(apinfo, ap_termid.at_port, + ainfo->ai_termid.at_port); +#else + STRUCT_FSET(apinfo, ap_termid.at_port, ainfo->ai_termid.at_port); +#endif + STRUCT_FSET(apinfo, ap_termid.at_type, ainfo->ai_termid.at_type); + STRUCT_FSET(apinfo, ap_termid.at_addr[0], ainfo->ai_termid.at_addr[0]); + STRUCT_FSET(apinfo, ap_termid.at_addr[1], ainfo->ai_termid.at_addr[1]); + STRUCT_FSET(apinfo, ap_termid.at_addr[2], ainfo->ai_termid.at_addr[2]); + STRUCT_FSET(apinfo, ap_termid.at_addr[3], ainfo->ai_termid.at_addr[3]); + STRUCT_FSET(apinfo, ap_mask, ainfo->ai_mask); + + crfree(cr); + + if (copyout(STRUCT_BUF(apinfo), data, STRUCT_SIZE(apinfo))) + return (EFAULT); + + return (0); +} + +static int +setpmask(caddr_t data) +{ + STRUCT_DECL(auditpinfo, apinfo); + proc_t *proc; + cred_t *newcred; + auditinfo_addr_t *ainfo; + struct p_audit_data *pad; + + model_t model; + + model = get_udatamodel(); + STRUCT_INIT(apinfo, model); + + if (copyin(data, STRUCT_BUF(apinfo), STRUCT_SIZE(apinfo))) + return (EFAULT); + + mutex_enter(&pidlock); + if ((proc = prfind(STRUCT_FGET(apinfo, ap_pid))) == NULL) { + mutex_exit(&pidlock); + return (ESRCH); + } + mutex_enter(&proc->p_lock); /* so process doesn't go away */ + mutex_exit(&pidlock); + + newcred = cralloc(); + if ((ainfo = crgetauinfo_modifiable(newcred)) == NULL) { + mutex_exit(&proc->p_lock); + crfree(newcred); + return (EINVAL); + } + + mutex_enter(&proc->p_crlock); + crcopy_to(proc->p_cred, newcred); + proc->p_cred = newcred; + + ainfo->ai_mask = STRUCT_FGET(apinfo, ap_mask); + + /* + * Unlock. No need to broadcast changes via set_proc_pre_sys(), + * since t_pre_sys is ALWAYS on when audit is enabled... due to + * syscall auditing. + */ + crfree(newcred); + mutex_exit(&proc->p_crlock); + + /* Reset flag for any previous pending mask change; this supercedes */ + pad = P2A(proc); + ASSERT(pad != NULL); + mutex_enter(&(pad->pad_lock)); + pad->pad_flags &= ~PAD_SETMASK; + mutex_exit(&(pad->pad_lock)); + + mutex_exit(&proc->p_lock); + + return (0); +} + +/* + * The out of control system call + * This is audit kitchen sink aka auditadm, aka auditon + */ +int +auditctl( + int cmd, + caddr_t data, + int length) +{ + int result; + + switch (cmd) { + case A_GETCOND: + case A_GETCAR: + case A_GETCLASS: + case A_GETCWD: + case A_GETKAUDIT: + case A_GETKMASK: + case A_GETPINFO: + case A_GETPINFO_ADDR: + case A_GETPOLICY: + case A_GETQCTRL: + case A_GETSTAT: + if (secpolicy_audit_getattr(CRED()) != 0) + return (EPERM); + break; + default: + if (secpolicy_audit_config(CRED()) != 0) + return (EPERM); + break; + } + + switch (cmd) { + case A_GETPOLICY: + result = getpolicy(data); + break; + case A_SETPOLICY: + result = setpolicy(data); + break; + case A_GETKMASK: + result = getkmask(data); + break; + case A_SETKMASK: + result = setkmask(data); + break; + case A_GETKAUDIT: + result = getkaudit(data, length); + break; + case A_SETKAUDIT: + result = setkaudit(data, length); + break; + case A_GETQCTRL: + result = getqctrl(data); + break; + case A_SETQCTRL: + result = setqctrl(data); + break; + case A_GETCWD: + result = getcwd(data, length); + break; + case A_GETCAR: + result = getcar(data, length); + break; + case A_GETSTAT: + result = getstat(data); + break; + case A_SETSTAT: + result = setstat(data); + break; + case A_SETUMASK: + result = setumask(data); + break; + case A_SETSMASK: + result = setsmask(data); + break; + case A_GETCOND: + result = getcond(data); + break; + case A_SETCOND: + result = setcond(data); + break; + case A_GETCLASS: + result = getclass(data); + break; + case A_SETCLASS: + result = setclass(data); + break; + case A_GETPINFO: + result = getpinfo(data); + break; + case A_GETPINFO_ADDR: + result = getpinfo_addr(data, length); + break; + case A_SETPMASK: + result = setpmask(data); + break; + default: + result = EINVAL; + break; + } + return (result); } |