1/*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. 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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <mach/mach_port.h>
29
30#include <IOKit/kext/OSKext.h>
31#include "kext_tools_util.h"
32#include "kextd_globals.h"
33#include <dispatch/dispatch.h>
34
35dispatch_source_t _gKextutilLock = NULL;
36
37void kextd_process_kernel_requests(void);
38
39/******************************************************************************
40 * _kextmanager_lock_volume tries to lock volumes for clients (kextutil)
41 *****************************************************************************/
42static void removeKextutilLock(void)
43{
44    if (_gKextutilLock) {
45        dispatch_source_cancel(_gKextutilLock);
46    }
47
48    if (gKernelRequestsPending) {
49        kextd_process_kernel_requests();
50    }
51
52    CFRunLoopWakeUp(CFRunLoopGetCurrent());
53    return;
54}
55
56/******************************************************************************
57 * _kextmanager_lock_volume tries to lock volumes for clients (kextutil)
58 *****************************************************************************/
59kern_return_t _kextmanager_lock_kextload(
60    mach_port_t server,
61    mach_port_t client,
62    int * lockstatus)
63{
64    kern_return_t mig_result = KERN_FAILURE;
65    int result;
66
67    if (!lockstatus) {
68        OSKextLog(/* kext */ NULL,
69            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
70            "kextmanager_lock_kextload requires non-NULL lockstatus.");
71        mig_result = KERN_SUCCESS;
72        result = EINVAL;
73        goto finish;
74    }
75
76    if (gClientUID != 0) {
77        OSKextLog(/* kext */ NULL,
78            kOSKextLogErrorLevel,
79            "Non-root process doesn't need to lock as it will fail to load.");
80        mig_result = KERN_SUCCESS;
81        result = EPERM;
82        goto finish;
83    }
84
85    if (_gKextutilLock) {
86        mig_result = KERN_SUCCESS;
87        result = EBUSY;
88        goto finish;
89    }
90
91    result = ENOMEM;
92
93    _gKextutilLock = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, client,
94                        DISPATCH_MACH_SEND_DEAD, dispatch_get_main_queue());
95
96    if (_gKextutilLock) {
97
98        dispatch_source_set_event_handler(_gKextutilLock, ^{
99                OSKextLog(/* kext */ NULL,
100                    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
101                    "Client exited without releasing kextutil lock.");
102                removeKextutilLock();
103            });
104
105        dispatch_source_set_cancel_handler(_gKextutilLock, ^{
106                dispatch_release(_gKextutilLock);
107                mach_port_deallocate(mach_task_self(), client);
108                _gKextutilLock = NULL;
109            });
110
111        dispatch_resume(_gKextutilLock);
112
113        mig_result = KERN_SUCCESS;
114        result = 0;
115    }
116
117finish:
118    if (mig_result != KERN_SUCCESS) {
119        if (gClientUID == 0) {
120            OSKextLog(/* kext */ NULL,
121                kOSKextLogErrorLevel | kOSKextLogIPCFlag,
122                "Trouble while locking for kextutil - %s.",
123                safe_mach_error_string(mig_result));
124        }
125        removeKextutilLock();
126    } else if (lockstatus) {
127        *lockstatus = result;  // only meaningful if mig_result == KERN_SUCCESS
128    }
129
130    return mig_result;
131}
132
133/******************************************************************************
134 * _kextmanager_unlock_kextload unlocks for clients (kextutil)
135 *****************************************************************************/
136kern_return_t _kextmanager_unlock_kextload(
137    mach_port_t server,
138    mach_port_t client)
139{
140    kern_return_t mig_result = KERN_FAILURE;
141
142    if (gClientUID != 0) {
143        OSKextLog(/* kext */ NULL,
144            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
145            "Non-root kextutil doesn't need to lock/unlock.");
146        mig_result = KERN_SUCCESS;
147        goto finish;
148    }
149
150    if (client != (mach_port_t)dispatch_source_get_handle(_gKextutilLock)) {
151        OSKextLog(/* kext */ NULL,
152            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
153            "%d not used to lock for kextutil.", client);
154        goto finish;
155    }
156
157    removeKextutilLock();
158
159    mig_result = KERN_SUCCESS;
160
161finish:
162    // we don't need the extra send right added by MiG
163    mach_port_deallocate(mach_task_self(), client);
164
165    return mig_result;
166}
167
168