summaryrefslogtreecommitdiff
path: root/usr/src/uts
diff options
context:
space:
mode:
authorcasper <none@none>2008-03-03 07:48:05 -0800
committercasper <none@none>2008-03-03 07:48:05 -0800
commitddf7fe95b8ad67aa16deb427a0b78f4dd4ff22b1 (patch)
treed61fb6e84331e6682c0c2fbd86826fc05b4edee1 /usr/src/uts
parentea4b186bb8308ae1daf547212cb90881570ff943 (diff)
downloadillumos-joyent-ddf7fe95b8ad67aa16deb427a0b78f4dd4ff22b1.tar.gz
PSARC 2008/109 Fine Grained Access Permissions (FGAP)
6664443 Implement PSARC/2008/109
Diffstat (limited to 'usr/src/uts')
-rw-r--r--usr/src/uts/common/Makefile.files7
-rw-r--r--usr/src/uts/common/fs/proc/prvnops.c19
-rw-r--r--usr/src/uts/common/inet/sctp/sctp_bind.c4
-rw-r--r--usr/src/uts/common/inet/tcp/tcp.c6
-rw-r--r--usr/src/uts/common/inet/udp/udp.c6
-rw-r--r--usr/src/uts/common/os/cred.c86
-rw-r--r--usr/src/uts/common/os/exec.c14
-rw-r--r--usr/src/uts/common/os/klpd.c722
-rw-r--r--usr/src/uts/common/os/policy.c161
-rw-r--r--usr/src/uts/common/os/project.c8
-rw-r--r--usr/src/uts/common/os/sysent.c6
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/cred.h4
-rw-r--r--usr/src/uts/common/sys/cred_impl.h4
-rw-r--r--usr/src/uts/common/sys/klpd.h102
-rw-r--r--usr/src/uts/common/sys/policy.h8
-rw-r--r--usr/src/uts/common/sys/priv.h8
-rw-r--r--usr/src/uts/common/sys/project.h4
-rw-r--r--usr/src/uts/common/syscall/gid.c33
-rw-r--r--usr/src/uts/common/syscall/groups.c9
-rw-r--r--usr/src/uts/common/syscall/ppriv.c59
-rw-r--r--usr/src/uts/common/syscall/uid.c59
22 files changed, 1209 insertions, 121 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index d42fb5855c..b802606d0d 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -179,6 +179,7 @@ GENUNIX_OBJS += \
kcpc.o \
kdi.o \
kiconv.o \
+ klpd.o \
kmem.o \
ksyms_snapshot.o \
l_strplumb.o \
@@ -1195,7 +1196,7 @@ RPCMOD_OBJS += rpcmod.o clnt_cots.o clnt_clts.o \
rpc_prot.o rpc_sztypes.o rpc_subr.o rpcb_prot.o \
svc.o svc_clts.o svc_gen.o svc_cots.o \
rpcsys.o xdr_sizeof.o clnt_rdma.o svc_rdma.o \
- xdr_rdma.o rdma_subr.o xdrrdma_sizeof.o
+ xdr_rdma.o rdma_subr.o xdrrdma_sizeof.o
TLIMOD_OBJS += tlimod.o t_kalloc.o t_kbind.o t_kclose.o \
t_kconnect.o t_kfree.o t_kgtstate.o t_kopen.o \
@@ -1260,7 +1261,7 @@ DPROV_OBJS += dprov.o
DCA_OBJS += dca.o dca_3des.o dca_debug.o dca_dsa.o dca_kstat.o dca_rng.o \
dca_rsa.o
-AESPROV_OBJS += aes.o aes_cbc_crypt.o aes_impl.o
+AESPROV_OBJS += aes.o aes_cbc_crypt.o aes_impl.o
ARCFOURPROV_OBJS += arcfour.o arcfour_crypt.o
@@ -1540,7 +1541,7 @@ CH_COM_OBJS = ch_mac.o ch_subr.o cspi.o espi.o ixf1010.o mc3.o mc4.o mc5.o \
vsc7321.o vsc7326.o xpak.o
#
-# PCI strings file
+# PCI strings file
#
PCI_STRING_OBJS = pci_strings.o
diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c
index 866dee6ca8..e8fae94160 100644
--- a/usr/src/uts/common/fs/proc/prvnops.c
+++ b/usr/src/uts/common/fs/proc/prvnops.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -3158,17 +3158,18 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
error = priv_proc_cred_perm(cr, p, NULL, mode);
if (error != 0 || p == curproc || (p->p_flag & SSYS) ||
- p->p_as == &kas || (xvp = p->p_exec) == NULL ||
- secpolicy_proc_access(cr) == 0) {
+ p->p_as == &kas || (xvp = p->p_exec) == NULL) {
prunlock(pnp);
} else {
/*
* Determine if the process's executable is readable.
- * We have to drop p->p_lock before the VOP operation.
+ * We have to drop p->p_lock before the secpolicy
+ * and VOP operation.
*/
VN_HOLD(xvp);
prunlock(pnp);
- error = VOP_ACCESS(xvp, VREAD, 0, cr, ct);
+ if (secpolicy_proc_access(cr) != 0)
+ error = VOP_ACCESS(xvp, VREAD, 0, cr, ct);
VN_RELE(xvp);
}
if (error)
@@ -3390,15 +3391,17 @@ pr_lookup_procdir(vnode_t *dp, char *comp)
return (NULL);
}
ASSERT(p->p_stat != 0);
- mutex_enter(&p->p_lock);
- mutex_exit(&pidlock);
+ /* NOTE: we're holding pidlock across the policy call. */
if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
- mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
prfreenode(pnp);
return (NULL);
}
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
/*
* If a process vnode already exists and it is not invalid
* and it was created by the current process and it belongs
diff --git a/usr/src/uts/common/inet/sctp/sctp_bind.c b/usr/src/uts/common/inet/sctp/sctp_bind.c
index a1cfa0dfe4..2091d91ab5 100644
--- a/usr/src/uts/common/inet/sctp/sctp_bind.c
+++ b/usr/src/uts/common/inet/sctp/sctp_bind.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -104,7 +104,7 @@ sctp_select_port(sctp_t *sctp, in_port_t *requested_port, int *user_specified)
* we can use it here.
*/
if (secpolicy_net_privaddr(sctp->sctp_credp,
- *requested_port) != 0) {
+ *requested_port, IPPROTO_SCTP) != 0) {
dprint(1,
("sctp_bind(x): no prive for port %d",
*requested_port));
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index e836fcdab2..0cd0217377 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -3249,7 +3249,8 @@ tcp_bind(tcp_t *tcp, mblk_t *mp)
}
}
if (priv) {
- if (secpolicy_net_privaddr(cr, requested_port) != 0) {
+ if (secpolicy_net_privaddr(cr, requested_port,
+ IPPROTO_TCP) != 0) {
if (tcp->tcp_debug) {
(void) strlog(TCP_MOD_ID, 0, 1,
SL_ERROR|SL_TRACE,
@@ -10513,7 +10514,8 @@ tcp_opt_set(queue_t *q, uint_t optset_context, int level, int name,
tcp->tcp_recvdstaddr = onoff;
break;
case TCP_ANONPRIVBIND:
- if ((reterr = secpolicy_net_privaddr(cr, 0)) != 0) {
+ if ((reterr = secpolicy_net_privaddr(cr, 0,
+ IPPROTO_TCP)) != 0) {
*outlenp = 0;
return (reterr);
}
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index adf3fd4390..29815e6b27 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -707,7 +707,8 @@ udp_bind(queue_t *q, mblk_t *mp)
if (priv) {
cred_t *cr = DB_CREDDEF(mp, connp->conn_cred);
- if (secpolicy_net_privaddr(cr, port) != 0) {
+ if (secpolicy_net_privaddr(cr, port,
+ IPPROTO_UDP) != 0) {
udp_err_ack(q, mp, TACCES, 0);
return;
}
@@ -3754,7 +3755,8 @@ udp_opt_set_locked(queue_t *q, uint_t optset_context, int level,
case IPPROTO_UDP:
switch (name) {
case UDP_ANONPRIVBIND:
- if ((error = secpolicy_net_privaddr(cr, 0)) != 0) {
+ if ((error = secpolicy_net_privaddr(cr, 0,
+ IPPROTO_UDP)) != 0) {
*outlenp = 0;
return (error);
}
diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c
index 7b3709de38..b0bf2cb36a 100644
--- a/usr/src/uts/common/os/cred.c
+++ b/usr/src/uts/common/os/cred.c
@@ -62,6 +62,7 @@
#include <sys/tsol/label.h>
#include <sys/sid.h>
#include <sys/idmap.h>
+#include <sys/klpd.h>
#include <sys/varargs.h>
@@ -132,6 +133,9 @@ get_ephemeral_zsd(zone_t *zone)
return (eph_zsd);
}
+static cred_t *crdup_flags(cred_t *, int);
+static cred_t *cralloc_flags(int);
+
/*
* This function is called when a zone is destroyed
*/
@@ -236,17 +240,28 @@ cred_init(void)
/*
* Allocate (nearly) uninitialized cred_t.
*/
-cred_t *
-cralloc(void)
+static cred_t *
+cralloc_flags(int flgs)
{
- cred_t *cr = kmem_cache_alloc(cred_cache, KM_SLEEP);
+ cred_t *cr = kmem_cache_alloc(cred_cache, flgs);
+
+ if (cr == NULL)
+ return (NULL);
+
cr->cr_ref = 1; /* So we can crfree() */
cr->cr_zone = NULL;
cr->cr_label = NULL;
cr->cr_ksid = NULL;
+ cr->cr_klpd = NULL;
return (cr);
}
+cred_t *
+cralloc(void)
+{
+ return (cralloc_flags(KM_SLEEP));
+}
+
/*
* As cralloc but prepared for ksid change (if appropriate).
*/
@@ -273,6 +288,7 @@ crget(void)
zone_cred_hold(cr->cr_zone);
if (cr->cr_label)
label_hold(cr->cr_label);
+ ASSERT(cr->cr_klpd == NULL);
return (cr);
}
@@ -337,6 +353,8 @@ crfree(cred_t *cr)
ASSERT(cr != kcred);
if (cr->cr_label)
label_rele(cr->cr_label);
+ if (cr->cr_klpd)
+ crklpd_rele(cr->cr_klpd);
if (cr->cr_zone)
zone_cred_rele(cr->cr_zone);
if (cr->cr_ksid)
@@ -360,9 +378,11 @@ crcopy(cred_t *cr)
if (newcr->cr_zone)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
- label_hold(cr->cr_label);
+ label_hold(newcr->cr_label);
if (newcr->cr_ksid)
- kcrsid_hold(cr->cr_ksid);
+ kcrsid_hold(newcr->cr_ksid);
+ if (newcr->cr_klpd)
+ crklpd_hold(newcr->cr_klpd);
crfree(cr);
newcr->cr_ref = 2; /* caller gets two references */
return (newcr);
@@ -385,6 +405,8 @@ crcopy_to(cred_t *oldcr, cred_t *newcr)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (newcr->cr_klpd)
+ crklpd_hold(newcr->cr_klpd);
if (nkcr) {
newcr->cr_ksid = nkcr;
kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
@@ -398,23 +420,35 @@ crcopy_to(cred_t *oldcr, cred_t *newcr)
* Dup a cred struct to a new held one.
* The old cred is not freed.
*/
-cred_t *
-crdup(cred_t *cr)
+static cred_t *
+crdup_flags(cred_t *cr, int flgs)
{
cred_t *newcr;
- newcr = cralloc();
+ newcr = cralloc_flags(flgs);
+
+ if (newcr == NULL)
+ return (NULL);
+
bcopy(cr, newcr, crsize);
if (newcr->cr_zone)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (newcr->cr_klpd)
+ crklpd_hold(newcr->cr_klpd);
if (newcr->cr_ksid)
kcrsid_hold(newcr->cr_ksid);
newcr->cr_ref = 1;
return (newcr);
}
+cred_t *
+crdup(cred_t *cr)
+{
+ return (crdup_flags(cr, KM_SLEEP));
+}
+
/*
* Dup a cred struct to a new held one.
* The old cred is not freed.
@@ -431,6 +465,8 @@ crdup_to(cred_t *oldcr, cred_t *newcr)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (newcr->cr_klpd)
+ crklpd_hold(newcr->cr_klpd);
if (nkcr) {
newcr->cr_ksid = nkcr;
kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
@@ -531,7 +567,7 @@ hasprocperm(const cred_t *tcrp, const cred_t *scrp)
* This interface replaces hasprocperm; it works like hasprocperm but
* additionally returns success if the proc_t's match
* It is the preferred interface for most uses.
- * And it will acquire pcrlock itself, so it assert's that it shouldn't
+ * And it will acquire p_crlock itself, so it assert's that it shouldn't
* be held.
*/
int
@@ -549,9 +585,10 @@ prochasprocperm(proc_t *tp, proc_t *sp, const cred_t *scrp)
return (0);
mutex_enter(&tp->p_crlock);
- tcrp = tp->p_cred;
- rets = hasprocperm(tcrp, scrp);
+ crhold(tcrp = tp->p_cred);
mutex_exit(&tp->p_crlock);
+ rets = hasprocperm(tcrp, scrp);
+ crfree(tcrp);
return (rets);
}
@@ -972,8 +1009,7 @@ newcred_from_bslabel(bslabel_t *blabel, uint32_t doi, int flags)
cred_t *cr = NULL;
if (lbl != NULL) {
- if ((cr = kmem_cache_alloc(cred_cache, flags)) != NULL) {
- bcopy(dummycr, cr, crsize);
+ if ((cr = crdup_flags(dummycr, flags)) != NULL) {
cr->cr_label = lbl;
} else {
label_rele(lbl);
@@ -995,12 +1031,10 @@ copycred_from_bslabel(cred_t *cr, bslabel_t *blabel, uint32_t doi, int flags)
cred_t *newcr = NULL;
if (lbl != NULL) {
- if ((newcr = kmem_cache_alloc(cred_cache, flags)) != NULL) {
- bcopy(cr, newcr, crsize);
- if (newcr->cr_zone)
- zone_cred_hold(newcr->cr_zone);
+ if ((newcr = crdup_flags(cr, flags)) != NULL) {
+ if (newcr->cr_label != NULL)
+ label_rele(newcr->cr_label);
newcr->cr_label = lbl;
- newcr->cr_ref = 1;
} else {
label_rele(lbl);
}
@@ -1245,3 +1279,19 @@ crsetpriv(cred_t *cr, ...)
va_end(ap);
return (0);
}
+
+struct credklpd *
+crgetcrklpd(const cred_t *cr)
+{
+ return (cr->cr_klpd);
+}
+
+void
+crsetcrklpd(cred_t *cr, struct credklpd *crklpd)
+{
+ ASSERT(cr->cr_ref <= 2);
+
+ if (cr->cr_klpd != NULL)
+ crklpd_rele(cr->cr_klpd);
+ cr->cr_klpd = crklpd;
+}
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index 9ac97c3146..1162410939 100644
--- a/usr/src/uts/common/os/exec.c
+++ b/usr/src/uts/common/os/exec.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -145,9 +145,6 @@ exec_common(const char *fname, const char **argp, const char **envp,
if (curthread == p->p_agenttp)
return (ENOTSUP);
- if ((error = secpolicy_basic_exec(CRED())) != 0)
- return (error);
-
if (brand_action != EBA_NONE) {
/*
* Brand actions are not supported for processes that are not
@@ -229,6 +226,15 @@ exec_common(const char *fname, const char **argp, const char **envp,
goto out;
}
+ if ((error = secpolicy_basic_exec(CRED(), vp)) != 0) {
+ if (dir != NULL)
+ VN_RELE(dir);
+ pn_free(&resolvepn);
+ pn_free(&pn);
+ VN_RELE(vp);
+ goto out;
+ }
+
/*
* We do not allow executing files in attribute directories.
* We test this by determining whether the resolved path
diff --git a/usr/src/uts/common/os/klpd.c b/usr/src/uts/common/os/klpd.c
new file mode 100644
index 0000000000..dc22fc5c41
--- /dev/null
+++ b/usr/src/uts/common/os/klpd.c
@@ -0,0 +1,722 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/atomic.h>
+#include <sys/door.h>
+#include <sys/proc.h>
+#include <sys/cred_impl.h>
+#include <sys/policy.h>
+#include <sys/priv.h>
+#include <sys/klpd.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/project.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/pathname.h>
+#include <sys/varargs.h>
+#include <sys/zone.h>
+#include <netinet/in.h>
+
+#define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
+
+static kmutex_t klpd_mutex;
+
+typedef struct klpd_reg {
+ struct klpd_reg *klpd_next;
+ struct klpd_reg **klpd_refp;
+ door_handle_t klpd_door;
+ pid_t klpd_door_pid;
+ priv_set_t klpd_pset;
+ cred_t *klpd_cred;
+ int klpd_indel; /* Disabled */
+ uint32_t klpd_ref;
+} klpd_reg_t;
+
+
+/*
+ * This data structure hangs off the credential of a process; the
+ * credential is finalized and cannot be changed; but this structure
+ * can be changed when a new door server for the particular group
+ * needs to be registered. It is refcounted and shared between
+ * processes with common ancestry.
+ *
+ * The reference count is atomically updated.
+ *
+ * But the registration probably needs to be updated under a lock.
+ */
+typedef struct credklpd {
+ kmutex_t crkl_lock;
+ klpd_reg_t *crkl_reg;
+ uint32_t crkl_ref;
+} credklpd_t;
+
+klpd_reg_t *klpd_list;
+
+static void klpd_unlink(klpd_reg_t *);
+static int klpd_unreg_dh(door_handle_t);
+
+static credklpd_t *crklpd_alloc(void);
+
+void crklpd_setreg(credklpd_t *, klpd_reg_t *);
+
+extern size_t max_vnode_path;
+
+void
+klpd_rele(klpd_reg_t *p)
+{
+ if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
+ if (p->klpd_refp != NULL)
+ klpd_unlink(p);
+ if (p->klpd_cred != NULL)
+ crfree(p->klpd_cred);
+ door_ki_rele(p->klpd_door);
+ kmem_free(p, sizeof (*p));
+ }
+}
+
+/*
+ * In order to be able to walk the lists, we can't unlink the entry
+ * until the reference count drops to 0. If we remove it too soon,
+ * list walkers will terminate when they happen to call a now orphaned
+ * entry.
+ */
+static klpd_reg_t *
+klpd_rele_next(klpd_reg_t *p)
+{
+ klpd_reg_t *r = p->klpd_next;
+
+ klpd_rele(p);
+ return (r);
+}
+
+
+static void
+klpd_hold(klpd_reg_t *p)
+{
+ atomic_add_32(&p->klpd_ref, 1);
+}
+
+/*
+ * Remove registration from where it is registered. Returns next in list.
+ */
+static void
+klpd_unlink(klpd_reg_t *p)
+{
+ ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
+
+ if (p->klpd_refp != NULL)
+ *p->klpd_refp = p->klpd_next;
+
+ if (p->klpd_next != NULL)
+ p->klpd_next->klpd_refp = p->klpd_refp;
+ p->klpd_refp = NULL;
+}
+
+/*
+ * Remove the head of the klpd list and decrement its refcnt.
+ * The lock guarding the list should be held; this function is
+ * called when we are sure we want to remove the entry from the
+ * list but not so sure that the reference count has dropped back to
+ * 1 and is specifically intended to remove the non-list variants.
+ */
+void
+klpd_remove(klpd_reg_t **pp)
+{
+ klpd_reg_t *p = *pp;
+ if (p == NULL)
+ return;
+ ASSERT(p->klpd_next == NULL);
+ klpd_unlink(p);
+ klpd_rele(p);
+}
+
+/*
+ * Link new entry in list. The Boolean argument specifies whether this
+ * list can contain only a single item or multiple items.
+ * Returns the entry which needs to be released if single is B_TRUE.
+ */
+static klpd_reg_t *
+klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
+{
+ klpd_reg_t *old = *listp;
+
+ ASSERT(p->klpd_ref == 1);
+
+ ASSERT(old == NULL || *old->klpd_refp == old);
+ p->klpd_refp = listp;
+ p->klpd_next = single ? NULL : old;
+ *listp = p;
+ if (old != NULL) {
+ if (single) {
+ ASSERT(old->klpd_next == NULL);
+ old->klpd_refp = NULL;
+ return (old);
+ } else
+ old->klpd_refp = &p->klpd_next;
+ }
+ return (NULL);
+}
+
+/*
+ * The typical call consists of:
+ * - priv_set_t
+ * - some integer data (type, value)
+ * for now, it's just one bit.
+ */
+static klpd_head_t *
+klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
+{
+ char *comp;
+ uint_t type;
+ vnode_t *vp;
+ size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t);
+ size_t plen, clen;
+ int proto;
+
+ klpd_arg_t *kap = NULL;
+ klpd_head_t *khp;
+
+ type = va_arg(ap, uint_t);
+ switch (type) {
+ case KLPDARG_NOMORE:
+ khp = kmem_zalloc(len, KM_SLEEP);
+ khp->klh_argoff = 0;
+ break;
+ case KLPDARG_VNODE:
+ len += offsetof(klpd_arg_t, kla_str);
+ vp = va_arg(ap, vnode_t *);
+ if (vp == NULL)
+ return (NULL);
+
+ comp = va_arg(ap, char *);
+
+ if (comp != NULL && *comp != '\0')
+ clen = strlen(comp) + 1;
+ else
+ clen = 0;
+
+ len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
+ khp = kmem_zalloc(len, KM_SLEEP);
+
+ khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
+ kap = KLH_ARG(khp);
+
+ if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
+ vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
+ kmem_free(khp, len);
+ return (NULL);
+ }
+ if (clen != 0) {
+ plen = strlen(kap->kla_str);
+ if (plen + clen + 1 >= MAXPATHLEN) {
+ kmem_free(khp, len);
+ return (NULL);
+ }
+ /* Don't make root into a double "/" */
+ if (plen <= 2)
+ plen = 0;
+ kap->kla_str[plen] = '/';
+ bcopy(comp, &kap->kla_str[plen + 1], clen);
+ }
+ break;
+ case KLPDARG_PORT:
+ proto = va_arg(ap, int);
+ switch (proto) {
+ case IPPROTO_TCP: type = KLPDARG_TCPPORT;
+ break;
+ case IPPROTO_UDP: type = KLPDARG_UDPPORT;
+ break;
+ case IPPROTO_SCTP: type = KLPDARG_SCTPPORT;
+ break;
+ case PROTO_SDP: type = KLPDARG_SDPPORT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case KLPDARG_INT:
+ case KLPDARG_TCPPORT:
+ case KLPDARG_UDPPORT:
+ case KLPDARG_SCTPPORT:
+ case KLPDARG_SDPPORT:
+ len += sizeof (*kap);
+ khp = kmem_zalloc(len, KM_SLEEP);
+ khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
+ kap = KLH_ARG(khp);
+ kap->kla_int = va_arg(ap, int);
+ break;
+ default:
+ return (NULL);
+ }
+ khp->klh_vers = KLPDCALL_VERS;
+ khp->klh_len = len;
+ khp->klh_privoff = sizeof (*khp);
+ *KLH_PRIVSET(khp) = *rq;
+ if (kap != NULL) {
+ kap->kla_type = type;
+ kap->kla_dlen = len - khp->klh_argoff;
+ }
+ return (khp);
+}
+
+static int
+klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
+{
+ door_arg_t da;
+ int res;
+ int dres;
+ klpd_head_t *klh;
+
+ if (p->klpd_door_pid == curproc->p_pid)
+ return (-1);
+
+ klh = klpd_marshall(p, req, ap);
+
+ if (klh == NULL)
+ return (-1);
+
+ da.data_ptr = (char *)klh;
+ da.data_size = klh->klh_len;
+ da.desc_ptr = NULL;
+ da.desc_num = 0;
+ da.rbuf = (char *)&res;
+ da.rsize = sizeof (res);
+
+ while ((dres = door_ki_upcall(p->klpd_door, &da)) != 0) {
+ switch (dres) {
+ case EAGAIN:
+ delay(1);
+ continue;
+ case EINVAL:
+ case EBADF:
+ /* Bad door, don't call it again. */
+ (void) klpd_unreg_dh(p->klpd_door);
+ /* FALLTHROUGH */
+ case EINTR:
+ /* Pending signal, nothing we can do. */
+ /* FALLTHROUGH */
+ default:
+ kmem_free(klh, klh->klh_len);
+ return (-1);
+ }
+ }
+ kmem_free(klh, klh->klh_len);
+ /* Bogus return value, must be a failure */
+ if (da.rbuf != (char *)&res) {
+ kmem_free(da.rbuf, da.rsize);
+ return (-1);
+ }
+ return (res);
+}
+
+uint32_t klpd_bad_locks;
+
+int
+klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
+{
+ klpd_reg_t *p;
+ int rv = -1;
+ credklpd_t *ckp;
+ zone_t *ckzone;
+
+ /*
+ * These locks must not be held when this code is called;
+ * callbacks to userland with these locks held will result
+ * in issues. That said, the code at the call sides was
+ * restructured not to call with any of the locks held and
+ * no policies operate by default on most processes.
+ */
+ if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
+ mutex_owned(&curproc->p_crlock)) {
+ atomic_add_32(&klpd_bad_locks, 1);
+ return (-1);
+ }
+
+ /*
+ * Enforce the limit set for the call process (still).
+ */
+ if (!priv_issubset(req, &CR_LPRIV(cr)))
+ return (-1);
+
+ /* Try 1: get the credential specific klpd */
+ if ((ckp = crgetcrklpd(cr)) != NULL) {
+ mutex_enter(&ckp->crkl_lock);
+ if ((p = ckp->crkl_reg) != NULL &&
+ p->klpd_indel == 0 &&
+ priv_issubset(req, &p->klpd_pset)) {
+ klpd_hold(p);
+ mutex_exit(&ckp->crkl_lock);
+ rv = klpd_do_call(p, req, ap);
+ mutex_enter(&ckp->crkl_lock);
+ klpd_rele(p);
+ mutex_exit(&ckp->crkl_lock);
+ if (rv != -1)
+ return (rv == 0 ? 0 : -1);
+ } else {
+ mutex_exit(&ckp->crkl_lock);
+ }
+ }
+
+ /* Try 2: get the project specific klpd */
+ mutex_enter(&klpd_mutex);
+
+ if ((p = curproj->kpj_klpd) != NULL) {
+ klpd_hold(p);
+ mutex_exit(&klpd_mutex);
+ if (p->klpd_indel == 0 &&
+ priv_issubset(req, &p->klpd_pset)) {
+ rv = klpd_do_call(p, req, ap);
+ }
+ mutex_enter(&klpd_mutex);
+ klpd_rele(p);
+ mutex_exit(&klpd_mutex);
+
+ if (rv != -1)
+ return (rv == 0 ? 0 : -1);
+ } else {
+ mutex_exit(&klpd_mutex);
+ }
+
+ /* Try 3: get the global klpd list */
+ ckzone = crgetzone(cr);
+ mutex_enter(&klpd_mutex);
+
+ for (p = klpd_list; p != NULL; ) {
+ zone_t *kkzone = crgetzone(p->klpd_cred);
+ if ((kkzone == &zone0 || kkzone == ckzone) &&
+ p->klpd_indel == 0 &&
+ priv_issubset(req, &p->klpd_pset)) {
+ klpd_hold(p);
+ mutex_exit(&klpd_mutex);
+ rv = klpd_do_call(p, req, ap);
+ mutex_enter(&klpd_mutex);
+
+ p = klpd_rele_next(p);
+
+ if (rv != -1)
+ break;
+ } else {
+ p = p->klpd_next;
+ }
+ }
+ mutex_exit(&klpd_mutex);
+ return (rv == 0 ? 0 : -1);
+}
+
+/*
+ * Register the klpd.
+ * If the pid_t passed in is positive, update the registration for
+ * the specific process; that is only possible if the process already
+ * has a registration on it. This change of registration will affect
+ * all processes which share common ancestry.
+ *
+ * MY_PID (pid 0) can be used to create or change the context for
+ * the current process, typically done after fork().
+ *
+ * A negative value can be used to register a klpd globally.
+ *
+ * The per-credential klpd needs to be cleaned up when entering
+ * a zone or unsetting the flag.
+ */
+int
+klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
+{
+ cred_t *cr = CRED();
+ door_handle_t dh;
+ klpd_reg_t *kpd;
+ priv_set_t pset;
+ door_info_t di;
+ credklpd_t *ckp = NULL;
+ pid_t pid = -1;
+ projid_t proj = -1;
+ kproject_t *kpp = NULL;
+
+ if (CR_FLAGS(cr) & PRIV_XPOLICY)
+ return (set_errno(EINVAL));
+
+ if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
+ return (set_errno(EFAULT));
+
+ if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
+ return (set_errno(EPERM));
+
+ switch (type) {
+ case P_PID:
+ pid = (pid_t)id;
+ if (pid == P_MYPID)
+ pid = curproc->p_pid;
+ if (pid == curproc->p_pid)
+ ckp = crklpd_alloc();
+ break;
+ case P_PROJID:
+ proj = (projid_t)id;
+ kpp = project_hold_by_id(proj, crgetzone(cr),
+ PROJECT_HOLD_FIND);
+ if (kpp == NULL)
+ return (set_errno(ESRCH));
+ break;
+ default:
+ return (set_errno(ENOTSUP));
+ }
+
+
+ /*
+ * Verify the door passed in; it must be a door and we won't
+ * allow processes to be called on their own behalf.
+ */
+ dh = door_ki_lookup(did);
+ if (dh == NULL || door_ki_info(dh, &di) != 0) {
+ if (ckp != NULL)
+ crklpd_rele(ckp);
+ if (kpp != NULL)
+ project_rele(kpp);
+ return (set_errno(EBADF));
+ }
+ if (type == P_PID && pid == di.di_target) {
+ if (ckp != NULL)
+ crklpd_rele(ckp);
+ ASSERT(kpp == NULL);
+ return (set_errno(EINVAL));
+ }
+
+ kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
+ crhold(kpd->klpd_cred = cr);
+ kpd->klpd_door = dh;
+ kpd->klpd_door_pid = di.di_target;
+ kpd->klpd_ref = 1;
+ kpd->klpd_pset = pset;
+
+ if (kpp != NULL) {
+ mutex_enter(&klpd_mutex);
+ kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
+ mutex_exit(&klpd_mutex);
+ if (kpd != NULL)
+ klpd_rele(kpd);
+ project_rele(kpp);
+ } else if ((int)pid < 0) {
+ /* Global daemon */
+ mutex_enter(&klpd_mutex);
+ (void) klpd_link(kpd, &klpd_list, B_FALSE);
+ mutex_exit(&klpd_mutex);
+ } else if (pid == curproc->p_pid) {
+ proc_t *p = curproc;
+ cred_t *newcr = cralloc();
+
+ /* No need to lock, sole reference to ckp */
+ kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
+
+ if (kpd != NULL)
+ klpd_rele(kpd);
+
+ mutex_enter(&p->p_crlock);
+ cr = p->p_cred;
+ crdup_to(cr, newcr);
+ crsetcrklpd(newcr, ckp);
+ p->p_cred = newcr; /* Already held for p_cred */
+
+ crhold(newcr); /* Hold once for the current thread */
+ mutex_exit(&p->p_crlock);
+ crfree(cr); /* One for the p_cred */
+ crset(p, newcr);
+ } else {
+ proc_t *p;
+ cred_t *pcr;
+ mutex_enter(&pidlock);
+ p = prfind(pid);
+ if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
+ mutex_exit(&pidlock);
+ klpd_rele(kpd);
+ return (set_errno(p == NULL ? ESRCH : EPERM));
+ }
+ mutex_enter(&p->p_crlock);
+ crhold(pcr = p->p_cred);
+ mutex_exit(&pidlock);
+ mutex_exit(&p->p_crlock);
+ /*
+ * We're going to update the credential's ckp in place;
+ * this requires that it exists.
+ */
+ ckp = crgetcrklpd(pcr);
+ if (ckp == NULL) {
+ crfree(pcr);
+ klpd_rele(kpd);
+ return (set_errno(EINVAL));
+ }
+ crklpd_setreg(ckp, kpd);
+ crfree(pcr);
+ }
+
+ return (0);
+}
+
+static int
+klpd_unreg_dh(door_handle_t dh)
+{
+ klpd_reg_t *p;
+
+ mutex_enter(&klpd_mutex);
+ for (p = klpd_list; p != NULL; p = p->klpd_next) {
+ if (p->klpd_door == dh)
+ break;
+ }
+ if (p == NULL) {
+ mutex_exit(&klpd_mutex);
+ return (EINVAL);
+ }
+ if (p->klpd_indel != 0) {
+ mutex_exit(&klpd_mutex);
+ return (EAGAIN);
+ }
+ p->klpd_indel = 1;
+ klpd_rele(p);
+ mutex_exit(&klpd_mutex);
+ return (0);
+}
+
+int
+klpd_unreg(int did, idtype_t type, id_t id)
+{
+ door_handle_t dh;
+ int res = 0;
+ proc_t *p;
+ pid_t pid;
+ projid_t proj;
+ kproject_t *kpp = NULL;
+ credklpd_t *ckp;
+
+ switch (type) {
+ case P_PID:
+ pid = (pid_t)id;
+ break;
+ case P_PROJID:
+ proj = (projid_t)id;
+ kpp = project_hold_by_id(proj, crgetzone(CRED()),
+ PROJECT_HOLD_FIND);
+ if (kpp == NULL)
+ return (set_errno(ESRCH));
+ break;
+ default:
+ return (set_errno(ENOTSUP));
+ }
+
+ dh = door_ki_lookup(did);
+ if (dh == NULL) {
+ if (kpp != NULL)
+ project_rele(kpp);
+ return (set_errno(EINVAL));
+ }
+
+ if (kpp != NULL) {
+ mutex_enter(&klpd_mutex);
+ if (kpp->kpj_klpd == NULL)
+ res = ESRCH;
+ else
+ klpd_remove(&kpp->kpj_klpd);
+ mutex_exit(&klpd_mutex);
+ project_rele(kpp);
+ goto out;
+ } else if ((int)pid > 0) {
+ mutex_enter(&pidlock);
+ p = prfind(pid);
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ door_ki_rele(dh);
+ return (set_errno(ESRCH));
+ }
+ mutex_enter(&p->p_crlock);
+ mutex_exit(&pidlock);
+ } else if (pid == 0) {
+ p = curproc;
+ mutex_enter(&p->p_crlock);
+ } else {
+ res = klpd_unreg_dh(dh);
+ goto out;
+ }
+
+ ckp = crgetcrklpd(p->p_cred);
+ if (ckp != NULL) {
+ crklpd_setreg(ckp, NULL);
+ } else {
+ res = ESRCH;
+ }
+ mutex_exit(&p->p_crlock);
+
+out:
+ door_ki_rele(dh);
+
+ if (res != 0)
+ return (set_errno(res));
+ return (0);
+}
+
+void
+crklpd_hold(credklpd_t *crkpd)
+{
+ atomic_add_32(&crkpd->crkl_ref, 1);
+}
+
+void
+crklpd_rele(credklpd_t *crkpd)
+{
+ if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
+ if (crkpd->crkl_reg != NULL)
+ klpd_rele(crkpd->crkl_reg);
+ mutex_destroy(&crkpd->crkl_lock);
+ kmem_free(crkpd, sizeof (*crkpd));
+ }
+}
+
+static credklpd_t *
+crklpd_alloc(void)
+{
+ credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
+
+ mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
+ res->crkl_ref = 1;
+ res->crkl_reg = NULL;
+
+ return (res);
+}
+
+void
+crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
+{
+ klpd_reg_t *old;
+
+ mutex_enter(&crk->crkl_lock);
+ if (new == NULL) {
+ old = crk->crkl_reg;
+ if (old != NULL)
+ klpd_unlink(old);
+ } else {
+ old = klpd_link(new, &crk->crkl_reg, B_TRUE);
+ }
+ mutex_exit(&crk->crkl_lock);
+
+ if (old != NULL)
+ klpd_rele(old);
+}
diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c
index 32c67b710f..2333681763 100644
--- a/usr/src/uts/common/os/policy.c
+++ b/usr/src/uts/common/os/policy.c
@@ -47,6 +47,7 @@
#include <sys/devpolicy.h>
#include <c2/audit.h>
#include <sys/varargs.h>
+#include <sys/klpd.h>
#include <sys/modctl.h>
#include <sys/disp.h>
#include <sys/zone.h>
@@ -147,20 +148,25 @@ int priv_debug = 0;
PRIV_ISASSERT(&CR_OEPRIV(cr), pr))
/*
- * Policy checking functions
+ * Policy checking functions.
*
- * In future, these will migrate to several files when policy
- * becomes more or less pluggable.
- *
- * For now, there's only one policy and this is it.
+ * All of the system's policy should be implemented here.
+ */
+
+/*
+ * Private functions which take an additional va_list argument to
+ * implement an object specific policy override.
*/
+static int priv_policy_ap(const cred_t *, int, boolean_t, int,
+ const char *, va_list);
+static int priv_policy_va(const cred_t *, int, boolean_t, int,
+ const char *, ...);
/*
* Generic policy calls
*
* The "bottom" functions of policy control
*/
-
static char *
mprintf(const char *fmt, ...)
{
@@ -299,13 +305,51 @@ priv_policy_errmsg(const cred_t *cr, int priv, const char *msg)
cr->cr_uid, curthread->t_sysnum, msg, sym, off);
curthread->t_post_sys = 1;
- } else {
+ }
+ if (priv_debug) {
cmn_err(CE_NOTE, fmt, cmd, me->p_pid, pname, cr->cr_uid,
curthread->t_sysnum, msg, sym, off);
}
}
/*
+ * Override the policy, if appropriate. Return 0 if the external
+ * policy engine approves.
+ */
+static int
+priv_policy_override(const cred_t *cr, int priv, boolean_t allzone, va_list ap)
+{
+ priv_set_t set;
+ int ret;
+
+ if (!(CR_FLAGS(cr) & PRIV_XPOLICY))
+ return (-1);
+
+ if (priv == PRIV_ALL) {
+ priv_fillset(&set);
+ } else if (allzone) {
+ set = *ZONEPRIVS(cr);
+ } else {
+ priv_emptyset(&set);
+ priv_addset(&set, priv);
+ }
+ ret = klpd_call(cr, &set, ap);
+ return (ret);
+}
+
+static int
+priv_policy_override_set(const cred_t *cr, const priv_set_t *req, ...)
+{
+ va_list ap;
+
+ if (CR_FLAGS(cr) & PRIV_XPOLICY) {
+ va_start(ap, req);
+ return (klpd_call(cr, req, ap));
+ }
+ return (-1);
+}
+
+/*
* Audit failure, log error message.
*/
static void
@@ -328,15 +372,17 @@ priv_policy_err(const cred_t *cr, int priv, boolean_t allzone, const char *msg)
}
/*
- * priv_policy()
+ * priv_policy_ap()
* return 0 or error.
* See block comment above for a description of "priv" and "allzone" usage.
*/
-int
-priv_policy(const cred_t *cr, int priv, boolean_t allzone, int err,
- const char *msg)
+static int
+priv_policy_ap(const cred_t *cr, int priv, boolean_t allzone, int err,
+ const char *msg, va_list ap)
{
- if (HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) {
+ if ((HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) ||
+ (!servicing_interrupt() &&
+ priv_policy_override(cr, priv, allzone, ap) == 0)) {
if ((allzone || priv == PRIV_ALL ||
!PRIV_ISASSERT(priv_basic, priv)) &&
!servicing_interrupt()) {
@@ -351,10 +397,30 @@ priv_policy(const cred_t *cr, int priv, boolean_t allzone, int err,
/* Failure audited in this procedure */
priv_policy_err(cr, priv, allzone, msg);
}
-
return (err);
}
+int
+priv_policy_va(const cred_t *cr, int priv, boolean_t allzone, int err,
+ const char *msg, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, msg);
+ ret = priv_policy_ap(cr, priv, allzone, err, msg, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+int
+priv_policy(const cred_t *cr, int priv, boolean_t allzone, int err,
+ const char *msg)
+{
+ return (priv_policy_va(cr, priv, allzone, err, msg, KLPDARG_NOMORE));
+}
+
/*
* Return B_TRUE for sufficient privileges, B_FALSE for insufficient privileges.
*/
@@ -410,6 +476,9 @@ secpolicy_require_set(const cred_t *cr, const priv_set_t *req, const char *msg)
return (0);
}
+ if (priv_policy_override_set(cr, req, KLPDARG_NOMORE) == 0)
+ return (0);
+
if (req == PRIV_FULLSET || priv_isfullset(req)) {
priv_policy_err(cr, PRIV_ALL, B_FALSE, msg);
return (EACCES);
@@ -475,7 +544,7 @@ secpolicy_setpriority(const cred_t *cr)
* order.
*/
int
-secpolicy_net_privaddr(const cred_t *cr, in_port_t port)
+secpolicy_net_privaddr(const cred_t *cr, in_port_t port, int proto)
{
char *reason;
int priv;
@@ -510,7 +579,8 @@ secpolicy_net_privaddr(const cred_t *cr, in_port_t port)
}
- return (PRIV_POLICY(cr, priv, B_FALSE, EACCES, reason));
+ return (priv_policy_va(cr, priv, B_FALSE, EACCES, reason,
+ KLPDARG_PORT, (int)proto, (int)port, KLPDARG_NOMORE));
}
/*
@@ -519,8 +589,7 @@ secpolicy_net_privaddr(const cred_t *cr, in_port_t port)
int
secpolicy_net_bindmlp(const cred_t *cr)
{
- return (PRIV_POLICY(cr, PRIV_NET_BINDMLP, B_FALSE, EACCES,
- NULL));
+ return (PRIV_POLICY(cr, PRIV_NET_BINDMLP, B_FALSE, EACCES, NULL));
}
/*
@@ -530,8 +599,7 @@ secpolicy_net_bindmlp(const cred_t *cr)
int
secpolicy_net_mac_aware(const cred_t *cr)
{
- return (PRIV_POLICY(cr, PRIV_NET_MAC_AWARE, B_FALSE, EACCES,
- NULL));
+ return (PRIV_POLICY(cr, PRIV_NET_MAC_AWARE, B_FALSE, EACCES, NULL));
}
/*
@@ -559,7 +627,8 @@ secpolicy_fs_common(cred_t *cr, vnode_t *mvp, const vfs_t *vfsp,
if (mounting)
*needoptcheck = B_FALSE;
- return (PRIV_POLICY(cr, PRIV_SYS_MOUNT, allzone, EPERM, NULL));
+ return (priv_policy_va(cr, PRIV_SYS_MOUNT, allzone, EPERM,
+ NULL, KLPDARG_VNODE, mvp, (char *)NULL, KLPDARG_NOMORE));
}
/*
@@ -611,7 +680,8 @@ secpolicy_fs_common(cred_t *cr, vnode_t *mvp, const vfs_t *vfsp,
return (EACCES);
}
}
- return (PRIV_POLICY(cr, PRIV_SYS_MOUNT, allzone, EPERM, NULL));
+ return (priv_policy_va(cr, PRIV_SYS_MOUNT, allzone, EPERM,
+ NULL, KLPDARG_VNODE, mvp, (char *)NULL, KLPDARG_NOMORE));
}
void
@@ -764,9 +834,11 @@ secpolicy_fs_linkdir(const cred_t *cr, const vfs_t *vfsp)
int
secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode)
{
- if ((mode & VREAD) &&
- PRIV_POLICY(cr, PRIV_FILE_DAC_READ, B_FALSE, EACCES, NULL) != 0)
+ if ((mode & VREAD) && priv_policy_va(cr, PRIV_FILE_DAC_READ, B_FALSE,
+ EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL,
+ KLPDARG_NOMORE) != 0) {
return (EACCES);
+ }
if (mode & VWRITE) {
boolean_t allzone;
@@ -775,23 +847,22 @@ secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode)
allzone = B_TRUE;
else
allzone = B_FALSE;
- if (PRIV_POLICY(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, NULL)
- != 0)
+ if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES,
+ NULL, KLPDARG_VNODE, vp, (char *)NULL,
+ KLPDARG_NOMORE) != 0) {
return (EACCES);
+ }
}
if (mode & VEXEC) {
/*
* Directories use file_dac_search to override the execute bit.
*/
- vtype_t vtype = vp->v_type;
+ int p = vp->v_type == VDIR ? PRIV_FILE_DAC_SEARCH :
+ PRIV_FILE_DAC_EXECUTE;
- if (vtype == VDIR)
- return (PRIV_POLICY(cr, PRIV_FILE_DAC_SEARCH, B_FALSE,
- EACCES, NULL));
- else
- return (PRIV_POLICY(cr, PRIV_FILE_DAC_EXECUTE, B_FALSE,
- EACCES, NULL));
+ return (priv_policy_va(cr, p, B_FALSE, EACCES, NULL,
+ KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE));
}
return (0);
}
@@ -1725,9 +1796,10 @@ secpolicy_tasksys(const cred_t *cr)
* Basic privilege checks.
*/
int
-secpolicy_basic_exec(const cred_t *cr)
+secpolicy_basic_exec(const cred_t *cr, vnode_t *vp)
{
- return (PRIV_POLICY(cr, PRIV_PROC_EXEC, B_FALSE, EPERM, NULL));
+ return (priv_policy_va(cr, PRIV_PROC_EXEC, B_FALSE, EPERM, NULL,
+ KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE));
}
int
@@ -2065,6 +2137,27 @@ secpolicy_sadopen(const cred_t *credp)
return (secpolicy_require_set(credp, &pset, "devpolicy"));
}
+
+/*
+ * Add privileges to a particular privilege set; this is called when the
+ * current sets of privileges are not sufficient. I.e., we should always
+ * call the policy override functions from here.
+ * What we are allowed to have is in the Observed Permitted set; so
+ * we compute the difference between that and the newset.
+ */
+int
+secpolicy_require_privs(const cred_t *cr, const priv_set_t *nset)
+{
+ priv_set_t rqd;
+
+ rqd = CR_OPPRIV(cr);
+
+ priv_inverse(&rqd);
+ priv_intersect(nset, &rqd);
+
+ return (secpolicy_require_set(cr, &rqd, NULL));
+}
+
/*
* secpolicy_smb
*
diff --git a/usr/src/uts/common/os/project.c b/usr/src/uts/common/os/project.c
index e19f1959bc..7d10ec9370 100644
--- a/usr/src/uts/common/os/project.c
+++ b/usr/src/uts/common/os/project.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,6 +42,7 @@
#include <sys/task.h>
#include <sys/zone.h>
#include <sys/cpucaps.h>
+#include <sys/klpd.h>
int project_hash_size = 64;
static kmutex_t project_hash_lock;
@@ -359,11 +360,14 @@ project_rele(kproject_t *p)
rctl_set_free(p->kpj_rctls);
project_kstat_delete(p);
+ if (p->kpj_klpd != NULL)
+ klpd_remove(&p->kpj_klpd);
+
if (mod_hash_destroy(projects_hash, (mod_hash_key_t)p))
panic("unable to delete project %d zone %d", p->kpj_id,
p->kpj_zoneid);
- }
+ }
mutex_exit(&project_hash_lock);
}
diff --git a/usr/src/uts/common/os/sysent.c b/usr/src/uts/common/os/sysent.c
index b6d097b585..eeb3b7edde 100644
--- a/usr/src/uts/common/os/sysent.c
+++ b/usr/src/uts/common/os/sysent.c
@@ -21,7 +21,7 @@
/* ONC_PLUS EXTRACT START */
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -533,7 +533,7 @@ struct sysent sysent[NSYSCALL] =
/* 81 */ IF_LP64(
SYSENT_CI("getdents", getdents64, 3),
SYSENT_CI("getdents", getdents32, 3)),
- /* 82 */ SYSENT_CI("privsys", privsys, 5),
+ /* 82 */ SYSENT_CI("privsys", privsys, 6),
/* 83 */ SYSENT_CI("ucredsys", ucredsys, 3),
/* 84 */ SYSENT_CI("sysfs", sysfs, 3),
/* 85 */ SYSENT_CI("getmsg", getmsg, 4),
@@ -930,7 +930,7 @@ struct sysent sysent32[NSYSCALL] =
/* 79 */ SYSENT_CI("rmdir", rmdir, 1),
/* 80 */ SYSENT_CI("mkdir", mkdir, 2),
/* 81 */ SYSENT_CI("getdents", getdents32, 3),
- /* 82 */ SYSENT_CI("privsys", privsys32, 5),
+ /* 82 */ SYSENT_CI("privsys", privsys32, 6),
/* 83 */ SYSENT_CI("ucredsys", ucredsys32, 3),
/* 84 */ SYSENT_CI("sysfs", sysfs, 3),
/* 85 */ SYSENT_CI("getmsg", getmsg32, 4),
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 93109cdd0c..ea50f4eeff 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -315,6 +315,7 @@ CHKHDRS= \
kiconv_utf8_hkscs.h \
kiconv_utf8_uhc.h \
kidmap.h \
+ klpd.h \
klwp.h \
kmdb.h \
kmem.h \
diff --git a/usr/src/uts/common/sys/cred.h b/usr/src/uts/common/sys/cred.h
index 35785d3c1d..e84f1e0430 100644
--- a/usr/src/uts/common/sys/cred.h
+++ b/usr/src/uts/common/sys/cred.h
@@ -57,6 +57,7 @@ struct proc; /* cred.h is included in proc.h */
struct prcred;
struct ksid;
struct ksidlist;
+struct credklpd;
struct auditinfo_addr; /* cred.h is included in audit.h */
@@ -173,6 +174,9 @@ extern struct ksidlist *crgetsidlist(const cred_t *);
extern int crsetpriv(cred_t *, ...);
+extern struct credklpd *crgetcrklpd(const cred_t *);
+extern void crsetcrklpd(cred_t *, struct credklpd *);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/cred_impl.h b/usr/src/uts/common/sys/cred_impl.h
index c2272de9a5..ad70ffd868 100644
--- a/usr/src/uts/common/sys/cred_impl.h
+++ b/usr/src/uts/common/sys/cred_impl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -64,6 +64,7 @@ extern "C" {
struct zone; /* forward reference */
struct ts_label_s; /* forward reference */
+struct credklpd; /* forward reference */
struct cred {
uint_t cr_ref; /* reference count */
@@ -79,6 +80,7 @@ struct cred {
projid_t cr_projid; /* project */
struct zone *cr_zone; /* pointer to per-zone structure */
struct ts_label_s *cr_label; /* pointer to the effective label */
+ struct credklpd *cr_klpd; /* pointer to the cred's klpd */
credsid_t *cr_ksid; /* pointer to SIDs */
gid_t cr_groups[1]; /* cr_groups size not fixed */
/* audit info is defined dynamically */
diff --git a/usr/src/uts/common/sys/klpd.h b/usr/src/uts/common/sys/klpd.h
new file mode 100644
index 0000000000..535af85f89
--- /dev/null
+++ b/usr/src/uts/common/sys/klpd.h
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_KLPD_H
+#define _SYS_KLPD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/priv.h>
+#include <sys/procset.h>
+
+#ifdef _KERNEL
+#include <sys/cred.h>
+#include <sys/sysmacros.h>
+#include <sys/varargs.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KLPDCALL_VERS 1
+
+#define KLPDARG_NOMORE 0 /* End of argument List */
+#define KLPDARG_NONE 0 /* No argument */
+#define KLPDARG_VNODE 1 /* vnode_t * */
+#define KLPDARG_INT 2 /* int */
+#define KLPDARG_PORT 3 /* int, port number */
+#define KLPDARG_TCPPORT 4 /* int, tcp port number */
+#define KLPDARG_UDPPORT 5 /* int, udp port number */
+#define KLPDARG_SCTPPORT 6 /* int, sctp port number */
+#define KLPDARG_SDPPORT 7 /* int, sdp port number */
+
+#ifdef _KERNEL
+
+struct klpd_reg;
+struct credklpd;
+
+int klpd_reg(int, idtype_t, id_t, priv_set_t *);
+int klpd_unreg(int, idtype_t, id_t);
+void klpd_remove(struct klpd_reg **);
+void klpd_rele(struct klpd_reg *);
+int klpd_call(const cred_t *, const priv_set_t *, va_list);
+void crklpd_hold(struct credklpd *);
+void crklpd_rele(struct credklpd *);
+
+#endif /* _KERNEL */
+
+typedef struct klpd_head {
+ uint32_t klh_vers; /* Version */
+ uint32_t klh_len; /* Length of full packet */
+ uint32_t klh_argoff; /* Offset of argument */
+ uint32_t klh_privoff; /* Offset of privilege set */
+} klpd_head_t;
+
+#define KLH_PRIVSET(kh) ((priv_set_t *)(((kh)->klh_privoff == 0 ? NULL : \
+ (char *)(kh) + (kh)->klh_privoff)))
+#define KLH_ARG(kh) ((void *)((kh)->klh_argoff != 0 ? \
+ (char *)(kh) + (kh)->klh_argoff : NULL))
+
+typedef struct klpd_arg {
+ uint_t kla_type;
+ uint_t kla_dlen;
+ union {
+ char __cdata[1];
+ int __idata;
+ uint_t __uidata;
+ } kla_data;
+} klpd_arg_t;
+
+#define kla_str kla_data.__cdata
+#define kla_int kla_data.__idata
+#define kla_uint kla_data.__uidata
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_KLPD_H */
diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h
index c0703cd9ee..fa1240845e 100644
--- a/usr/src/uts/common/sys/policy.h
+++ b/usr/src/uts/common/sys/policy.h
@@ -70,8 +70,10 @@ boolean_t priv_policy_choice(const cred_t *, int, boolean_t);
struct kipc_perm;
struct vfs;
struct proc;
+struct priv_set;
int secpolicy_acct(const cred_t *);
+int secpolicy_require_privs(const cred_t *, const struct priv_set *);
int secpolicy_allow_setid(const cred_t *, uid_t, boolean_t);
int secpolicy_audit_config(const cred_t *);
int secpolicy_audit_getattr(const cred_t *);
@@ -110,7 +112,7 @@ int secpolicy_net_bindmlp(const cred_t *);
int secpolicy_net_config(const cred_t *, boolean_t);
int secpolicy_net_icmpaccess(const cred_t *);
int secpolicy_net_mac_aware(const cred_t *);
-int secpolicy_net_privaddr(const cred_t *, in_port_t);
+int secpolicy_net_privaddr(const cred_t *, in_port_t, int proto);
int secpolicy_net_rawaccess(const cred_t *);
boolean_t secpolicy_net_reply_equal(const cred_t *);
int secpolicy_newproc(const cred_t *);
@@ -160,11 +162,11 @@ int secpolicy_setid_setsticky_clear(vnode_t *, vattr_t *,
const vattr_t *, cred_t *);
int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, vtype_t);
-int secpolicy_basic_exec(const cred_t *);
+int secpolicy_basic_exec(const cred_t *, vnode_t *);
int secpolicy_basic_fork(const cred_t *);
+int secpolicy_basic_link(const cred_t *);
int secpolicy_basic_proc(const cred_t *);
int secpolicy_basic_procinfo(const cred_t *, struct proc *, struct proc *);
-int secpolicy_basic_link(const cred_t *);
int secpolicy_gart_access(const cred_t *);
int secpolicy_gart_map(const cred_t *);
diff --git a/usr/src/uts/common/sys/priv.h b/usr/src/uts/common/sys/priv.h
index 08c58ef679..d9be377cd9 100644
--- a/usr/src/uts/common/sys/priv.h
+++ b/usr/src/uts/common/sys/priv.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -82,6 +82,8 @@ typedef enum priv_op {
#define PRIVSYS_SETPFLAGS 3
#define PRIVSYS_GETPFLAGS 4
#define PRIVSYS_ISSETUGID 5
+#define PRIVSYS_KLPD_REG 6
+#define PRIVSYS_KLPD_UNREG 7
/*
* Maximum length of a user defined privilege name.
@@ -135,9 +137,11 @@ typedef struct priv_impl_info {
#define __PROC_PROTECT 0x0008 /* Private */
#define NET_MAC_AWARE 0x0010 /* Is MAC aware */
#define NET_MAC_AWARE_INHERIT 0x0020 /* Inherit MAC aware */
+#define PRIV_XPOLICY 0x0080 /* Extended policy */
/* user-settable flags: */
-#define PRIV_USER (PRIV_DEBUG | NET_MAC_AWARE | NET_MAC_AWARE_INHERIT)
+#define PRIV_USER (PRIV_DEBUG | NET_MAC_AWARE | NET_MAC_AWARE_INHERIT |\
+ PRIV_XPOLICY)
/*
* Header of the privilege info data structure; multiple structures can
diff --git a/usr/src/uts/common/sys/project.h b/usr/src/uts/common/sys/project.h
index 53dc2df61d..a46a8082c6 100644
--- a/usr/src/uts/common/sys/project.h
+++ b/usr/src/uts/common/sys/project.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -83,6 +83,8 @@ typedef struct kproject {
/* zone_nlwps_lock */
rctl_qty_t kpj_ntasks_ctl; /* protected by kpj_rctls->rcs_lock */
struct cpucap *kpj_cpucap; /* CPU cap data */
+ struct klpd_reg *kpj_klpd; /* our extended policy */
+ /* protected by klpd_mutex */
} kproject_t;
#ifdef _KERNEL
diff --git a/usr/src/uts/common/syscall/gid.c b/usr/src/uts/common/syscall/gid.c
index fbf90d3d3e..ecad0add3f 100644
--- a/usr/src/uts/common/syscall/gid.c
+++ b/usr/src/uts/common/syscall/gid.c
@@ -64,21 +64,35 @@ setgid(gid_t gid)
/*
* Need to pre-allocate the new cred structure before grabbing
- * the p_crlock mutex.
+ * the p_crlock mutex. We cannot hold the mutex across the
+ * secpolicy functions.
*/
newcr = cralloc_ksid();
p = ttoproc(curthread);
mutex_enter(&p->p_crlock);
+retry:
cr = p->p_cred;
+ crhold(cr);
+ mutex_exit(&p->p_crlock);
+
if ((gid == cr->cr_rgid || gid == cr->cr_sgid) &&
secpolicy_allow_setid(cr, -1, B_TRUE) != 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry;
error = 0;
crcopy_to(cr, newcr);
p->p_cred = newcr;
newcr->cr_gid = gid;
crsetsid(newcr, ksp, KSID_GROUP);
+ mutex_exit(&p->p_crlock);
} else if ((error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry;
/*
* A privileged process that makes itself look like a
* set-gid process must be marked to produce no core dump.
@@ -93,15 +107,15 @@ setgid(gid_t gid)
newcr->cr_rgid = gid;
newcr->cr_sgid = gid;
crsetsid(newcr, ksp, KSID_GROUP);
+ mutex_exit(&p->p_crlock);
} else {
crfree(newcr);
+ crfree(cr);
if (ksp != NULL)
ksid_rele(ksp);
}
- mutex_exit(&p->p_crlock);
-
if (error == 0) {
if (do_nocd) {
mutex_enter(&p->p_lock);
@@ -153,9 +167,16 @@ setegid(gid_t gid)
newcr = cralloc_ksid();
p = ttoproc(curthread);
mutex_enter(&p->p_crlock);
- cr = p->p_cred;
+retry:
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
+
if (gid == cr->cr_rgid || gid == cr->cr_gid || gid == cr->cr_sgid ||
(error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry;
/*
* A privileged process that makes itself look like a
* set-gid process must be marked to produce no core dump.
@@ -167,14 +188,14 @@ setegid(gid_t gid)
p->p_cred = newcr;
newcr->cr_gid = gid;
crsetsid(newcr, ksp, KSID_GROUP);
+ mutex_exit(&p->p_crlock);
} else {
crfree(newcr);
+ crfree(cr);
if (ksp != NULL)
ksid_rele(ksp);
}
- mutex_exit(&p->p_crlock);
-
if (error == 0) {
if (do_nocd) {
mutex_enter(&p->p_lock);
diff --git a/usr/src/uts/common/syscall/groups.c b/usr/src/uts/common/syscall/groups.c
index 8273171d0c..3a46812f8a 100644
--- a/usr/src/uts/common/syscall/groups.c
+++ b/usr/src/uts/common/syscall/groups.c
@@ -89,17 +89,24 @@ setgroups(int gidsetsize, gid_t *gidset)
newcr = cralloc_ksid();
p = ttoproc(curthread);
mutex_enter(&p->p_crlock);
+retry:
cr = p->p_cred;
+ crhold(cr);
+ mutex_exit(&p->p_crlock);
if ((error = secpolicy_allow_setid(cr, -1, B_FALSE)) != 0) {
- mutex_exit(&p->p_crlock);
if (groups != NULL)
kmem_free(groups, n * sizeof (gid_t));
if (ksl != NULL)
ksidlist_rele(ksl);
crfree(newcr);
+ crfree(cr);
return (set_errno(error));
}
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry;
crdup_to(cr, newcr);
crsetsidlist(newcr, ksl);
diff --git a/usr/src/uts/common/syscall/ppriv.c b/usr/src/uts/common/syscall/ppriv.c
index 11804ffbdd..bbb75b6dd6 100644
--- a/usr/src/uts/common/syscall/ppriv.c
+++ b/usr/src/uts/common/syscall/ppriv.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,6 +31,7 @@
#include <sys/systm.h>
#include <sys/cred_impl.h>
#include <sys/errno.h>
+#include <sys/klpd.h>
#include <sys/proc.h>
#include <sys/priv_impl.h>
#include <sys/policy.h>
@@ -58,7 +59,7 @@ setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset)
priv_set_t pset, *target;
cred_t *cr, *pcr;
proc_t *p;
- boolean_t donocd;
+ boolean_t donocd = B_FALSE;
if (!PRIV_VALIDSET(type) || !PRIV_VALIDOP(op))
return (set_errno(EINVAL));
@@ -70,6 +71,7 @@ setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset)
cr = cralloc();
mutex_enter(&p->p_crlock);
+retry:
pcr = p->p_cred;
if (audit_active)
@@ -86,14 +88,26 @@ setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset)
* other sets can but only as long as they remain subsets
* of P. Only immediately after exec holds that P <= L.
*/
- if (((type == PRIV_LIMIT &&
- !priv_issubset(&pset, &CR_LPRIV(pcr))) ||
- !priv_issubset(&pset, &CR_OPPRIV(pcr))) &&
- !priv_issubset(&pset, priv_getset(pcr, type))) {
- mutex_exit(&p->p_crlock);
+ if (type == PRIV_LIMIT &&
+ !priv_issubset(&pset, &CR_LPRIV(pcr))) {
crfree(cr);
return (set_errno(EPERM));
}
+ if (!priv_issubset(&pset, &CR_OPPRIV(pcr)) &&
+ !priv_issubset(&pset, priv_getset(pcr, type))) {
+ mutex_exit(&p->p_crlock);
+ /* Policy override should not grow beyond L either */
+ if (type != PRIV_INHERITABLE ||
+ !priv_issubset(&pset, &CR_LPRIV(pcr)) ||
+ secpolicy_require_privs(CRED(), &pset) != 0) {
+ crfree(cr);
+ return (set_errno(EPERM));
+ }
+ mutex_enter(&p->p_crlock);
+ if (pcr != p->p_cred)
+ goto retry;
+ donocd = B_TRUE;
+ }
break;
case PRIV_OFF:
@@ -157,8 +171,6 @@ setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset)
priv_inverse(&diff);
priv_intersect(&CR_OPPRIV(pcr), &diff);
donocd = !priv_issubset(&diff, &CR_IPRIV(cr));
- } else {
- donocd = B_FALSE;
}
p->p_cred = cr;
@@ -225,7 +237,7 @@ setpflags(uint_t flag, uint_t val, cred_t *tcr)
if (val > 1 || (flag != PRIV_DEBUG && flag != PRIV_AWARE &&
flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT &&
- flag != __PROC_PROTECT)) {
+ flag != __PROC_PROTECT && flag != PRIV_XPOLICY)) {
return (EINVAL);
}
@@ -305,6 +317,13 @@ setpflags(uint_t flag, uint_t val, cred_t *tcr)
CR_FLAGS(cr) = newflags;
}
+ /*
+ * Unsetting the flag has as side effect getting rid of
+ * the per-credential policy.
+ */
+ if (flag == PRIV_XPOLICY && val == 0)
+ crsetcrklpd(cr, NULL);
+
if (use_curcred) {
p->p_cred = cr;
mutex_exit(&p->p_crlock);
@@ -321,7 +340,8 @@ uint_t
getpflags(uint_t flag, const cred_t *cr)
{
if (flag != PRIV_DEBUG && flag != PRIV_AWARE &&
- flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT)
+ flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT &&
+ flag != PRIV_XPOLICY)
return ((uint_t)-1);
return ((CR_FLAGS(cr) & flag) != 0);
@@ -331,7 +351,8 @@ getpflags(uint_t flag, const cred_t *cr)
* Privilege system call entry point
*/
int
-privsys(int code, priv_op_t op, priv_ptype_t type, void *buf, size_t bufsize)
+privsys(int code, priv_op_t op, priv_ptype_t type, void *buf, size_t bufsize,
+ int itype)
{
int retv;
extern int issetugid(void);
@@ -355,15 +376,23 @@ privsys(int code, priv_op_t op, priv_ptype_t type, void *buf, size_t bufsize)
return (retv == -1 ? set_errno(EINVAL) : retv);
case PRIVSYS_ISSETUGID:
return (issetugid());
+ case PRIVSYS_KLPD_REG:
+ if (bufsize < sizeof (priv_set_t))
+ return (set_errno(ENOMEM));
+ return ((int)klpd_reg((int)op, (idtype_t)itype, (id_t)type,
+ buf));
+ case PRIVSYS_KLPD_UNREG:
+ return ((int)klpd_unreg((int)op, (idtype_t)itype, (id_t)type));
}
return (set_errno(EINVAL));
}
#ifdef _SYSCALL32_IMPL
int
-privsys32(int code, priv_op_t op, priv_ptype_t type, caddr32_t *buf,
- size32_t bufsize)
+privsys32(int code, priv_op_t op, priv_ptype_t type, caddr32_t buf,
+ size32_t bufsize, int itype)
{
- return (privsys(code, op, type, (void *)buf, (size_t)bufsize));
+ return (privsys(code, op, type, (void *)(uintptr_t)buf,
+ (size_t)bufsize, itype));
}
#endif
diff --git a/usr/src/uts/common/syscall/uid.c b/usr/src/uts/common/syscall/uid.c
index 9973b17e43..967ebaf462 100644
--- a/usr/src/uts/common/syscall/uid.c
+++ b/usr/src/uts/common/syscall/uid.c
@@ -68,7 +68,9 @@ setuid(uid_t uid)
}
/*
* Need to pre-allocate the new cred structure before grabbing
- * the p_crlock mutex.
+ * the p_crlock mutex. We can't hold on to the p_crlock for most
+ * if this though, now that we allow kernel upcalls from the
+ * policy routines.
*/
newcr = cralloc_ksid();
@@ -76,16 +78,28 @@ setuid(uid_t uid)
retry:
mutex_enter(&p->p_crlock);
+retry_locked:
cr = p->p_cred;
+ crhold(cr);
+ mutex_exit(&p->p_crlock);
if ((uid == cr->cr_ruid || uid == cr->cr_suid) &&
secpolicy_allow_setid(cr, uid, B_TRUE) != 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry_locked;
error = 0;
crcopy_to(cr, newcr);
p->p_cred = newcr;
newcr->cr_uid = uid;
crsetsid(newcr, ksp, KSID_USER);
+ mutex_exit(&p->p_crlock);
} else if ((error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry_locked;
if (!uidchge && uid != cr->cr_ruid) {
/*
* The ruid of the process is going to change. In order
@@ -123,14 +137,14 @@ retry:
newcr->cr_uid = uid;
crsetsid(newcr, ksp, KSID_USER);
ASSERT(uid != oldruid ? uidchge : 1);
+ mutex_exit(&p->p_crlock);
} else {
crfree(newcr);
+ crfree(cr);
if (ksp != NULL)
ksid_rele(ksp);
}
- mutex_exit(&p->p_crlock);
-
/*
* We decrement the number of processes associated with the oldruid
* to match the increment above, even if the ruid of the process
@@ -194,7 +208,9 @@ seteuid(uid_t uid)
newcr = cralloc_ksid();
p = ttoproc(curthread);
mutex_enter(&p->p_crlock);
- cr = p->p_cred;
+retry:
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
if (uid == cr->cr_ruid || uid == cr->cr_uid || uid == cr->cr_suid ||
(error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) {
@@ -203,6 +219,10 @@ seteuid(uid_t uid)
* set-uid process must be marked to produce no core dump,
* if the effective uid did changed.
*/
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry;
if (cr->cr_uid != uid && error == 0)
do_nocd = 1;
error = 0;
@@ -210,15 +230,7 @@ seteuid(uid_t uid)
p->p_cred = newcr;
newcr->cr_uid = uid;
crsetsid(newcr, ksp, KSID_USER);
- } else {
- crfree(newcr);
- if (ksp != NULL)
- ksid_rele(ksp);
- }
-
- mutex_exit(&p->p_crlock);
-
- if (error == 0) {
+ mutex_exit(&p->p_crlock);
if (do_nocd) {
mutex_enter(&p->p_lock);
p->p_flag |= SNOCD;
@@ -227,6 +239,11 @@ seteuid(uid_t uid)
crset(p, newcr); /* broadcast to process threads */
return (0);
}
+
+ crfree(newcr);
+ crfree(cr);
+ if (ksp != NULL)
+ ksid_rele(ksp);
return (set_errno(error));
}
@@ -272,16 +289,30 @@ setreuid(uid_t ruid, uid_t euid)
retry:
mutex_enter(&p->p_crlock);
- cr = p->p_cred;
+retry_locked:
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid &&
secpolicy_allow_setid(cr, ruid, B_FALSE) != 0) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry_locked;
error = EPERM;
} else if (euid != -1 &&
euid != cr->cr_ruid && euid != cr->cr_uid &&
euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry_locked;
error = EPERM;
} else {
+ mutex_enter(&p->p_crlock);
+ crfree(cr);
+ if (cr != p->p_cred)
+ goto retry_locked;
if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) {
/*
* The ruid of the process is going to change. In order