2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 #include <sys/cprotect.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>
40 #include "hfs_cnode.h"
43 static struct cp_wrap_func g_cp_wrap_func
= {NULL
, NULL
};
44 static struct cp_global_state g_cp_state
= {0, 0};
46 extern int (**hfs_vnodeop_p
) (void *);
49 * CP private functions
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 *);
66 #if DEVELOPMENT || DEBUG
67 #define CP_ASSERT(x) \
69 panic("CP: failed assertion in %s", __FUNCTION__); \
76 cp_key_store_action(int action
)
78 g_cp_state
.lock_state
= action
;
79 if (action
== CP_LOCKED_STATE
)
80 return vfs_iterate(0, cp_lock_vfs_callback
, (void *)action
);
87 cp_register_wraps(cp_wrap_func_t key_store_func
)
89 g_cp_wrap_func
.wrapper
= key_store_func
->wrapper
;
90 g_cp_wrap_func
.unwrapper
= key_store_func
->unwrapper
;
92 g_cp_state
.wrap_functions_set
= 1;
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.
104 cp_entry_init(cnode_t
*cnode
, struct mount
*mp
)
106 struct cprotect
*entry
;
107 struct cp_xattr xattr
;
110 if (!cp_fs_protected (mp
)) {
111 cnode
->c_cpentry
= NULL
;
115 if (!S_ISREG(cnode
->c_mode
)) {
116 cnode
->c_cpentry
= NULL
;
120 if (!g_cp_state
.wrap_functions_set
) {
121 printf("hfs: cp_update_entry: wrap functions not yet set\n");
125 CP_ASSERT (cnode
->c_cpentry
== NULL
);
127 entry
= cp_entry_alloc();
131 entry
->cp_flags
|= CP_KEY_FLUSHED
;
132 cnode
->c_cpentry
= entry
;
134 error
= cp_getxattr(cnode
, &xattr
);
135 if (error
== ENOATTR
) {
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
141 entry
->cp_flags
|= CP_NEEDS_KEYS
;
144 if (xattr
.xattr_major_version
!= CP_CURRENT_MAJOR_VERS
) {
145 printf("hfs: cp_entry_init: bad xattr version\n");
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
);
157 cp_entry_destroy (cnode
);
163 * Set up initial key/class pair on cnode. The cnode is locked exclusive.
166 cp_entry_create_keys(cnode_t
*cnode
)
168 struct cprotect
*entry
= cnode
->c_cpentry
;
171 //unprotected file: continue
175 CP_ASSERT((entry
->cp_flags
& CP_NEEDS_KEYS
));
177 return cp_make_keys(entry
);
181 * Tear down and clear a cprotect blob for a closing file.
182 * Called at hfs_reclaim_cnode: cnode is locked exclusive.
185 cp_entry_destroy(cnode_t
*cnode
)
187 struct cprotect
*entry
= cnode
->c_cpentry
;
189 /* nothing to clean up */
192 cnode
->c_cpentry
= NULL
;
193 bzero(entry
, sizeof(*entry
));
198 cp_fs_protected (mount_t mnt
) {
199 return (vfs_flags(mnt
) & MNT_CPROTECT
);
204 * Return a pointer to underlying cnode if there is one for this vnode.
205 * Done without taking cnode lock, inspecting only vnode state.
208 cp_get_protected_cnode(vnode_t vp
)
210 if (!cp_vnode_is_eligible(vp
)) {
214 if (!cp_fs_protected(VTOVFS(vp
))) {
215 /* mount point doesn't support it */
219 return (cnode_t
*) vp
->v_data
;
224 * Sets *class to persistent class associated with vnode,
228 cp_vnode_getclass(vnode_t vp
, int *class)
230 struct cp_xattr xattr
;
234 if (!cp_vnode_is_eligible (vp
)) {
240 hfs_lock(cnode
, HFS_SHARED_LOCK
);
242 if (cp_fs_protected(VTOVFS(vp
))) {
243 /* pull the class from the live entry */
244 struct cprotect
*entry
= cnode
->c_cpentry
;
246 panic("Content Protection: uninitialized cnode %p", cnode
);
249 if ((entry
->cp_flags
& CP_NEEDS_KEYS
)) {
250 error
= cp_make_keys(entry
);
252 *class = entry
->cp_pclass
;
256 * Mount point is not formatted for content protection. If a class
257 * has been specified anyway, report it. Otherwise, report D.
259 error
= cp_getxattr(cnode
, &xattr
);
260 if (error
== ENOATTR
) {
261 *class = PROTECTION_CLASS_D
;
263 } else if (error
== 0) {
264 *class = xattr
.persistent_class
;
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.
280 cp_vnode_setclass(vnode_t vp
, uint32_t newclass
)
283 struct cp_xattr xattr
;
284 struct cprotect
*entry
= 0;
287 if (!cp_is_valid_class(newclass
)) {
288 printf("hfs: CP: cp_setclass called with invalid class %d\n", newclass
);
292 /* is this an interesting file? */
293 if (!cp_vnode_is_eligible(vp
)) {
299 if (hfs_lock(cnode
, HFS_EXCLUSIVE_LOCK
)) {
303 /* is the volume formatted for content protection? */
304 if (cp_fs_protected(VTOVFS(vp
))) {
305 entry
= cnode
->c_cpentry
;
311 if ((entry
->cp_flags
& CP_NEEDS_KEYS
)) {
312 if ((error
= cp_make_keys(entry
)) != 0) {
317 if (entry
->cp_flags
& CP_KEY_FLUSHED
) {
318 error
= cp_restore_keys(entry
);
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]);
328 /* we didn't have perms to set this class. leave file as-is and error out */
332 entry
->cp_pclass
= newclass
;
334 /* prepare to write the xattr out */
335 bcopy(&entry
->cp_persistent_key
, &xattr
.persistent_key
, CP_WRAPPEDKEYSIZE
);
337 /* no live keys for this file. just remember intended class */
338 bzero(&xattr
.persistent_key
, CP_WRAPPEDKEYSIZE
);
341 xattr
.xattr_major_version
= CP_CURRENT_MAJOR_VERS
;
342 xattr
.xattr_minor_version
= CP_CURRENT_MINOR_VERS
;
343 xattr
.key_size
= CP_WRAPPEDKEYSIZE
;
345 xattr
.persistent_class
= newclass
;
346 error
= cp_setxattr(cnode
, &xattr
, XATTR_REPLACE
);
348 if (error
== ENOATTR
) {
349 error
= cp_setxattr (cnode
, &xattr
, XATTR_CREATE
);
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
364 * Takes cnode lock, and upgrades to exclusive if modifying cprotect.
367 cp_handle_vnop(cnode_t
*cnode
, int vnop
)
369 struct cprotect
*entry
;
371 struct cp_xattr xattr
;
373 if ((error
= hfs_lock(cnode
, HFS_SHARED_LOCK
)) != KERN_SUCCESS
) {
377 entry
= cnode
->c_cpentry
;
381 if ((error
= cp_check_access(cnode
, vnop
)) != KERN_SUCCESS
) {
385 if (entry
->cp_flags
== 0) {
386 /* no more work to do */
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
) {
396 cnode
->c_lockowner
= current_thread();
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) {
406 /* unwrap keys if needed */
407 if (entry
->cp_flags
& CP_KEY_FLUSHED
) {
408 error
= cp_restore_keys(entry
);
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
);
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.
436 int cp_handle_relocate (cnode_t
*cp
) {
437 struct cprotect
*entry
;
440 /* cp is already locked */
441 entry
= cp
->c_cpentry
;
446 * Still need to validate whether to permit access to the file or not
447 * based on lock status
449 if ((error
= cp_check_access(cp
, CP_READ_ACCESS
| CP_WRITE_ACCESS
)) != KERN_SUCCESS
) {
453 if (entry
->cp_flags
== 0) {
454 /* no more work to do */
459 /* it must have keys since it is an existing file with actual payload */
461 /* unwrap keys if needed */
462 if (entry
->cp_flags
& CP_KEY_FLUSHED
) {
463 error
= cp_restore_keys(entry
);
466 /* don't need to write out the EA since the file is extant */
469 /* return the cp still locked */
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.
483 int cp_getrootxattr(struct hfsmount
* hfsmp
, struct cp_root_xattr
*outxattr
) {
485 char uio_buf
[UIO_SIZEOF(1)];
486 size_t attrsize
= sizeof(struct cp_root_xattr
);
488 struct vnop_getxattr_args args
;
491 panic("cp_xattr called with xattr == NULL");
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
);
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
;
501 args
.a_size
= &attrsize
;
502 args
.a_options
= XATTR_REPLACE
;
503 args
.a_context
= NULL
; // unused
505 error
= hfs_getxattr_internal(NULL
, &args
, hfsmp
, 1);
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
);
512 if (error
!= KERN_SUCCESS
) {
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.
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.
532 cp_setrootxattr(struct hfsmount
*hfsmp
, struct cp_root_xattr
*newxattr
)
535 struct vnop_setxattr_args args
;
539 args
.a_name
= CONTENT_PROTECTION_XATTR_NAME
;
540 args
.a_uio
= NULL
; //pass data ptr instead
542 args
.a_context
= NULL
; //no context needed, only done from mount.
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
);
549 error
= hfs_setxattr_internal(NULL
, (caddr_t
)newxattr
,
550 sizeof(struct cp_root_xattr
), &args
, hfsmp
, 1);
557 /********************
562 cp_vnode_is_eligible(vnode_t vp
)
564 return ((vp
->v_op
== hfs_vnodeop_p
) &&
565 (!vnode_issystem(vp
)) &&
572 cp_is_valid_class(int class)
574 return ((class >= PROTECTION_CLASS_A
) &&
575 (class <= PROTECTION_CLASS_F
));
579 static struct cprotect
*
582 struct cprotect
*cp_entry
;
584 MALLOC(cp_entry
, struct cprotect
*, sizeof(struct cprotect
),
586 if (cp_entry
== NULL
)
589 bzero(cp_entry
, sizeof(*cp_entry
));
595 * Reads xattr data off the cnode and into provided xattr.
596 * cnode lock held shared
599 cp_getxattr(cnode_t
*cnode
, struct cp_xattr
*outxattr
)
602 char uio_buf
[UIO_SIZEOF(1)];
603 size_t attrsize
= sizeof(struct cp_xattr
);
605 struct vnop_getxattr_args args
;
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
);
610 args
.a_desc
= NULL
; // unused
611 args
.a_vp
= cnode
->c_vp
;
612 args
.a_name
= CONTENT_PROTECTION_XATTR_NAME
;
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
) {
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
);
635 * Stores new xattr data on the cnode.
636 * cnode lock held exclusive
639 cp_setxattr(cnode_t
*cnode
, struct cp_xattr
*newxattr
, int options
)
642 struct vnop_setxattr_args args
;
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();
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
);
658 error
= hfs_setxattr_internal(cnode
, (caddr_t
)newxattr
,
659 sizeof(struct cp_xattr
), &args
, VTOHFS(cnode
->c_vp
), 0);
661 if ((error
== KERN_SUCCESS
) && (cnode
->c_cpentry
)) {
662 cnode
->c_cpentry
->cp_flags
&= ~CP_NO_XATTR
;
670 * Make a new random per-file key and wrap it.
673 cp_make_keys(struct cprotect
*entry
)
677 if (g_cp_state
.wrap_functions_set
!= 1) {
678 printf("hfs: CP: could not create keys: no wrappers set\n");
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
;
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]);
692 panic("could not wrap new key in class D\n");
695 /* ready for business */
696 entry
->cp_flags
&= ~CP_NEEDS_KEYS
;
697 entry
->cp_flags
|= CP_NO_XATTR
;
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
708 cp_restore_keys(struct cprotect
*entry
)
712 error
= cp_unwrap(entry
->cp_pclass
,
713 &entry
->cp_persistent_key
[0],
714 &entry
->cp_cache_key
[0]);
717 entry
->cp_flags
|= CP_KEY_FLUSHED
;
718 bzero(entry
->cp_cache_key
, CP_KEYSIZE
);
722 entry
->cp_flags
&= ~CP_KEY_FLUSHED
;
728 cp_lock_vfs_callback(mount_t mp
, void *arg
)
730 if (!cp_fs_protected(mp
)) {
731 /* not interested in this mount point */
735 return vnode_iterate(mp
, 0, cp_lock_vnode_callback
, arg
);
740 * Deny access to protected files if keys have been locked.
742 * cnode lock is taken shared.
745 cp_check_access(cnode_t
*cnode
, int vnop
)
749 if (g_cp_state
.lock_state
== CP_UNLOCKED_STATE
) {
753 if (!cnode
->c_cpentry
) {
754 /* unprotected node */
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
: {
764 case PROTECTION_CLASS_B
: {
765 if (vnop
& CP_READ_ACCESS
)
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).
787 cp_lock_vnode_callback(vnode_t vp
, void *arg
)
790 struct cprotect
*entry
= NULL
;
795 error
= vnode_getwithref (vp
);
801 hfs_lock(cp
, HFS_FORCE_LOCK
);
803 entry
= cp
->c_cpentry
;
805 /* unprotected vnode: not a regular file */
809 action
= (int)((uintptr_t) arg
);
811 case CP_LOCKED_STATE
: {
813 if (entry
->cp_pclass
!= PROTECTION_CLASS_A
) {
814 /* no change at lock for other classes */
818 /* Before doing anything else, zero-fille sparse ranges as needed */
819 ctx
= vfs_context_current();
820 (void) hfs_filedone (vp
, ctx
);
822 /* first, sync back dirty pages */
824 ubc_msync (vp
, 0, ubc_getsize(vp
), NULL
, UBC_PUSHALL
| UBC_INVALIDATE
| UBC_SYNC
);
825 hfs_lock (cp
, HFS_FORCE_LOCK
);
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 */
834 ubc_msync (vp
, 0, ubc_getsize(vp
), NULL
, UBC_INVALIDATE
| UBC_SYNC
);
837 case CP_UNLOCKED_STATE
: {
842 panic("unknown lock action %d\n", action
);
853 cp_wrap(int class, void *inkey
, void *outkey
)
856 size_t keyln
= CP_WRAPPEDKEYSIZE
;
858 if (class == PROTECTION_CLASS_F
) {
859 bzero(outkey
, CP_WRAPPEDKEYSIZE
);
863 error
= g_cp_wrap_func
.wrapper(class,
874 cp_unwrap(int class, void *inkey
, void *outkey
)
877 size_t keyln
= CP_KEYSIZE
;
879 if (class == PROTECTION_CLASS_F
) {
880 /* we didn't save a wrapped key, so nothing to unwrap */
884 error
= g_cp_wrap_func
.unwrapper(class,
897 int cp_key_store_action(int action __unused
)
903 int cp_register_wraps(cp_wrap_func_t key_store_func __unused
)
908 #endif /* CONFIG_PROTECT */