summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/cred.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/cred.c')
-rw-r--r--usr/src/uts/common/os/cred.c200
1 files changed, 189 insertions, 11 deletions
diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c
index e6452fe4f2..4ebda51792 100644
--- a/usr/src/uts/common/os/cred.c
+++ b/usr/src/uts/common/os/cred.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -55,9 +55,18 @@
#include <sys/ucred.h>
#include <sys/prsystm.h>
#include <sys/modctl.h>
+#include <sys/avl.h>
#include <c2/audit.h>
#include <sys/zone.h>
#include <sys/tsol/label.h>
+#include <sys/sid.h>
+
+typedef struct ephidmap_data {
+ uid_t min_uid, last_uid;
+ gid_t min_gid, last_gid;
+ cred_t *nobody;
+ kmutex_t eph_lock;
+} ephidmap_data_t;
static struct kmem_cache *cred_cache;
static size_t crsize = 0;
@@ -76,6 +85,16 @@ static int get_c2audit_load(void);
#define REMOTE_PEER_CRED(c) ((c)->cr_gid == -1)
/*
+ * XXX: should be per-zone.
+ * Start with an invalid value for atomic increments.
+ */
+static ephidmap_data_t ephemeral_data = {
+ MAXUID, MAXUID, MAXUID, MAXUID
+};
+
+static boolean_t hasephids = B_FALSE;
+
+/*
* Initialize credentials data structures.
*/
@@ -111,12 +130,13 @@ cred_init(void)
dummycr = cralloc();
bzero(dummycr, crsize);
dummycr->cr_ref = 1;
- dummycr->cr_uid = -1;
- dummycr->cr_gid = -1;
- dummycr->cr_ruid = -1;
- dummycr->cr_rgid = -1;
- dummycr->cr_suid = -1;
- dummycr->cr_sgid = -1;
+ dummycr->cr_uid = (uid_t)-1;
+ dummycr->cr_gid = (gid_t)-1;
+ dummycr->cr_ruid = (uid_t)-1;
+ dummycr->cr_rgid = (gid_t)-1;
+ dummycr->cr_suid = (uid_t)-1;
+ dummycr->cr_sgid = (gid_t)-1;
+
/*
* kcred is used by anything that needs all privileges; it's
@@ -152,6 +172,13 @@ cred_init(void)
ttoproc(curthread)->p_cred = kcred;
curthread->t_cred = kcred;
+ /*
+ * nobody is used to map SID containing CRs.
+ */
+ ephemeral_data.nobody = crdup(kcred);
+ (void) crsetugid(ephemeral_data.nobody, UID_NOBODY, GID_NOBODY);
+ CR_FLAGS(kcred) = 0;
+
ucredsize = UCRED_SIZE;
}
@@ -165,6 +192,19 @@ cralloc(void)
cr->cr_ref = 1; /* So we can crfree() */
cr->cr_zone = NULL;
cr->cr_label = NULL;
+ cr->cr_ksid = NULL;
+ return (cr);
+}
+
+/*
+ * As cralloc but prepared for ksid change (if appropriate).
+ */
+cred_t *
+cralloc_ksid(void)
+{
+ cred_t *cr = cralloc();
+ if (hasephids)
+ cr->cr_ksid = kcrsid_alloc();
return (cr);
}
@@ -248,6 +288,8 @@ crfree(cred_t *cr)
label_rele(cr->cr_label);
if (cr->cr_zone)
zone_cred_rele(cr->cr_zone);
+ if (cr->cr_ksid)
+ kcrsid_rele(cr->cr_ksid);
kmem_cache_free(cred_cache, cr);
}
}
@@ -268,6 +310,8 @@ crcopy(cred_t *cr)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(cr->cr_label);
+ if (newcr->cr_ksid)
+ kcrsid_hold(cr->cr_ksid);
crfree(cr);
newcr->cr_ref = 2; /* caller gets two references */
return (newcr);
@@ -283,11 +327,18 @@ crcopy(cred_t *cr)
void
crcopy_to(cred_t *oldcr, cred_t *newcr)
{
+ credsid_t *nkcr = newcr->cr_ksid;
+
bcopy(oldcr, newcr, crsize);
if (newcr->cr_zone)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (nkcr) {
+ newcr->cr_ksid = nkcr;
+ kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
+ } else if (newcr->cr_ksid)
+ kcrsid_hold(newcr->cr_ksid);
crfree(oldcr);
newcr->cr_ref = 2; /* caller gets two references */
}
@@ -307,6 +358,8 @@ crdup(cred_t *cr)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (newcr->cr_ksid)
+ kcrsid_hold(newcr->cr_ksid);
newcr->cr_ref = 1;
return (newcr);
}
@@ -320,11 +373,18 @@ crdup(cred_t *cr)
void
crdup_to(cred_t *oldcr, cred_t *newcr)
{
+ credsid_t *nkcr = newcr->cr_ksid;
+
bcopy(oldcr, newcr, crsize);
if (newcr->cr_zone)
zone_cred_hold(newcr->cr_zone);
if (newcr->cr_label)
label_hold(newcr->cr_label);
+ if (nkcr) {
+ newcr->cr_ksid = nkcr;
+ kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
+ } else if (newcr->cr_ksid)
+ kcrsid_hold(newcr->cr_ksid);
newcr->cr_ref = 1;
}
@@ -559,14 +619,15 @@ crisremote(const cred_t *cr)
return (REMOTE_PEER_CRED(cr));
}
-#define BADID(x) ((x) != -1 && (unsigned int)(x) > MAXUID)
+#define BADUID(x) ((x) != -1 && !VALID_UID(x))
+#define BADGID(x) ((x) != -1 && !VALID_GID(x))
int
crsetresuid(cred_t *cr, uid_t r, uid_t e, uid_t s)
{
ASSERT(cr->cr_ref <= 2);
- if (BADID(r) || BADID(e) || BADID(s))
+ if (BADUID(r) || BADUID(e) || BADUID(s))
return (-1);
if (r != -1)
@@ -584,7 +645,7 @@ crsetresgid(cred_t *cr, gid_t r, gid_t e, gid_t s)
{
ASSERT(cr->cr_ref <= 2);
- if (BADID(r) || BADID(e) || BADID(s))
+ if (BADGID(r) || BADGID(e) || BADGID(s))
return (-1);
if (r != -1)
@@ -602,7 +663,7 @@ crsetugid(cred_t *cr, uid_t uid, gid_t gid)
{
ASSERT(cr->cr_ref <= 2);
- if (uid < 0 || uid > MAXUID || gid < 0 || gid > MAXUID)
+ if (!VALID_UID(uid) || !VALID_GID(gid))
return (-1);
cr->cr_uid = cr->cr_ruid = cr->cr_suid = uid;
@@ -904,3 +965,120 @@ zone_kcred(void)
else
return (kcred);
}
+
+boolean_t
+valid_ephemeral_uid(uid_t id)
+{
+ membar_consumer();
+ return (id > ephemeral_data.min_uid && id <= ephemeral_data.last_uid);
+}
+
+boolean_t
+valid_ephemeral_gid(gid_t id)
+{
+ membar_consumer();
+ return (id > ephemeral_data.min_gid && id <= ephemeral_data.last_gid);
+}
+
+int
+eph_uid_alloc(int flags, uid_t *start, int count)
+{
+ mutex_enter(&ephemeral_data.eph_lock);
+
+ /* Test for unsigned integer wrap around */
+ if (ephemeral_data.last_uid + count < ephemeral_data.last_uid) {
+ mutex_exit(&ephemeral_data.eph_lock);
+ return (-1);
+ }
+
+ /* first call or idmap crashed and state corrupted */
+ if (flags != 0)
+ ephemeral_data.min_uid = ephemeral_data.last_uid;
+
+ hasephids = B_TRUE;
+ *start = ephemeral_data.last_uid + 1;
+ atomic_add_32(&ephemeral_data.last_uid, count);
+ mutex_exit(&ephemeral_data.eph_lock);
+ return (0);
+}
+
+int
+eph_gid_alloc(int flags, gid_t *start, int count)
+{
+ mutex_enter(&ephemeral_data.eph_lock);
+
+ /* Test for unsigned integer wrap around */
+ if (ephemeral_data.last_gid + count < ephemeral_data.last_gid) {
+ mutex_exit(&ephemeral_data.eph_lock);
+ return (-1);
+ }
+
+ /* first call or idmap crashed and state corrupted */
+ if (flags != 0)
+ ephemeral_data.min_gid = ephemeral_data.last_gid;
+
+ hasephids = B_TRUE;
+ *start = ephemeral_data.last_gid + 1;
+ atomic_add_32(&ephemeral_data.last_gid, count);
+ mutex_exit(&ephemeral_data.eph_lock);
+ return (0);
+}
+
+/*
+ * If the credential contains any ephemeral IDs, map the credential
+ * to nobody.
+ */
+cred_t *
+crgetmapped(const cred_t *cr)
+{
+ if (cr->cr_ksid != NULL) {
+ int i;
+
+ for (i = 0; i < KSID_COUNT; i++)
+ if (cr->cr_ksid->kr_sidx[i].ks_id > MAXUID)
+ return (ephemeral_data.nobody);
+ if (cr->cr_ksid->kr_sidlist != NULL &&
+ cr->cr_ksid->kr_sidlist->ksl_neid > 0) {
+ return (ephemeral_data.nobody);
+ }
+ }
+
+ return ((cred_t *)cr);
+}
+
+/* index should be in range for a ksidindex_t */
+void
+crsetsid(cred_t *cr, ksid_t *ksp, int index)
+{
+ ASSERT(cr->cr_ref <= 2);
+ ASSERT(index >= 0 && index < KSID_COUNT);
+ if (cr->cr_ksid == NULL && ksp == NULL)
+ return;
+ cr->cr_ksid = kcrsid_setsid(cr->cr_ksid, ksp, index);
+}
+
+void
+crsetsidlist(cred_t *cr, ksidlist_t *ksl)
+{
+ ASSERT(cr->cr_ref <= 2);
+ if (cr->cr_ksid == NULL && ksl == NULL)
+ return;
+ cr->cr_ksid = kcrsid_setsidlist(cr->cr_ksid, ksl);
+}
+
+ksid_t *
+crgetsid(const cred_t *cr, int i)
+{
+ ASSERT(i >= 0 && i < KSID_COUNT);
+ if (cr->cr_ksid != NULL && cr->cr_ksid->kr_sidx[i].ks_domain)
+ return ((ksid_t *)&cr->cr_ksid->kr_sidx[i]);
+ return (NULL);
+}
+
+ksidlist_t *
+crgetsidlist(const cred_t *cr)
+{
+ if (cr->cr_ksid != NULL && cr->cr_ksid->kr_sidlist != NULL)
+ return ((ksidlist_t *)&cr->cr_ksid->kr_sidlist);
+ return (NULL);
+}