]>
Commit | Line | Data |
---|---|---|
fa7225c8 A |
1 | /* |
2 | * Copyright (c) 2016 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <exception> | |
25 | #include <dlfcn.h> | |
26 | #include <dispatch/dispatch.h> | |
27 | #include <DiskArbitration/DiskArbitration.h> | |
28 | ||
29 | #include <security_utilities/logging.h> | |
30 | #include <security_utilities/cfutilities.h> | |
31 | #include <security_utilities/unix++.h> | |
32 | ||
33 | #include "SecTranslocateDANotification.hpp" | |
34 | #include "SecTranslocateShared.hpp" | |
6b200bc3 | 35 | #include "SecTranslocateUtilities.hpp" |
fa7225c8 A |
36 | |
37 | #define DA_FRAMEWORK_PATH "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration" | |
38 | ||
39 | namespace Security { | |
40 | namespace SecTranslocate { | |
41 | ||
866f8763 A |
42 | typedef enum { |
43 | SCT_DA_DISK_DISAPPEARED, | |
44 | SCT_DA_DISK_UNMOUNT_APPROVAL, | |
45 | SCT_DA_DISK_DESCRIPTION_CHANGE | |
46 | } SecTranslocateDACallbackType_t; | |
47 | ||
fa7225c8 A |
48 | typedef CFDictionaryRef (*DiskCopyDescription_t)(DADiskRef); |
49 | typedef DASessionRef (*SessionCreate_t) (CFAllocatorRef); | |
50 | typedef void (*SessionSetDispatchQueue_t)(DASessionRef,dispatch_queue_t); | |
51 | typedef void (*RegisterDiskDisappearedCallback_t) (DASessionRef, CFDictionaryRef, DADiskDisappearedCallback, void*); | |
52 | typedef void (*RegisterDiskUnmountApprovalCallback_t) (DASessionRef, CFDictionaryRef, DADiskUnmountApprovalCallback, void*); | |
866f8763 A |
53 | typedef void (*RegisterDiskDescriptionChangedCallback_t) (DASessionRef, CFDictionaryRef, CFArrayRef, |
54 | DADiskDescriptionChangedCallback, void*); | |
fa7225c8 A |
55 | typedef void (*UnregisterCallback_t)(DASessionRef, void*, void*); |
56 | ||
57 | class DiskArbitrationProxy | |
58 | { | |
59 | public: | |
60 | static DiskArbitrationProxy* get(); | |
61 | ||
62 | inline CFDictionaryRef diskCopyDescription(DADiskRef disk) const | |
63 | { return pDiskCopyDescription ? pDiskCopyDescription(disk) : NULL; }; | |
64 | inline DASessionRef sessionCreate (CFAllocatorRef allocator) const | |
65 | { return pSessionCreate ? pSessionCreate(allocator) : NULL; }; | |
66 | inline void sessionSetDispatchQueue (DASessionRef s, dispatch_queue_t q) const | |
67 | { if(pSessionSetDispatchQueue) pSessionSetDispatchQueue(s,q); }; | |
68 | inline void registerDiskDisappearedCallback (DASessionRef s, CFDictionaryRef d, DADiskDisappearedCallback c, void* x) const | |
69 | { if(pRegisterDiskDisappearedCallback) pRegisterDiskDisappearedCallback(s,d,c,x); }; | |
70 | inline void registerDiskUnmountApprovalCallback (DASessionRef s, CFDictionaryRef d, DADiskUnmountApprovalCallback c, void* x) const | |
71 | { if(pRegisterDiskUnmountApprovalCallback) pRegisterDiskUnmountApprovalCallback(s,d,c,x); }; | |
866f8763 A |
72 | inline void registerDiskDescriptionChangedCallback (DASessionRef s, CFDictionaryRef d, CFArrayRef a, DADiskDescriptionChangedCallback c, void* x) const |
73 | { if(pRegisterDiskDescriptionChangedCallback) pRegisterDiskDescriptionChangedCallback(s,d,a,c,x); }; | |
fa7225c8 A |
74 | inline void unregisterCallback (DASessionRef s, void* c, void* x) const |
75 | { if(pUnregisterCallback) pUnregisterCallback(s,c,x); }; | |
76 | inline CFDictionaryRef diskDescriptionMatchVolumeMountable() const | |
77 | { return pDiskDescriptionMatchVolumeMountable ? *pDiskDescriptionMatchVolumeMountable : NULL; }; | |
78 | inline CFStringRef diskDescriptionVolumePathKey() const | |
79 | { return pDiskDescriptionVolumePathKey ? *pDiskDescriptionVolumePathKey : NULL; }; | |
80 | ||
81 | private: | |
82 | DiskArbitrationProxy(); | |
83 | ||
84 | void* handle; | |
85 | DiskCopyDescription_t pDiskCopyDescription; | |
86 | SessionCreate_t pSessionCreate; | |
87 | SessionSetDispatchQueue_t pSessionSetDispatchQueue; | |
88 | RegisterDiskDisappearedCallback_t pRegisterDiskDisappearedCallback; | |
89 | RegisterDiskUnmountApprovalCallback_t pRegisterDiskUnmountApprovalCallback; | |
866f8763 | 90 | RegisterDiskDescriptionChangedCallback_t pRegisterDiskDescriptionChangedCallback; |
fa7225c8 A |
91 | UnregisterCallback_t pUnregisterCallback; |
92 | CFDictionaryRef* pDiskDescriptionMatchVolumeMountable; | |
93 | CFStringRef* pDiskDescriptionVolumePathKey; | |
94 | }; | |
95 | ||
96 | DiskArbitrationProxy::DiskArbitrationProxy() | |
97 | { | |
98 | handle = checkedDlopen(DA_FRAMEWORK_PATH, RTLD_LAZY | RTLD_NOLOAD); | |
99 | ||
100 | pDiskCopyDescription = (DiskCopyDescription_t) checkedDlsym(handle, "DADiskCopyDescription"); | |
101 | pSessionCreate = (SessionCreate_t) checkedDlsym(handle, "DASessionCreate"); | |
102 | ||
103 | pSessionSetDispatchQueue = (SessionSetDispatchQueue_t) checkedDlsym(handle, "DASessionSetDispatchQueue"); | |
104 | pRegisterDiskDisappearedCallback = (RegisterDiskDisappearedCallback_t) checkedDlsym(handle, "DARegisterDiskDisappearedCallback"); | |
105 | pRegisterDiskUnmountApprovalCallback = (RegisterDiskUnmountApprovalCallback_t) checkedDlsym(handle, "DARegisterDiskUnmountApprovalCallback"); | |
866f8763 | 106 | pRegisterDiskDescriptionChangedCallback = (RegisterDiskDescriptionChangedCallback_t) checkedDlsym(handle,"DARegisterDiskDescriptionChangedCallback"); |
fa7225c8 A |
107 | pUnregisterCallback = (UnregisterCallback_t) checkedDlsym(handle, "DAUnregisterCallback"); |
108 | pDiskDescriptionMatchVolumeMountable = (CFDictionaryRef*) checkedDlsym(handle, "kDADiskDescriptionMatchVolumeMountable"); | |
109 | pDiskDescriptionVolumePathKey = (CFStringRef*) checkedDlsym(handle, "kDADiskDescriptionVolumePathKey"); | |
110 | } | |
111 | ||
112 | DiskArbitrationProxy* DiskArbitrationProxy::get() | |
113 | { | |
114 | static dispatch_once_t initialized; | |
115 | static DiskArbitrationProxy* me = NULL; | |
116 | __block exception_ptr exception(0); | |
117 | ||
118 | dispatch_once(&initialized, ^{ | |
119 | try | |
120 | { | |
121 | me = new DiskArbitrationProxy(); | |
122 | } | |
123 | catch (...) | |
124 | { | |
125 | Syslog::critical("SecTranslocate: error while creating DiskArbitrationProxy"); | |
126 | exception = current_exception(); | |
127 | } | |
128 | }); | |
129 | ||
130 | if (me == NULL) | |
131 | { | |
132 | if(exception) | |
133 | { | |
134 | rethrow_exception(exception); //already logged in this case | |
135 | } | |
136 | else | |
137 | { | |
138 | Syslog::critical("SecTranslocate: DiskArbitrationProxy initialization has failed"); | |
139 | UnixError::throwMe(EINVAL); | |
140 | } | |
141 | } | |
142 | ||
143 | return me; | |
144 | } | |
145 | /* | |
146 | For Disk Arbitration need to | |
147 | 1. create a session and hold on to it. | |
148 | 2. associate it with a queue | |
866f8763 | 149 | 3. register for call backs (DADiskDisappearedCallback, DADiskDescriptionChangedCallback and DADiskUnmountApprovalCallback) |
fa7225c8 A |
150 | 4. provide a function to get the mounton path from DADiskref |
151 | 5. Return a dissenter if unmount is gonna fail because something is in use (i.e. if my unmount fails) | |
152 | */ | |
153 | ||
154 | /* Returns false if we failed an unmount call. anything else returns true */ | |
866f8763 | 155 | static bool cleanupDisksOnVolume(DADiskRef disk, SecTranslocateDACallbackType_t type) |
fa7225c8 A |
156 | { |
157 | bool result = true; | |
158 | string fspathString; | |
159 | try | |
160 | { | |
161 | DiskArbitrationProxy *dap = DiskArbitrationProxy::get(); | |
162 | CFRef<CFDictionaryRef> dict = dap->diskCopyDescription(disk); | |
163 | ||
164 | if(!dict) | |
165 | { | |
166 | Syslog::error("SecTranslocate:disk cleanup, failed to get disk description"); | |
167 | UnixError::throwMe(EINVAL); | |
168 | } | |
169 | ||
170 | CFURLRef fspath = (CFURLRef)CFDictionaryGetValue(dict, dap->diskDescriptionVolumePathKey()); | |
171 | ||
172 | if(fspath) | |
173 | { | |
866f8763 A |
174 | if (type != SCT_DA_DISK_DESCRIPTION_CHANGE) { |
175 | //For the disk disappeared call back, it looks like we won't get a volume path so we'll keep the empty string | |
176 | fspathString = cfString(fspath); | |
177 | } else { | |
178 | // For disk description changed, volume path key will be populated on mount and null on unmount | |
179 | // we only care about unmount, so bail if there is a volume path key | |
180 | return result; | |
181 | } | |
fa7225c8 A |
182 | } |
183 | ||
184 | result = destroyTranslocatedPathsForUserOnVolume(fspathString); | |
185 | } | |
186 | catch (...) | |
187 | { | |
188 | // This function is called from inside a DiskArbitration callback so we need to consume the exception | |
189 | // more specific errors are assumed to be logged by the thrower | |
190 | Syslog::error("SecTranslocate: DiskArbitration callback: failed to clean up mountpoint(s) related to volume: %s", | |
191 | fspathString.empty() ? "unknown" : fspathString.c_str()); | |
192 | } | |
193 | ||
194 | return result; | |
195 | } | |
196 | ||
866f8763 | 197 | static void diskDisappearedCallback(DADiskRef disk, void* __unused context) |
fa7225c8 | 198 | { |
866f8763 | 199 | (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DISAPPEARED); |
fa7225c8 A |
200 | } |
201 | ||
866f8763 | 202 | static DADissenterRef unmountApprovalCallback(DADiskRef disk, void* __unused context) |
fa7225c8 | 203 | { |
866f8763 | 204 | (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_UNMOUNT_APPROVAL); |
fa7225c8 A |
205 | return NULL; //For now, we won't raise a dissent, just let the unmount fail. The dissent text would get used by UI. |
206 | } | |
207 | ||
866f8763 A |
208 | static void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef __unused daKeys, void* __unused context) |
209 | { | |
210 | (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DESCRIPTION_CHANGE); | |
211 | } | |
212 | ||
fa7225c8 A |
213 | DANotificationMonitor::DANotificationMonitor(dispatch_queue_t q) |
214 | { | |
215 | DiskArbitrationProxy *dap = DiskArbitrationProxy::get(); | |
216 | if (q == NULL) | |
217 | { | |
218 | Syslog::critical("SecTranslocate::DANotificationMonitor initialized without a queue."); | |
219 | UnixError::throwMe(EINVAL); | |
220 | } | |
221 | ||
222 | diskArbitrationSession = dap->sessionCreate(kCFAllocatorDefault); | |
223 | if(!diskArbitrationSession) | |
224 | { | |
225 | Syslog::critical("SecTranslocate: Failed to create the disk arbitration session"); | |
226 | UnixError::throwMe(ENOMEM); | |
227 | } | |
228 | ||
229 | dap->sessionSetDispatchQueue(diskArbitrationSession, q); | |
230 | /* register so we can cleanup from force unmounts */ | |
231 | dap->registerDiskDisappearedCallback( diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), diskDisappearedCallback, NULL ); | |
866f8763 | 232 | dap->registerDiskDescriptionChangedCallback(diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), NULL, diskDescriptionChangedCallback, NULL); |
fa7225c8 A |
233 | /* register so we can clean up pre-unmount */ |
234 | dap->registerDiskUnmountApprovalCallback( diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), unmountApprovalCallback, NULL ); | |
235 | } | |
236 | ||
237 | DANotificationMonitor::~DANotificationMonitor() | |
238 | { | |
239 | DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession,(void*)diskDisappearedCallback, NULL); | |
240 | DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession,(void*)unmountApprovalCallback, NULL); | |
241 | CFRelease(diskArbitrationSession); | |
242 | } | |
243 | ||
244 | } //namespace SecTranslocate | |
245 | } //namespace Security |