summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/syscall/sidsys.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/syscall/sidsys.c')
-rw-r--r--usr/src/uts/common/syscall/sidsys.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/usr/src/uts/common/syscall/sidsys.c b/usr/src/uts/common/syscall/sidsys.c
new file mode 100644
index 0000000000..bcb749cadc
--- /dev/null
+++ b/usr/src/uts/common/syscall/sidsys.c
@@ -0,0 +1,299 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SID system call.
+ */
+
+#include <sys/sid.h>
+#include <sys/cred.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/policy.h>
+#include <sys/door.h>
+
+static kmutex_t idmap_mutex;
+
+typedef struct idmap_reg {
+ door_handle_t idmap_door;
+ int idmap_flags;
+ int idmap_ref;
+} idmap_reg_t;
+
+static idmap_reg_t *idmap_ptr;
+
+static int idmap_unreg_dh(door_handle_t);
+
+static void
+idmap_freeone(idmap_reg_t *p)
+{
+ ASSERT(p->idmap_ref == 0);
+ ASSERT(MUTEX_HELD(&idmap_mutex));
+
+ door_ki_rele(p->idmap_door);
+ if (idmap_ptr == p)
+ idmap_ptr = NULL;
+
+ kmem_free(p, sizeof (*p));
+}
+
+static int
+idmap_do_call(sidmap_call_t *callp, size_t callsz, void **resp, size_t *respsz)
+{
+ door_arg_t da;
+ idmap_reg_t *p;
+ int ret;
+ int dres;
+
+ mutex_enter(&idmap_mutex);
+ p = idmap_ptr;
+ if (p != NULL) {
+ p->idmap_ref++;
+ } else {
+ mutex_exit(&idmap_mutex);
+ return (-1);
+ }
+ mutex_exit(&idmap_mutex);
+
+ da.data_ptr = (char *)callp;
+ da.data_size = callsz;
+ da.desc_ptr = NULL;
+ da.desc_num = 0;
+ da.rbuf = *resp;
+ da.rsize = *respsz;
+
+ while ((dres = door_ki_upcall(p->idmap_door, &da)) != 0) {
+ switch (dres) {
+ case EINTR:
+ case EAGAIN:
+ delay(1);
+ continue;
+ case EINVAL:
+ case EBADF:
+ (void) idmap_unreg_dh(p->idmap_door);
+ /* FALLTHROUGH */
+ default:
+ ret = -1;
+ goto out;
+ }
+ }
+ *resp = da.rbuf;
+ *respsz = da.rsize;
+ ret = 0;
+out:
+ mutex_enter(&idmap_mutex);
+ if (--p->idmap_ref == 0)
+ idmap_freeone(p);
+ mutex_exit(&idmap_mutex);
+ return (ret);
+}
+
+/*
+ * Current code only attempts to map ids to sids.
+ */
+int
+idmap_call_byid(uid_t id, ksid_t *ksid)
+{
+ sidmap_call_t call;
+ domsid_t res, *resp = &res;
+ size_t respsz = sizeof (res);
+
+ call.sc_type = SIDSYS_ID2SID;
+ call.sc_val.sc_id = id;
+
+ if (idmap_do_call(&call, sizeof (call), (void **)&resp, &respsz) != 0)
+ return (-1);
+
+ ksid->ks_domain = ksid_lookupdomain(resp->ds_dom);
+ ksid->ks_rid = resp->ds_rid;
+
+ /* Larger SID return value; this usually happens */
+ if (resp != &res)
+ kmem_free(resp, respsz);
+
+ return (0);
+}
+
+uid_t
+idmap_call_bysid(ksid_t *ksid)
+{
+ ksiddomain_t *domp = ksid->ks_domain;
+ sidmap_call_t *callp;
+ uid_t res = (uid_t)-1;
+ uid_t *resp = &res;
+ size_t callsz;
+ size_t respsz = sizeof (res);
+
+ callsz = sizeof (sidmap_call_t) + domp->kd_len;
+
+ callp = kmem_alloc(callsz, KM_SLEEP);
+ callp->sc_type = SIDSYS_SID2ID;
+ bcopy(domp->kd_name, callp->sc_val.sc_sid.ds_dom, domp->kd_len);
+ callp->sc_val.sc_sid.ds_rid = ksid->ks_rid;
+
+ if (idmap_do_call(callp, callsz, (void **)&resp, &respsz) != 0)
+ goto out;
+
+ /* Should never happen; the original buffer should be large enough */
+ if (resp != &res) {
+ kmem_free(resp, respsz);
+ goto out;
+ }
+
+ if (respsz != sizeof (uid_t))
+ res = (uid_t)-1;
+
+out:
+ kmem_free(callp, callsz);
+ return (res);
+}
+
+static int
+idmap_reg(int did)
+{
+ door_handle_t dh;
+ idmap_reg_t *idmp;
+ int err;
+
+ if ((err = secpolicy_idmap(CRED())) != 0)
+ return (set_errno(err));
+
+ dh = door_ki_lookup(did);
+
+ if (dh == NULL)
+ return (set_errno(EBADF));
+
+ idmp = kmem_alloc(sizeof (*idmp), KM_SLEEP);
+
+ idmp->idmap_door = dh;
+ mutex_enter(&idmap_mutex);
+ if (idmap_ptr != NULL) {
+ if (--idmap_ptr->idmap_ref == 0)
+ idmap_freeone(idmap_ptr);
+ }
+ idmp->idmap_flags = 0;
+ idmp->idmap_ref = 1;
+ idmap_ptr = idmp;
+ mutex_exit(&idmap_mutex);
+ return (0);
+}
+
+static int
+idmap_unreg_dh(door_handle_t dh)
+{
+ mutex_enter(&idmap_mutex);
+ if (idmap_ptr == NULL || idmap_ptr->idmap_door != dh) {
+ mutex_exit(&idmap_mutex);
+ return (EINVAL);
+ }
+
+ if (idmap_ptr->idmap_flags != 0) {
+ mutex_exit(&idmap_mutex);
+ return (EAGAIN);
+ }
+ idmap_ptr->idmap_flags = 1;
+ if (--idmap_ptr->idmap_ref == 0)
+ idmap_freeone(idmap_ptr);
+ mutex_exit(&idmap_mutex);
+ return (0);
+}
+
+static int
+idmap_unreg(int did)
+{
+ door_handle_t dh = door_ki_lookup(did);
+ int res;
+
+ if (dh == NULL)
+ return (set_errno(EINVAL));
+
+ res = idmap_unreg_dh(dh);
+ door_ki_rele(dh);
+
+ if (res != 0)
+ return (set_errno(res));
+ return (0);
+}
+
+static boolean_t
+its_my_door(void)
+{
+ mutex_enter(&idmap_mutex);
+ if (idmap_ptr != NULL) {
+ struct door_info info;
+ int err = door_ki_info(idmap_ptr->idmap_door, &info);
+ if (err == 0 && info.di_target == curproc->p_pid) {
+ mutex_exit(&idmap_mutex);
+ return (B_TRUE);
+ }
+ }
+ mutex_exit(&idmap_mutex);
+ return (B_FALSE);
+}
+
+static uint64_t
+allocids(int flag, int nuids, int ngids)
+{
+ rval_t r;
+ uid_t su = 0;
+ gid_t sg = 0;
+ int err;
+
+ if (!its_my_door())
+ return (set_errno(EPERM));
+
+ if (nuids < 0 || ngids < 0)
+ return (set_errno(EINVAL));
+
+ if (flag != 0 || nuids > 0)
+ err = eph_uid_alloc(flag, &su, nuids);
+ if (err == 0 && (flag != 0 || ngids > 0))
+ err = eph_gid_alloc(flag, &sg, ngids);
+
+ if (err != 0)
+ return (set_errno(EOVERFLOW));
+
+ r.r_val1 = su;
+ r.r_val2 = sg;
+ return (r.r_vals);
+}
+
+uint64_t
+sidsys(int op, int flag, int nuids, int ngids)
+{
+ switch (op) {
+ case SIDSYS_ALLOC_IDS:
+ return (allocids(flag, nuids, ngids));
+ case SIDSYS_IDMAP_REG:
+ return (idmap_reg(flag));
+ case SIDSYS_IDMAP_UNREG:
+ return (idmap_unreg(flag));
+ default:
+ return (set_errno(EINVAL));
+ }
+}