Print this page
5045 use atomic_{inc,dec}_* instead of atomic_add_*
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/os/klpd.c
+++ new/usr/src/uts/common/os/klpd.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 25
26 26 #include <sys/atomic.h>
27 27 #include <sys/door.h>
28 28 #include <sys/proc.h>
29 29 #include <sys/cred_impl.h>
30 30 #include <sys/policy.h>
31 31 #include <sys/priv.h>
32 32 #include <sys/klpd.h>
33 33 #include <sys/errno.h>
34 34 #include <sys/kmem.h>
35 35 #include <sys/project.h>
36 36 #include <sys/systm.h>
37 37 #include <sys/sysmacros.h>
38 38 #include <sys/pathname.h>
39 39 #include <sys/varargs.h>
40 40 #include <sys/zone.h>
41 41 #include <netinet/in.h>
42 42
43 43 #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
44 44
45 45 static kmutex_t klpd_mutex;
46 46
47 47 typedef struct klpd_reg {
48 48 struct klpd_reg *klpd_next;
49 49 struct klpd_reg **klpd_refp;
50 50 door_handle_t klpd_door;
51 51 pid_t klpd_door_pid;
52 52 priv_set_t klpd_pset;
53 53 cred_t *klpd_cred;
54 54 int klpd_indel; /* Disabled */
55 55 uint32_t klpd_ref;
56 56 } klpd_reg_t;
57 57
58 58
59 59 /*
60 60 * This data structure hangs off the credential of a process; the
61 61 * credential is finalized and cannot be changed; but this structure
62 62 * can be changed when a new door server for the particular group
63 63 * needs to be registered. It is refcounted and shared between
64 64 * processes with common ancestry.
65 65 *
66 66 * The reference count is atomically updated.
67 67 *
68 68 * But the registration probably needs to be updated under a lock.
69 69 */
70 70 typedef struct credklpd {
71 71 kmutex_t crkl_lock;
72 72 klpd_reg_t *crkl_reg;
73 73 uint32_t crkl_ref;
74 74 } credklpd_t;
75 75
76 76 klpd_reg_t *klpd_list;
77 77
78 78 static void klpd_unlink(klpd_reg_t *);
79 79 static int klpd_unreg_dh(door_handle_t);
↓ open down ↓ |
79 lines elided |
↑ open up ↑ |
80 80
81 81 static credklpd_t *crklpd_alloc(void);
82 82
83 83 void crklpd_setreg(credklpd_t *, klpd_reg_t *);
84 84
85 85 extern size_t max_vnode_path;
86 86
87 87 void
88 88 klpd_rele(klpd_reg_t *p)
89 89 {
90 - if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
90 + if (atomic_dec_32_nv(&p->klpd_ref) == 0) {
91 91 if (p->klpd_refp != NULL)
92 92 klpd_unlink(p);
93 93 if (p->klpd_cred != NULL)
94 94 crfree(p->klpd_cred);
95 95 door_ki_rele(p->klpd_door);
96 96 kmem_free(p, sizeof (*p));
97 97 }
98 98 }
99 99
100 100 /*
101 101 * In order to be able to walk the lists, we can't unlink the entry
102 102 * until the reference count drops to 0. If we remove it too soon,
103 103 * list walkers will terminate when they happen to call a now orphaned
104 104 * entry.
105 105 */
106 106 static klpd_reg_t *
107 107 klpd_rele_next(klpd_reg_t *p)
108 108 {
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
109 109 klpd_reg_t *r = p->klpd_next;
110 110
111 111 klpd_rele(p);
112 112 return (r);
113 113 }
114 114
115 115
116 116 static void
117 117 klpd_hold(klpd_reg_t *p)
118 118 {
119 - atomic_add_32(&p->klpd_ref, 1);
119 + atomic_inc_32(&p->klpd_ref);
120 120 }
121 121
122 122 /*
123 123 * Remove registration from where it is registered. Returns next in list.
124 124 */
125 125 static void
126 126 klpd_unlink(klpd_reg_t *p)
127 127 {
128 128 ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
129 129
130 130 if (p->klpd_refp != NULL)
131 131 *p->klpd_refp = p->klpd_next;
132 132
133 133 if (p->klpd_next != NULL)
134 134 p->klpd_next->klpd_refp = p->klpd_refp;
135 135 p->klpd_refp = NULL;
136 136 }
137 137
138 138 /*
139 139 * Remove all elements of the klpd list and decrement their refcnts.
140 140 * The lock guarding the list should be held; this function is
141 141 * called when we are sure we want to destroy the list completely
142 142 * list but not so sure that the reference counts of all elements have
143 143 * dropped back to 1.
144 144 */
145 145 void
146 146 klpd_freelist(klpd_reg_t **pp)
147 147 {
148 148 klpd_reg_t *p;
149 149
150 150 while ((p = *pp) != NULL) {
151 151 klpd_unlink(p);
152 152 klpd_rele(p);
153 153 }
154 154 }
155 155
156 156 /*
157 157 * Link new entry in list. The Boolean argument specifies whether this
158 158 * list can contain only a single item or multiple items.
159 159 * Returns the entry which needs to be released if single is B_TRUE.
160 160 */
161 161 static klpd_reg_t *
162 162 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
163 163 {
164 164 klpd_reg_t *old = *listp;
165 165
166 166 ASSERT(p->klpd_ref == 1);
167 167
168 168 ASSERT(old == NULL || *old->klpd_refp == old);
169 169 p->klpd_refp = listp;
170 170 p->klpd_next = single ? NULL : old;
171 171 *listp = p;
172 172 if (old != NULL) {
173 173 if (single) {
174 174 ASSERT(old->klpd_next == NULL);
175 175 old->klpd_refp = NULL;
176 176 return (old);
177 177 } else
178 178 old->klpd_refp = &p->klpd_next;
179 179 }
180 180 return (NULL);
181 181 }
182 182
183 183 /*
184 184 * The typical call consists of:
185 185 * - priv_set_t
186 186 * - some integer data (type, value)
187 187 * for now, it's just one bit.
188 188 */
189 189 static klpd_head_t *
190 190 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
191 191 {
192 192 char *tmp;
193 193 uint_t type;
194 194 vnode_t *vp;
195 195 size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t);
196 196 size_t plen, clen;
197 197 int proto;
198 198
199 199 klpd_arg_t *kap = NULL;
200 200 klpd_head_t *khp;
201 201
202 202 type = va_arg(ap, uint_t);
203 203 switch (type) {
204 204 case KLPDARG_NOMORE:
205 205 khp = kmem_zalloc(len, KM_SLEEP);
206 206 khp->klh_argoff = 0;
207 207 break;
208 208 case KLPDARG_VNODE:
209 209 len += offsetof(klpd_arg_t, kla_str);
210 210 vp = va_arg(ap, vnode_t *);
211 211 if (vp == NULL)
212 212 return (NULL);
213 213
214 214 tmp = va_arg(ap, char *);
215 215
216 216 if (tmp != NULL && *tmp != '\0')
217 217 clen = strlen(tmp) + 1;
218 218 else
219 219 clen = 0;
220 220
221 221 len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
222 222 khp = kmem_zalloc(len, KM_SLEEP);
223 223
224 224 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
225 225 kap = KLH_ARG(khp);
226 226
227 227 if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
228 228 vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
229 229 kmem_free(khp, len);
230 230 return (NULL);
231 231 }
232 232 if (clen != 0) {
233 233 plen = strlen(kap->kla_str);
234 234 if (plen + clen + 1 >= MAXPATHLEN) {
235 235 kmem_free(khp, len);
236 236 return (NULL);
237 237 }
238 238 /* Don't make root into a double "/" */
239 239 if (plen <= 2)
240 240 plen = 0;
241 241 kap->kla_str[plen] = '/';
242 242 bcopy(tmp, &kap->kla_str[plen + 1], clen);
243 243 }
244 244 break;
245 245 case KLPDARG_PORT:
246 246 proto = va_arg(ap, int);
247 247 switch (proto) {
248 248 case IPPROTO_TCP: type = KLPDARG_TCPPORT;
249 249 break;
250 250 case IPPROTO_UDP: type = KLPDARG_UDPPORT;
251 251 break;
252 252 case IPPROTO_SCTP: type = KLPDARG_SCTPPORT;
253 253 break;
254 254 case PROTO_SDP: type = KLPDARG_SDPPORT;
255 255 break;
256 256 }
257 257 /* FALLTHROUGH */
258 258 case KLPDARG_INT:
259 259 case KLPDARG_TCPPORT:
260 260 case KLPDARG_UDPPORT:
261 261 case KLPDARG_SCTPPORT:
262 262 case KLPDARG_SDPPORT:
263 263 len += sizeof (*kap);
264 264 khp = kmem_zalloc(len, KM_SLEEP);
265 265 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
266 266 kap = KLH_ARG(khp);
267 267 kap->kla_int = va_arg(ap, int);
268 268 break;
269 269 default:
270 270 return (NULL);
271 271 }
272 272 khp->klh_vers = KLPDCALL_VERS;
273 273 khp->klh_len = len;
274 274 khp->klh_privoff = sizeof (*khp);
275 275 *KLH_PRIVSET(khp) = *rq;
276 276 if (kap != NULL) {
277 277 kap->kla_type = type;
278 278 kap->kla_dlen = len - khp->klh_argoff;
279 279 }
280 280 return (khp);
281 281 }
282 282
283 283 static int
284 284 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
285 285 {
286 286 door_arg_t da;
287 287 int res;
288 288 int dres;
289 289 klpd_head_t *klh;
290 290
291 291 if (p->klpd_door_pid == curproc->p_pid)
292 292 return (-1);
293 293
294 294 klh = klpd_marshall(p, req, ap);
295 295
296 296 if (klh == NULL)
297 297 return (-1);
298 298
299 299 da.data_ptr = (char *)klh;
300 300 da.data_size = klh->klh_len;
301 301 da.desc_ptr = NULL;
302 302 da.desc_num = 0;
303 303 da.rbuf = (char *)&res;
304 304 da.rsize = sizeof (res);
305 305
306 306 while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
307 307 SIZE_MAX, 0)) != 0) {
308 308 switch (dres) {
309 309 case EAGAIN:
310 310 delay(1);
311 311 continue;
312 312 case EINVAL:
313 313 case EBADF:
314 314 /* Bad door, don't call it again. */
315 315 (void) klpd_unreg_dh(p->klpd_door);
316 316 /* FALLTHROUGH */
317 317 case EINTR:
318 318 /* Pending signal, nothing we can do. */
319 319 /* FALLTHROUGH */
320 320 default:
321 321 kmem_free(klh, klh->klh_len);
322 322 return (-1);
323 323 }
324 324 }
325 325 kmem_free(klh, klh->klh_len);
326 326 /* Bogus return value, must be a failure */
327 327 if (da.rbuf != (char *)&res) {
328 328 kmem_free(da.rbuf, da.rsize);
329 329 return (-1);
330 330 }
331 331 return (res);
332 332 }
333 333
334 334 uint32_t klpd_bad_locks;
335 335
336 336 int
337 337 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
338 338 {
339 339 klpd_reg_t *p;
340 340 int rv = -1;
341 341 credklpd_t *ckp;
342 342 zone_t *ckzone;
↓ open down ↓ |
213 lines elided |
↑ open up ↑ |
343 343
344 344 /*
345 345 * These locks must not be held when this code is called;
346 346 * callbacks to userland with these locks held will result
347 347 * in issues. That said, the code at the call sides was
348 348 * restructured not to call with any of the locks held and
349 349 * no policies operate by default on most processes.
350 350 */
351 351 if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
352 352 mutex_owned(&curproc->p_crlock)) {
353 - atomic_add_32(&klpd_bad_locks, 1);
353 + atomic_inc_32(&klpd_bad_locks);
354 354 return (-1);
355 355 }
356 356
357 357 /*
358 358 * Enforce the limit set for the call process (still).
359 359 */
360 360 if (!priv_issubset(req, &CR_LPRIV(cr)))
361 361 return (-1);
362 362
363 363 /* Try 1: get the credential specific klpd */
364 364 if ((ckp = crgetcrklpd(cr)) != NULL) {
365 365 mutex_enter(&ckp->crkl_lock);
366 366 if ((p = ckp->crkl_reg) != NULL &&
367 367 p->klpd_indel == 0 &&
368 368 priv_issubset(req, &p->klpd_pset)) {
369 369 klpd_hold(p);
370 370 mutex_exit(&ckp->crkl_lock);
371 371 rv = klpd_do_call(p, req, ap);
372 372 mutex_enter(&ckp->crkl_lock);
373 373 klpd_rele(p);
374 374 mutex_exit(&ckp->crkl_lock);
375 375 if (rv != -1)
376 376 return (rv == 0 ? 0 : -1);
377 377 } else {
378 378 mutex_exit(&ckp->crkl_lock);
379 379 }
380 380 }
381 381
382 382 /* Try 2: get the project specific klpd */
383 383 mutex_enter(&klpd_mutex);
384 384
385 385 if ((p = curproj->kpj_klpd) != NULL) {
386 386 klpd_hold(p);
387 387 mutex_exit(&klpd_mutex);
388 388 if (p->klpd_indel == 0 &&
389 389 priv_issubset(req, &p->klpd_pset)) {
390 390 rv = klpd_do_call(p, req, ap);
391 391 }
392 392 mutex_enter(&klpd_mutex);
393 393 klpd_rele(p);
394 394 mutex_exit(&klpd_mutex);
395 395
396 396 if (rv != -1)
397 397 return (rv == 0 ? 0 : -1);
398 398 } else {
399 399 mutex_exit(&klpd_mutex);
400 400 }
401 401
402 402 /* Try 3: get the global klpd list */
403 403 ckzone = crgetzone(cr);
404 404 mutex_enter(&klpd_mutex);
405 405
406 406 for (p = klpd_list; p != NULL; ) {
407 407 zone_t *kkzone = crgetzone(p->klpd_cred);
408 408 if ((kkzone == &zone0 || kkzone == ckzone) &&
409 409 p->klpd_indel == 0 &&
410 410 priv_issubset(req, &p->klpd_pset)) {
411 411 klpd_hold(p);
412 412 mutex_exit(&klpd_mutex);
413 413 rv = klpd_do_call(p, req, ap);
414 414 mutex_enter(&klpd_mutex);
415 415
416 416 p = klpd_rele_next(p);
417 417
418 418 if (rv != -1)
419 419 break;
420 420 } else {
421 421 p = p->klpd_next;
422 422 }
423 423 }
424 424 mutex_exit(&klpd_mutex);
425 425 return (rv == 0 ? 0 : -1);
426 426 }
427 427
428 428 /*
429 429 * Register the klpd.
430 430 * If the pid_t passed in is positive, update the registration for
431 431 * the specific process; that is only possible if the process already
432 432 * has a registration on it. This change of registration will affect
433 433 * all processes which share common ancestry.
434 434 *
435 435 * MY_PID (pid 0) can be used to create or change the context for
436 436 * the current process, typically done after fork().
437 437 *
438 438 * A negative value can be used to register a klpd globally.
439 439 *
440 440 * The per-credential klpd needs to be cleaned up when entering
441 441 * a zone or unsetting the flag.
442 442 */
443 443 int
444 444 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
445 445 {
446 446 cred_t *cr = CRED();
447 447 door_handle_t dh;
448 448 klpd_reg_t *kpd;
449 449 priv_set_t pset;
450 450 door_info_t di;
451 451 credklpd_t *ckp = NULL;
452 452 pid_t pid = -1;
453 453 projid_t proj = -1;
454 454 kproject_t *kpp = NULL;
455 455
456 456 if (CR_FLAGS(cr) & PRIV_XPOLICY)
457 457 return (set_errno(EINVAL));
458 458
459 459 if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
460 460 return (set_errno(EFAULT));
461 461
462 462 if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
463 463 return (set_errno(EPERM));
464 464
465 465 switch (type) {
466 466 case P_PID:
467 467 pid = (pid_t)id;
468 468 if (pid == P_MYPID)
469 469 pid = curproc->p_pid;
470 470 if (pid == curproc->p_pid)
471 471 ckp = crklpd_alloc();
472 472 break;
473 473 case P_PROJID:
474 474 proj = (projid_t)id;
475 475 kpp = project_hold_by_id(proj, crgetzone(cr),
476 476 PROJECT_HOLD_FIND);
477 477 if (kpp == NULL)
478 478 return (set_errno(ESRCH));
479 479 break;
480 480 default:
481 481 return (set_errno(ENOTSUP));
482 482 }
483 483
484 484
485 485 /*
486 486 * Verify the door passed in; it must be a door and we won't
487 487 * allow processes to be called on their own behalf.
488 488 */
489 489 dh = door_ki_lookup(did);
490 490 if (dh == NULL || door_ki_info(dh, &di) != 0) {
491 491 if (ckp != NULL)
492 492 crklpd_rele(ckp);
493 493 if (kpp != NULL)
494 494 project_rele(kpp);
495 495 return (set_errno(EBADF));
496 496 }
497 497 if (type == P_PID && pid == di.di_target) {
498 498 if (ckp != NULL)
499 499 crklpd_rele(ckp);
500 500 ASSERT(kpp == NULL);
501 501 return (set_errno(EINVAL));
502 502 }
503 503
504 504 kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
505 505 crhold(kpd->klpd_cred = cr);
506 506 kpd->klpd_door = dh;
507 507 kpd->klpd_door_pid = di.di_target;
508 508 kpd->klpd_ref = 1;
509 509 kpd->klpd_pset = pset;
510 510
511 511 if (kpp != NULL) {
512 512 mutex_enter(&klpd_mutex);
513 513 kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
514 514 mutex_exit(&klpd_mutex);
515 515 if (kpd != NULL)
516 516 klpd_rele(kpd);
517 517 project_rele(kpp);
518 518 } else if ((int)pid < 0) {
519 519 /* Global daemon */
520 520 mutex_enter(&klpd_mutex);
521 521 (void) klpd_link(kpd, &klpd_list, B_FALSE);
522 522 mutex_exit(&klpd_mutex);
523 523 } else if (pid == curproc->p_pid) {
524 524 proc_t *p = curproc;
525 525 cred_t *newcr = cralloc();
526 526
527 527 /* No need to lock, sole reference to ckp */
528 528 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
529 529
530 530 if (kpd != NULL)
531 531 klpd_rele(kpd);
532 532
533 533 mutex_enter(&p->p_crlock);
534 534 cr = p->p_cred;
535 535 crdup_to(cr, newcr);
536 536 crsetcrklpd(newcr, ckp);
537 537 p->p_cred = newcr; /* Already held for p_cred */
538 538
539 539 crhold(newcr); /* Hold once for the current thread */
540 540 mutex_exit(&p->p_crlock);
541 541 crfree(cr); /* One for the p_cred */
542 542 crset(p, newcr);
543 543 } else {
544 544 proc_t *p;
545 545 cred_t *pcr;
546 546 mutex_enter(&pidlock);
547 547 p = prfind(pid);
548 548 if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
549 549 mutex_exit(&pidlock);
550 550 klpd_rele(kpd);
551 551 return (set_errno(p == NULL ? ESRCH : EPERM));
552 552 }
553 553 mutex_enter(&p->p_crlock);
554 554 crhold(pcr = p->p_cred);
555 555 mutex_exit(&pidlock);
556 556 mutex_exit(&p->p_crlock);
557 557 /*
558 558 * We're going to update the credential's ckp in place;
559 559 * this requires that it exists.
560 560 */
561 561 ckp = crgetcrklpd(pcr);
562 562 if (ckp == NULL) {
563 563 crfree(pcr);
564 564 klpd_rele(kpd);
565 565 return (set_errno(EINVAL));
566 566 }
567 567 crklpd_setreg(ckp, kpd);
568 568 crfree(pcr);
569 569 }
570 570
571 571 return (0);
572 572 }
573 573
574 574 static int
575 575 klpd_unreg_dh(door_handle_t dh)
576 576 {
577 577 klpd_reg_t *p;
578 578
579 579 mutex_enter(&klpd_mutex);
580 580 for (p = klpd_list; p != NULL; p = p->klpd_next) {
581 581 if (p->klpd_door == dh)
582 582 break;
583 583 }
584 584 if (p == NULL) {
585 585 mutex_exit(&klpd_mutex);
586 586 return (EINVAL);
587 587 }
588 588 if (p->klpd_indel != 0) {
589 589 mutex_exit(&klpd_mutex);
590 590 return (EAGAIN);
591 591 }
592 592 p->klpd_indel = 1;
593 593 klpd_rele(p);
594 594 mutex_exit(&klpd_mutex);
595 595 return (0);
596 596 }
597 597
598 598 int
599 599 klpd_unreg(int did, idtype_t type, id_t id)
600 600 {
601 601 door_handle_t dh;
602 602 int res = 0;
603 603 proc_t *p;
604 604 pid_t pid;
605 605 projid_t proj;
606 606 kproject_t *kpp = NULL;
607 607 credklpd_t *ckp;
608 608
609 609 switch (type) {
610 610 case P_PID:
611 611 pid = (pid_t)id;
612 612 break;
613 613 case P_PROJID:
614 614 proj = (projid_t)id;
615 615 kpp = project_hold_by_id(proj, crgetzone(CRED()),
616 616 PROJECT_HOLD_FIND);
617 617 if (kpp == NULL)
618 618 return (set_errno(ESRCH));
619 619 break;
620 620 default:
621 621 return (set_errno(ENOTSUP));
622 622 }
623 623
624 624 dh = door_ki_lookup(did);
625 625 if (dh == NULL) {
626 626 if (kpp != NULL)
627 627 project_rele(kpp);
628 628 return (set_errno(EINVAL));
629 629 }
630 630
631 631 if (kpp != NULL) {
632 632 mutex_enter(&klpd_mutex);
633 633 if (kpp->kpj_klpd == NULL)
634 634 res = ESRCH;
635 635 else
636 636 klpd_freelist(&kpp->kpj_klpd);
637 637 mutex_exit(&klpd_mutex);
638 638 project_rele(kpp);
639 639 goto out;
640 640 } else if ((int)pid > 0) {
641 641 mutex_enter(&pidlock);
642 642 p = prfind(pid);
643 643 if (p == NULL) {
644 644 mutex_exit(&pidlock);
645 645 door_ki_rele(dh);
646 646 return (set_errno(ESRCH));
647 647 }
648 648 mutex_enter(&p->p_crlock);
649 649 mutex_exit(&pidlock);
650 650 } else if (pid == 0) {
651 651 p = curproc;
652 652 mutex_enter(&p->p_crlock);
653 653 } else {
654 654 res = klpd_unreg_dh(dh);
655 655 goto out;
656 656 }
657 657
658 658 ckp = crgetcrklpd(p->p_cred);
659 659 if (ckp != NULL) {
660 660 crklpd_setreg(ckp, NULL);
661 661 } else {
662 662 res = ESRCH;
663 663 }
664 664 mutex_exit(&p->p_crlock);
665 665
666 666 out:
↓ open down ↓ |
303 lines elided |
↑ open up ↑ |
667 667 door_ki_rele(dh);
668 668
669 669 if (res != 0)
670 670 return (set_errno(res));
671 671 return (0);
672 672 }
673 673
674 674 void
675 675 crklpd_hold(credklpd_t *crkpd)
676 676 {
677 - atomic_add_32(&crkpd->crkl_ref, 1);
677 + atomic_inc_32(&crkpd->crkl_ref);
678 678 }
679 679
680 680 void
681 681 crklpd_rele(credklpd_t *crkpd)
682 682 {
683 - if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
683 + if (atomic_dec_32_nv(&crkpd->crkl_ref) == 0) {
684 684 if (crkpd->crkl_reg != NULL)
685 685 klpd_rele(crkpd->crkl_reg);
686 686 mutex_destroy(&crkpd->crkl_lock);
687 687 kmem_free(crkpd, sizeof (*crkpd));
688 688 }
689 689 }
690 690
691 691 static credklpd_t *
692 692 crklpd_alloc(void)
693 693 {
694 694 credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
695 695
696 696 mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
697 697 res->crkl_ref = 1;
698 698 res->crkl_reg = NULL;
699 699
700 700 return (res);
701 701 }
702 702
703 703 void
704 704 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
705 705 {
706 706 klpd_reg_t *old;
707 707
708 708 mutex_enter(&crk->crkl_lock);
709 709 if (new == NULL) {
710 710 old = crk->crkl_reg;
711 711 if (old != NULL)
712 712 klpd_unlink(old);
713 713 } else {
714 714 old = klpd_link(new, &crk->crkl_reg, B_TRUE);
715 715 }
716 716 mutex_exit(&crk->crkl_lock);
717 717
718 718 if (old != NULL)
719 719 klpd_rele(old);
720 720 }
721 721
722 722 /* Allocate and register the pfexec specific callback */
723 723 int
724 724 pfexec_reg(int did)
725 725 {
726 726 door_handle_t dh;
727 727 int err = secpolicy_pfexec_register(CRED());
728 728 klpd_reg_t *pfx;
729 729 door_info_t di;
730 730 zone_t *myzone = crgetzone(CRED());
731 731
732 732 if (err != 0)
733 733 return (set_errno(err));
734 734
735 735 dh = door_ki_lookup(did);
736 736 if (dh == NULL || door_ki_info(dh, &di) != 0)
737 737 return (set_errno(EBADF));
738 738
739 739 pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
740 740
741 741 pfx->klpd_door = dh;
742 742 pfx->klpd_door_pid = di.di_target;
743 743 pfx->klpd_ref = 1;
744 744 pfx->klpd_cred = NULL;
745 745 mutex_enter(&myzone->zone_lock);
746 746 pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
747 747 mutex_exit(&myzone->zone_lock);
748 748 if (pfx != NULL)
749 749 klpd_rele(pfx);
750 750
751 751 return (0);
752 752 }
753 753
754 754 int
755 755 pfexec_unreg(int did)
756 756 {
757 757 door_handle_t dh;
758 758 int err = 0;
759 759 zone_t *myzone = crgetzone(CRED());
760 760 klpd_reg_t *pfd;
761 761
762 762 dh = door_ki_lookup(did);
763 763 if (dh == NULL)
764 764 return (set_errno(EBADF));
765 765
766 766 mutex_enter(&myzone->zone_lock);
767 767 pfd = myzone->zone_pfexecd;
768 768 if (pfd != NULL && pfd->klpd_door == dh) {
769 769 klpd_unlink(pfd);
770 770 } else {
771 771 pfd = NULL;
772 772 err = EINVAL;
773 773 }
774 774 mutex_exit(&myzone->zone_lock);
775 775 door_ki_rele(dh);
776 776 /*
777 777 * crfree() cannot be called with zone_lock held; it is called
778 778 * indirectly through closing the door handle
779 779 */
780 780 if (pfd != NULL)
781 781 klpd_rele(pfd);
782 782 if (err != 0)
783 783 return (set_errno(err));
784 784 return (0);
785 785 }
786 786
787 787 static int
788 788 get_path(char *buf, const char *path, int len)
789 789 {
790 790 size_t lc;
791 791 char *s;
792 792
793 793 if (len < 0)
794 794 len = strlen(path);
795 795
796 796 if (*path == '/' && len < MAXPATHLEN) {
797 797 (void) strcpy(buf, path);
798 798 return (0);
799 799 }
800 800 /*
801 801 * Build the pathname using the current directory + resolve pathname.
802 802 * The resolve pathname either starts with a normal component and
803 803 * we can just concatenate them or it starts with one
804 804 * or more ".." component and we can remove those; the
805 805 * last one cannot be a ".." and the current directory has
806 806 * more components than the number of ".." in the resolved pathname.
807 807 */
808 808 if (dogetcwd(buf, MAXPATHLEN) != 0)
809 809 return (-1);
810 810
811 811 lc = strlen(buf);
812 812
813 813 while (len > 3 && strncmp("../", path, 3) == 0) {
814 814 len -= 3;
815 815 path += 3;
816 816
817 817 s = strrchr(buf, '/');
818 818 if (s == NULL || s == buf)
819 819 return (-1);
820 820
821 821 *s = '\0';
822 822 lc = s - buf;
823 823 }
824 824 /* Add a "/" and a NUL */
825 825 if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
826 826 return (-1);
827 827
828 828 buf[lc] = '/';
829 829 (void) strcpy(buf + lc + 1, path);
830 830
831 831 return (0);
832 832 }
833 833
834 834 /*
835 835 * Perform the pfexec upcall.
836 836 *
837 837 * The pfexec upcall is different from the klpd_upcall in that a failure
838 838 * will lead to a denial of execution.
839 839 */
840 840 int
841 841 pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
842 842 boolean_t *scrub)
843 843 {
844 844 klpd_reg_t *pfd;
845 845 pfexec_arg_t *pap;
846 846 pfexec_reply_t pr, *prp;
847 847 door_arg_t da;
848 848 int dres;
849 849 cred_t *ncr = NULL;
850 850 int err = -1;
851 851 priv_set_t *iset;
852 852 priv_set_t *lset;
853 853 zone_t *myzone = crgetzone(CRED());
854 854 size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
855 855
856 856 /* Find registration */
857 857 mutex_enter(&myzone->zone_lock);
858 858 if ((pfd = myzone->zone_pfexecd) != NULL)
859 859 klpd_hold(pfd);
860 860 mutex_exit(&myzone->zone_lock);
861 861
862 862 if (pfd == NULL)
863 863 return (0);
864 864
865 865 if (pfd->klpd_door_pid == curproc->p_pid) {
866 866 klpd_rele(pfd);
867 867 return (0);
868 868 }
869 869
870 870 pap = kmem_zalloc(pasize, KM_SLEEP);
871 871
872 872 if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
873 873 goto out1;
874 874
875 875 pap->pfa_vers = PFEXEC_ARG_VERS;
876 876 pap->pfa_call = PFEXEC_EXEC_ATTRS;
877 877 pap->pfa_len = pasize;
878 878 pap->pfa_uid = crgetruid(cr);
879 879
880 880 da.data_ptr = (char *)pap;
881 881 da.data_size = pap->pfa_len;
882 882 da.desc_ptr = NULL;
883 883 da.desc_num = 0;
884 884 da.rbuf = (char *)≺
885 885 da.rsize = sizeof (pr);
886 886
887 887 while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
888 888 switch (dres) {
889 889 case EAGAIN:
890 890 delay(1);
891 891 continue;
892 892 case EINVAL:
893 893 case EBADF:
894 894 /* FALLTHROUGH */
895 895 case EINTR:
896 896 /* FALLTHROUGH */
897 897 default:
898 898 goto out;
899 899 }
900 900 }
901 901
902 902 prp = (pfexec_reply_t *)da.rbuf;
903 903 /*
904 904 * Check the size of the result and the alignment of the
905 905 * privilege sets.
906 906 */
907 907 if (da.rsize < sizeof (pr) ||
908 908 prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
909 909 prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
910 910 (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
911 911 (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0)
912 912 goto out;
913 913
914 914 /*
915 915 * Get results:
916 916 * allow/allow with additional credentials/disallow[*]
917 917 *
918 918 * euid, uid, egid, gid, privs, and limitprivs
919 919 * We now have somewhat more flexibility we could even set E and P
920 920 * judiciously but that would break some currently valid assumptions
921 921 * [*] Disallow is not readily supported by always including
922 922 * the Basic Solaris User profile in all user's profiles.
923 923 */
924 924
925 925 if (!prp->pfr_allowed) {
926 926 err = EACCES;
927 927 goto out;
928 928 }
929 929 if (!prp->pfr_setcred) {
930 930 err = 0;
931 931 goto out;
932 932 }
933 933 ncr = crdup((cred_t *)cr);
934 934
935 935 /*
936 936 * Generate the new credential set scrubenv if ruid != euid (or set)
937 937 * the "I'm set-uid flag" but that is not inherited so scrubbing
938 938 * the environment is a requirement.
939 939 */
940 940 /* Set uids or gids, note that -1 will do the right thing */
941 941 if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
942 942 goto out;
943 943 if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
944 944 goto out;
945 945
946 946 *scrub = prp->pfr_scrubenv;
947 947
948 948 if (prp->pfr_clearflag)
949 949 CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
950 950
951 951 /* We cannot exceed our Limit set, no matter what */
952 952 iset = PFEXEC_REPLY_IPRIV(prp);
953 953
954 954 if (iset != NULL) {
955 955 if (!priv_issubset(iset, &CR_LPRIV(ncr)))
956 956 goto out;
957 957 priv_union(iset, &CR_IPRIV(ncr));
958 958 }
959 959
960 960 /* Nor can we increate our Limit set itself */
961 961 lset = PFEXEC_REPLY_LPRIV(prp);
962 962
963 963 if (lset != NULL) {
964 964 if (!priv_issubset(lset, &CR_LPRIV(ncr)))
965 965 goto out;
966 966 CR_LPRIV(ncr) = *lset;
967 967 }
968 968
969 969 /* Exec will do the standard set operations */
970 970
971 971 err = 0;
972 972 out:
973 973 if (da.rbuf != (char *)&pr)
974 974 kmem_free(da.rbuf, da.rsize);
975 975 out1:
976 976 kmem_free(pap, pasize);
977 977 klpd_rele(pfd);
978 978 if (ncr != NULL) {
979 979 if (err == 0)
980 980 *pfcr = ncr;
981 981 else
982 982 crfree(ncr);
983 983 }
984 984 return (err);
985 985 }
986 986
987 987 int
988 988 get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
989 989 {
990 990 klpd_reg_t *pfd;
991 991 pfexec_arg_t *pap;
992 992 door_arg_t da;
993 993 int dres;
994 994 int err = -1;
995 995 priv_set_t *fset, pmem;
996 996 cred_t *zkcr;
997 997 zone_t *myzone = crgetzone(cr);
998 998 size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
999 999
1000 1000 mutex_enter(&myzone->zone_lock);
1001 1001 if ((pfd = myzone->zone_pfexecd) != NULL)
1002 1002 klpd_hold(pfd);
1003 1003 mutex_exit(&myzone->zone_lock);
1004 1004
1005 1005 if (pfd == NULL)
1006 1006 return (-1);
1007 1007
1008 1008 if (pfd->klpd_door_pid == curproc->p_pid) {
1009 1009 klpd_rele(pfd);
1010 1010 return (0);
1011 1011 }
1012 1012
1013 1013 pap = kmem_zalloc(pasize, KM_SLEEP);
1014 1014
1015 1015 if (get_path(pap->pfa_path, respn, -1) == -1)
1016 1016 goto out1;
1017 1017
1018 1018 pap->pfa_vers = PFEXEC_ARG_VERS;
1019 1019 pap->pfa_call = PFEXEC_FORCED_PRIVS;
1020 1020 pap->pfa_len = pasize;
1021 1021 pap->pfa_uid = (uid_t)-1; /* Not relevant */
1022 1022
1023 1023 da.data_ptr = (char *)pap;
1024 1024 da.data_size = pap->pfa_len;
1025 1025 da.desc_ptr = NULL;
1026 1026 da.desc_num = 0;
1027 1027 da.rbuf = (char *)&pmem;
1028 1028 da.rsize = sizeof (pmem);
1029 1029
1030 1030 while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1031 1031 switch (dres) {
1032 1032 case EAGAIN:
1033 1033 delay(1);
1034 1034 continue;
1035 1035 case EINVAL:
1036 1036 case EBADF:
1037 1037 case EINTR:
1038 1038 default:
1039 1039 goto out;
1040 1040 }
1041 1041 }
1042 1042
1043 1043 /*
1044 1044 * Check the size of the result, it's a privilege set.
1045 1045 */
1046 1046 if (da.rsize != sizeof (priv_set_t))
1047 1047 goto out;
1048 1048
1049 1049 fset = (priv_set_t *)da.rbuf;
1050 1050
1051 1051 /*
1052 1052 * We restrict the forced privileges with whatever is available in
1053 1053 * the current zone.
1054 1054 */
1055 1055 zkcr = zone_kcred();
1056 1056 priv_intersect(&CR_LPRIV(zkcr), fset);
1057 1057
1058 1058 /*
1059 1059 * But we fail if the forced privileges are not found in the current
1060 1060 * Limit set.
1061 1061 */
1062 1062 if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1063 1063 err = EACCES;
1064 1064 } else if (!priv_isemptyset(fset)) {
1065 1065 err = 0;
1066 1066 *set = *fset;
1067 1067 }
1068 1068 out:
1069 1069 if (da.rbuf != (char *)&pmem)
1070 1070 kmem_free(da.rbuf, da.rsize);
1071 1071 out1:
1072 1072 kmem_free(pap, pasize);
1073 1073 klpd_rele(pfd);
1074 1074 return (err);
1075 1075 }
1076 1076
1077 1077 int
1078 1078 check_user_privs(const cred_t *cr, const priv_set_t *set)
1079 1079 {
1080 1080 klpd_reg_t *pfd;
1081 1081 pfexec_arg_t *pap;
1082 1082 door_arg_t da;
1083 1083 int dres;
1084 1084 int err = -1;
1085 1085 zone_t *myzone = crgetzone(cr);
1086 1086 size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1087 1087 uint32_t res;
1088 1088
1089 1089 mutex_enter(&myzone->zone_lock);
1090 1090 if ((pfd = myzone->zone_pfexecd) != NULL)
1091 1091 klpd_hold(pfd);
1092 1092 mutex_exit(&myzone->zone_lock);
1093 1093
1094 1094 if (pfd == NULL)
1095 1095 return (-1);
1096 1096
1097 1097 if (pfd->klpd_door_pid == curproc->p_pid) {
1098 1098 klpd_rele(pfd);
1099 1099 return (0);
1100 1100 }
1101 1101
1102 1102 pap = kmem_zalloc(pasize, KM_SLEEP);
1103 1103
1104 1104 *(priv_set_t *)&pap->pfa_buf = *set;
1105 1105
1106 1106 pap->pfa_vers = PFEXEC_ARG_VERS;
1107 1107 pap->pfa_call = PFEXEC_USER_PRIVS;
1108 1108 pap->pfa_len = pasize;
1109 1109 pap->pfa_uid = crgetruid(cr);
1110 1110
1111 1111 da.data_ptr = (char *)pap;
1112 1112 da.data_size = pap->pfa_len;
1113 1113 da.desc_ptr = NULL;
1114 1114 da.desc_num = 0;
1115 1115 da.rbuf = (char *)&res;
1116 1116 da.rsize = sizeof (res);
1117 1117
1118 1118 while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1119 1119 switch (dres) {
1120 1120 case EAGAIN:
1121 1121 delay(1);
1122 1122 continue;
1123 1123 case EINVAL:
1124 1124 case EBADF:
1125 1125 case EINTR:
1126 1126 default:
1127 1127 goto out;
1128 1128 }
1129 1129 }
1130 1130
1131 1131 /*
1132 1132 * Check the size of the result.
1133 1133 */
1134 1134 if (da.rsize != sizeof (res))
1135 1135 goto out;
1136 1136
1137 1137 if (*(uint32_t *)da.rbuf == 1)
1138 1138 err = 0;
1139 1139 out:
1140 1140 if (da.rbuf != (char *)&res)
1141 1141 kmem_free(da.rbuf, da.rsize);
1142 1142 out1:
1143 1143 kmem_free(pap, pasize);
1144 1144 klpd_rele(pfd);
1145 1145 return (err);
1146 1146 }
↓ open down ↓ |
453 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX