]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_translocate/lib/SecTranslocateDANotification.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_translocate / lib / SecTranslocateDANotification.cpp
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"
35 #include "SecTranslocateUtilities.hpp"
36
37 #define DA_FRAMEWORK_PATH "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration"
38
39 namespace Security {
40 namespace SecTranslocate {
41
42 typedef enum {
43 SCT_DA_DISK_DISAPPEARED,
44 SCT_DA_DISK_UNMOUNT_APPROVAL,
45 SCT_DA_DISK_DESCRIPTION_CHANGE
46 } SecTranslocateDACallbackType_t;
47
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*);
53 typedef void (*RegisterDiskDescriptionChangedCallback_t) (DASessionRef, CFDictionaryRef, CFArrayRef,
54 DADiskDescriptionChangedCallback, void*);
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); };
72 inline void registerDiskDescriptionChangedCallback (DASessionRef s, CFDictionaryRef d, CFArrayRef a, DADiskDescriptionChangedCallback c, void* x) const
73 { if(pRegisterDiskDescriptionChangedCallback) pRegisterDiskDescriptionChangedCallback(s,d,a,c,x); };
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;
90 RegisterDiskDescriptionChangedCallback_t pRegisterDiskDescriptionChangedCallback;
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");
106 pRegisterDiskDescriptionChangedCallback = (RegisterDiskDescriptionChangedCallback_t) checkedDlsym(handle,"DARegisterDiskDescriptionChangedCallback");
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
149 3. register for call backs (DADiskDisappearedCallback, DADiskDescriptionChangedCallback and DADiskUnmountApprovalCallback)
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 */
155 static bool cleanupDisksOnVolume(DADiskRef disk, SecTranslocateDACallbackType_t type)
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 {
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 }
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
197 static void diskDisappearedCallback(DADiskRef disk, void* __unused context)
198 {
199 (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DISAPPEARED);
200 }
201
202 static DADissenterRef unmountApprovalCallback(DADiskRef disk, void* __unused context)
203 {
204 (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_UNMOUNT_APPROVAL);
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
208 static void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef __unused daKeys, void* __unused context)
209 {
210 (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DESCRIPTION_CHANGE);
211 }
212
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 );
232 dap->registerDiskDescriptionChangedCallback(diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), NULL, diskDescriptionChangedCallback, NULL);
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