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