diff options
Diffstat (limited to 'usr/src')
33 files changed, 1536 insertions, 143 deletions
diff --git a/usr/src/cmd/ptools/ppriv/ppriv.c b/usr/src/cmd/ptools/ppriv/ppriv.c index 61af0efdc9..9b88f01f30 100644 --- a/usr/src/cmd/ptools/ppriv/ppriv.c +++ b/usr/src/cmd/ptools/ppriv/ppriv.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Program to examine or set process privileges. @@ -62,6 +62,7 @@ static boolean_t Don = B_FALSE; static boolean_t Doff = B_FALSE; static boolean_t list = B_FALSE; static boolean_t mac_aware = B_FALSE; +static boolean_t xpol = B_FALSE; static int mode = PRIV_STR_PORT; int @@ -79,7 +80,7 @@ main(int argc, char **argv) else command = argv[0]; - while ((opt = getopt(argc, argv, "lDMNevs:S")) != EOF) { + while ((opt = getopt(argc, argv, "lDMNevs:xS")) != EOF) { switch (opt) { case 'l': list = B_TRUE; @@ -110,6 +111,10 @@ main(int argc, char **argv) if ((rc = parsespec(optarg)) != 0) return (rc); break; + case 'x': + set = B_TRUE; + xpol = B_TRUE; + break; default: usage(); /*NOTREACHED*/ @@ -137,7 +142,7 @@ main(int argc, char **argv) privupdate_self(); rc = execvp(argv[0], &argv[0]); (void) fprintf(stderr, "%s: %s: %s\n", command, argv[0], - strerror(errno)); + strerror(errno)); } else if (list) { rc = dumppriv(argv); } else { @@ -192,8 +197,8 @@ look(char *arg) sz / ppriv->pr_nsets < ppriv->pr_setsize || ppriv->pr_infosize > sz || sz > 1024 * 1024) { (void) fprintf(stderr, - "%s: %s: bad PRNOTES section, size = %lx\n", - command, arg, (long)sz); + "%s: %s: bad PRNOTES section, size = %lx\n", + command, arg, (long)sz); Prelease(Pr, 0); return (1); } @@ -248,14 +253,14 @@ look(char *arg) break; default: (void) fprintf(stderr, "%s: unknown priv_info: %d\n", - arg, pi->priv_info_type); + arg, pi->priv_info_type); break; } if (pi->priv_info_size > ppriv->pr_infosize || pi->priv_info_size <= sizeof (priv_info_t) || (pi->priv_info_size & 3) != 0) { (void) fprintf(stderr, "%s: bad priv_info_size: %u\n", - arg, pi->priv_info_size); + arg, pi->priv_info_size); break; } x += pi->priv_info_size; @@ -263,16 +268,16 @@ look(char *arg) for (i = 0; i < ppriv->pr_nsets; i++) { extern const char *__priv_getsetbynum(const void *, int); - const char *setnm = pdata ? __priv_getsetbynum(pdata, i) - : priv_getsetbynum(i); - priv_chunk_t *pc = (priv_chunk_t *) - &ppriv->pr_sets[ppriv->pr_setsize * i]; + const char *setnm = pdata ? __priv_getsetbynum(pdata, i) : + priv_getsetbynum(i); + priv_chunk_t *pc = + (priv_chunk_t *)&ppriv->pr_sets[ppriv->pr_setsize * i]; (void) printf("\t%c: ", setnm && !nodata ? *setnm : '?'); if (!nodata) { extern char *__priv_set_to_str(void *, - const priv_set_t *, char, int); + const priv_set_t *, char, int); priv_set_t *pset = (priv_set_t *)pc; char *s; @@ -355,7 +360,7 @@ static void badspec(const char *spec) { (void) fprintf(stderr, "%s: bad privilege specification: \"%s\"\n", - command, spec); + command, spec); exit(3); /*NOTREACHED*/ } @@ -452,9 +457,9 @@ parsespec(const char *spec) /* Assign is mutually exclusive with add/remove and itself */ if (((toupd == &rem || toupd == &add) && assign[ind] != NULL) || (toupd == &assign && (assign[ind] != NULL || - rem[ind] != NULL || add[ind] != NULL))) { + rem[ind] != NULL || add[ind] != NULL))) { (void) fprintf(stderr, "%s: conflicting spec: %s\n", - command, spec); + command, spec); exit(1); } if ((*toupd)[ind] != NULL) { @@ -480,7 +485,7 @@ privupdate(prpriv_t *pr, const char *arg) if (sets != NULL) { for (i = 0; i < pri->priv_nsets; i++) { priv_set_t *target = - (priv_set_t *)&pr->pr_sets[pr->pr_setsize * i]; + (priv_set_t *)&pr->pr_sets[pr->pr_setsize * i]; if (rem[i] != NULL) priv_intersect(rem[i], target); if (add[i] != NULL) @@ -490,7 +495,7 @@ privupdate(prpriv_t *pr, const char *arg) } } - if (Doff || Don) { + if (Doff || Don || xpol) { priv_info_uint_t *pii; int sz = PRIV_PRPRIV_SIZE(pr); char *x = (char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr); @@ -513,7 +518,7 @@ privupdate(prpriv_t *pr, const char *arg) x += pi->priv_info_size; } (void) fprintf(stderr, - "%s: cannot find privilege flags to set\n", arg); + "%s: cannot find privilege flags to set\n", arg); pr->pr_infosize = 0; return; done: @@ -521,12 +526,14 @@ done: pr->pr_infosize = sizeof (priv_info_uint_t); /* LINTED: alignment */ pii = (priv_info_uint_t *) - ((char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr)); + ((char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr)); if (Don) fl |= PRIV_DEBUG; - else + if (Doff) fl &= ~PRIV_DEBUG; + if (xpol) + fl |= PRIV_XPOLICY; pii->info.priv_info_size = sizeof (*pii); pii->info.priv_info_type = PRIV_INFO_FLAGS; @@ -585,6 +592,8 @@ privupdate_self(void) if (Doff || Don) (void) setpflags(PRIV_DEBUG, Don ? 1 : 0); + if (xpol) + (void) setpflags(PRIV_XPOLICY, 1); } static int @@ -640,6 +649,9 @@ static struct { { PRIV_DEBUG, "PRIV_DEBUG" }, { PRIV_AWARE, "PRIV_AWARE" }, { PRIV_AWARE_INHERIT, "PRIV_AWARE_INHERIT" }, + { PRIV_XPOLICY, "PRIV_XPOLICY" }, + { NET_MAC_AWARE, "NET_MAC_AWARE" }, + { NET_MAC_AWARE_INHERIT, "NET_MAC_AWARE_INHERIT" }, }; /* diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c index 6dc5efc8fd..edc610559d 100644 --- a/usr/src/cmd/truss/print.c +++ b/usr/src/cmd/truss/print.c @@ -2266,6 +2266,11 @@ prt_pfl(private_t *pri, int raw, long val) switch ((int)val) { case PRIV_DEBUG: s = "PRIV_DEBUG"; break; case PRIV_AWARE: s = "PRIV_AWARE"; break; + case PRIV_XPOLICY: s = "PRIV_XPOLICY"; break; + case NET_MAC_AWARE: s = "NET_MAC_AWARE"; break; + case NET_MAC_AWARE_INHERIT: + s = "NET_MAC_AWARE_INHERIT"; + break; } } diff --git a/usr/src/head/Makefile b/usr/src/head/Makefile index d63931a8a4..177d935d5e 100644 --- a/usr/src/head/Makefile +++ b/usr/src/head/Makefile @@ -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. # # ident "%Z%%M% %I% %E% SMI" @@ -83,6 +83,7 @@ HDRS= $($(MACH)_HDRS) $(ATTRDB_HDRS) \ ieeefp.h \ inttypes.h \ iso646.h \ + klpd.h \ langinfo.h \ lastlog.h \ lber.h \ diff --git a/usr/src/head/klpd.h b/usr/src/head/klpd.h new file mode 100644 index 0000000000..6370d7fef3 --- /dev/null +++ b/usr/src/head/klpd.h @@ -0,0 +1,53 @@ +/* + * 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 _KLPD_H +#define _KLPD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/klpd.h> +#include <priv.h> +#include <ucred.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *klpd_create(boolean_t (*)(void *, const priv_set_t *, void *), + void *); +extern int klpd_register_id(const priv_set_t *, void *, idtype_t, id_t); +extern int klpd_register(const priv_set_t *, void *); +extern int klpd_unregister_id(void *, idtype_t, id_t); +extern int klpd_unregister(void *); +extern const char *klpd_getpath(void *); +extern int klpd_getport(void *, int *); +extern int klpd_getucred(ucred_t **, void *); + +#ifdef __cplusplus +} +#endif + +#endif /* _KLPD_H */ diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index 8b931d406f..e1d52f7c61 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -446,6 +446,7 @@ PORTGEN= \ isastream.o \ isatty.o \ killpg.o \ + klpdlib.o \ l64a.o \ lckpwdf.o \ lconstants.o \ diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com index 8e80952846..c773e4f8a0 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -480,6 +480,7 @@ PORTGEN= \ isastream.o \ isatty.o \ killpg.o \ + klpdlib.o \ l64a.o \ lckpwdf.o \ lconstants.o \ diff --git a/usr/src/lib/libc/port/gen/klpdlib.c b/usr/src/lib/libc/port/gen/klpdlib.c new file mode 100644 index 0000000000..db0403c950 --- /dev/null +++ b/usr/src/lib/libc/port/gen/klpdlib.c @@ -0,0 +1,220 @@ +/* + * 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 "synonyms.h" + +#include "priv_private.h" +#include "mtlib.h" +#include "libc.h" + +#include <door.h> +#include <errno.h> +#include <priv.h> +#include <klpd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/klpd.h> +#include <sys/param.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <netinet/in.h> + +typedef struct klpd_data { + boolean_t (*kd_callback)(void *, const priv_set_t *, void *); + void *kd_user_cookie; + int kd_doorfd; +} klpd_data_t; + +typedef struct klpd_ctxt { + klpd_data_t *kc_data; + char *kc_path; + int kc_int; + int kc_type; +} klpd_ctxt_t; + +/* ARGSUSED */ +static void +klpd_door_callback(void *kd_cookie, char *argp, size_t arg_size, + door_desc_t *dp, uint_t ndesc) +{ + klpd_data_t *p = kd_cookie; + int res; + klpd_ctxt_t ctx; + klpd_head_t *klh; + klpd_arg_t *ka; + priv_set_t *pset; + + if (argp == DOOR_UNREF_DATA) { + (void) p->kd_callback(p->kd_user_cookie, NULL, NULL); + (void) door_return(NULL, 0, NULL, 0); + } + + klh = (void *)argp; + ka = KLH_ARG(klh); + pset = KLH_PRIVSET(klh); + + ctx.kc_type = ka == NULL ? KLPDARG_NONE : ka->kla_type; + + switch (ctx.kc_type) { + case KLPDARG_NONE: + ctx.kc_path = NULL; + ctx.kc_int = -1; + break; + case KLPDARG_VNODE: + ctx.kc_path = ka->kla_str; + ctx.kc_int = -1; + break; + default: + ctx.kc_int = ka->kla_int; + ctx.kc_path = NULL; + break; + } + + ctx.kc_data = p; + + if (p->kd_callback(p->kd_user_cookie, pset, &ctx)) + res = 0; + else + res = 1; + + (void) door_return((char *)&res, sizeof (res), NULL, 0); +} + +void * +klpd_create(boolean_t (*callback)(void *, const priv_set_t *, void *), + void *cookie) +{ + klpd_data_t *p = malloc(sizeof (klpd_data_t)); + + if (p == NULL) + return (NULL); + + p->kd_doorfd = door_create(klpd_door_callback, p, + DOOR_REFUSE_DESC | DOOR_UNREF); + if (p->kd_doorfd == -1) + goto out; + + p->kd_user_cookie = cookie; + p->kd_callback = callback; + + return (p); + +out: + free(p); + return (NULL); +} + +int +klpd_register_id(const priv_set_t *set, void *handle, idtype_t type, id_t id) +{ + klpd_data_t *p = handle; + priv_data_t *d; + + LOADPRIVDATA(d); + + /* We really need to have the privilege set as argument here */ + if (syscall(SYS_privsys, PRIVSYS_KLPD_REG, p->kd_doorfd, id, + set, d->pd_setsize, type) == -1) + return (-1); + + /* Registration for the current process? Then do the thing. */ + if (type == P_PID && (id == 0 || (pid_t)id == getpid())) { + (void) setppriv(PRIV_OFF, PRIV_INHERITABLE, set); + (void) setpflags(PRIV_XPOLICY, 1); + } + return (0); +} + +int +klpd_register(const priv_set_t *set, void *handle) +{ + return (klpd_register_id(set, handle, P_PID, -1)); +} + +int +klpd_unregister_id(void *handle, idtype_t type, id_t id) +{ + klpd_data_t *p = handle; + int err; + + err = syscall(SYS_privsys, PRIVSYS_KLPD_UNREG, p->kd_doorfd, id, + (void *)NULL, 0L, type); + if (close(p->kd_doorfd) != 0) + err = -1; + free(p); + return (err); +} + +int +klpd_unregister(void *handle) +{ + return (klpd_unregister_id(handle, P_PID, -1)); +} + +const char * +klpd_getpath(void *context) +{ + klpd_ctxt_t *p = context; + + if (p->kc_type != KLPDARG_VNODE) + errno = EINVAL; + return (p->kc_path); +} + +int +klpd_getport(void *context, int *proto) +{ + klpd_ctxt_t *p = context; + + switch (p->kc_type) { + case KLPDARG_TCPPORT: + *proto = IPPROTO_TCP; + break; + case KLPDARG_UDPPORT: + *proto = IPPROTO_UDP; + break; + case KLPDARG_SCTPPORT: + *proto = IPPROTO_SCTP; + break; + case KLPDARG_SDPPORT: + *proto = PROTO_SDP; + break; + default: + errno = EINVAL; + return (-1); + } + return (p->kc_int); +} + +/*ARGSUSED*/ +int +klpd_getucred(ucred_t **uc, void *context) +{ + return (door_ucred(uc)); +} diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index e89764af46..6c07dcf2d5 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -1653,6 +1653,14 @@ SUNWprivate_1.1 { __iswctype_sb; __iswctype_std; _jrand48; + klpd_create; + klpd_getpath; + klpd_getport; + klpd_getucred; + klpd_register; + klpd_register_id; + klpd_unregister; + klpd_unregister_id; _l64a; _ladd; __lc_charmap; diff --git a/usr/src/lib/libc/sparc/Makefile b/usr/src/lib/libc/sparc/Makefile index 5697b855c9..a9f1b9642b 100644 --- a/usr/src/lib/libc/sparc/Makefile +++ b/usr/src/lib/libc/sparc/Makefile @@ -503,6 +503,7 @@ PORTGEN= \ isastream.o \ isatty.o \ killpg.o \ + klpdlib.o \ l64a.o \ lckpwdf.o \ lconstants.o \ diff --git a/usr/src/lib/libc/sparcv9/Makefile b/usr/src/lib/libc/sparcv9/Makefile index 3582c80351..9a201f7b60 100644 --- a/usr/src/lib/libc/sparcv9/Makefile +++ b/usr/src/lib/libc/sparcv9/Makefile @@ -464,6 +464,7 @@ PORTGEN= \ isastream.o \ isatty.o \ killpg.o \ + klpdlib.o \ l64a.o \ lckpwdf.o \ lconstants.o \ diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index e3d1f00917..003ab182d6 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -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. # # ident "%Z%%M% %I% %E% SMI" @@ -282,6 +282,7 @@ f none usr/include/kerberosv5/com_err.h 644 root bin f none usr/include/kerberosv5/krb5.h 644 root bin f none usr/include/kerberosv5/mit_copyright.h 644 root bin f none usr/include/kerberosv5/mit-sipb-copyright.h 644 root bin +f none usr/include/klpd.h 644 root bin f none usr/include/kmfapi.h 644 root bin f none usr/include/kmftypes.h 644 root bin f none usr/include/kstat.h 644 root bin @@ -948,6 +949,7 @@ f none usr/include/sys/kdi_impl.h 644 root bin f none usr/include/sys/kdi_machimpl.h 644 root bin f none usr/include/sys/kiconv.h 644 root bin f none usr/include/sys/kidmap.h 644 root bin +f none usr/include/sys/klpd.h 644 root bin f none usr/include/sys/klwp.h 644 root bin f none usr/include/sys/kmem.h 644 root bin f none usr/include/sys/kmem_impl.h 644 root bin 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 |
