]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_translocate/lib/SecTranslocateDANotification.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_translocate / lib / SecTranslocateDANotification.cpp
CommitLineData
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
39namespace Security {
40namespace SecTranslocate {
41
866f8763
A
42typedef enum {
43 SCT_DA_DISK_DISAPPEARED,
44 SCT_DA_DISK_UNMOUNT_APPROVAL,
45 SCT_DA_DISK_DESCRIPTION_CHANGE
46} SecTranslocateDACallbackType_t;
47
fa7225c8
A
48typedef CFDictionaryRef (*DiskCopyDescription_t)(DADiskRef);
49typedef DASessionRef (*SessionCreate_t) (CFAllocatorRef);
50typedef void (*SessionSetDispatchQueue_t)(DASessionRef,dispatch_queue_t);
51typedef void (*RegisterDiskDisappearedCallback_t) (DASessionRef, CFDictionaryRef, DADiskDisappearedCallback, void*);
52typedef void (*RegisterDiskUnmountApprovalCallback_t) (DASessionRef, CFDictionaryRef, DADiskUnmountApprovalCallback, void*);
866f8763
A
53typedef void (*RegisterDiskDescriptionChangedCallback_t) (DASessionRef, CFDictionaryRef, CFArrayRef,
54 DADiskDescriptionChangedCallback, void*);
fa7225c8
A
55typedef void (*UnregisterCallback_t)(DASessionRef, void*, void*);
56
57class DiskArbitrationProxy
58{
59public:
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
81private:
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
96DiskArbitrationProxy::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
112DiskArbitrationProxy* 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 155static 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 197static void diskDisappearedCallback(DADiskRef disk, void* __unused context)
fa7225c8 198{
866f8763 199 (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DISAPPEARED);
fa7225c8
A
200}
201
866f8763 202static 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
208static void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef __unused daKeys, void* __unused context)
209{
210 (void)cleanupDisksOnVolume(disk, SCT_DA_DISK_DESCRIPTION_CHANGE);
211}
212
fa7225c8
A
213DANotificationMonitor::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
237DANotificationMonitor::~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