]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/ipc_voucher.c
xnu-3248.60.10.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_voucher.c
1 /*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <mach/mach_types.h>
30 #include <mach/notify.h>
31 #include <ipc/ipc_types.h>
32 #include <ipc/ipc_port.h>
33 #include <ipc/ipc_voucher.h>
34 #include <kern/ipc_kobject.h>
35 #include <kern/ipc_tt.h>
36 #include <kern/mach_param.h>
37 #include <kern/kalloc.h>
38 #include <kern/zalloc.h>
39
40 #include <libkern/OSAtomic.h>
41
42 #include <mach/mach_voucher_server.h>
43 #include <mach/mach_voucher_attr_control_server.h>
44 #include <mach/mach_host_server.h>
45
46 /*
47 * Sysctl variable; enable and disable tracing of voucher contents
48 */
49 uint32_t ipc_voucher_trace_contents = 0;
50
51 static zone_t ipc_voucher_zone;
52 static zone_t ipc_voucher_attr_control_zone;
53
54 /*
55 * Voucher hash table
56 */
57 #define IV_HASH_BUCKETS 127
58 #define IV_HASH_BUCKET(x) ((x) % IV_HASH_BUCKETS)
59
60 static queue_head_t ivht_bucket[IV_HASH_BUCKETS];
61 static lck_spin_t ivht_lock_data;
62 static uint32_t ivht_count = 0;
63
64 #define ivht_lock_init() \
65 lck_spin_init(&ivht_lock_data, &ipc_lck_grp, &ipc_lck_attr)
66 #define ivht_lock_destroy() \
67 lck_spin_destroy(&ivht_lock_data, &ipc_lck_grp)
68 #define ivht_lock() \
69 lck_spin_lock(&ivht_lock_data)
70 #define ivht_lock_try() \
71 lck_spin_try_lock(&ivht_lock_data)
72 #define ivht_unlock() \
73 lck_spin_unlock(&ivht_lock_data)
74
75 /*
76 * Global table of resource manager registrations
77 *
78 * NOTE: For now, limited to well-known resource managers
79 * eventually, will include dynamic allocations requiring
80 * table growth and hashing by key.
81 */
82 static iv_index_t ivgt_keys_in_use = MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN;
83 static ipc_voucher_global_table_element iv_global_table[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN];
84 static lck_spin_t ivgt_lock_data;
85
86 #define ivgt_lock_init() \
87 lck_spin_init(&ivgt_lock_data, &ipc_lck_grp, &ipc_lck_attr)
88 #define ivgt_lock_destroy() \
89 lck_spin_destroy(&ivgt_lock_data, &ipc_lck_grp)
90 #define ivgt_lock() \
91 lck_spin_lock(&ivgt_lock_data)
92 #define ivgt_lock_try() \
93 lck_spin_try_lock(&ivgt_lock_data)
94 #define ivgt_unlock() \
95 lck_spin_unlock(&ivgt_lock_data)
96
97 ipc_voucher_t iv_alloc(iv_index_t entries);
98 void iv_dealloc(ipc_voucher_t iv, boolean_t unhash);
99
100 static inline iv_refs_t
101 iv_reference(ipc_voucher_t iv)
102 {
103 iv_refs_t refs;
104
105 refs = hw_atomic_add(&iv->iv_refs, 1);
106 return refs;
107 }
108
109 static inline void
110 iv_release(ipc_voucher_t iv)
111 {
112 iv_refs_t refs;
113
114 assert(0 < iv->iv_refs);
115 refs = hw_atomic_sub(&iv->iv_refs, 1);
116 if (0 == refs)
117 iv_dealloc(iv, TRUE);
118 }
119
120 /*
121 * freelist helper macros
122 */
123 #define IV_FREELIST_END ((iv_index_t) 0)
124
125 /*
126 * Attribute value hashing helper macros
127 */
128 #define IV_HASH_END UINT32_MAX
129 #define IV_HASH_VAL(sz, val) \
130 (((val) >> 3) % (sz))
131
132 static inline iv_index_t
133 iv_hash_value(
134 iv_index_t key_index,
135 mach_voucher_attr_value_handle_t value)
136 {
137 ipc_voucher_attr_control_t ivac;
138
139 ivac = iv_global_table[key_index].ivgte_control;
140 assert(IVAC_NULL != ivac);
141 return IV_HASH_VAL(ivac->ivac_init_table_size, value);
142 }
143
144 /*
145 * Convert a key to an index. This key-index is used to both index
146 * into the voucher table of attribute cache indexes and also the
147 * table of resource managers by key.
148 *
149 * For now, well-known keys have a one-to-one mapping of indexes
150 * into these tables. But as time goes on, that may not always
151 * be the case (sparse use over time). This isolates the code from
152 * having to change in these cases - yet still lets us keep a densely
153 * packed set of tables.
154 */
155 static inline iv_index_t
156 iv_key_to_index(mach_voucher_attr_key_t key)
157 {
158 if (MACH_VOUCHER_ATTR_KEY_ALL == key ||
159 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN < key)
160 return IV_UNUSED_KEYINDEX;
161 return (iv_index_t)key - 1;
162 }
163
164 static inline mach_voucher_attr_key_t
165 iv_index_to_key(iv_index_t key_index)
166 {
167 if (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN > key_index)
168 return iv_global_table[key_index].ivgte_key;
169 return MACH_VOUCHER_ATTR_KEY_NONE;
170
171 }
172
173 static void ivace_release(iv_index_t key_index, iv_index_t value_index);
174 static void ivace_lookup_values(iv_index_t key_index, iv_index_t value_index,
175 mach_voucher_attr_value_handle_array_t values,
176 mach_voucher_attr_value_handle_array_size_t *count);
177
178 static iv_index_t iv_lookup(ipc_voucher_t, iv_index_t);
179
180
181 static void ivgt_lookup(iv_index_t,
182 boolean_t,
183 ipc_voucher_attr_manager_t *,
184 ipc_voucher_attr_control_t *);
185
186 static kern_return_t
187 ipc_voucher_prepare_processing_recipe(
188 ipc_voucher_t voucher,
189 ipc_voucher_attr_raw_recipe_array_t recipes,
190 ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
191 mach_voucher_attr_recipe_command_t command,
192 ipc_voucher_attr_manager_flags flags,
193 int *need_processing);
194
195 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
196 void user_data_attr_manager_init(void);
197 #endif
198
199 void
200 ipc_voucher_init(void)
201 {
202 natural_t ipc_voucher_max = (task_max + thread_max) * 2;
203 natural_t attr_manager_max = MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN;
204 iv_index_t i;
205
206 ipc_voucher_zone = zinit(sizeof(struct ipc_voucher),
207 ipc_voucher_max * sizeof(struct ipc_voucher),
208 sizeof(struct ipc_voucher),
209 "ipc vouchers");
210 zone_change(ipc_voucher_zone, Z_NOENCRYPT, TRUE);
211
212 ipc_voucher_attr_control_zone = zinit(sizeof(struct ipc_voucher_attr_control),
213 attr_manager_max * sizeof(struct ipc_voucher_attr_control),
214 sizeof(struct ipc_voucher_attr_control),
215 "ipc voucher attr controls");
216 zone_change(ipc_voucher_attr_control_zone, Z_NOENCRYPT, TRUE);
217
218 /* initialize voucher hash */
219 ivht_lock_init();
220 for (i = 0; i < IV_HASH_BUCKETS; i++)
221 queue_init(&ivht_bucket[i]);
222
223 /* initialize global table locking */
224 ivgt_lock_init();
225
226 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
227 user_data_attr_manager_init();
228 #endif
229 }
230
231 ipc_voucher_t
232 iv_alloc(iv_index_t entries)
233 {
234 ipc_voucher_t iv;
235 iv_index_t i;
236
237
238 iv = (ipc_voucher_t)zalloc(ipc_voucher_zone);
239 if (IV_NULL == iv)
240 return IV_NULL;
241
242 iv->iv_refs = 1;
243 iv->iv_sum = 0;
244 iv->iv_hash = 0;
245 iv->iv_port = IP_NULL;
246
247 if (entries > IV_ENTRIES_INLINE) {
248 iv_entry_t table;
249
250 /* TODO - switch to ipc_table method of allocation */
251 table = (iv_entry_t) kalloc(sizeof(*table) * entries);
252 if (IVE_NULL == table) {
253 zfree(ipc_voucher_zone, iv);
254 return IV_NULL;
255 }
256 iv->iv_table = table;
257 iv->iv_table_size = entries;
258 } else {
259 iv->iv_table = iv->iv_inline_table;
260 iv->iv_table_size = IV_ENTRIES_INLINE;
261 }
262
263 /* initialize the table entries */
264 for (i=0; i < iv->iv_table_size; i++)
265 iv->iv_table[i] = IV_UNUSED_VALINDEX;
266
267 return (iv);
268 }
269
270 /*
271 * Routine: iv_set
272 * Purpose:
273 * Set the voucher's value index for a given key index.
274 * Conditions:
275 * This is only called during voucher creation, as
276 * they are immutable once references are distributed.
277 */
278 static void
279 iv_set(ipc_voucher_t iv,
280 iv_index_t key_index,
281 iv_index_t value_index)
282 {
283 assert(key_index < iv->iv_table_size);
284 iv->iv_table[key_index] = value_index;
285 }
286
287 void
288 iv_dealloc(ipc_voucher_t iv, boolean_t unhash)
289 {
290 ipc_port_t port = iv->iv_port;
291 natural_t i;
292
293 /*
294 * Do we have to remove it from the hash?
295 */
296 if (unhash) {
297 ivht_lock();
298 assert(0 == iv->iv_refs);
299 assert(IV_HASH_BUCKETS > iv->iv_hash);
300 queue_remove(&ivht_bucket[iv->iv_hash], iv, ipc_voucher_t, iv_hash_link);
301 ivht_count--;
302 ivht_unlock();
303
304 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_DESTROY) | DBG_FUNC_NONE,
305 VM_KERNEL_ADDRPERM((uintptr_t)iv), 0, ivht_count, 0, 0);
306
307 } else
308 assert(0 == --iv->iv_refs);
309
310 /*
311 * if a port was allocated for this voucher,
312 * it must not have any remaining send rights,
313 * because the port's reference on the voucher
314 * is gone. We can just discard it now.
315 */
316 if (IP_VALID(port)) {
317 assert(ip_active(port));
318 assert(port->ip_srights == 0);
319
320 ipc_port_dealloc_kernel(port);
321 }
322
323 /* release the attribute references held by this voucher */
324 for (i = 0; i < iv->iv_table_size; i++) {
325 ivace_release(i, iv->iv_table[i]);
326 #if MACH_ASSERT
327 iv_set(iv, i, ~0);
328 #endif
329 }
330
331 if (iv->iv_table != iv->iv_inline_table)
332 kfree(iv->iv_table,
333 iv->iv_table_size * sizeof(*iv->iv_table));
334
335 zfree(ipc_voucher_zone, iv);
336 }
337
338 /*
339 * Routine: iv_lookup
340 * Purpose:
341 * Find the voucher's value index for a given key_index
342 * Conditions:
343 * Vouchers are immutable, so no locking required to do
344 * a lookup.
345 */
346 static inline iv_index_t
347 iv_lookup(ipc_voucher_t iv, iv_index_t key_index)
348 {
349 if (key_index < iv->iv_table_size)
350 return iv->iv_table[key_index];
351 return IV_UNUSED_VALINDEX;
352 }
353
354 /*
355 * Routine: unsafe_convert_port_to_voucher
356 * Purpose:
357 * Unsafe conversion of a port to a voucher.
358 * Intended only for use by trace and debugging
359 * code. Consumes nothing, validates very little,
360 * produces an unreferenced voucher, which you
361 * MAY NOT use as a voucher, only log as an
362 * address.
363 * Conditions:
364 * Caller has a send-right reference to port.
365 * Port may or may not be locked.
366 */
367 uintptr_t
368 unsafe_convert_port_to_voucher(
369 ipc_port_t port)
370 {
371 if (IP_VALID(port)) {
372 uintptr_t voucher = (uintptr_t) port->ip_kobject;
373
374 /*
375 * No need to lock because we have a reference on the
376 * port, and if it is a true voucher port, that reference
377 * keeps the voucher bound to the port (and active).
378 */
379 if (ip_kotype(port) == IKOT_VOUCHER)
380 return (voucher);
381 }
382 return (uintptr_t)IV_NULL;
383 }
384
385 /*
386 * Routine: convert_port_to_voucher
387 * Purpose:
388 * Convert from a port to a voucher.
389 * Doesn't consume the port [send-right] ref;
390 * produces a voucher ref, which may be null.
391 * Conditions:
392 * Caller has a send-right reference to port.
393 * Port may or may not be locked.
394 */
395 ipc_voucher_t
396 convert_port_to_voucher(
397 ipc_port_t port)
398 {
399 if (IP_VALID(port)) {
400 ipc_voucher_t voucher = (ipc_voucher_t) port->ip_kobject;
401
402 /*
403 * No need to lock because we have a reference on the
404 * port, and if it is a true voucher port, that reference
405 * keeps the voucher bound to the port (and active).
406 */
407 if (ip_kotype(port) != IKOT_VOUCHER)
408 return IV_NULL;
409
410 assert(ip_active(port));
411
412 ipc_voucher_reference(voucher);
413 return (voucher);
414 }
415 return IV_NULL;
416 }
417
418 /*
419 * Routine: convert_port_name_to_voucher
420 * Purpose:
421 * Convert from a port name in the current space to a voucher.
422 * Produces a voucher ref, which may be null.
423 * Conditions:
424 * Nothing locked.
425 */
426
427 ipc_voucher_t
428 convert_port_name_to_voucher(
429 mach_port_name_t voucher_name)
430 {
431 ipc_voucher_t iv;
432 kern_return_t kr;
433 ipc_port_t port;
434
435 if (MACH_PORT_VALID(voucher_name)) {
436 kr = ipc_port_translate_send(current_space(), voucher_name, &port);
437 if (KERN_SUCCESS != kr)
438 return IV_NULL;
439
440 iv = convert_port_to_voucher(port);
441 ip_unlock(port);
442 return iv;
443 }
444 return IV_NULL;
445 }
446
447
448 void
449 ipc_voucher_reference(ipc_voucher_t voucher)
450 {
451 iv_refs_t refs;
452
453 if (IPC_VOUCHER_NULL == voucher)
454 return;
455
456 refs = iv_reference(voucher);
457 assert(1 < refs);
458 }
459
460 void
461 ipc_voucher_release(ipc_voucher_t voucher)
462 {
463 if (IPC_VOUCHER_NULL != voucher)
464 iv_release(voucher);
465 }
466
467 /*
468 * Routine: ipc_voucher_notify
469 * Purpose:
470 * Called whenever the Mach port system detects no-senders
471 * on the voucher port.
472 *
473 * Each time the send-right count goes positive, a no-senders
474 * notification is armed (and a voucher reference is donated).
475 * So, each notification that comes in must release a voucher
476 * reference. If more send rights have been added since it
477 * fired (asynchronously), they will be protected by a different
478 * reference hold.
479 */
480 void
481 ipc_voucher_notify(mach_msg_header_t *msg)
482 {
483 mach_no_senders_notification_t *notification = (void *)msg;
484 ipc_port_t port = notification->not_header.msgh_remote_port;
485 ipc_voucher_t iv;
486
487 assert(ip_active(port));
488 assert(IKOT_VOUCHER == ip_kotype(port));
489 iv = (ipc_voucher_t)port->ip_kobject;
490
491 ipc_voucher_release(iv);
492 }
493
494 /*
495 * Convert a voucher to a port.
496 */
497 ipc_port_t
498 convert_voucher_to_port(ipc_voucher_t voucher)
499 {
500 ipc_port_t port, send;
501
502 if (IV_NULL == voucher)
503 return (IP_NULL);
504
505 assert(0 < voucher->iv_refs);
506
507 /* create a port if needed */
508 port = voucher->iv_port;
509 if (!IP_VALID(port)) {
510 port = ipc_port_alloc_kernel();
511 assert(IP_VALID(port));
512 ipc_kobject_set_atomically(port, (ipc_kobject_t) voucher, IKOT_VOUCHER);
513
514 /* If we lose the race, deallocate and pick up the other guy's port */
515 if (!OSCompareAndSwapPtr(IP_NULL, port, &voucher->iv_port)) {
516 ipc_port_dealloc_kernel(port);
517 port = voucher->iv_port;
518 assert(ip_kotype(port) == IKOT_VOUCHER);
519 assert(port->ip_kobject == (ipc_kobject_t)voucher);
520 }
521 }
522
523 ip_lock(port);
524 assert(ip_active(port));
525 send = ipc_port_make_send_locked(port);
526
527 if (1 == port->ip_srights) {
528 ipc_port_t old_notify;
529
530 /* transfer our ref to the port, and arm the no-senders notification */
531 assert(IP_NULL == port->ip_nsrequest);
532 ipc_port_nsrequest(port, port->ip_mscount, ipc_port_make_sonce_locked(port), &old_notify);
533 /* port unlocked */
534 assert(IP_NULL == old_notify);
535 } else {
536 /* piggyback on the existing port reference, so consume ours */
537 ip_unlock(port);
538 ipc_voucher_release(voucher);
539 }
540 return (send);
541 }
542
543 #define ivace_reset_data(ivace_elem, next_index) { \
544 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
545 (ivace_elem)->ivace_refs = 0; \
546 (ivace_elem)->ivace_persist = 0; \
547 (ivace_elem)->ivace_made = 0; \
548 (ivace_elem)->ivace_free = TRUE; \
549 (ivace_elem)->ivace_releasing = FALSE; \
550 (ivace_elem)->ivace_layered = 0; \
551 (ivace_elem)->ivace_index = IV_HASH_END; \
552 (ivace_elem)->ivace_next = (next_index); \
553 }
554
555 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
556 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
557 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
558 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
559 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
560 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
561 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
562 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
563 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
564 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
565 }
566
567 ipc_voucher_attr_control_t
568 ivac_alloc(iv_index_t key_index)
569 {
570 ipc_voucher_attr_control_t ivac;
571 ivac_entry_t table;
572 natural_t i;
573
574
575 ivac = (ipc_voucher_attr_control_t)zalloc(ipc_voucher_attr_control_zone);
576 if (IVAC_NULL == ivac)
577 return IVAC_NULL;
578
579 ivac->ivac_refs = 1;
580 ivac->ivac_is_growing = FALSE;
581 ivac->ivac_port = IP_NULL;
582
583 /* start with just the inline table */
584 table = (ivac_entry_t) kalloc(IVAC_ENTRIES_MIN * sizeof(ivac_entry));
585 ivac->ivac_table = table;
586 ivac->ivac_table_size = IVAC_ENTRIES_MIN;
587 ivac->ivac_init_table_size = IVAC_ENTRIES_MIN;
588 for (i = 0; i < ivac->ivac_table_size; i++) {
589 ivace_reset_data(&table[i], i+1);
590 }
591
592 /* the default table entry is never on freelist */
593 table[0].ivace_next = IV_HASH_END;
594 table[0].ivace_free = FALSE;
595 table[i-1].ivace_next = IV_FREELIST_END;
596 ivac->ivac_freelist = 1;
597 ivac_lock_init(ivac);
598 ivac->ivac_key_index = key_index;
599 return (ivac);
600 }
601
602
603 void
604 ivac_dealloc(ipc_voucher_attr_control_t ivac)
605 {
606 ipc_voucher_attr_manager_t ivam = IVAM_NULL;
607 iv_index_t key_index = ivac->ivac_key_index;
608 ipc_port_t port = ivac->ivac_port;
609 natural_t i;
610
611 /*
612 * If the control is in the global table, we
613 * have to remove it from there before we (re)confirm
614 * that the reference count is still zero.
615 */
616 ivgt_lock();
617 if (ivac->ivac_refs > 0) {
618 ivgt_unlock();
619 return;
620 }
621
622 /* take it out of the global table */
623 if (iv_global_table[key_index].ivgte_control == ivac) {
624 ivam = iv_global_table[key_index].ivgte_manager;
625 iv_global_table[key_index].ivgte_manager = IVAM_NULL;
626 iv_global_table[key_index].ivgte_control = IVAC_NULL;
627 iv_global_table[key_index].ivgte_key = MACH_VOUCHER_ATTR_KEY_NONE;
628 }
629 ivgt_unlock();
630
631 /* release the reference held on the resource manager */
632 if (IVAM_NULL != ivam)
633 (ivam->ivam_release)(ivam);
634
635 /*
636 * if a port was allocated for this voucher,
637 * it must not have any remaining send rights,
638 * because the port's reference on the voucher
639 * is gone. We can just discard it now.
640 */
641 if (IP_VALID(port)) {
642 assert(ip_active(port));
643 assert(port->ip_srights == 0);
644
645 ipc_port_dealloc_kernel(port);
646 }
647
648 /*
649 * the resource manager's control reference and all references
650 * held by the specific value caches are gone, so free the
651 * table.
652 */
653 #ifdef MACH_DEBUG
654 for (i = 0; i < ivac->ivac_table_size; i++)
655 if (ivac->ivac_table[i].ivace_refs != 0)
656 panic("deallocing a resource manager with live refs to its attr values\n");
657 #endif
658 kfree(ivac->ivac_table, ivac->ivac_table_size * sizeof(*ivac->ivac_table));
659 ivac_lock_destroy(ivac);
660 zfree(ipc_voucher_attr_control_zone, ivac);
661 }
662
663 void
664 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control)
665 {
666 ivac_reference(control);
667 }
668
669 void
670 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control)
671 {
672 ivac_release(control);
673 }
674
675 /*
676 * Routine: convert_port_to_voucher_attr_control reference
677 * Purpose:
678 * Convert from a port to a voucher attribute control.
679 * Doesn't consume the port ref; produces a voucher ref,
680 * which may be null.
681 * Conditions:
682 * Nothing locked.
683 */
684 ipc_voucher_attr_control_t
685 convert_port_to_voucher_attr_control(
686 ipc_port_t port)
687 {
688 if (IP_VALID(port)) {
689 ipc_voucher_attr_control_t ivac = (ipc_voucher_attr_control_t) port->ip_kobject;
690
691 /*
692 * No need to lock because we have a reference on the
693 * port, and if it is a true voucher control port,
694 * that reference keeps the voucher bound to the port
695 * (and active).
696 */
697 if (ip_kotype(port) != IKOT_VOUCHER_ATTR_CONTROL)
698 return IVAC_NULL;
699
700 assert(ip_active(port));
701
702 ivac_reference(ivac);
703 return (ivac);
704 }
705 return IVAC_NULL;
706 }
707
708 void
709 ipc_voucher_attr_control_notify(mach_msg_header_t *msg)
710 {
711 mach_no_senders_notification_t *notification = (void *)msg;
712 ipc_port_t port = notification->not_header.msgh_remote_port;
713 ipc_voucher_attr_control_t ivac;
714
715 assert(IKOT_VOUCHER_ATTR_CONTROL == ip_kotype(port));
716 ip_lock(port);
717 assert(ip_active(port));
718
719 /* if no new send rights, drop a control reference */
720 if (port->ip_mscount == notification->not_count) {
721 ivac = (ipc_voucher_attr_control_t)port->ip_kobject;
722 ip_unlock(port);
723
724 ivac_release(ivac);
725 }
726 ip_unlock(port);
727 }
728
729 /*
730 * Convert a voucher attr control to a port.
731 */
732 ipc_port_t
733 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control)
734 {
735 ipc_port_t port, send;
736
737 if (IVAC_NULL == control)
738 return (IP_NULL);
739
740 /* create a port if needed */
741 port = control->ivac_port;
742 if (!IP_VALID(port)) {
743 port = ipc_port_alloc_kernel();
744 assert(IP_VALID(port));
745 if (OSCompareAndSwapPtr(IP_NULL, port, &control->ivac_port)) {
746 ip_lock(port);
747 ipc_kobject_set_atomically(port, (ipc_kobject_t) control, IKOT_VOUCHER_ATTR_CONTROL);
748 } else {
749 ipc_port_dealloc_kernel(port);
750 port = control->ivac_port;
751 ip_lock(port);
752 assert(ip_kotype(port) == IKOT_VOUCHER_ATTR_CONTROL);
753 assert(port->ip_kobject == (ipc_kobject_t)control);
754 }
755 } else
756 ip_lock(port);
757
758 assert(ip_active(port));
759 send = ipc_port_make_send_locked(port);
760
761 if (1 == port->ip_srights) {
762 ipc_port_t old_notify;
763
764 /* transfer our ref to the port, and arm the no-senders notification */
765 assert(IP_NULL == port->ip_nsrequest);
766 ipc_port_nsrequest(port, port->ip_mscount, ipc_port_make_sonce_locked(port), &old_notify);
767 assert(IP_NULL == old_notify);
768 ip_unlock(port);
769 } else {
770 /* piggyback on the existing port reference, so consume ours */
771 ip_unlock(port);
772 ivac_release(control);
773 }
774 return (send);
775 }
776
777 /*
778 * Look up the values for a given <key, index> pair.
779 */
780 static void
781 ivace_lookup_values(
782 iv_index_t key_index,
783 iv_index_t value_index,
784 mach_voucher_attr_value_handle_array_t values,
785 mach_voucher_attr_value_handle_array_size_t *count)
786 {
787 ipc_voucher_attr_control_t ivac;
788 ivac_entry_t ivace;
789
790 if (IV_UNUSED_VALINDEX == value_index ||
791 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN <= key_index) {
792 *count = 0;
793 return;
794 }
795
796 ivac = iv_global_table[key_index].ivgte_control;
797 assert(IVAC_NULL != ivac);
798
799 /*
800 * Get the entry and then the linked values.
801 */
802 ivac_lock(ivac);
803 assert(value_index < ivac->ivac_table_size);
804 ivace = &ivac->ivac_table[value_index];
805
806 /*
807 * TODO: support chained values (for effective vouchers).
808 */
809 assert(ivace->ivace_refs > 0);
810 values[0] = ivace->ivace_value;
811 ivac_unlock(ivac);
812 *count = 1;
813 }
814
815 /*
816 * ivac_grow_table - Allocate a bigger table of attribute values
817 *
818 * Conditions: ivac is locked on entry and again on return
819 */
820 static void
821 ivac_grow_table(ipc_voucher_attr_control_t ivac)
822 {
823 iv_index_t i = 0;
824
825 /* NOTE: do not modify *_table and *_size values once set */
826 ivac_entry_t new_table = NULL, old_table = NULL;
827 iv_index_t new_size, old_size;
828
829 if (ivac->ivac_is_growing) {
830 ivac_sleep(ivac);
831 return;
832 }
833
834 ivac->ivac_is_growing = 1;
835 if (ivac->ivac_table_size >= IVAC_ENTRIES_MAX) {
836 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
837 return;
838 }
839
840 old_size = ivac->ivac_table_size;
841 ivac_unlock(ivac);
842
843 new_size = old_size * 2;
844
845 assert(new_size > old_size);
846 assert(new_size < IVAC_ENTRIES_MAX);
847
848 new_table = kalloc(sizeof(ivac_entry) * new_size);
849 if (!new_table){
850 panic("Failed to grow ivac table to size %d\n", new_size);
851 return;
852 }
853
854 /* setup the free list for new entries */
855 for (i = old_size; i < new_size; i++) {
856 ivace_reset_data(&new_table[i], i+1);
857 }
858
859 ivac_lock(ivac);
860
861 for (i = 0; i < ivac->ivac_table_size; i++){
862 ivace_copy_data(&ivac->ivac_table[i], &new_table[i]);
863 }
864
865 old_table = ivac->ivac_table;
866
867 ivac->ivac_table = new_table;
868 ivac->ivac_table_size = new_size;
869
870 /* adding new free entries at head of freelist */
871 ivac->ivac_table[new_size - 1].ivace_next = ivac->ivac_freelist;
872 ivac->ivac_freelist = old_size;
873 ivac->ivac_is_growing = 0;
874 ivac_wakeup(ivac);
875
876 if (old_table){
877 ivac_unlock(ivac);
878 kfree(old_table, old_size * sizeof(ivac_entry));
879 ivac_lock(ivac);
880 }
881 }
882
883 /*
884 * ivace_reference_by_index
885 *
886 * Take an additional reference on the <key_index, val_index>
887 * cached value. It is assumed the caller already holds a
888 * reference to the same cached key-value pair.
889 */
890 static void
891 ivace_reference_by_index(
892 iv_index_t key_index,
893 iv_index_t val_index)
894 {
895 ipc_voucher_attr_control_t ivac;
896 ivac_entry_t ivace;
897
898 if (IV_UNUSED_VALINDEX == val_index)
899 return;
900
901 ivgt_lookup(key_index, FALSE, NULL, &ivac);
902 assert(IVAC_NULL != ivac);
903
904 ivac_lock(ivac);
905 assert(val_index < ivac->ivac_table_size);
906 ivace = &ivac->ivac_table[val_index];
907
908 assert(0xdeadc0dedeadc0de != ivace->ivace_value);
909 assert(0 < ivace->ivace_refs);
910 assert(!ivace->ivace_free);
911
912 /* Take ref only on non-persistent values */
913 if (!ivace->ivace_persist) {
914 ivace->ivace_refs++;
915 }
916 ivac_unlock(ivac);
917 }
918
919
920 /*
921 * Look up the values for a given <key, index> pair.
922 *
923 * Consumes a reference on the passed voucher control.
924 * Either it is donated to a newly-created value cache
925 * or it is released (if we piggy back on an existing
926 * value cache entry).
927 */
928 static iv_index_t
929 ivace_reference_by_value(
930 ipc_voucher_attr_control_t ivac,
931 mach_voucher_attr_value_handle_t value,
932 mach_voucher_attr_value_flags_t flag)
933 {
934 ivac_entry_t ivace = IVACE_NULL;
935 iv_index_t hash_index;
936 iv_index_t index;
937
938 if (IVAC_NULL == ivac) {
939 return IV_UNUSED_VALINDEX;
940 }
941
942 ivac_lock(ivac);
943 restart:
944 hash_index = IV_HASH_VAL(ivac->ivac_init_table_size, value);
945 index = ivac->ivac_table[hash_index].ivace_index;
946 while (index != IV_HASH_END) {
947 assert(index < ivac->ivac_table_size);
948 ivace = &ivac->ivac_table[index];
949 assert(!ivace->ivace_free);
950
951 if (ivace->ivace_value == value)
952 break;
953
954 assert(ivace->ivace_next != index);
955 index = ivace->ivace_next;
956 }
957
958 /* found it? */
959 if (index != IV_HASH_END) {
960 /* only add reference on non-persistent value */
961 if (!ivace->ivace_persist) {
962 ivace->ivace_refs++;
963 ivace->ivace_made++;
964 }
965
966 ivac_unlock(ivac);
967 ivac_release(ivac);
968 return index;
969 }
970
971 /* insert new entry in the table */
972 index = ivac->ivac_freelist;
973 if (IV_FREELIST_END == index) {
974 /* freelist empty */
975 ivac_grow_table(ivac);
976 goto restart;
977 }
978
979 /* take the entry off the freelist */
980 ivace = &ivac->ivac_table[index];
981 ivac->ivac_freelist = ivace->ivace_next;
982
983 /* initialize the new entry */
984 ivace->ivace_value = value;
985 ivace->ivace_refs = 1;
986 ivace->ivace_made = 1;
987 ivace->ivace_free = FALSE;
988 ivace->ivace_persist = (flag & MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST) ? TRUE : FALSE;
989
990 /* insert the new entry in the proper hash chain */
991 ivace->ivace_next = ivac->ivac_table[hash_index].ivace_index;
992 ivac->ivac_table[hash_index].ivace_index = index;
993 ivac_unlock(ivac);
994
995 /* donated passed in ivac reference to new entry */
996
997 return index;
998 }
999
1000 /*
1001 * Release a reference on the given <key_index, value_index> pair.
1002 *
1003 * Conditions: called with nothing locked, as it may cause
1004 * callouts and/or messaging to the resource
1005 * manager.
1006 */
1007 static void ivace_release(
1008 iv_index_t key_index,
1009 iv_index_t value_index)
1010 {
1011 ipc_voucher_attr_control_t ivac;
1012 ipc_voucher_attr_manager_t ivam;
1013 mach_voucher_attr_value_handle_t value;
1014 mach_voucher_attr_value_reference_t made;
1015 mach_voucher_attr_key_t key;
1016 iv_index_t hash_index;
1017 ivac_entry_t ivace;
1018 kern_return_t kr;
1019
1020 /* cant release the default value */
1021 if (IV_UNUSED_VALINDEX == value_index)
1022 return;
1023
1024 ivgt_lookup(key_index, FALSE, &ivam, &ivac);
1025 assert(IVAC_NULL != ivac);
1026 assert(IVAM_NULL != ivam);
1027
1028 ivac_lock(ivac);
1029 assert(value_index < ivac->ivac_table_size);
1030 ivace = &ivac->ivac_table[value_index];
1031
1032 assert(0 < ivace->ivace_refs);
1033
1034 /* cant release persistent values */
1035 if (ivace->ivace_persist) {
1036 ivac_unlock(ivac);
1037 return;
1038 }
1039
1040 if (0 < --ivace->ivace_refs) {
1041 ivac_unlock(ivac);
1042 return;
1043 }
1044
1045 key = iv_index_to_key(key_index);
1046 assert(MACH_VOUCHER_ATTR_KEY_NONE != key);
1047
1048 /*
1049 * if last return reply is still pending,
1050 * let it handle this later return when
1051 * the previous reply comes in.
1052 */
1053 if (ivace->ivace_releasing) {
1054 ivac_unlock(ivac);
1055 return;
1056 }
1057
1058 /* claim releasing */
1059 ivace->ivace_releasing = TRUE;
1060 value = ivace->ivace_value;
1061
1062 redrive:
1063 assert(value == ivace->ivace_value);
1064 assert(!ivace->ivace_free);
1065 made = ivace->ivace_made;
1066 ivac_unlock(ivac);
1067
1068 /* callout to manager's release_value */
1069 kr = (ivam->ivam_release_value)(ivam, key, value, made);
1070
1071 /* recalculate entry address as table may have changed */
1072 ivac_lock(ivac);
1073 ivace = &ivac->ivac_table[value_index];
1074 assert(value == ivace->ivace_value);
1075
1076 /*
1077 * new made values raced with this return. If the
1078 * manager OK'ed the prior release, we have to start
1079 * the made numbering over again (pretend the race
1080 * didn't happen). If the entry has zero refs again,
1081 * re-drive the release.
1082 */
1083 if (ivace->ivace_made != made) {
1084 assert(made < ivace->ivace_made);
1085
1086 if (KERN_SUCCESS == kr)
1087 ivace->ivace_made -= made;
1088
1089 if (0 == ivace->ivace_refs)
1090 goto redrive;
1091
1092 ivace->ivace_releasing = FALSE;
1093 ivac_unlock(ivac);
1094 return;
1095 } else {
1096 /*
1097 * If the manager returned FAILURE, someone took a
1098 * reference on the value but have not updated the ivace,
1099 * release the lock and return since thread who got
1100 * the new reference will update the ivace and will have
1101 * non-zero reference on the value.
1102 */
1103 if (KERN_SUCCESS != kr) {
1104 ivace->ivace_releasing = FALSE;
1105 ivac_unlock(ivac);
1106 return;
1107 }
1108 }
1109
1110 assert(0 == ivace->ivace_refs);
1111
1112 /*
1113 * going away - remove entry from its hash
1114 * If its at the head of the hash bucket list (common), unchain
1115 * at the head. Otherwise walk the chain until the next points
1116 * at this entry, and remove it from the the list there.
1117 */
1118 hash_index = iv_hash_value(key_index, value);
1119 if (ivac->ivac_table[hash_index].ivace_index == value_index) {
1120 ivac->ivac_table[hash_index].ivace_index = ivace->ivace_next;
1121 } else {
1122 hash_index = ivac->ivac_table[hash_index].ivace_index;
1123 assert(IV_HASH_END != hash_index);
1124 while (ivac->ivac_table[hash_index].ivace_next != value_index) {
1125 hash_index = ivac->ivac_table[hash_index].ivace_next;
1126 assert(IV_HASH_END != hash_index);
1127 }
1128 ivac->ivac_table[hash_index].ivace_next = ivace->ivace_next;
1129 }
1130
1131 /* Put this entry on the freelist */
1132 ivace->ivace_value = 0xdeadc0dedeadc0de;
1133 ivace->ivace_releasing = FALSE;
1134 ivace->ivace_free = TRUE;
1135 ivace->ivace_made = 0;
1136 ivace->ivace_next = ivac->ivac_freelist;
1137 ivac->ivac_freelist = value_index;
1138 ivac_unlock(ivac);
1139
1140 /* release the reference this value held on its cache control */
1141 ivac_release(ivac);
1142
1143 return;
1144 }
1145
1146
1147 /*
1148 * ivgt_looup
1149 *
1150 * Lookup an entry in the global table from the context of a manager
1151 * registration. Adds a reference to the control to keep the results
1152 * around (if needed).
1153 *
1154 * Because of the calling point, we can't be sure the manager is
1155 * [fully] registered yet. So, we must hold the global table lock
1156 * during the lookup to synchronize with in-parallel registrations
1157 * (and possible table growth).
1158 */
1159 static void
1160 ivgt_lookup(iv_index_t key_index,
1161 boolean_t take_reference,
1162 ipc_voucher_attr_manager_t *manager,
1163 ipc_voucher_attr_control_t *control)
1164 {
1165 ipc_voucher_attr_control_t ivac;
1166
1167 if (key_index < MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN) {
1168 ivgt_lock();
1169 if (NULL != manager)
1170 *manager = iv_global_table[key_index].ivgte_manager;
1171 ivac = iv_global_table[key_index].ivgte_control;
1172 if (IVAC_NULL != ivac) {
1173 assert(key_index == ivac->ivac_key_index);
1174 if (take_reference) {
1175 assert(NULL != control);
1176 ivac_reference(ivac);
1177 }
1178 }
1179 ivgt_unlock();
1180 if (NULL != control)
1181 *control = ivac;
1182 } else {
1183 if (NULL != manager)
1184 *manager = IVAM_NULL;
1185 if (NULL != control)
1186 *control = IVAC_NULL;
1187 }
1188 }
1189
1190 /*
1191 * Routine: ipc_replace_voucher_value
1192 * Purpose:
1193 * Replace the <voucher, key> value with the results of
1194 * running the supplied command through the resource
1195 * manager's get-value callback.
1196 * Conditions:
1197 * Nothing locked (may invoke user-space repeatedly).
1198 * Caller holds references on voucher and previous voucher.
1199 */
1200 static kern_return_t
1201 ipc_replace_voucher_value(
1202 ipc_voucher_t voucher,
1203 mach_voucher_attr_key_t key,
1204 mach_voucher_attr_recipe_command_t command,
1205 ipc_voucher_t prev_voucher,
1206 mach_voucher_attr_content_t content,
1207 mach_voucher_attr_content_size_t content_size)
1208 {
1209 mach_voucher_attr_value_handle_t previous_vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1210 mach_voucher_attr_value_handle_array_size_t previous_vals_count;
1211 mach_voucher_attr_value_handle_t new_value;
1212 mach_voucher_attr_value_flags_t new_flag;
1213 ipc_voucher_t new_value_voucher;
1214 ipc_voucher_attr_manager_t ivam;
1215 ipc_voucher_attr_control_t ivac;
1216 iv_index_t prev_val_index;
1217 iv_index_t save_val_index;
1218 iv_index_t val_index;
1219 iv_index_t key_index;
1220 kern_return_t kr;
1221
1222 /*
1223 * Get the manager for this key_index.
1224 * Returns a reference on the control.
1225 */
1226 key_index = iv_key_to_index(key);
1227 ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1228 if (IVAM_NULL == ivam)
1229 return KERN_INVALID_ARGUMENT;
1230
1231 /* save the current value stored in the forming voucher */
1232 save_val_index = iv_lookup(voucher, key_index);
1233
1234 /*
1235 * Get the previous value(s) for this key creation.
1236 * If a previous voucher is specified, they come from there.
1237 * Otherwise, they come from the intermediate values already
1238 * in the forming voucher.
1239 */
1240 prev_val_index = (IV_NULL != prev_voucher) ?
1241 iv_lookup(prev_voucher, key_index) :
1242 save_val_index;
1243 ivace_lookup_values(key_index, prev_val_index,
1244 previous_vals, &previous_vals_count);
1245
1246 /* Call out to resource manager to get new value */
1247 new_value_voucher = IV_NULL;
1248 kr = (ivam->ivam_get_value)(
1249 ivam, key, command,
1250 previous_vals, previous_vals_count,
1251 content, content_size,
1252 &new_value, &new_flag, &new_value_voucher);
1253 if (KERN_SUCCESS != kr) {
1254 ivac_release(ivac);
1255 return kr;
1256 }
1257
1258 /* TODO: value insertion from returned voucher */
1259 if (IV_NULL != new_value_voucher)
1260 iv_release(new_value_voucher);
1261
1262 /*
1263 * Find or create a slot in the table associated
1264 * with this attribute value. The ivac reference
1265 * is transferred to a new value, or consumed if
1266 * we find a matching existing value.
1267 */
1268 val_index = ivace_reference_by_value(ivac, new_value, new_flag);
1269 iv_set(voucher, key_index, val_index);
1270
1271 /*
1272 * release saved old value from the newly forming voucher
1273 * This is saved until the end to avoid churning the
1274 * release logic in cases where the same value is returned
1275 * as was there before.
1276 */
1277 ivace_release(key_index, save_val_index);
1278
1279 return KERN_SUCCESS;
1280 }
1281
1282 /*
1283 * Routine: ipc_directly_replace_voucher_value
1284 * Purpose:
1285 * Replace the <voucher, key> value with the value-handle
1286 * supplied directly by the attribute manager.
1287 * Conditions:
1288 * Nothing locked.
1289 * Caller holds references on voucher.
1290 * A made reference to the value-handle is donated by the caller.
1291 */
1292 static kern_return_t
1293 ipc_directly_replace_voucher_value(
1294 ipc_voucher_t voucher,
1295 mach_voucher_attr_key_t key,
1296 mach_voucher_attr_value_handle_t new_value)
1297 {
1298 ipc_voucher_attr_manager_t ivam;
1299 ipc_voucher_attr_control_t ivac;
1300 iv_index_t save_val_index;
1301 iv_index_t val_index;
1302 iv_index_t key_index;
1303
1304 /*
1305 * Get the manager for this key_index.
1306 * Returns a reference on the control.
1307 */
1308 key_index = iv_key_to_index(key);
1309 ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1310 if (IVAM_NULL == ivam)
1311 return KERN_INVALID_ARGUMENT;
1312
1313 /* save the current value stored in the forming voucher */
1314 save_val_index = iv_lookup(voucher, key_index);
1315
1316 /*
1317 * Find or create a slot in the table associated
1318 * with this attribute value. The ivac reference
1319 * is transferred to a new value, or consumed if
1320 * we find a matching existing value.
1321 */
1322 val_index = ivace_reference_by_value(ivac, new_value,
1323 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE);
1324 iv_set(voucher, key_index, val_index);
1325
1326 /*
1327 * release saved old value from the newly forming voucher
1328 * This is saved until the end to avoid churning the
1329 * release logic in cases where the same value is returned
1330 * as was there before.
1331 */
1332 ivace_release(key_index, save_val_index);
1333
1334 return KERN_SUCCESS;
1335 }
1336
1337 static kern_return_t
1338 ipc_execute_voucher_recipe_command(
1339 ipc_voucher_t voucher,
1340 mach_voucher_attr_key_t key,
1341 mach_voucher_attr_recipe_command_t command,
1342 ipc_voucher_t prev_iv,
1343 mach_voucher_attr_content_t content,
1344 mach_voucher_attr_content_size_t content_size,
1345 boolean_t key_priv)
1346 {
1347 iv_index_t prev_val_index;
1348 iv_index_t val_index;
1349 kern_return_t kr;
1350
1351 switch (command) {
1352
1353 /*
1354 * MACH_VOUCHER_ATTR_COPY
1355 * Copy the attribute(s) from the previous voucher to the new
1356 * one. A wildcard key is an acceptable value - indicating a
1357 * desire to copy all the attribute values from the previous
1358 * voucher.
1359 */
1360 case MACH_VOUCHER_ATTR_COPY:
1361
1362 /* no recipe data on a copy */
1363 if (0 < content_size)
1364 return KERN_INVALID_ARGUMENT;
1365
1366 /* nothing to copy from? - done */
1367 if (IV_NULL == prev_iv)
1368 return KERN_SUCCESS;
1369
1370 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1371 iv_index_t limit, j;
1372
1373 /* reconcile possible difference in voucher sizes */
1374 limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1375 prev_iv->iv_table_size :
1376 voucher->iv_table_size;
1377
1378 /* wildcard matching */
1379 for (j = 0; j < limit; j++) {
1380 /* release old value being replaced */
1381 val_index = iv_lookup(voucher, j);
1382 ivace_release(j, val_index);
1383
1384 /* replace with reference to prev voucher's value */
1385 prev_val_index = iv_lookup(prev_iv, j);
1386 ivace_reference_by_index(j, prev_val_index);
1387 iv_set(voucher, j, prev_val_index);
1388 }
1389 } else {
1390 iv_index_t key_index;
1391
1392 /* copy just one key */
1393 key_index = iv_key_to_index(key);
1394 if (ivgt_keys_in_use < key_index)
1395 return KERN_INVALID_ARGUMENT;
1396
1397 /* release old value being replaced */
1398 val_index = iv_lookup(voucher, key_index);
1399 ivace_release(key_index, val_index);
1400
1401 /* replace with reference to prev voucher's value */
1402 prev_val_index = iv_lookup(prev_iv, key_index);
1403 ivace_reference_by_index(key_index, prev_val_index);
1404 iv_set(voucher, key_index, prev_val_index);
1405 }
1406 break;
1407
1408 /*
1409 * MACH_VOUCHER_ATTR_REMOVE
1410 * Remove the attribute(s) from the under construction voucher.
1411 * A wildcard key is an acceptable value - indicating a desire
1412 * to remove all the attribute values set up so far in the voucher.
1413 * If a previous voucher is specified, only remove the value it
1414 * it matches the value in the previous voucher.
1415 */
1416 case MACH_VOUCHER_ATTR_REMOVE:
1417 /* no recipe data on a remove */
1418 if (0 < content_size)
1419 return KERN_INVALID_ARGUMENT;
1420
1421 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1422 iv_index_t limit, j;
1423
1424 /* reconcile possible difference in voucher sizes */
1425 limit = (IV_NULL == prev_iv) ? voucher->iv_table_size :
1426 ((prev_iv->iv_table_size < voucher->iv_table_size) ?
1427 prev_iv->iv_table_size : voucher->iv_table_size);
1428
1429 /* wildcard matching */
1430 for (j = 0; j < limit; j++) {
1431 val_index = iv_lookup(voucher, j);
1432
1433 /* If not matched in previous, skip */
1434 if (IV_NULL != prev_iv) {
1435 prev_val_index = iv_lookup(prev_iv, j);
1436 if (val_index != prev_val_index)
1437 continue;
1438 }
1439 /* release and clear */
1440 ivace_release(j, val_index);
1441 iv_set(voucher, j, IV_UNUSED_VALINDEX);
1442 }
1443 } else {
1444 iv_index_t key_index;
1445
1446 /* copy just one key */
1447 key_index = iv_key_to_index(key);
1448 if (ivgt_keys_in_use < key_index)
1449 return KERN_INVALID_ARGUMENT;
1450
1451 val_index = iv_lookup(voucher, key_index);
1452
1453 /* If not matched in previous, skip */
1454 if (IV_NULL != prev_iv) {
1455 prev_val_index = iv_lookup(prev_iv, key_index);
1456 if (val_index != prev_val_index)
1457 break;
1458 }
1459
1460 /* release and clear */
1461 ivace_release(key_index, val_index);
1462 iv_set(voucher, key_index, IV_UNUSED_VALINDEX);
1463 }
1464 break;
1465
1466 /*
1467 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1468 * Use key-privilege to set a value handle for the attribute directly,
1469 * rather than triggering a callback into the attribute manager to
1470 * interpret a recipe to generate the value handle.
1471 */
1472 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE:
1473 if (key_priv) {
1474 mach_voucher_attr_value_handle_t new_value;
1475
1476 if (sizeof(mach_voucher_attr_value_handle_t) != content_size)
1477 return KERN_INVALID_ARGUMENT;
1478
1479 new_value = *(mach_voucher_attr_value_handle_t *)(void *)content;
1480 kr = ipc_directly_replace_voucher_value(voucher,
1481 key,
1482 new_value);
1483 if (KERN_SUCCESS != kr)
1484 return kr;
1485 } else
1486 return KERN_INVALID_CAPABILITY;
1487 break;
1488
1489 /*
1490 * MACH_VOUCHER_ATTR_REDEEM
1491 * Redeem the attribute(s) from the previous voucher for a possibly
1492 * new value in the new voucher. A wildcard key is an acceptable value,
1493 * indicating a desire to redeem all the values.
1494 */
1495 case MACH_VOUCHER_ATTR_REDEEM:
1496
1497 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1498 iv_index_t limit, j;
1499
1500 /* reconcile possible difference in voucher sizes */
1501 if (IV_NULL != prev_iv)
1502 limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1503 prev_iv->iv_table_size :
1504 voucher->iv_table_size;
1505 else
1506 limit = voucher->iv_table_size;
1507
1508 /* wildcard matching */
1509 for (j = 0; j < limit; j++) {
1510 mach_voucher_attr_key_t j_key;
1511
1512 j_key = iv_index_to_key(j);
1513
1514 /* skip non-existent managers */
1515 if (MACH_VOUCHER_ATTR_KEY_NONE == j_key)
1516 continue;
1517
1518 /* get the new value from redeem (skip empty previous) */
1519 kr = ipc_replace_voucher_value(voucher,
1520 j_key,
1521 command,
1522 prev_iv,
1523 content,
1524 content_size);
1525 if (KERN_SUCCESS != kr)
1526 return kr;
1527 }
1528 break;
1529 }
1530 /* fall thru for single key redemption */
1531
1532 /*
1533 * DEFAULT:
1534 * Replace the current value for the <voucher, key> pair with whatever
1535 * value the resource manager returns for the command and recipe
1536 * combination provided.
1537 */
1538 default:
1539 kr = ipc_replace_voucher_value(voucher,
1540 key,
1541 command,
1542 prev_iv,
1543 content,
1544 content_size);
1545 if (KERN_SUCCESS != kr)
1546 return kr;
1547
1548 break;
1549 }
1550 return KERN_SUCCESS;
1551 }
1552
1553 /*
1554 * Routine: iv_checksum
1555 * Purpose:
1556 * Compute the voucher sum. This is more position-
1557 * relevant than many other checksums - important for
1558 * vouchers (arrays of low, oft-reused, indexes).
1559 */
1560 static inline iv_index_t
1561 iv_checksum(ipc_voucher_t voucher, boolean_t *emptyp)
1562 {
1563 iv_index_t c = 0;
1564
1565 boolean_t empty = TRUE;
1566 if (0 < voucher->iv_table_size) {
1567 iv_index_t i = voucher->iv_table_size - 1;
1568
1569 do {
1570 iv_index_t v = voucher->iv_table[i];
1571 c = c << 3 | c >> (32 - 3); /* rotate */
1572 c = ~c; /* invert */
1573 if (0 < v) {
1574 c += v; /* add in */
1575 empty = FALSE;
1576 }
1577 } while (0 < i--);
1578 }
1579 *emptyp = empty;
1580 return c;
1581 }
1582
1583 /*
1584 * Routine: iv_dedup
1585 * Purpose:
1586 * See if the set of values represented by this new voucher
1587 * already exist in another voucher. If so return a reference
1588 * to the existing voucher and deallocate the voucher provided.
1589 * Otherwise, insert this one in the hash and return it.
1590 * Conditions:
1591 * A voucher reference is donated on entry.
1592 * Returns:
1593 * A voucher reference (may be different than on entry).
1594 */
1595 static ipc_voucher_t
1596 iv_dedup(ipc_voucher_t new_iv)
1597 {
1598 boolean_t empty;
1599 iv_index_t sum;
1600 iv_index_t hash;
1601 ipc_voucher_t iv;
1602
1603 sum = iv_checksum(new_iv, &empty);
1604
1605 /* If all values are default, that's the empty (NULL) voucher */
1606 if (empty) {
1607 iv_dealloc(new_iv, FALSE);
1608 return IV_NULL;
1609 }
1610
1611 hash = IV_HASH_BUCKET(sum);
1612
1613 ivht_lock();
1614 queue_iterate(&ivht_bucket[hash], iv, ipc_voucher_t, iv_hash_link) {
1615 assert(iv->iv_hash == hash);
1616
1617 /* if not already deallocating and sums match... */
1618 if (0 < iv->iv_refs && iv->iv_sum == sum) {
1619 iv_refs_t refs;
1620 iv_index_t i;
1621
1622 assert(iv->iv_table_size <= new_iv->iv_table_size);
1623
1624 /* and common entries match... */
1625 for (i = 0; i < iv->iv_table_size; i++)
1626 if (iv->iv_table[i] != new_iv->iv_table[i])
1627 break;
1628 if (i < iv->iv_table_size)
1629 continue;
1630
1631 /* and all extra entries in new one are unused... */
1632 while (i < new_iv->iv_table_size)
1633 if (new_iv->iv_table[i++] != IV_UNUSED_VALINDEX)
1634 break;
1635 if (i < new_iv->iv_table_size)
1636 continue;
1637
1638 /* ... we found a match... */
1639
1640 /* can we get a ref before it hits 0
1641 *
1642 * This is thread safe. The reference is just an atomic
1643 * add. If the reference count is zero when we adjust it,
1644 * no other thread can have a reference to the voucher.
1645 * The dealloc code requires holding the ivht_lock, so
1646 * the voucher cannot be yanked out from under us.
1647 */
1648 refs = iv_reference(iv);
1649 if (1 == refs) {
1650 /* drats! going away. Put back to zero */
1651 iv->iv_refs = 0;
1652 continue;
1653 }
1654
1655 ivht_unlock();
1656
1657 /* referenced previous, so deallocate the new one */
1658 iv_dealloc(new_iv, FALSE);
1659 return iv;
1660 }
1661 }
1662
1663 /* add the new voucher to the hash, and return it */
1664 new_iv->iv_sum = sum;
1665 new_iv->iv_hash = hash;
1666 queue_enter(&ivht_bucket[hash], new_iv, ipc_voucher_t, iv_hash_link);
1667 ivht_count++;
1668 ivht_unlock();
1669
1670 /*
1671 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1672 */
1673 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1674 if (kdebug_enable & ~KDEBUG_ENABLE_PPT) {
1675 uintptr_t voucher_addr = VM_KERNEL_ADDRPERM((uintptr_t)new_iv);
1676 uintptr_t attr_tracepoints_needed = 0;
1677
1678 if (ipc_voucher_trace_contents) {
1679 /*
1680 * voucher_contents sizing is a bit more constrained
1681 * than might be obvious.
1682 *
1683 * This is typically a uint8_t typed array. However,
1684 * we want to access it as a uintptr_t to efficiently
1685 * copyout the data in tracepoints.
1686 *
1687 * This constrains the size to uintptr_t bytes, and
1688 * adds a minimimum alignment requirement equivalent
1689 * to a uintptr_t.
1690 *
1691 * Further constraining the size is the fact that it
1692 * is copied out 4 uintptr_t chunks at a time. We do
1693 * NOT want to run off the end of the array and copyout
1694 * random stack data.
1695 *
1696 * So the minimum size is 4 * sizeof(uintptr_t), and
1697 * the minimum alignment is uintptr_t aligned.
1698 */
1699
1700 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1701 #define PAYLOAD_SIZE 1024
1702
1703 _Static_assert(PAYLOAD_SIZE % PAYLOAD_PER_TRACEPOINT == 0, "size invariant violated");
1704
1705 mach_voucher_attr_raw_recipe_array_size_t payload_size = PAYLOAD_SIZE;
1706 uintptr_t payload[PAYLOAD_SIZE / sizeof(uintptr_t)];
1707 kern_return_t kr;
1708
1709 kr = mach_voucher_extract_all_attr_recipes(new_iv, (mach_voucher_attr_raw_recipe_array_t)payload, &payload_size);
1710 if (KERN_SUCCESS == kr) {
1711 attr_tracepoints_needed = (payload_size + PAYLOAD_PER_TRACEPOINT - 1) / PAYLOAD_PER_TRACEPOINT;
1712
1713 /*
1714 * To prevent leaking data from the stack, we
1715 * need to zero data to the end of a tracepoint
1716 * payload.
1717 */
1718 size_t remainder = payload_size % PAYLOAD_PER_TRACEPOINT;
1719 if (remainder) {
1720 bzero((uint8_t*)payload + payload_size,
1721 PAYLOAD_PER_TRACEPOINT - remainder);
1722 }
1723 }
1724
1725 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
1726 voucher_addr,
1727 new_iv->iv_table_size, ivht_count, payload_size, 0);
1728
1729 uintptr_t index = 0;
1730 while (attr_tracepoints_needed--) {
1731 KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE_ATTR_DATA) | DBG_FUNC_NONE,
1732 payload[index],
1733 payload[index+1],
1734 payload[index+2],
1735 payload[index+3],
1736 voucher_addr);
1737 index += 4;
1738 }
1739 } else {
1740 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
1741 voucher_addr,
1742 new_iv->iv_table_size, ivht_count, 0, 0);
1743 }
1744 }
1745 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1746
1747 return new_iv;
1748 }
1749
1750 /*
1751 * Routine: ipc_create_mach_voucher
1752 * Purpose:
1753 * Create a new mach voucher and initialize it with the
1754 * value(s) created by having the appropriate resource
1755 * managers interpret the supplied recipe commands and
1756 * data.
1757 * Conditions:
1758 * Nothing locked (may invoke user-space repeatedly).
1759 * Caller holds references on previous vouchers.
1760 * Previous vouchers are passed as voucher indexes.
1761 */
1762 kern_return_t
1763 ipc_create_mach_voucher(
1764 ipc_voucher_attr_raw_recipe_array_t recipes,
1765 ipc_voucher_attr_raw_recipe_array_size_t recipe_size,
1766 ipc_voucher_t *new_voucher)
1767 {
1768 ipc_voucher_attr_recipe_t sub_recipe;
1769 ipc_voucher_attr_recipe_size_t recipe_used = 0;
1770 ipc_voucher_t voucher;
1771 kern_return_t kr = KERN_SUCCESS;
1772
1773 /* if nothing to do ... */
1774 if (0 == recipe_size) {
1775 *new_voucher = IV_NULL;
1776 return KERN_SUCCESS;
1777 }
1778
1779 /* allocate a voucher */
1780 voucher = iv_alloc(ivgt_keys_in_use);
1781 if (IV_NULL == voucher)
1782 return KERN_RESOURCE_SHORTAGE;
1783
1784 /* iterate over the recipe items */
1785 while (0 < recipe_size - recipe_used) {
1786
1787 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1788 kr = KERN_INVALID_ARGUMENT;
1789 break;
1790 }
1791
1792 /* find the next recipe */
1793 sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1794 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1795 kr = KERN_INVALID_ARGUMENT;
1796 break;
1797 }
1798 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1799
1800 kr = ipc_execute_voucher_recipe_command(voucher,
1801 sub_recipe->key,
1802 sub_recipe->command,
1803 sub_recipe->previous_voucher,
1804 sub_recipe->content,
1805 sub_recipe->content_size,
1806 FALSE);
1807 if (KERN_SUCCESS != kr)
1808 break;
1809 }
1810
1811 if (KERN_SUCCESS == kr) {
1812 *new_voucher = iv_dedup(voucher);
1813 } else {
1814 iv_dealloc(voucher, FALSE);
1815 *new_voucher = IV_NULL;
1816 }
1817 return kr;
1818 }
1819
1820 /*
1821 * Routine: ipc_voucher_attr_control_create_mach_voucher
1822 * Purpose:
1823 * Create a new mach voucher and initialize it with the
1824 * value(s) created by having the appropriate resource
1825 * managers interpret the supplied recipe commands and
1826 * data.
1827 *
1828 * The resource manager control's privilege over its
1829 * particular key value is reflected on to the execution
1830 * code, allowing internal commands (like setting a
1831 * key value handle directly, rather than having to
1832 * create a recipe, that will generate a callback just
1833 * to get the value.
1834 *
1835 * Conditions:
1836 * Nothing locked (may invoke user-space repeatedly).
1837 * Caller holds references on previous vouchers.
1838 * Previous vouchers are passed as voucher indexes.
1839 */
1840 kern_return_t
1841 ipc_voucher_attr_control_create_mach_voucher(
1842 ipc_voucher_attr_control_t control,
1843 ipc_voucher_attr_raw_recipe_array_t recipes,
1844 ipc_voucher_attr_raw_recipe_array_size_t recipe_size,
1845 ipc_voucher_t *new_voucher)
1846 {
1847 mach_voucher_attr_key_t control_key;
1848 ipc_voucher_attr_recipe_t sub_recipe;
1849 ipc_voucher_attr_recipe_size_t recipe_used = 0;
1850 ipc_voucher_t voucher = IV_NULL;
1851 kern_return_t kr = KERN_SUCCESS;
1852
1853 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
1854 return KERN_INVALID_CAPABILITY;
1855
1856 /* if nothing to do ... */
1857 if (0 == recipe_size) {
1858 *new_voucher = IV_NULL;
1859 return KERN_SUCCESS;
1860 }
1861
1862 /* allocate new voucher */
1863 voucher = iv_alloc(ivgt_keys_in_use);
1864 if (IV_NULL == voucher)
1865 return KERN_RESOURCE_SHORTAGE;
1866
1867 control_key = iv_index_to_key(control->ivac_key_index);
1868
1869 /* iterate over the recipe items */
1870 while (0 < recipe_size - recipe_used) {
1871
1872 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1873 kr = KERN_INVALID_ARGUMENT;
1874 break;
1875 }
1876
1877 /* find the next recipe */
1878 sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1879 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1880 kr = KERN_INVALID_ARGUMENT;
1881 break;
1882 }
1883 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1884
1885 kr = ipc_execute_voucher_recipe_command(voucher,
1886 sub_recipe->key,
1887 sub_recipe->command,
1888 sub_recipe->previous_voucher,
1889 sub_recipe->content,
1890 sub_recipe->content_size,
1891 (sub_recipe->key == control_key));
1892 if (KERN_SUCCESS != kr)
1893 break;
1894 }
1895
1896 if (KERN_SUCCESS == kr) {
1897 *new_voucher = iv_dedup(voucher);
1898 } else {
1899 *new_voucher = IV_NULL;
1900 iv_dealloc(voucher, FALSE);
1901 }
1902 return kr;
1903 }
1904
1905 /*
1906 * ipc_register_well_known_mach_voucher_attr_manager
1907 *
1908 * Register the resource manager responsible for a given key value.
1909 */
1910 kern_return_t
1911 ipc_register_well_known_mach_voucher_attr_manager(
1912 ipc_voucher_attr_manager_t manager,
1913 mach_voucher_attr_value_handle_t default_value,
1914 mach_voucher_attr_key_t key,
1915 ipc_voucher_attr_control_t *control)
1916 {
1917 ipc_voucher_attr_control_t new_control;
1918 iv_index_t key_index;
1919 iv_index_t hash_index;
1920
1921 if (IVAM_NULL == manager)
1922 return KERN_INVALID_ARGUMENT;
1923
1924 key_index = iv_key_to_index(key);
1925 if (IV_UNUSED_KEYINDEX == key_index)
1926 return KERN_INVALID_ARGUMENT;
1927
1928 new_control = ivac_alloc(key_index);
1929 if (IVAC_NULL == new_control)
1930 return KERN_RESOURCE_SHORTAGE;
1931
1932 /* insert the default value into slot 0 */
1933 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_value = default_value;
1934 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_refs = IVACE_REFS_MAX;
1935 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_made = IVACE_REFS_MAX;
1936 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_persist = TRUE;
1937 assert(IV_HASH_END == new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_next);
1938
1939 ivgt_lock();
1940 if (IVAM_NULL != iv_global_table[key_index].ivgte_manager) {
1941 ivgt_unlock();
1942 ivac_release(new_control);
1943 return KERN_INVALID_ARGUMENT;
1944 }
1945
1946 /* fill in the global table slot for this key */
1947 iv_global_table[key_index].ivgte_manager = manager;
1948 iv_global_table[key_index].ivgte_control = new_control;
1949 iv_global_table[key_index].ivgte_key = key;
1950
1951 /* insert the default value into the hash (in case it is returned later) */
1952 hash_index = iv_hash_value(key_index, default_value);
1953 assert(IV_HASH_END == new_control->ivac_table[hash_index].ivace_index);
1954 new_control->ivac_table[hash_index].ivace_index = IV_UNUSED_VALINDEX;
1955
1956 ivgt_unlock();
1957
1958 /* return the reference on the new cache control to the caller */
1959 *control = new_control;
1960
1961 return KERN_SUCCESS;
1962 }
1963
1964 /*
1965 * Routine: mach_voucher_extract_attr_content
1966 * Purpose:
1967 * Extract the content for a given <voucher, key> pair.
1968 *
1969 * If a value other than the default is present for this
1970 * <voucher,key> pair, we need to contact the resource
1971 * manager to extract the content/meaning of the value(s)
1972 * present. Otherwise, return success (but no data).
1973 *
1974 * Conditions:
1975 * Nothing locked - as it may upcall to user-space.
1976 * The caller holds a reference on the voucher.
1977 */
1978 kern_return_t
1979 mach_voucher_extract_attr_content(
1980 ipc_voucher_t voucher,
1981 mach_voucher_attr_key_t key,
1982 mach_voucher_attr_content_t content,
1983 mach_voucher_attr_content_size_t *in_out_size)
1984 {
1985 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1986 mach_voucher_attr_value_handle_array_size_t vals_count;
1987 mach_voucher_attr_recipe_command_t command;
1988 ipc_voucher_attr_manager_t manager;
1989 iv_index_t value_index;
1990 iv_index_t key_index;
1991 kern_return_t kr;
1992
1993
1994 if (IV_NULL == voucher)
1995 return KERN_INVALID_ARGUMENT;
1996
1997 key_index = iv_key_to_index(key);
1998
1999 value_index = iv_lookup(voucher, key_index);
2000 if (IV_UNUSED_VALINDEX == value_index) {
2001 *in_out_size = 0;
2002 return KERN_SUCCESS;
2003 }
2004
2005 /*
2006 * Get the manager for this key_index. The
2007 * existence of a non-default value for this
2008 * slot within our voucher will keep the
2009 * manager referenced during the callout.
2010 */
2011 ivgt_lookup(key_index, FALSE, &manager, NULL);
2012 if (IVAM_NULL == manager) {
2013 return KERN_INVALID_ARGUMENT;
2014 }
2015
2016 /*
2017 * Get the value(s) to pass to the manager
2018 * for this value_index.
2019 */
2020 ivace_lookup_values(key_index, value_index,
2021 vals, &vals_count);
2022 assert(0 < vals_count);
2023
2024 /* callout to manager */
2025
2026 kr = (manager->ivam_extract_content)(manager, key,
2027 vals, vals_count,
2028 &command,
2029 content, in_out_size);
2030 return kr;
2031 }
2032
2033 /*
2034 * Routine: mach_voucher_extract_attr_recipe
2035 * Purpose:
2036 * Extract a recipe for a given <voucher, key> pair.
2037 *
2038 * If a value other than the default is present for this
2039 * <voucher,key> pair, we need to contact the resource
2040 * manager to extract the content/meaning of the value(s)
2041 * present. Otherwise, return success (but no data).
2042 *
2043 * Conditions:
2044 * Nothing locked - as it may upcall to user-space.
2045 * The caller holds a reference on the voucher.
2046 */
2047 kern_return_t
2048 mach_voucher_extract_attr_recipe(
2049 ipc_voucher_t voucher,
2050 mach_voucher_attr_key_t key,
2051 mach_voucher_attr_raw_recipe_t raw_recipe,
2052 mach_voucher_attr_raw_recipe_size_t *in_out_size)
2053 {
2054 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2055 mach_voucher_attr_value_handle_array_size_t vals_count;
2056 ipc_voucher_attr_manager_t manager;
2057 mach_voucher_attr_recipe_t recipe;
2058 iv_index_t value_index;
2059 iv_index_t key_index;
2060 kern_return_t kr;
2061
2062
2063 if (IV_NULL == voucher)
2064 return KERN_INVALID_ARGUMENT;
2065
2066 key_index = iv_key_to_index(key);
2067
2068 value_index = iv_lookup(voucher, key_index);
2069 if (IV_UNUSED_VALINDEX == value_index) {
2070 *in_out_size = 0;
2071 return KERN_SUCCESS;
2072 }
2073
2074 if (*in_out_size < sizeof(*recipe))
2075 return KERN_NO_SPACE;
2076
2077 recipe = (mach_voucher_attr_recipe_t)(void *)raw_recipe;
2078 recipe->key = key;
2079 recipe->command = MACH_VOUCHER_ATTR_NOOP;
2080 recipe->previous_voucher = MACH_VOUCHER_NAME_NULL;
2081 recipe->content_size = *in_out_size - sizeof(*recipe);
2082
2083 /*
2084 * Get the manager for this key_index. The
2085 * existence of a non-default value for this
2086 * slot within our voucher will keep the
2087 * manager referenced during the callout.
2088 */
2089 ivgt_lookup(key_index, FALSE, &manager, NULL);
2090 if (IVAM_NULL == manager) {
2091 return KERN_INVALID_ARGUMENT;
2092 }
2093
2094 /*
2095 * Get the value(s) to pass to the manager
2096 * for this value_index.
2097 */
2098 ivace_lookup_values(key_index, value_index,
2099 vals, &vals_count);
2100 assert(0 < vals_count);
2101
2102 /* callout to manager */
2103 kr = (manager->ivam_extract_content)(manager, key,
2104 vals, vals_count,
2105 &recipe->command,
2106 recipe->content, &recipe->content_size);
2107 if (KERN_SUCCESS == kr) {
2108 assert(*in_out_size - sizeof(*recipe) >= recipe->content_size);
2109 *in_out_size = sizeof(*recipe) + recipe->content_size;
2110 }
2111
2112 return kr;
2113 }
2114
2115
2116
2117 /*
2118 * Routine: mach_voucher_extract_all_attr_recipes
2119 * Purpose:
2120 * Extract all the (non-default) contents for a given voucher,
2121 * building up a recipe that could be provided to a future
2122 * voucher creation call.
2123 * Conditions:
2124 * Nothing locked (may invoke user-space).
2125 * Caller holds a reference on the supplied voucher.
2126 */
2127 kern_return_t
2128 mach_voucher_extract_all_attr_recipes(
2129 ipc_voucher_t voucher,
2130 mach_voucher_attr_raw_recipe_array_t recipes,
2131 mach_voucher_attr_raw_recipe_array_size_t *in_out_size)
2132 {
2133 mach_voucher_attr_recipe_size_t recipe_size = *in_out_size;
2134 mach_voucher_attr_recipe_size_t recipe_used = 0;
2135 iv_index_t key_index;
2136
2137 if (IV_NULL == voucher)
2138 return KERN_INVALID_ARGUMENT;
2139
2140 for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
2141 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2142 mach_voucher_attr_value_handle_array_size_t vals_count;
2143 mach_voucher_attr_content_size_t content_size;
2144 ipc_voucher_attr_manager_t manager;
2145 mach_voucher_attr_recipe_t recipe;
2146 mach_voucher_attr_key_t key;
2147 iv_index_t value_index;
2148 kern_return_t kr;
2149
2150 /* don't output anything for a default value */
2151 value_index = iv_lookup(voucher, key_index);
2152 if (IV_UNUSED_VALINDEX == value_index)
2153 continue;
2154
2155 if (recipe_size - recipe_used < sizeof(*recipe))
2156 return KERN_NO_SPACE;
2157
2158 /*
2159 * Get the manager for this key_index. The
2160 * existence of a non-default value for this
2161 * slot within our voucher will keep the
2162 * manager referenced during the callout.
2163 */
2164 ivgt_lookup(key_index, FALSE, &manager, NULL);
2165 assert(IVAM_NULL != manager);
2166 if (IVAM_NULL == manager) {
2167 continue;
2168 }
2169
2170 recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2171 content_size = recipe_size - recipe_used - sizeof(*recipe);
2172
2173 /*
2174 * Get the value(s) to pass to the manager
2175 * for this value_index.
2176 */
2177 ivace_lookup_values(key_index, value_index,
2178 vals, &vals_count);
2179 assert(0 < vals_count);
2180
2181 key = iv_index_to_key(key_index);
2182
2183 recipe->key = key;
2184 recipe->command = MACH_VOUCHER_ATTR_NOOP;
2185 recipe->content_size = content_size;
2186
2187 /* callout to manager */
2188 kr = (manager->ivam_extract_content)(manager, key,
2189 vals, vals_count,
2190 &recipe->command,
2191 recipe->content, &recipe->content_size);
2192 if (KERN_SUCCESS != kr)
2193 return kr;
2194
2195 assert(recipe->content_size <= content_size);
2196 recipe_used += sizeof(*recipe) + recipe->content_size;
2197 }
2198
2199 *in_out_size = recipe_used;
2200 return KERN_SUCCESS;
2201 }
2202
2203 /*
2204 * Routine: mach_voucher_debug_info
2205 * Purpose:
2206 * Extract all the (non-default) contents for a given mach port name,
2207 * building up a recipe that could be provided to a future
2208 * voucher creation call.
2209 * Conditions:
2210 * Nothing locked (may invoke user-space).
2211 * Caller may not hold a reference on the supplied voucher.
2212 */
2213 #if !(DEVELOPMENT || DEBUG)
2214 kern_return_t
2215 mach_voucher_debug_info(
2216 ipc_space_t __unused space,
2217 mach_port_name_t __unused voucher_name,
2218 mach_voucher_attr_raw_recipe_array_t __unused recipes,
2219 mach_voucher_attr_raw_recipe_array_size_t __unused *in_out_size)
2220 {
2221 return KERN_NOT_SUPPORTED;
2222 }
2223 #else
2224 kern_return_t
2225 mach_voucher_debug_info(
2226 ipc_space_t space,
2227 mach_port_name_t voucher_name,
2228 mach_voucher_attr_raw_recipe_array_t recipes,
2229 mach_voucher_attr_raw_recipe_array_size_t *in_out_size)
2230 {
2231 ipc_voucher_t voucher = IPC_VOUCHER_NULL;
2232 kern_return_t kr;
2233 ipc_port_t port = MACH_PORT_NULL;
2234
2235 if (!MACH_PORT_VALID(voucher_name)) {
2236 return KERN_INVALID_ARGUMENT;
2237 }
2238
2239 kr = ipc_port_translate_send(space, voucher_name, &port);
2240 if (KERN_SUCCESS != kr)
2241 return KERN_INVALID_ARGUMENT;
2242
2243 voucher = convert_port_to_voucher(port);
2244 ip_unlock(port);
2245
2246 if (voucher) {
2247 kr = mach_voucher_extract_all_attr_recipes(voucher, recipes, in_out_size);
2248 ipc_voucher_release(voucher);
2249 return kr;
2250 }
2251
2252 return KERN_FAILURE;
2253 }
2254 #endif
2255
2256 /*
2257 * Routine: mach_voucher_attr_command
2258 * Purpose:
2259 * Invoke an attribute-specific command through this voucher.
2260 *
2261 * The voucher layout, membership, etc... is not altered
2262 * through the execution of this command.
2263 *
2264 * Conditions:
2265 * Nothing locked - as it may upcall to user-space.
2266 * The caller holds a reference on the voucher.
2267 */
2268 kern_return_t
2269 mach_voucher_attr_command(
2270 ipc_voucher_t voucher,
2271 mach_voucher_attr_key_t key,
2272 mach_voucher_attr_command_t command,
2273 mach_voucher_attr_content_t in_content,
2274 mach_voucher_attr_content_size_t in_content_size,
2275 mach_voucher_attr_content_t out_content,
2276 mach_voucher_attr_content_size_t *out_content_size)
2277 {
2278 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2279 mach_voucher_attr_value_handle_array_size_t vals_count;
2280 ipc_voucher_attr_manager_t manager;
2281 ipc_voucher_attr_control_t control;
2282 iv_index_t value_index;
2283 iv_index_t key_index;
2284 kern_return_t kr;
2285
2286
2287 if (IV_NULL == voucher)
2288 return KERN_INVALID_ARGUMENT;
2289
2290 key_index = iv_key_to_index(key);
2291
2292 /*
2293 * Get the manager for this key_index.
2294 * Allowing commands against the default value
2295 * for an attribute means that we have to hold
2296 * reference on the attribute manager control
2297 * to keep the manager around during the command
2298 * execution.
2299 */
2300 ivgt_lookup(key_index, TRUE, &manager, &control);
2301 if (IVAM_NULL == manager) {
2302 return KERN_INVALID_ARGUMENT;
2303 }
2304
2305 /*
2306 * Get the values for this <voucher, key> pair
2307 * to pass to the attribute manager. It is still
2308 * permissible to execute a command against the
2309 * default value (empty value array).
2310 */
2311 value_index = iv_lookup(voucher, key_index);
2312 ivace_lookup_values(key_index, value_index,
2313 vals, &vals_count);
2314
2315 /* callout to manager */
2316 kr = (manager->ivam_command)(manager, key,
2317 vals, vals_count,
2318 command,
2319 in_content, in_content_size,
2320 out_content, out_content_size);
2321
2322 /* release reference on control */
2323 ivac_release(control);
2324
2325 return kr;
2326 }
2327
2328 /*
2329 * Routine: mach_voucher_attr_control_get_values
2330 * Purpose:
2331 * For a given voucher, get the value handle associated with the
2332 * specified attribute manager.
2333 */
2334 kern_return_t
2335 mach_voucher_attr_control_get_values(
2336 ipc_voucher_attr_control_t control,
2337 ipc_voucher_t voucher,
2338 mach_voucher_attr_value_handle_array_t out_values,
2339 mach_voucher_attr_value_handle_array_size_t *in_out_size)
2340 {
2341 iv_index_t key_index, value_index;
2342
2343 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
2344 return KERN_INVALID_CAPABILITY;
2345
2346 if (IV_NULL == voucher)
2347 return KERN_INVALID_ARGUMENT;
2348
2349 if (0 == *in_out_size)
2350 return KERN_SUCCESS;
2351
2352 key_index = control->ivac_key_index;
2353
2354 assert(0 < voucher->iv_refs);
2355 value_index = iv_lookup(voucher, key_index);
2356 ivace_lookup_values(key_index, value_index,
2357 out_values, in_out_size);
2358 return KERN_SUCCESS;
2359 }
2360
2361 /*
2362 * Routine: mach_voucher_attr_control_create_mach_voucher
2363 * Purpose:
2364 * Create a new mach voucher and initialize it by processing the
2365 * supplied recipe(s).
2366 *
2367 * Coming in on the attribute control port denotes special privileges
2368 * over they key associated with the control port.
2369 *
2370 * Coming in from user-space, each recipe item will have a previous
2371 * recipe port name that needs to be converted to a voucher. Because
2372 * we can't rely on the port namespace to hold a reference on each
2373 * previous voucher port for the duration of processing that command,
2374 * we have to convert the name to a voucher reference and release it
2375 * after the command processing is done.
2376 */
2377 kern_return_t
2378 mach_voucher_attr_control_create_mach_voucher(
2379 ipc_voucher_attr_control_t control,
2380 mach_voucher_attr_raw_recipe_array_t recipes,
2381 mach_voucher_attr_raw_recipe_size_t recipe_size,
2382 ipc_voucher_t *new_voucher)
2383 {
2384 mach_voucher_attr_key_t control_key;
2385 mach_voucher_attr_recipe_t sub_recipe;
2386 mach_voucher_attr_recipe_size_t recipe_used = 0;
2387 ipc_voucher_t voucher = IV_NULL;
2388 kern_return_t kr = KERN_SUCCESS;
2389
2390 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
2391 return KERN_INVALID_CAPABILITY;
2392
2393 /* if nothing to do ... */
2394 if (0 == recipe_size) {
2395 *new_voucher = IV_NULL;
2396 return KERN_SUCCESS;
2397 }
2398
2399 /* allocate new voucher */
2400 voucher = iv_alloc(ivgt_keys_in_use);
2401 if (IV_NULL == voucher)
2402 return KERN_RESOURCE_SHORTAGE;
2403
2404 control_key = iv_index_to_key(control->ivac_key_index);
2405
2406 /* iterate over the recipe items */
2407 while (0 < recipe_size - recipe_used) {
2408 ipc_voucher_t prev_iv;
2409
2410 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2411 kr = KERN_INVALID_ARGUMENT;
2412 break;
2413 }
2414
2415 /* find the next recipe */
2416 sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2417 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2418 kr = KERN_INVALID_ARGUMENT;
2419 break;
2420 }
2421 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2422
2423 /* convert voucher port name (current space) into a voucher reference */
2424 prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2425 if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2426 kr = KERN_INVALID_CAPABILITY;
2427 break;
2428 }
2429
2430 kr = ipc_execute_voucher_recipe_command(voucher,
2431 sub_recipe->key,
2432 sub_recipe->command,
2433 prev_iv,
2434 sub_recipe->content,
2435 sub_recipe->content_size,
2436 (sub_recipe->key == control_key));
2437 ipc_voucher_release(prev_iv);
2438
2439 if (KERN_SUCCESS != kr)
2440 break;
2441 }
2442
2443 if (KERN_SUCCESS == kr) {
2444 *new_voucher = iv_dedup(voucher);
2445 } else {
2446 *new_voucher = IV_NULL;
2447 iv_dealloc(voucher, FALSE);
2448 }
2449 return kr;
2450 }
2451
2452 /*
2453 * Routine: host_create_mach_voucher
2454 * Purpose:
2455 * Create a new mach voucher and initialize it by processing the
2456 * supplied recipe(s).
2457 *
2458 * Comming in from user-space, each recipe item will have a previous
2459 * recipe port name that needs to be converted to a voucher. Because
2460 * we can't rely on the port namespace to hold a reference on each
2461 * previous voucher port for the duration of processing that command,
2462 * we have to convert the name to a voucher reference and release it
2463 * after the command processing is done.
2464 */
2465 kern_return_t
2466 host_create_mach_voucher(
2467 host_t host,
2468 mach_voucher_attr_raw_recipe_array_t recipes,
2469 mach_voucher_attr_raw_recipe_size_t recipe_size,
2470 ipc_voucher_t *new_voucher)
2471 {
2472 mach_voucher_attr_recipe_t sub_recipe;
2473 mach_voucher_attr_recipe_size_t recipe_used = 0;
2474 ipc_voucher_t voucher = IV_NULL;
2475 kern_return_t kr = KERN_SUCCESS;
2476
2477 if (host == HOST_NULL)
2478 return KERN_INVALID_ARGUMENT;
2479
2480 /* if nothing to do ... */
2481 if (0 == recipe_size) {
2482 *new_voucher = IV_NULL;
2483 return KERN_SUCCESS;
2484 }
2485
2486 /* allocate new voucher */
2487 voucher = iv_alloc(ivgt_keys_in_use);
2488 if (IV_NULL == voucher)
2489 return KERN_RESOURCE_SHORTAGE;
2490
2491 /* iterate over the recipe items */
2492 while (0 < recipe_size - recipe_used) {
2493 ipc_voucher_t prev_iv;
2494
2495 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2496 kr = KERN_INVALID_ARGUMENT;
2497 break;
2498 }
2499
2500 /* find the next recipe */
2501 sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2502 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2503 kr = KERN_INVALID_ARGUMENT;
2504 break;
2505 }
2506 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2507
2508 /* convert voucher port name (current space) into a voucher reference */
2509 prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2510 if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2511 kr = KERN_INVALID_CAPABILITY;
2512 break;
2513 }
2514
2515 kr = ipc_execute_voucher_recipe_command(voucher,
2516 sub_recipe->key,
2517 sub_recipe->command,
2518 prev_iv,
2519 sub_recipe->content,
2520 sub_recipe->content_size,
2521 FALSE);
2522 ipc_voucher_release(prev_iv);
2523
2524 if (KERN_SUCCESS != kr)
2525 break;
2526 }
2527
2528 if (KERN_SUCCESS == kr) {
2529 *new_voucher = iv_dedup(voucher);
2530 } else {
2531 *new_voucher = IV_NULL;
2532 iv_dealloc(voucher, FALSE);
2533 }
2534 return kr;
2535 }
2536
2537 /*
2538 * Routine: host_register_well_known_mach_voucher_attr_manager
2539 * Purpose:
2540 * Register the user-level resource manager responsible for a given
2541 * key value.
2542 * Conditions:
2543 * The manager port passed in has to be converted/wrapped
2544 * in an ipc_voucher_attr_manager_t structure and then call the
2545 * internal variant. We have a generic ipc voucher manager
2546 * type that implements a MIG proxy out to user-space just for
2547 * this purpose.
2548 */
2549 kern_return_t
2550 host_register_well_known_mach_voucher_attr_manager(
2551 host_t host,
2552 mach_voucher_attr_manager_t __unused manager,
2553 mach_voucher_attr_value_handle_t __unused default_value,
2554 mach_voucher_attr_key_t __unused key,
2555 ipc_voucher_attr_control_t __unused *control)
2556 {
2557 if (HOST_NULL == host)
2558 return KERN_INVALID_HOST;
2559
2560 #if 1
2561 return KERN_NOT_SUPPORTED;
2562 #else
2563 /*
2564 * Allocate a mig_voucher_attr_manager_t that provides the
2565 * MIG proxy functions for the three manager callbacks and
2566 * store the port right in there.
2567 *
2568 * If the user-space manager dies, we'll detect it on our
2569 * next upcall, and cleanup the proxy at that point.
2570 */
2571 mig_voucher_attr_manager_t proxy;
2572 kern_return_t kr;
2573
2574 proxy = mvam_alloc(manager);
2575
2576 kr = ipc_register_well_known_mach_voucher_attr_manager(&proxy->mvam_manager,
2577 default_value,
2578 key,
2579 control);
2580 if (KERN_SUCCESS != kr)
2581 mvam_release(proxy);
2582
2583 return kr;
2584 #endif
2585 }
2586
2587 /*
2588 * Routine: host_register_mach_voucher_attr_manager
2589 * Purpose:
2590 * Register the user-space resource manager and return a
2591 * dynamically allocated key.
2592 * Conditions:
2593 * Wrap the supplied port with the MIG proxy ipc
2594 * voucher resource manager, and then call the internal
2595 * variant.
2596 */
2597 kern_return_t
2598 host_register_mach_voucher_attr_manager(
2599 host_t host,
2600 mach_voucher_attr_manager_t __unused manager,
2601 mach_voucher_attr_value_handle_t __unused default_value,
2602 mach_voucher_attr_key_t __unused *key,
2603 ipc_voucher_attr_control_t __unused *control)
2604 {
2605 if (HOST_NULL == host)
2606 return KERN_INVALID_HOST;
2607
2608 return KERN_NOT_SUPPORTED;
2609 }
2610
2611 /*
2612 * Routine: ipc_voucher_send_preprocessing
2613 * Purpose:
2614 * Processing of the voucher in the kmsg before sending it.
2615 * Currently use to switch PERSONA_TOKEN in case of process with
2616 * no com.apple.private.personas.propagate entitlement.
2617 */
2618 void
2619 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg)
2620 {
2621 uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
2622 ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
2623 sizeof(ipc_voucher_attr_recipe_data_t);
2624 ipc_voucher_t pre_processed_voucher;
2625 ipc_voucher_t voucher_to_send;
2626 kern_return_t kr;
2627 int need_preprocessing = FALSE;
2628
2629 if (!IP_VALID(kmsg->ikm_voucher) || current_task() == kernel_task) {
2630 return;
2631 }
2632
2633 /* setup recipe for preprocessing of all the attributes. */
2634 pre_processed_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
2635
2636 kr = ipc_voucher_prepare_processing_recipe(pre_processed_voucher,
2637 (mach_voucher_attr_raw_recipe_array_t)recipes,
2638 &recipe_size, MACH_VOUCHER_ATTR_SEND_PREPROCESS,
2639 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS, &need_preprocessing);
2640
2641 assert(KERN_SUCCESS == kr);
2642 /*
2643 * Only do send preprocessing if the voucher needs any pre processing.
2644 */
2645 if (need_preprocessing) {
2646 kr = ipc_create_mach_voucher(recipes,
2647 recipe_size,
2648 &voucher_to_send);
2649 assert(KERN_SUCCESS == kr);
2650 ipc_port_release_send(kmsg->ikm_voucher);
2651 kmsg->ikm_voucher = convert_voucher_to_port(voucher_to_send);
2652 }
2653 }
2654
2655 /*
2656 * Routine: ipc_voucher_receive_postprocessing
2657 * Purpose:
2658 * Redeems the voucher attached to the kmsg.
2659 * Note:
2660 * Although it is possible to call ipc_importance_receive
2661 * here, it is called in mach_msg_receive_results and not here
2662 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2663 */
2664 void
2665 ipc_voucher_receive_postprocessing(
2666 ipc_kmsg_t kmsg,
2667 mach_msg_option_t option)
2668 {
2669 uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
2670 ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
2671 sizeof(ipc_voucher_attr_recipe_data_t);
2672 ipc_voucher_t recv_voucher;
2673 ipc_voucher_t sent_voucher;
2674 kern_return_t kr;
2675 int need_postprocessing = FALSE;
2676
2677 if ((option & MACH_RCV_VOUCHER) == 0 || (!IP_VALID(kmsg->ikm_voucher)) ||
2678 current_task() == kernel_task) {
2679 return;
2680 }
2681
2682 /* setup recipe for auto redeem of all the attributes. */
2683 sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
2684
2685 kr = ipc_voucher_prepare_processing_recipe(sent_voucher,
2686 (mach_voucher_attr_raw_recipe_array_t)recipes,
2687 &recipe_size, MACH_VOUCHER_ATTR_AUTO_REDEEM,
2688 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS, &need_postprocessing);
2689
2690 assert(KERN_SUCCESS == kr);
2691
2692 /*
2693 * Only do receive postprocessing if the voucher needs any post processing.
2694 */
2695 if (need_postprocessing) {
2696 kr = ipc_create_mach_voucher(recipes,
2697 recipe_size,
2698 &recv_voucher);
2699 assert(KERN_SUCCESS == kr);
2700 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2701 kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
2702 ipc_port_release_send(kmsg->ikm_voucher);
2703 kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
2704 }
2705 }
2706
2707 /*
2708 * Routine: ipc_voucher_prepare_processing_recipe
2709 * Purpose:
2710 * Check if the given voucher has an attribute which supports
2711 * the given flag and prepare a recipe to apply that supported
2712 * command.
2713 */
2714 static kern_return_t
2715 ipc_voucher_prepare_processing_recipe(
2716 ipc_voucher_t voucher,
2717 ipc_voucher_attr_raw_recipe_array_t recipes,
2718 ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
2719 mach_voucher_attr_recipe_command_t command,
2720 ipc_voucher_attr_manager_flags flags,
2721 int *need_processing)
2722 {
2723 ipc_voucher_attr_raw_recipe_array_size_t recipe_size = *in_out_size;
2724 ipc_voucher_attr_raw_recipe_array_size_t recipe_used = 0;
2725 iv_index_t key_index;
2726 ipc_voucher_attr_recipe_t recipe;
2727
2728 if (IV_NULL == voucher)
2729 return KERN_INVALID_ARGUMENT;
2730
2731 /* Setup a recipe to copy all attributes. */
2732 if (recipe_size < sizeof(*recipe))
2733 return KERN_NO_SPACE;
2734
2735 *need_processing = FALSE;
2736 recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2737 recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
2738 recipe->command = MACH_VOUCHER_ATTR_COPY;
2739 recipe->previous_voucher = voucher;
2740 recipe->content_size = 0;
2741 recipe_used += sizeof(*recipe) + recipe->content_size;
2742
2743 for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
2744 ipc_voucher_attr_manager_t manager;
2745 mach_voucher_attr_key_t key;
2746 iv_index_t value_index;
2747
2748 /* don't output anything for a default value */
2749 value_index = iv_lookup(voucher, key_index);
2750 if (IV_UNUSED_VALINDEX == value_index)
2751 continue;
2752
2753 if (recipe_size - recipe_used < sizeof(*recipe))
2754 return KERN_NO_SPACE;
2755
2756 recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2757
2758 /*
2759 * Get the manager for this key_index. The
2760 * existence of a non-default value for this
2761 * slot within our voucher will keep the
2762 * manager referenced during the callout.
2763 */
2764 ivgt_lookup(key_index, FALSE, &manager, NULL);
2765 assert(IVAM_NULL != manager);
2766 if (IVAM_NULL == manager) {
2767 continue;
2768 }
2769
2770 /* Check if the supported flag is set in the manager */
2771 if ((manager->ivam_flags & flags) == 0)
2772 continue;
2773
2774 key = iv_index_to_key(key_index);
2775
2776 recipe->key = key;
2777 recipe->command = command;
2778 recipe->content_size = 0;
2779 recipe->previous_voucher = voucher;
2780
2781 recipe_used += sizeof(*recipe) + recipe->content_size;
2782 *need_processing = TRUE;
2783 }
2784
2785 *in_out_size = recipe_used;
2786 return KERN_SUCCESS;
2787 }
2788
2789 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2790
2791 /*
2792 * Build-in a simple User Data Resource Manager
2793 */
2794 #define USER_DATA_MAX_DATA (16*1024)
2795
2796 struct user_data_value_element {
2797 mach_voucher_attr_value_reference_t e_made;
2798 mach_voucher_attr_content_size_t e_size;
2799 iv_index_t e_sum;
2800 iv_index_t e_hash;
2801 queue_chain_t e_hash_link;
2802 uint8_t e_data[];
2803 };
2804
2805 typedef struct user_data_value_element *user_data_element_t;
2806
2807 /*
2808 * User Data Voucher Hash Table
2809 */
2810 #define USER_DATA_HASH_BUCKETS 127
2811 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2812
2813 static queue_head_t user_data_bucket[USER_DATA_HASH_BUCKETS];
2814 static lck_spin_t user_data_lock_data;
2815
2816 #define user_data_lock_init() \
2817 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2818 #define user_data_lock_destroy() \
2819 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2820 #define user_data_lock() \
2821 lck_spin_lock(&user_data_lock_data)
2822 #define user_data_lock_try() \
2823 lck_spin_try_lock(&user_data_lock_data)
2824 #define user_data_unlock() \
2825 lck_spin_unlock(&user_data_lock_data)
2826
2827 static kern_return_t
2828 user_data_release_value(
2829 ipc_voucher_attr_manager_t manager,
2830 mach_voucher_attr_key_t key,
2831 mach_voucher_attr_value_handle_t value,
2832 mach_voucher_attr_value_reference_t sync);
2833
2834 static kern_return_t
2835 user_data_get_value(
2836 ipc_voucher_attr_manager_t manager,
2837 mach_voucher_attr_key_t key,
2838 mach_voucher_attr_recipe_command_t command,
2839 mach_voucher_attr_value_handle_array_t prev_values,
2840 mach_voucher_attr_value_handle_array_size_t prev_value_count,
2841 mach_voucher_attr_content_t content,
2842 mach_voucher_attr_content_size_t content_size,
2843 mach_voucher_attr_value_handle_t *out_value,
2844 mach_voucher_attr_value_flags_t *out_flags,
2845 ipc_voucher_t *out_value_voucher);
2846
2847 static kern_return_t
2848 user_data_extract_content(
2849 ipc_voucher_attr_manager_t manager,
2850 mach_voucher_attr_key_t key,
2851 mach_voucher_attr_value_handle_array_t values,
2852 mach_voucher_attr_value_handle_array_size_t value_count,
2853 mach_voucher_attr_recipe_command_t *out_command,
2854 mach_voucher_attr_content_t out_content,
2855 mach_voucher_attr_content_size_t *in_out_content_size);
2856
2857 static kern_return_t
2858 user_data_command(
2859 ipc_voucher_attr_manager_t manager,
2860 mach_voucher_attr_key_t key,
2861 mach_voucher_attr_value_handle_array_t values,
2862 mach_msg_type_number_t value_count,
2863 mach_voucher_attr_command_t command,
2864 mach_voucher_attr_content_t in_content,
2865 mach_voucher_attr_content_size_t in_content_size,
2866 mach_voucher_attr_content_t out_content,
2867 mach_voucher_attr_content_size_t *out_content_size);
2868
2869 static void
2870 user_data_release(
2871 ipc_voucher_attr_manager_t manager);
2872
2873 struct ipc_voucher_attr_manager user_data_manager = {
2874 .ivam_release_value = user_data_release_value,
2875 .ivam_get_value = user_data_get_value,
2876 .ivam_extract_content = user_data_extract_content,
2877 .ivam_command = user_data_command,
2878 .ivam_release = user_data_release,
2879 .ivam_flags = IVAM_FLAGS_NONE,
2880 };
2881
2882 ipc_voucher_attr_control_t user_data_control;
2883 ipc_voucher_attr_control_t test_control;
2884
2885 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2886 #define USER_DATA_ASSERT_KEY(key) \
2887 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2888 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2889 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2890 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2891 #else
2892 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2893 #endif
2894
2895 /*
2896 * Routine: user_data_release_value
2897 * Purpose:
2898 * Release a made reference on a specific value managed by
2899 * this voucher attribute manager.
2900 * Conditions:
2901 * Must remove the element associated with this value from
2902 * the hash if this is the last know made reference.
2903 */
2904 static kern_return_t
2905 user_data_release_value(
2906 ipc_voucher_attr_manager_t __assert_only manager,
2907 mach_voucher_attr_key_t __assert_only key,
2908 mach_voucher_attr_value_handle_t value,
2909 mach_voucher_attr_value_reference_t sync)
2910 {
2911 user_data_element_t elem;
2912 iv_index_t hash;
2913
2914 assert (&user_data_manager == manager);
2915 USER_DATA_ASSERT_KEY(key);
2916
2917 elem = (user_data_element_t)value;
2918 hash = elem->e_hash;
2919
2920 user_data_lock();
2921 if (sync == elem->e_made) {
2922 queue_remove(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link);
2923 user_data_unlock();
2924 kfree(elem, sizeof(*elem) + elem->e_size);
2925 return KERN_SUCCESS;
2926 }
2927 assert(sync < elem->e_made);
2928 user_data_unlock();
2929
2930 return KERN_FAILURE;
2931 }
2932
2933 /*
2934 * Routine: user_data_checksum
2935 * Purpose:
2936 * Provide a rudimentary checksum for the data presented
2937 * to these voucher attribute managers.
2938 */
2939 static iv_index_t
2940 user_data_checksum(
2941 mach_voucher_attr_content_t content,
2942 mach_voucher_attr_content_size_t content_size)
2943 {
2944 mach_voucher_attr_content_size_t i;
2945 iv_index_t cksum = 0;
2946
2947 for(i = 0; i < content_size; i++, content++) {
2948 cksum = (cksum << 8) ^ (cksum + *(unsigned char *)content);
2949 }
2950
2951 return (~cksum);
2952 }
2953
2954 /*
2955 * Routine: user_data_dedup
2956 * Purpose:
2957 * See if the content represented by this request already exists
2958 * in another user data element. If so return a made reference
2959 * to the existing element. Otherwise, create a new element and
2960 * return that (after inserting it in the hash).
2961 * Conditions:
2962 * Nothing locked.
2963 * Returns:
2964 * A made reference on the user_data_element_t
2965 */
2966 static user_data_element_t
2967 user_data_dedup(
2968 mach_voucher_attr_content_t content,
2969 mach_voucher_attr_content_size_t content_size)
2970 {
2971 iv_index_t sum;
2972 iv_index_t hash;
2973 user_data_element_t elem;
2974 user_data_element_t alloc = NULL;
2975
2976 sum = user_data_checksum(content, content_size);
2977 hash = USER_DATA_HASH_BUCKET(sum);
2978
2979 retry:
2980 user_data_lock();
2981 queue_iterate(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link) {
2982 assert(elem->e_hash == hash);
2983
2984 /* if sums match... */
2985 if (elem->e_sum == sum && elem->e_size == content_size) {
2986 iv_index_t i;
2987
2988 /* and all data matches */
2989 for (i = 0; i < content_size; i++)
2990 if (elem->e_data[i] != content[i])
2991 break;
2992 if (i < content_size)
2993 continue;
2994
2995 /* ... we found a match... */
2996
2997 elem->e_made++;
2998 user_data_unlock();
2999
3000 if (NULL != alloc)
3001 kfree(alloc, sizeof(*alloc) + content_size);
3002
3003 return elem;
3004 }
3005 }
3006
3007 if (NULL == alloc) {
3008 user_data_unlock();
3009
3010 alloc = (user_data_element_t)kalloc(sizeof(*alloc) + content_size);
3011 alloc->e_made = 1;
3012 alloc->e_size = content_size;
3013 alloc->e_sum = sum;
3014 alloc->e_hash = hash;
3015 memcpy(alloc->e_data, content, content_size);
3016 goto retry;
3017 }
3018
3019 queue_enter(&user_data_bucket[hash], alloc, user_data_element_t, e_hash_link);
3020 user_data_unlock();
3021
3022 return alloc;
3023 }
3024
3025 static kern_return_t
3026 user_data_get_value(
3027 ipc_voucher_attr_manager_t __assert_only manager,
3028 mach_voucher_attr_key_t __assert_only key,
3029 mach_voucher_attr_recipe_command_t command,
3030 mach_voucher_attr_value_handle_array_t prev_values,
3031 mach_voucher_attr_value_handle_array_size_t prev_value_count,
3032 mach_voucher_attr_content_t content,
3033 mach_voucher_attr_content_size_t content_size,
3034 mach_voucher_attr_value_handle_t *out_value,
3035 mach_voucher_attr_value_flags_t *out_flags,
3036 ipc_voucher_t *out_value_voucher)
3037 {
3038 user_data_element_t elem;
3039
3040 assert (&user_data_manager == manager);
3041 USER_DATA_ASSERT_KEY(key);
3042
3043 /* never an out voucher */
3044 *out_value_voucher = IPC_VOUCHER_NULL;
3045 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
3046
3047 switch (command) {
3048
3049 case MACH_VOUCHER_ATTR_REDEEM:
3050
3051 /* redeem of previous values is the value */
3052 if (0 < prev_value_count) {
3053 elem = (user_data_element_t)prev_values[0];
3054 assert(0 < elem->e_made);
3055 elem->e_made++;
3056 *out_value = prev_values[0];
3057 return KERN_SUCCESS;
3058 }
3059
3060 /* redeem of default is default */
3061 *out_value = 0;
3062 return KERN_SUCCESS;
3063
3064 case MACH_VOUCHER_ATTR_USER_DATA_STORE:
3065 if (USER_DATA_MAX_DATA < content_size)
3066 return KERN_RESOURCE_SHORTAGE;
3067
3068 /* empty is the default */
3069 if (0 == content_size) {
3070 *out_value = 0;
3071 return KERN_SUCCESS;
3072 }
3073
3074 elem = user_data_dedup(content, content_size);
3075 *out_value = (mach_voucher_attr_value_handle_t)elem;
3076 return KERN_SUCCESS;
3077
3078 default:
3079 /* every other command is unknown */
3080 return KERN_INVALID_ARGUMENT;
3081 }
3082 }
3083
3084 static kern_return_t
3085 user_data_extract_content(
3086 ipc_voucher_attr_manager_t __assert_only manager,
3087 mach_voucher_attr_key_t __assert_only key,
3088 mach_voucher_attr_value_handle_array_t values,
3089 mach_voucher_attr_value_handle_array_size_t value_count,
3090 mach_voucher_attr_recipe_command_t *out_command,
3091 mach_voucher_attr_content_t out_content,
3092 mach_voucher_attr_content_size_t *in_out_content_size)
3093 {
3094 mach_voucher_attr_content_size_t size = 0;
3095 user_data_element_t elem;
3096 unsigned int i;
3097
3098 assert (&user_data_manager == manager);
3099 USER_DATA_ASSERT_KEY(key);
3100
3101 /* concatenate the stored data items */
3102 for (i = 0; i < value_count ; i++) {
3103 elem = (user_data_element_t)values[i];
3104 assert(USER_DATA_MAX_DATA >= elem->e_size);
3105
3106 if (size + elem->e_size > *in_out_content_size)
3107 return KERN_NO_SPACE;
3108
3109 memcpy(&out_content[size], elem->e_data, elem->e_size);
3110 size += elem->e_size;
3111 }
3112 *out_command = MACH_VOUCHER_ATTR_BITS_STORE;
3113 *in_out_content_size = size;
3114 return KERN_SUCCESS;
3115 }
3116
3117 static kern_return_t
3118 user_data_command(
3119 ipc_voucher_attr_manager_t __assert_only manager,
3120 mach_voucher_attr_key_t __assert_only key,
3121 mach_voucher_attr_value_handle_array_t __unused values,
3122 mach_msg_type_number_t __unused value_count,
3123 mach_voucher_attr_command_t __unused command,
3124 mach_voucher_attr_content_t __unused in_content,
3125 mach_voucher_attr_content_size_t __unused in_content_size,
3126 mach_voucher_attr_content_t __unused out_content,
3127 mach_voucher_attr_content_size_t __unused *out_content_size)
3128 {
3129 assert (&user_data_manager == manager);
3130 USER_DATA_ASSERT_KEY(key);
3131 return KERN_FAILURE;
3132 }
3133
3134 static void
3135 user_data_release(
3136 ipc_voucher_attr_manager_t manager)
3137 {
3138 if (manager != &user_data_manager)
3139 return;
3140
3141 panic("Voucher user-data manager released");
3142 }
3143
3144 static int user_data_manager_inited = 0;
3145
3146 void
3147 user_data_attr_manager_init()
3148 {
3149 kern_return_t kr;
3150
3151 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3152 if ((user_data_manager_inited & 0x1) != 0x1) {
3153 kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
3154 (mach_voucher_attr_value_handle_t)0,
3155 MACH_VOUCHER_ATTR_KEY_USER_DATA,
3156 &user_data_control);
3157 if (KERN_SUCCESS != kr)
3158 printf("Voucher user-data manager register(USER-DATA) returned %d", kr);
3159 else
3160 user_data_manager_inited |= 0x1;
3161 }
3162 #endif
3163 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3164 if ((user_data_manager_inited & 0x2) != 0x2) {
3165 kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
3166 (mach_voucher_attr_value_handle_t)0,
3167 MACH_VOUCHER_ATTR_KEY_TEST,
3168 &test_control);
3169 if (KERN_SUCCESS != kr)
3170 printf("Voucher user-data manager register(TEST) returned %d", kr);
3171 else
3172 user_data_manager_inited |= 0x2;
3173 }
3174 #endif
3175 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3176 int i;
3177
3178 for (i=0; i < USER_DATA_HASH_BUCKETS; i++)
3179 queue_init(&user_data_bucket[i]);
3180
3181 user_data_lock_init();
3182 #endif
3183 }
3184
3185 #endif /* MACH_DEBUG */