]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_cprotect.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_cprotect.c
1 /*
2 * Copyright (c) 2000-2009 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 #include <sys/cprotect.h>
29 #include <sys/mman.h>
30 #include <sys/mount.h>
31 #include <sys/random.h>
32 #include <sys/xattr.h>
33 #include <sys/uio_internal.h>
34 #include <sys/ubc_internal.h>
35 #include <sys/vnode_if.h>
36 #include <sys/vnode_internal.h>
37 #include <libkern/OSByteOrder.h>
38
39 #include "hfs.h"
40 #include "hfs_cnode.h"
41
42 #ifdef CONFIG_PROTECT
43 static struct cp_wrap_func g_cp_wrap_func = {NULL, NULL};
44 static struct cp_global_state g_cp_state = {0, 0};
45
46 extern int (**hfs_vnodeop_p) (void *);
47
48 /*
49 * CP private functions
50 */
51 static int cp_is_valid_class(int);
52 static int cp_getxattr(cnode_t *, struct cp_xattr *);
53 static int cp_setxattr(cnode_t *, struct cp_xattr *, int);
54 static struct cprotect *cp_entry_alloc(void);
55 static int cp_make_keys (struct cprotect *);
56 static int cp_restore_keys(struct cprotect *);
57 static int cp_lock_vfs_callback(mount_t, void *);
58 static int cp_lock_vnode_callback(vnode_t, void *);
59 static int cp_vnode_is_eligible (vnode_t);
60 static int cp_check_access (cnode_t *, int);
61 static int cp_wrap(int, void *, void *);
62 static int cp_unwrap(int, void *, void *);
63
64
65
66 #if DEVELOPMENT || DEBUG
67 #define CP_ASSERT(x) \
68 if ((x) == 0) { \
69 panic("CP: failed assertion in %s", __FUNCTION__); \
70 }
71 #else
72 #define CP_ASSERT(x)
73 #endif
74
75 int
76 cp_key_store_action(int action)
77 {
78 g_cp_state.lock_state = action;
79 if (action == CP_LOCKED_STATE)
80 return vfs_iterate(0, cp_lock_vfs_callback, (void *)action);
81 else
82 return 0;
83 }
84
85
86 int
87 cp_register_wraps(cp_wrap_func_t key_store_func)
88 {
89 g_cp_wrap_func.wrapper = key_store_func->wrapper;
90 g_cp_wrap_func.unwrapper = key_store_func->unwrapper;
91
92 g_cp_state.wrap_functions_set = 1;
93
94 return 0;
95 }
96
97 /*
98 * Allocate and initialize a cprotect blob for a new cnode.
99 * Called from hfs_getnewcnode: cnode is locked exclusive.
100 * Read xattr data off the cnode. Then, if conditions permit,
101 * unwrap the file key and cache it in the cprotect blob.
102 */
103 int
104 cp_entry_init(cnode_t *cnode, struct mount *mp)
105 {
106 struct cprotect *entry;
107 struct cp_xattr xattr;
108 int error = 0;
109
110 if (!cp_fs_protected (mp)) {
111 cnode->c_cpentry = NULL;
112 return 0;
113 }
114
115 if (!S_ISREG(cnode->c_mode)) {
116 cnode->c_cpentry = NULL;
117 return 0;
118 }
119
120 if (!g_cp_state.wrap_functions_set) {
121 printf("hfs: cp_update_entry: wrap functions not yet set\n");
122 return ENXIO;
123 }
124
125 CP_ASSERT (cnode->c_cpentry == NULL);
126
127 entry = cp_entry_alloc();
128 if (!entry)
129 return ENOMEM;
130
131 entry->cp_flags |= CP_KEY_FLUSHED;
132 cnode->c_cpentry = entry;
133
134 error = cp_getxattr(cnode, &xattr);
135 if (error == ENOATTR) {
136 /*
137 * Can't tell if the file is new, or was previously created but never
138 * written to or set-classed. In either case, it'll need a fresh
139 * per-file key.
140 */
141 entry->cp_flags |= CP_NEEDS_KEYS;
142 error = 0;
143 } else {
144 if (xattr.xattr_major_version != CP_CURRENT_MAJOR_VERS) {
145 printf("hfs: cp_entry_init: bad xattr version\n");
146 error = EINVAL;
147 goto out;
148 }
149
150 /* set up entry with information from xattr */
151 entry->cp_pclass = xattr.persistent_class;
152 bcopy(&xattr.persistent_key, &entry->cp_persistent_key, CP_WRAPPEDKEYSIZE);
153 }
154
155 out:
156 if (error) {
157 cp_entry_destroy (cnode);
158 }
159 return error;
160 }
161
162 /*
163 * Set up initial key/class pair on cnode. The cnode is locked exclusive.
164 */
165 int
166 cp_entry_create_keys(cnode_t *cnode)
167 {
168 struct cprotect *entry = cnode->c_cpentry;
169
170 if (!entry) {
171 //unprotected file: continue
172 return 0;
173 }
174
175 CP_ASSERT((entry->cp_flags & CP_NEEDS_KEYS));
176
177 return cp_make_keys(entry);
178 }
179
180 /*
181 * Tear down and clear a cprotect blob for a closing file.
182 * Called at hfs_reclaim_cnode: cnode is locked exclusive.
183 */
184 void
185 cp_entry_destroy(cnode_t *cnode)
186 {
187 struct cprotect *entry = cnode->c_cpentry;
188 if (!entry) {
189 /* nothing to clean up */
190 return;
191 }
192 cnode->c_cpentry = NULL;
193 bzero(entry, sizeof(*entry));
194 FREE(entry, M_TEMP);
195 }
196
197 int
198 cp_fs_protected (mount_t mnt) {
199 return (vfs_flags(mnt) & MNT_CPROTECT);
200 }
201
202
203 /*
204 * Return a pointer to underlying cnode if there is one for this vnode.
205 * Done without taking cnode lock, inspecting only vnode state.
206 */
207 cnode_t *
208 cp_get_protected_cnode(vnode_t vp)
209 {
210 if (!cp_vnode_is_eligible(vp)) {
211 return NULL;
212 }
213
214 if (!cp_fs_protected(VTOVFS(vp))) {
215 /* mount point doesn't support it */
216 return NULL;
217 }
218
219 return (cnode_t *) vp->v_data;
220 }
221
222
223 /*
224 * Sets *class to persistent class associated with vnode,
225 * or returns error.
226 */
227 int
228 cp_vnode_getclass(vnode_t vp, int *class)
229 {
230 struct cp_xattr xattr;
231 int error = 0;
232 struct cnode *cnode;
233
234 if (!cp_vnode_is_eligible (vp)) {
235 return EBADF;
236 }
237
238 cnode = VTOC(vp);
239
240 hfs_lock(cnode, HFS_SHARED_LOCK);
241
242 if (cp_fs_protected(VTOVFS(vp))) {
243 /* pull the class from the live entry */
244 struct cprotect *entry = cnode->c_cpentry;
245 if (!entry) {
246 panic("Content Protection: uninitialized cnode %p", cnode);
247 }
248
249 if ((entry->cp_flags & CP_NEEDS_KEYS)) {
250 error = cp_make_keys(entry);
251 }
252 *class = entry->cp_pclass;
253
254 } else {
255 /*
256 * Mount point is not formatted for content protection. If a class
257 * has been specified anyway, report it. Otherwise, report D.
258 */
259 error = cp_getxattr(cnode, &xattr);
260 if (error == ENOATTR) {
261 *class = PROTECTION_CLASS_D;
262 error = 0;
263 } else if (error == 0) {
264 *class = xattr.persistent_class;
265 }
266 }
267
268 hfs_unlock(cnode);
269 return error;
270 }
271
272
273 /*
274 * Sets persistent class for this file.
275 * If vnode cannot be protected (system file, non-regular file, non-hfs), EBADF.
276 * If the new class can't be accessed now, EPERM.
277 * Otherwise, record class and re-wrap key if the mount point is content-protected.
278 */
279 int
280 cp_vnode_setclass(vnode_t vp, uint32_t newclass)
281 {
282 struct cnode *cnode;
283 struct cp_xattr xattr;
284 struct cprotect *entry = 0;
285 int error = 0;
286
287 if (!cp_is_valid_class(newclass)) {
288 printf("hfs: CP: cp_setclass called with invalid class %d\n", newclass);
289 return EINVAL;
290 }
291
292 /* is this an interesting file? */
293 if (!cp_vnode_is_eligible(vp)) {
294 return EBADF;
295 }
296
297 cnode = VTOC(vp);
298
299 if (hfs_lock(cnode, HFS_EXCLUSIVE_LOCK)) {
300 return EINVAL;
301 }
302
303 /* is the volume formatted for content protection? */
304 if (cp_fs_protected(VTOVFS(vp))) {
305 entry = cnode->c_cpentry;
306 if (entry == NULL) {
307 error = EINVAL;
308 goto out;
309 }
310
311 if ((entry->cp_flags & CP_NEEDS_KEYS)) {
312 if ((error = cp_make_keys(entry)) != 0) {
313 goto out;
314 }
315 }
316
317 if (entry->cp_flags & CP_KEY_FLUSHED) {
318 error = cp_restore_keys(entry);
319 if (error)
320 goto out;
321 }
322
323 /* re-wrap per-file key with new class */
324 error = cp_wrap(newclass,
325 &entry->cp_cache_key[0],
326 &entry->cp_persistent_key[0]);
327 if (error) {
328 /* we didn't have perms to set this class. leave file as-is and error out */
329 goto out;
330 }
331
332 entry->cp_pclass = newclass;
333
334 /* prepare to write the xattr out */
335 bcopy(&entry->cp_persistent_key, &xattr.persistent_key, CP_WRAPPEDKEYSIZE);
336 } else {
337 /* no live keys for this file. just remember intended class */
338 bzero(&xattr.persistent_key, CP_WRAPPEDKEYSIZE);
339 }
340
341 xattr.xattr_major_version = CP_CURRENT_MAJOR_VERS;
342 xattr.xattr_minor_version = CP_CURRENT_MINOR_VERS;
343 xattr.key_size = CP_WRAPPEDKEYSIZE;
344 xattr.flags = 0;
345 xattr.persistent_class = newclass;
346 error = cp_setxattr(cnode, &xattr, XATTR_REPLACE);
347
348 if (error == ENOATTR) {
349 error = cp_setxattr (cnode, &xattr, XATTR_CREATE);
350 }
351
352 out:
353 hfs_unlock(cnode);
354 return error;
355 }
356
357 /*
358 * Check permission for the given operation (read, write, page in) on this node.
359 * Additionally, if the node needs work, do it:
360 * - create a new key for the file if one hasn't been set before
361 * - write out the xattr if it hasn't already been saved
362 * - unwrap the key if needed
363 *
364 * Takes cnode lock, and upgrades to exclusive if modifying cprotect.
365 */
366 int
367 cp_handle_vnop(cnode_t *cnode, int vnop)
368 {
369 struct cprotect *entry;
370 int error = 0;
371 struct cp_xattr xattr;
372
373 if ((error = hfs_lock(cnode, HFS_SHARED_LOCK)) != KERN_SUCCESS) {
374 return error;
375 }
376
377 entry = cnode->c_cpentry;
378 if (!entry)
379 goto out;
380
381 if ((error = cp_check_access(cnode, vnop)) != KERN_SUCCESS) {
382 goto out;
383 }
384
385 if (entry->cp_flags == 0) {
386 /* no more work to do */
387 goto out;
388 }
389
390 /* upgrade to exclusive lock */
391 if (lck_rw_lock_shared_to_exclusive(&cnode->c_rwlock) == FALSE) {
392 if ((error = hfs_lock(cnode, HFS_EXCLUSIVE_LOCK)) != KERN_SUCCESS) {
393 return error;
394 }
395 } else {
396 cnode->c_lockowner = current_thread();
397 }
398
399 /* generate new keys if none have ever been saved */
400 if ((entry->cp_flags & CP_NEEDS_KEYS)) {
401 if ((error = cp_make_keys(entry)) != 0) {
402 goto out;
403 }
404 }
405
406 /* unwrap keys if needed */
407 if (entry->cp_flags & CP_KEY_FLUSHED) {
408 error = cp_restore_keys(entry);
409 if (error)
410 goto out;
411 }
412
413 /* write out the xattr if it's new */
414 if (entry->cp_flags & CP_NO_XATTR) {
415 bcopy(&entry->cp_persistent_key[0], &xattr.persistent_key, CP_WRAPPEDKEYSIZE);
416 xattr.xattr_major_version = CP_CURRENT_MAJOR_VERS;
417 xattr.xattr_minor_version = CP_CURRENT_MINOR_VERS;
418 xattr.key_size = CP_WRAPPEDKEYSIZE;
419 xattr.persistent_class = entry->cp_pclass;
420 error = cp_setxattr(cnode, &xattr, XATTR_CREATE);
421 }
422
423 out:
424 hfs_unlock(cnode);
425 return error;
426 }
427
428 /*
429 * During hfs resize operations, we have slightly different constraints than during
430 * normal VNOPS that read/write data to files. Specifically, we already have the cnode
431 * locked (so nobody else can modify it), and we are doing the IO with root privileges, since
432 * we are moving the data behind the user's back. So, we skip access checks here (for unlock
433 * vs. lock), and don't worry about non-existing keys. If the file exists on-disk with valid
434 * payload, then it must have keys set up already by definition.
435 */
436 int cp_handle_relocate (cnode_t *cp) {
437 struct cprotect *entry;
438 int error = -1;
439
440 /* cp is already locked */
441 entry = cp->c_cpentry;
442 if (!entry)
443 goto out;
444
445 /*
446 * Still need to validate whether to permit access to the file or not
447 * based on lock status
448 */
449 if ((error = cp_check_access(cp, CP_READ_ACCESS | CP_WRITE_ACCESS)) != KERN_SUCCESS) {
450 goto out;
451 }
452
453 if (entry->cp_flags == 0) {
454 /* no more work to do */
455 error = 0;
456 goto out;
457 }
458
459 /* it must have keys since it is an existing file with actual payload */
460
461 /* unwrap keys if needed */
462 if (entry->cp_flags & CP_KEY_FLUSHED) {
463 error = cp_restore_keys(entry);
464 }
465
466 /* don't need to write out the EA since the file is extant */
467 out:
468
469 /* return the cp still locked */
470 return error;
471 }
472
473
474
475 /*
476 * cp_getrootxattr:
477 * Gets the EA we set on the root folder (fileid 1) to get information about the
478 * version of Content Protection that was used to write to this filesystem.
479 * Note that all multi-byte fields are written to disk little endian so they must be
480 * converted to native endian-ness as needed.
481 */
482
483 int cp_getrootxattr(struct hfsmount* hfsmp, struct cp_root_xattr *outxattr) {
484 uio_t auio;
485 char uio_buf[UIO_SIZEOF(1)];
486 size_t attrsize = sizeof(struct cp_root_xattr);
487 int error = 0;
488 struct vnop_getxattr_args args;
489
490 if (!outxattr) {
491 panic("cp_xattr called with xattr == NULL");
492 }
493
494 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
495 uio_addiov(auio, CAST_USER_ADDR_T(outxattr), attrsize);
496
497 args.a_desc = NULL; // unused
498 args.a_vp = NULL; //unused since we're writing EA to root folder.
499 args.a_name = CONTENT_PROTECTION_XATTR_NAME;
500 args.a_uio = auio;
501 args.a_size = &attrsize;
502 args.a_options = XATTR_REPLACE;
503 args.a_context = NULL; // unused
504
505 error = hfs_getxattr_internal(NULL, &args, hfsmp, 1);
506
507 /* Now convert the multi-byte fields to native endianness */
508 outxattr->major_version = OSSwapLittleToHostInt16(outxattr->major_version);
509 outxattr->minor_version = OSSwapLittleToHostInt16(outxattr->minor_version);
510 outxattr->flags = OSSwapLittleToHostInt64(outxattr->flags);
511
512 if (error != KERN_SUCCESS) {
513 goto out;
514 }
515
516 out:
517 uio_free(auio);
518 return error;
519 }
520
521 /*
522 * cp_setrootxattr:
523 * Sets the EA we set on the root folder (fileid 1) to get information about the
524 * version of Content Protection that was used to write to this filesystem.
525 * Note that all multi-byte fields are written to disk little endian so they must be
526 * converted to little endian as needed.
527 *
528 * This will be written to the disk when it detects the EA is not there, or when we need
529 * to make a modification to the on-disk version that can be done in-place.
530 */
531 int
532 cp_setrootxattr(struct hfsmount *hfsmp, struct cp_root_xattr *newxattr)
533 {
534 int error = 0;
535 struct vnop_setxattr_args args;
536
537 args.a_desc = NULL;
538 args.a_vp = NULL;
539 args.a_name = CONTENT_PROTECTION_XATTR_NAME;
540 args.a_uio = NULL; //pass data ptr instead
541 args.a_options = 0;
542 args.a_context = NULL; //no context needed, only done from mount.
543
544 /* Now convert the multi-byte fields to little endian before writing to disk. */
545 newxattr->major_version = OSSwapHostToLittleInt16(newxattr->major_version);
546 newxattr->minor_version = OSSwapHostToLittleInt16(newxattr->minor_version);
547 newxattr->flags = OSSwapHostToLittleInt64(newxattr->flags);
548
549 error = hfs_setxattr_internal(NULL, (caddr_t)newxattr,
550 sizeof(struct cp_root_xattr), &args, hfsmp, 1);
551 return error;
552 }
553
554
555
556
557 /********************
558 * Private Functions
559 *******************/
560
561 static int
562 cp_vnode_is_eligible(vnode_t vp)
563 {
564 return ((vp->v_op == hfs_vnodeop_p) &&
565 (!vnode_issystem(vp)) &&
566 (vnode_isreg(vp)));
567 }
568
569
570
571 static int
572 cp_is_valid_class(int class)
573 {
574 return ((class >= PROTECTION_CLASS_A) &&
575 (class <= PROTECTION_CLASS_F));
576 }
577
578
579 static struct cprotect *
580 cp_entry_alloc(void)
581 {
582 struct cprotect *cp_entry;
583
584 MALLOC(cp_entry, struct cprotect *, sizeof(struct cprotect),
585 M_TEMP, M_WAITOK);
586 if (cp_entry == NULL)
587 return (NULL);
588
589 bzero(cp_entry, sizeof(*cp_entry));
590 return (cp_entry);
591 }
592
593
594 /*
595 * Reads xattr data off the cnode and into provided xattr.
596 * cnode lock held shared
597 */
598 static int
599 cp_getxattr(cnode_t *cnode, struct cp_xattr *outxattr)
600 {
601 uio_t auio;
602 char uio_buf[UIO_SIZEOF(1)];
603 size_t attrsize = sizeof(struct cp_xattr);
604 int error = 0;
605 struct vnop_getxattr_args args;
606
607 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
608 uio_addiov(auio, CAST_USER_ADDR_T(outxattr), attrsize);
609
610 args.a_desc = NULL; // unused
611 args.a_vp = cnode->c_vp;
612 args.a_name = CONTENT_PROTECTION_XATTR_NAME;
613 args.a_uio = auio;
614 args.a_size = &attrsize;
615 args.a_options = XATTR_REPLACE;
616 args.a_context = vfs_context_current(); // unused
617 error = hfs_getxattr_internal(cnode, &args, VTOHFS(cnode->c_vp), 0);
618 if (error != KERN_SUCCESS) {
619 goto out;
620 }
621
622 /* Endian swap the multi-byte fields into host endianness from L.E. */
623 outxattr->xattr_major_version = OSSwapLittleToHostInt16(outxattr->xattr_major_version);
624 outxattr->xattr_minor_version = OSSwapLittleToHostInt16(outxattr->xattr_minor_version);
625 outxattr->key_size = OSSwapLittleToHostInt32(outxattr->key_size);
626 outxattr->flags = OSSwapLittleToHostInt32(outxattr->flags);
627 outxattr->persistent_class = OSSwapLittleToHostInt32(outxattr->persistent_class);
628
629 out:
630 uio_free(auio);
631 return error;
632 }
633
634 /*
635 * Stores new xattr data on the cnode.
636 * cnode lock held exclusive
637 */
638 static int
639 cp_setxattr(cnode_t *cnode, struct cp_xattr *newxattr, int options)
640 {
641 int error = 0;
642 struct vnop_setxattr_args args;
643
644 args.a_desc = NULL;
645 args.a_vp = cnode->c_vp;
646 args.a_name = CONTENT_PROTECTION_XATTR_NAME;
647 args.a_uio = NULL; //pass data ptr instead
648 args.a_options = options;
649 args.a_context = vfs_context_current();
650
651 /* Endian swap the multi-byte fields into L.E from host. */
652 newxattr->xattr_major_version = OSSwapHostToLittleInt16(newxattr->xattr_major_version);
653 newxattr->xattr_minor_version = OSSwapHostToLittleInt16(newxattr->xattr_minor_version);
654 newxattr->key_size = OSSwapHostToLittleInt32(newxattr->key_size);
655 newxattr->flags = OSSwapHostToLittleInt32(newxattr->flags);
656 newxattr->persistent_class = OSSwapHostToLittleInt32(newxattr->persistent_class);
657
658 error = hfs_setxattr_internal(cnode, (caddr_t)newxattr,
659 sizeof(struct cp_xattr), &args, VTOHFS(cnode->c_vp), 0);
660
661 if ((error == KERN_SUCCESS) && (cnode->c_cpentry)) {
662 cnode->c_cpentry->cp_flags &= ~CP_NO_XATTR;
663 }
664
665 return error;
666 }
667
668
669 /*
670 * Make a new random per-file key and wrap it.
671 */
672 static int
673 cp_make_keys(struct cprotect *entry)
674 {
675 int error = 0;
676
677 if (g_cp_state.wrap_functions_set != 1) {
678 printf("hfs: CP: could not create keys: no wrappers set\n");
679 return ENXIO;
680 }
681
682 /* create new cp data: key and class */
683 read_random(&entry->cp_cache_key[0], CP_KEYSIZE);
684 entry->cp_pclass = PROTECTION_CLASS_D;
685
686 /* wrap the new key in the class key */
687 error = cp_wrap(PROTECTION_CLASS_D,
688 &entry->cp_cache_key[0],
689 &entry->cp_persistent_key[0]);
690
691 if (error) {
692 panic("could not wrap new key in class D\n");
693 }
694
695 /* ready for business */
696 entry->cp_flags &= ~CP_NEEDS_KEYS;
697 entry->cp_flags |= CP_NO_XATTR;
698
699 return error;
700 }
701
702 /*
703 * If permitted, restore entry's unwrapped key from the persistent key.
704 * If not, clear key and set CP_ENTRY_FLUSHED.
705 * cnode lock held exclusive
706 */
707 static int
708 cp_restore_keys(struct cprotect *entry)
709 {
710 int error = 0;
711
712 error = cp_unwrap(entry->cp_pclass,
713 &entry->cp_persistent_key[0],
714 &entry->cp_cache_key[0]);
715
716 if (error) {
717 entry->cp_flags |= CP_KEY_FLUSHED;
718 bzero(entry->cp_cache_key, CP_KEYSIZE);
719 error = EPERM;
720 }
721 else {
722 entry->cp_flags &= ~CP_KEY_FLUSHED;
723 }
724 return error;
725 }
726
727 static int
728 cp_lock_vfs_callback(mount_t mp, void *arg)
729 {
730 if (!cp_fs_protected(mp)) {
731 /* not interested in this mount point */
732 return 0;
733 }
734
735 return vnode_iterate(mp, 0, cp_lock_vnode_callback, arg);
736 }
737
738
739 /*
740 * Deny access to protected files if keys have been locked.
741 *
742 * cnode lock is taken shared.
743 */
744 static int
745 cp_check_access(cnode_t *cnode, int vnop)
746 {
747 int error = 0;
748
749 if (g_cp_state.lock_state == CP_UNLOCKED_STATE) {
750 return KERN_SUCCESS;
751 }
752
753 if (!cnode->c_cpentry) {
754 /* unprotected node */
755 return KERN_SUCCESS;
756 }
757
758 /* Deny all access for class A files, and read access for class B */
759 switch (cnode->c_cpentry->cp_pclass) {
760 case PROTECTION_CLASS_A: {
761 error = EPERM;
762 break;
763 }
764 case PROTECTION_CLASS_B: {
765 if (vnop & CP_READ_ACCESS)
766 error = EPERM;
767 else
768 error = 0;
769 break;
770 }
771 default:
772 error = 0;
773 break;
774 }
775
776 return error;
777 }
778
779
780
781 /*
782 * Respond to a lock or unlock event.
783 * On lock: clear out keys from memory, then flush file contents.
784 * On unlock: nothing (function not called).
785 */
786 static int
787 cp_lock_vnode_callback(vnode_t vp, void *arg)
788 {
789 cnode_t *cp = NULL;
790 struct cprotect *entry = NULL;
791 int error = 0;
792 int locked = 1;
793 int action = 0;
794
795 error = vnode_getwithref (vp);
796 if (error) {
797 return error;
798 }
799
800 cp = VTOC(vp);
801 hfs_lock(cp, HFS_FORCE_LOCK);
802
803 entry = cp->c_cpentry;
804 if (!entry) {
805 /* unprotected vnode: not a regular file */
806 goto out;
807 }
808
809 action = (int)((uintptr_t) arg);
810 switch (action) {
811 case CP_LOCKED_STATE: {
812 vfs_context_t ctx;
813 if (entry->cp_pclass != PROTECTION_CLASS_A) {
814 /* no change at lock for other classes */
815 goto out;
816 }
817
818 /* Before doing anything else, zero-fille sparse ranges as needed */
819 ctx = vfs_context_current();
820 (void) hfs_filedone (vp, ctx);
821
822 /* first, sync back dirty pages */
823 hfs_unlock (cp);
824 ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHALL | UBC_INVALIDATE | UBC_SYNC);
825 hfs_lock (cp, HFS_FORCE_LOCK);
826
827 /* flush keys */
828 entry->cp_flags |= CP_KEY_FLUSHED;
829 bzero(&entry->cp_cache_key, CP_KEYSIZE);
830 /* some write may have arrived in the mean time. dump those pages */
831 hfs_unlock(cp);
832 locked = 0;
833
834 ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_INVALIDATE | UBC_SYNC);
835 break;
836 }
837 case CP_UNLOCKED_STATE: {
838 /* no-op */
839 break;
840 }
841 default:
842 panic("unknown lock action %d\n", action);
843 }
844
845 out:
846 if (locked)
847 hfs_unlock(cp);
848 vnode_put (vp);
849 return error;
850 }
851
852 static int
853 cp_wrap(int class, void *inkey, void *outkey)
854 {
855 int error = 0;
856 size_t keyln = CP_WRAPPEDKEYSIZE;
857
858 if (class == PROTECTION_CLASS_F) {
859 bzero(outkey, CP_WRAPPEDKEYSIZE);
860 return 0;
861 }
862
863 error = g_cp_wrap_func.wrapper(class,
864 inkey,
865 CP_KEYSIZE,
866 outkey,
867 &keyln);
868
869 return error;
870 }
871
872
873 static int
874 cp_unwrap(int class, void *inkey, void *outkey)
875 {
876 int error = 0;
877 size_t keyln = CP_KEYSIZE;
878
879 if (class == PROTECTION_CLASS_F) {
880 /* we didn't save a wrapped key, so nothing to unwrap */
881 return EPERM;
882 }
883
884 error = g_cp_wrap_func.unwrapper(class,
885 inkey,
886 CP_WRAPPEDKEYSIZE,
887 outkey,
888 &keyln);
889
890 return error;
891
892 }
893
894
895 #else
896
897 int cp_key_store_action(int action __unused)
898 {
899 return ENOTSUP;
900 }
901
902
903 int cp_register_wraps(cp_wrap_func_t key_store_func __unused)
904 {
905 return ENOTSUP;
906 }
907
908 #endif /* CONFIG_PROTECT */