]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_translocate/lib/SecTranslocateDANotification.cpp
Security-57740.51.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 CFDictionaryRef (*DiskCopyDescription_t)(DADiskRef);
43 typedef DASessionRef (*SessionCreate_t) (CFAllocatorRef);
44 typedef void (*SessionSetDispatchQueue_t)(DASessionRef,dispatch_queue_t);
45 typedef void (*RegisterDiskDisappearedCallback_t) (DASessionRef, CFDictionaryRef, DADiskDisappearedCallback, void*);
46 typedef void (*RegisterDiskUnmountApprovalCallback_t) (DASessionRef, CFDictionaryRef, DADiskUnmountApprovalCallback, void*);
47 typedef void (*UnregisterCallback_t)(DASessionRef, void*, void*);
48
49 class DiskArbitrationProxy
50 {
51 public:
52 static DiskArbitrationProxy* get();
53
54 inline CFDictionaryRef diskCopyDescription(DADiskRef disk) const
55 { return pDiskCopyDescription ? pDiskCopyDescription(disk) : NULL; };
56 inline DASessionRef sessionCreate (CFAllocatorRef allocator) const
57 { return pSessionCreate ? pSessionCreate(allocator) : NULL; };
58 inline void sessionSetDispatchQueue (DASessionRef s, dispatch_queue_t q) const
59 { if(pSessionSetDispatchQueue) pSessionSetDispatchQueue(s,q); };
60 inline void registerDiskDisappearedCallback (DASessionRef s, CFDictionaryRef d, DADiskDisappearedCallback c, void* x) const
61 { if(pRegisterDiskDisappearedCallback) pRegisterDiskDisappearedCallback(s,d,c,x); };
62 inline void registerDiskUnmountApprovalCallback (DASessionRef s, CFDictionaryRef d, DADiskUnmountApprovalCallback c, void* x) const
63 { if(pRegisterDiskUnmountApprovalCallback) pRegisterDiskUnmountApprovalCallback(s,d,c,x); };
64 inline void unregisterCallback (DASessionRef s, void* c, void* x) const
65 { if(pUnregisterCallback) pUnregisterCallback(s,c,x); };
66 inline CFDictionaryRef diskDescriptionMatchVolumeMountable() const
67 { return pDiskDescriptionMatchVolumeMountable ? *pDiskDescriptionMatchVolumeMountable : NULL; };
68 inline CFStringRef diskDescriptionVolumePathKey() const
69 { return pDiskDescriptionVolumePathKey ? *pDiskDescriptionVolumePathKey : NULL; };
70
71 private:
72 DiskArbitrationProxy();
73
74 void* handle;
75 DiskCopyDescription_t pDiskCopyDescription;
76 SessionCreate_t pSessionCreate;
77 SessionSetDispatchQueue_t pSessionSetDispatchQueue;
78 RegisterDiskDisappearedCallback_t pRegisterDiskDisappearedCallback;
79 RegisterDiskUnmountApprovalCallback_t pRegisterDiskUnmountApprovalCallback;
80 UnregisterCallback_t pUnregisterCallback;
81 CFDictionaryRef* pDiskDescriptionMatchVolumeMountable;
82 CFStringRef* pDiskDescriptionVolumePathKey;
83 };
84
85 DiskArbitrationProxy::DiskArbitrationProxy()
86 {
87 handle = checkedDlopen(DA_FRAMEWORK_PATH, RTLD_LAZY | RTLD_NOLOAD);
88
89 pDiskCopyDescription = (DiskCopyDescription_t) checkedDlsym(handle, "DADiskCopyDescription");
90 pSessionCreate = (SessionCreate_t) checkedDlsym(handle, "DASessionCreate");
91
92 pSessionSetDispatchQueue = (SessionSetDispatchQueue_t) checkedDlsym(handle, "DASessionSetDispatchQueue");
93 pRegisterDiskDisappearedCallback = (RegisterDiskDisappearedCallback_t) checkedDlsym(handle, "DARegisterDiskDisappearedCallback");
94 pRegisterDiskUnmountApprovalCallback = (RegisterDiskUnmountApprovalCallback_t) checkedDlsym(handle, "DARegisterDiskUnmountApprovalCallback");
95 pUnregisterCallback = (UnregisterCallback_t) checkedDlsym(handle, "DAUnregisterCallback");
96 pDiskDescriptionMatchVolumeMountable = (CFDictionaryRef*) checkedDlsym(handle, "kDADiskDescriptionMatchVolumeMountable");
97 pDiskDescriptionVolumePathKey = (CFStringRef*) checkedDlsym(handle, "kDADiskDescriptionVolumePathKey");
98 }
99
100 DiskArbitrationProxy* DiskArbitrationProxy::get()
101 {
102 static dispatch_once_t initialized;
103 static DiskArbitrationProxy* me = NULL;
104 __block exception_ptr exception(0);
105
106 dispatch_once(&initialized, ^{
107 try
108 {
109 me = new DiskArbitrationProxy();
110 }
111 catch (...)
112 {
113 Syslog::critical("SecTranslocate: error while creating DiskArbitrationProxy");
114 exception = current_exception();
115 }
116 });
117
118 if (me == NULL)
119 {
120 if(exception)
121 {
122 rethrow_exception(exception); //already logged in this case
123 }
124 else
125 {
126 Syslog::critical("SecTranslocate: DiskArbitrationProxy initialization has failed");
127 UnixError::throwMe(EINVAL);
128 }
129 }
130
131 return me;
132 }
133 /*
134 For Disk Arbitration need to
135 1. create a session and hold on to it.
136 2. associate it with a queue
137 3. register for call backs (DADiskDisappearedCallback and DADiskUnmountApprovalCallback)
138 4. provide a function to get the mounton path from DADiskref
139 5. Return a dissenter if unmount is gonna fail because something is in use (i.e. if my unmount fails)
140 */
141
142 /* Returns false if we failed an unmount call. anything else returns true */
143 static bool cleanupDisksOnVolume(DADiskRef disk)
144 {
145 bool result = true;
146 string fspathString;
147 try
148 {
149 DiskArbitrationProxy *dap = DiskArbitrationProxy::get();
150 CFRef<CFDictionaryRef> dict = dap->diskCopyDescription(disk);
151
152 if(!dict)
153 {
154 Syslog::error("SecTranslocate:disk cleanup, failed to get disk description");
155 UnixError::throwMe(EINVAL);
156 }
157
158 CFURLRef fspath = (CFURLRef)CFDictionaryGetValue(dict, dap->diskDescriptionVolumePathKey());
159
160 if(fspath)
161 {
162 //For the disk disappeared call back, it looks like we won't get a volume path so we'll keep the empty string
163 fspathString = cfString(fspath);
164 }
165
166 result = destroyTranslocatedPathsForUserOnVolume(fspathString);
167 }
168 catch (...)
169 {
170 // This function is called from inside a DiskArbitration callback so we need to consume the exception
171 // more specific errors are assumed to be logged by the thrower
172 Syslog::error("SecTranslocate: DiskArbitration callback: failed to clean up mountpoint(s) related to volume: %s",
173 fspathString.empty() ? "unknown" : fspathString.c_str());
174 }
175
176 return result;
177 }
178
179 static void diskDisappearedCallback(DADiskRef disk, void* context)
180 {
181 (void)cleanupDisksOnVolume(disk);
182 }
183
184 static DADissenterRef unmountApprovalCallback(DADiskRef disk, void *context)
185 {
186 (void)cleanupDisksOnVolume(disk);
187 return NULL; //For now, we won't raise a dissent, just let the unmount fail. The dissent text would get used by UI.
188 }
189
190 DANotificationMonitor::DANotificationMonitor(dispatch_queue_t q)
191 {
192 DiskArbitrationProxy *dap = DiskArbitrationProxy::get();
193 if (q == NULL)
194 {
195 Syslog::critical("SecTranslocate::DANotificationMonitor initialized without a queue.");
196 UnixError::throwMe(EINVAL);
197 }
198
199 diskArbitrationSession = dap->sessionCreate(kCFAllocatorDefault);
200 if(!diskArbitrationSession)
201 {
202 Syslog::critical("SecTranslocate: Failed to create the disk arbitration session");
203 UnixError::throwMe(ENOMEM);
204 }
205
206 dap->sessionSetDispatchQueue(diskArbitrationSession, q);
207 /* register so we can cleanup from force unmounts */
208 dap->registerDiskDisappearedCallback( diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), diskDisappearedCallback, NULL );
209 /* register so we can clean up pre-unmount */
210 dap->registerDiskUnmountApprovalCallback( diskArbitrationSession, dap->diskDescriptionMatchVolumeMountable(), unmountApprovalCallback, NULL );
211 }
212
213 DANotificationMonitor::~DANotificationMonitor()
214 {
215 DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession,(void*)diskDisappearedCallback, NULL);
216 DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession,(void*)unmountApprovalCallback, NULL);
217 CFRelease(diskArbitrationSession);
218 }
219
220 } //namespace SecTranslocate
221 } //namespace Security