1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * SID system call. 31 */ 32 33 #include <sys/sid.h> 34 #include <sys/cred.h> 35 #include <sys/errno.h> 36 #include <sys/systm.h> 37 #include <sys/policy.h> 38 #include <sys/door.h> 39 40 static kmutex_t idmap_mutex; 41 42 typedef struct idmap_reg { 43 door_handle_t idmap_door; 44 int idmap_flags; 45 int idmap_ref; 46 } idmap_reg_t; 47 48 static idmap_reg_t *idmap_ptr; 49 50 static int idmap_unreg_dh(door_handle_t); 51 52 static void 53 idmap_freeone(idmap_reg_t *p) 54 { 55 ASSERT(p->idmap_ref == 0); 56 ASSERT(MUTEX_HELD(&idmap_mutex)); 57 58 door_ki_rele(p->idmap_door); 59 if (idmap_ptr == p) 60 idmap_ptr = NULL; 61 62 kmem_free(p, sizeof (*p)); 63 } 64 65 static int 66 idmap_do_call(sidmap_call_t *callp, size_t callsz, void **resp, size_t *respsz) 67 { 68 door_arg_t da; 69 idmap_reg_t *p; 70 int ret; 71 int dres; 72 73 mutex_enter(&idmap_mutex); 74 p = idmap_ptr; 75 if (p != NULL) { 76 p->idmap_ref++; 77 } else { 78 mutex_exit(&idmap_mutex); 79 return (-1); 80 } 81 mutex_exit(&idmap_mutex); 82 83 da.data_ptr = (char *)callp; 84 da.data_size = callsz; 85 da.desc_ptr = NULL; 86 da.desc_num = 0; 87 da.rbuf = *resp; 88 da.rsize = *respsz; 89 90 while ((dres = door_ki_upcall(p->idmap_door, &da)) != 0) { 91 switch (dres) { 92 case EINTR: 93 case EAGAIN: 94 delay(1); 95 continue; 96 case EINVAL: 97 case EBADF: 98 (void) idmap_unreg_dh(p->idmap_door); 99 /* FALLTHROUGH */ 100 default: 101 ret = -1; 102 goto out; 103 } 104 } 105 *resp = da.rbuf; 106 *respsz = da.rsize; 107 ret = 0; 108 out: 109 mutex_enter(&idmap_mutex); 110 if (--p->idmap_ref == 0) 111 idmap_freeone(p); 112 mutex_exit(&idmap_mutex); 113 return (ret); 114 } 115 116 /* 117 * Current code only attempts to map ids to sids. 118 */ 119 int 120 idmap_call_byid(uid_t id, ksid_t *ksid) 121 { 122 sidmap_call_t call; 123 domsid_t res, *resp = &res; 124 size_t respsz = sizeof (res); 125 126 call.sc_type = SIDSYS_ID2SID; 127 call.sc_val.sc_id = id; 128 129 if (idmap_do_call(&call, sizeof (call), (void **)&resp, &respsz) != 0) 130 return (-1); 131 132 ksid->ks_domain = ksid_lookupdomain(resp->ds_dom); 133 ksid->ks_rid = resp->ds_rid; 134 135 /* Larger SID return value; this usually happens */ 136 if (resp != &res) 137 kmem_free(resp, respsz); 138 139 return (0); 140 } 141 142 uid_t 143 idmap_call_bysid(ksid_t *ksid) 144 { 145 ksiddomain_t *domp = ksid->ks_domain; 146 sidmap_call_t *callp; 147 uid_t res = (uid_t)-1; 148 uid_t *resp = &res; 149 size_t callsz; 150 size_t respsz = sizeof (res); 151 152 callsz = sizeof (sidmap_call_t) + domp->kd_len; 153 154 callp = kmem_alloc(callsz, KM_SLEEP); 155 callp->sc_type = SIDSYS_SID2ID; 156 bcopy(domp->kd_name, callp->sc_val.sc_sid.ds_dom, domp->kd_len); 157 callp->sc_val.sc_sid.ds_rid = ksid->ks_rid; 158 159 if (idmap_do_call(callp, callsz, (void **)&resp, &respsz) != 0) 160 goto out; 161 162 /* Should never happen; the original buffer should be large enough */ 163 if (resp != &res) { 164 kmem_free(resp, respsz); 165 goto out; 166 } 167 168 if (respsz != sizeof (uid_t)) 169 res = (uid_t)-1; 170 171 out: 172 kmem_free(callp, callsz); 173 return (res); 174 } 175 176 static int 177 idmap_reg(int did) 178 { 179 door_handle_t dh; 180 idmap_reg_t *idmp; 181 int err; 182 183 if ((err = secpolicy_idmap(CRED())) != 0) 184 return (set_errno(err)); 185 186 dh = door_ki_lookup(did); 187 188 if (dh == NULL) 189 return (set_errno(EBADF)); 190 191 idmp = kmem_alloc(sizeof (*idmp), KM_SLEEP); 192 193 idmp->idmap_door = dh; 194 mutex_enter(&idmap_mutex); 195 if (idmap_ptr != NULL) { 196 if (--idmap_ptr->idmap_ref == 0) 197 idmap_freeone(idmap_ptr); 198 } 199 idmp->idmap_flags = 0; 200 idmp->idmap_ref = 1; 201 idmap_ptr = idmp; 202 mutex_exit(&idmap_mutex); 203 return (0); 204 } 205 206 static int 207 idmap_unreg_dh(door_handle_t dh) 208 { 209 mutex_enter(&idmap_mutex); 210 if (idmap_ptr == NULL || idmap_ptr->idmap_door != dh) { 211 mutex_exit(&idmap_mutex); 212 return (EINVAL); 213 } 214 215 if (idmap_ptr->idmap_flags != 0) { 216 mutex_exit(&idmap_mutex); 217 return (EAGAIN); 218 } 219 idmap_ptr->idmap_flags = 1; 220 if (--idmap_ptr->idmap_ref == 0) 221 idmap_freeone(idmap_ptr); 222 mutex_exit(&idmap_mutex); 223 return (0); 224 } 225 226 static int 227 idmap_unreg(int did) 228 { 229 door_handle_t dh = door_ki_lookup(did); 230 int res; 231 232 if (dh == NULL) 233 return (set_errno(EINVAL)); 234 235 res = idmap_unreg_dh(dh); 236 door_ki_rele(dh); 237 238 if (res != 0) 239 return (set_errno(res)); 240 return (0); 241 } 242 243 static boolean_t 244 its_my_door(void) 245 { 246 mutex_enter(&idmap_mutex); 247 if (idmap_ptr != NULL) { 248 struct door_info info; 249 int err = door_ki_info(idmap_ptr->idmap_door, &info); 250 if (err == 0 && info.di_target == curproc->p_pid) { 251 mutex_exit(&idmap_mutex); 252 return (B_TRUE); 253 } 254 } 255 mutex_exit(&idmap_mutex); 256 return (B_FALSE); 257 } 258 259 static uint64_t 260 allocids(int flag, int nuids, int ngids) 261 { 262 rval_t r; 263 uid_t su = 0; 264 gid_t sg = 0; 265 int err; 266 267 if (!its_my_door()) 268 return (set_errno(EPERM)); 269 270 if (nuids < 0 || ngids < 0) 271 return (set_errno(EINVAL)); 272 273 if (flag != 0 || nuids > 0) 274 err = eph_uid_alloc(flag, &su, nuids); 275 if (err == 0 && (flag != 0 || ngids > 0)) 276 err = eph_gid_alloc(flag, &sg, ngids); 277 278 if (err != 0) 279 return (set_errno(EOVERFLOW)); 280 281 r.r_val1 = su; 282 r.r_val2 = sg; 283 return (r.r_vals); 284 } 285 286 uint64_t 287 sidsys(int op, int flag, int nuids, int ngids) 288 { 289 switch (op) { 290 case SIDSYS_ALLOC_IDS: 291 return (allocids(flag, nuids, ngids)); 292 case SIDSYS_IDMAP_REG: 293 return (idmap_reg(flag)); 294 case SIDSYS_IDMAP_UNREG: 295 return (idmap_unreg(flag)); 296 default: 297 return (set_errno(EINVAL)); 298 } 299 } 300