1/*
2 * Copyright (c) 2000-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 <CoreFoundation/CoreFoundation.h>
29
30#include <mach/mach.h>
31#include <mach/mach_port.h>
32#include <servers/bootstrap.h>
33#include <sysexits.h>
34
35#include <IOKit/kext/OSKext.h>
36#include "kext_tools_util.h"
37#include "kextd_globals.h"
38#include "kextd_mig_server.h"
39
40/* mig-generated externals and functions */
41extern struct mig_subsystem _kextmanager_subsystem;
42extern boolean_t kextmanager_server(mach_msg_header_t *, mach_msg_header_t *);
43
44extern struct mig_subsystem svc_kextd_kernel_request_subsystem;
45extern boolean_t kextd_kernel_request_server(
46        mach_msg_header_t *InHeadP,
47        mach_msg_header_t *OutHeadP);
48
49uid_t gClientUID = -1;
50
51/*******************************************************************************
52*******************************************************************************/
53boolean_t kextd_demux(
54    mach_msg_header_t * request,
55    mach_msg_header_t * reply)
56{
57    boolean_t processed = FALSE;
58
59    mach_msg_format_0_trailer_t * trailer;
60
61   /* Feed the request into the ("MiG" generated) server. We have two
62    * subsystems, one for user-space clients, one for the kernel.
63    */
64    if (!processed) {
65        if (request->msgh_id >= _kextmanager_subsystem.start &&
66            request->msgh_id < _kextmanager_subsystem.end) {
67
68            /*
69             * Get the caller's credentials (eUID/eGID) from the message trailer.
70             */
71            trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
72                round_msg(request->msgh_size));
73
74            if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
75               (trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
76
77                gClientUID = trailer->msgh_sender.val[0];
78
79                OSKextLog(/* kext */ NULL,
80                    kOSKextLogDebugLevel | kOSKextLogIPCFlag,
81                    "MIG message received: caller has eUID = %d, eGID = %d.",
82                    trailer->msgh_sender.val[0],
83                    trailer->msgh_sender.val[1]);
84
85            } else {
86                OSKextLog(/* kext */ NULL,
87                    kOSKextLogWarningLevel | kOSKextLogIPCFlag,
88                    "Caller's credentials not available.");
89                gClientUID = -1;
90
91            }
92
93           /* Process user task requests.
94            */
95            processed = kextmanager_server(request, reply);
96
97        } else if (request->msgh_id >= svc_kextd_kernel_request_subsystem.start &&
98            request->msgh_id < svc_kextd_kernel_request_subsystem.end) {
99
100           /* Process kernel requests.
101            */
102            processed = kextd_kernel_request_server(request, reply);
103        }
104    }
105
106    if (!processed) {
107        if (request->msgh_id >= MACH_NOTIFY_FIRST &&
108            request->msgh_id < MACH_NOTIFY_LAST) {
109
110            OSKextLog(/* kext */ NULL,
111                kOSKextLogErrorLevel | kOSKextLogIPCFlag,
112                "Failed to process MIG message.");
113        } else {
114            OSKextLog(/* kext */ NULL,
115                kOSKextLogErrorLevel | kOSKextLogIPCFlag,
116                "Unknown MIG message received.");
117        }
118    }
119
120    gClientUID = (uid_t)-1;
121
122    return processed;
123}
124
125/*******************************************************************************
126*******************************************************************************/
127void kextd_mach_port_callback(
128    CFMachPortRef port,
129    void *msg,
130    CFIndex size,
131    void *info)
132{
133    mig_reply_error_t * bufRequest = msg;
134    mig_reply_error_t * bufReply = CFAllocatorAllocate(
135        NULL, _kextmanager_subsystem.maxsize, 0);
136    mach_msg_return_t   mr;
137    int                 options;
138
139    /* we have a request message */
140    (void) kextd_demux(&bufRequest->Head, &bufReply->Head);
141
142    if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
143        (bufReply->RetCode != KERN_SUCCESS)) {
144
145        if (bufReply->RetCode == MIG_NO_REPLY) {
146            /*
147             * This return code is a little tricky -- it appears that the
148             * demux routine found an error of some sort, but since that
149             * error would not normally get returned either to the local
150             * user or the remote one, we pretend it's ok.
151             */
152            CFAllocatorDeallocate(NULL, bufReply);
153            return;
154        }
155
156        /*
157         * destroy any out-of-line data in the request buffer but don't destroy
158         * the reply port right (since we need that to send an error message).
159         */
160        bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
161        mach_msg_destroy(&bufRequest->Head);
162    }
163
164    if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
165        /* no reply port, so destroy the reply */
166        if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
167            mach_msg_destroy(&bufReply->Head);
168        }
169        CFAllocatorDeallocate(NULL, bufReply);
170        return;
171    }
172
173    /*
174     * send reply.
175     *
176     * We don't want to block indefinitely because the client
177     * isn't receiving messages from the reply port.
178     * If we have a send-once right for the reply port, then
179     * this isn't a concern because the send won't block.
180     * If we have a send right, we need to use MACH_SEND_TIMEOUT.
181     * To avoid falling off the kernel's fast RPC path unnecessarily,
182     * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
183     */
184
185    options = MACH_SEND_MSG;
186    if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
187        options |= MACH_SEND_TIMEOUT;
188    }
189    mr = mach_msg(&bufReply->Head,        /* msg */
190              options,            /* option */
191              bufReply->Head.msgh_size,    /* send_size */
192              0,            /* rcv_size */
193              MACH_PORT_NULL,        /* rcv_name */
194              MACH_MSG_TIMEOUT_NONE,    /* timeout */
195              MACH_PORT_NULL);        /* notify */
196
197
198    /* Has a message error occurred? */
199    switch (mr) {
200        case MACH_SEND_INVALID_DEST:
201        case MACH_SEND_TIMED_OUT:
202            /* the reply can't be delivered, so destroy it */
203            mach_msg_destroy(&bufReply->Head);
204            break;
205
206        default :
207            /* Includes success case.  */
208            break;
209    }
210
211    CFAllocatorDeallocate(NULL, bufReply);
212}
213