1 | /* |
2 | * Copyright (c) 2000-2019 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 | |
29 | #include <mach/port.h> |
30 | #include <mach/message.h> |
31 | #include <mach/kern_return.h> |
32 | #include <mach/host_priv.h> |
33 | |
34 | #include <kern/kern_types.h> |
35 | #include <kern/kalloc.h> |
36 | #include <kern/host.h> |
37 | #include <kern/ipc_kobject.h> |
38 | |
39 | #include <ipc/ipc_port.h> |
40 | |
41 | #include <UserNotification/UNDTypes.h> |
42 | #include <UserNotification/UNDRequest.h> |
43 | #include <UserNotification/UNDReplyServer.h> |
44 | #include <UserNotification/KUNCUserNotifications.h> |
45 | |
46 | #ifdef KERNEL_CF |
47 | // external |
48 | #include <IOKit/IOCFSerialize.h> |
49 | #include <IOKit/IOCFUnserialize.h> |
50 | #endif |
51 | |
52 | #if CONFIG_USER_NOTIFICATION |
53 | /* |
54 | * DEFINES AND STRUCTURES |
55 | */ |
56 | |
57 | struct UNDReply { |
58 | decl_lck_mtx_data(, lock); /* UNDReply lock */ |
59 | int userLandNotificationKey; |
60 | KUNCUserNotificationCallBack callback; |
61 | boolean_t inprogress; |
62 | ipc_port_t self_port; /* Our port */ |
63 | }; |
64 | |
65 | static void |
66 | UNDReply_no_senders(ipc_port_t port, mach_port_mscount_t mscount); |
67 | |
68 | IPC_KOBJECT_DEFINE(IKOT_UND_REPLY, |
69 | .iko_op_stable = true, |
70 | .iko_op_no_senders = UNDReply_no_senders); |
71 | |
72 | #define UNDReply_lock(reply) lck_mtx_lock(&reply->lock) |
73 | #define UNDReply_unlock(reply) lck_mtx_unlock(&reply->lock) |
74 | |
75 | LCK_GRP_DECLARE(UNDLckGrp, "UND" ); |
76 | |
77 | static UNDServerRef |
78 | UNDServer_reference(void) |
79 | { |
80 | UNDServerRef UNDServer; |
81 | kern_return_t kr; |
82 | |
83 | kr = host_get_user_notification_port(host_priv_self(), &UNDServer); |
84 | assert(kr == KERN_SUCCESS); |
85 | return UNDServer; |
86 | } |
87 | |
88 | static void |
89 | UNDServer_deallocate( |
90 | UNDServerRef UNDServer) |
91 | { |
92 | if (IP_VALID(UNDServer)) { |
93 | ipc_port_release_send(port: UNDServer); |
94 | } |
95 | } |
96 | |
97 | /* |
98 | * UND Mig Callbacks |
99 | */ |
100 | |
101 | kern_return_t |
102 | UNDAlertCompletedWithResult_rpc( |
103 | UNDReplyRef reply, |
104 | int result, |
105 | xmlData_t keyRef, /* raw XML bytes */ |
106 | #ifdef KERNEL_CF |
107 | mach_msg_type_number_t keyLen) |
108 | #else |
109 | __unused mach_msg_type_number_t keyLen) |
110 | #endif |
111 | { |
112 | #ifdef KERNEL_CF |
113 | CFStringRef xmlError = NULL; |
114 | CFDictionaryRef dict = NULL; |
115 | #else |
116 | const void *dict = (const void *)keyRef; |
117 | #endif |
118 | |
119 | if (reply == UND_REPLY_NULL || !reply->inprogress) { |
120 | return KERN_INVALID_ARGUMENT; |
121 | } |
122 | |
123 | /* |
124 | * JMM - No C vesion of the Unserialize code in-kernel |
125 | * and no C type for a CFDictionary either. For now, |
126 | * just pass the raw keyRef through. |
127 | */ |
128 | #ifdef KERNEL_CF |
129 | if (keyRef && keyLen) { |
130 | dict = IOCFUnserialize(keyRef, NULL, NULL, &xmlError); |
131 | } |
132 | |
133 | if (xmlError) { |
134 | CFShow(xmlError); |
135 | CFRelease(xmlError); |
136 | } |
137 | #endif /* KERNEL_CF */ |
138 | |
139 | if (reply->callback) { |
140 | (reply->callback)((int)(KUNCUserNotificationID)reply, result, dict); |
141 | } |
142 | |
143 | UNDReply_lock(reply); |
144 | reply->inprogress = FALSE; |
145 | reply->userLandNotificationKey = -1; |
146 | UNDReply_unlock(reply); |
147 | |
148 | return KERN_SUCCESS; |
149 | } |
150 | |
151 | /* |
152 | * Routine: UNDNotificationCreated_rpc |
153 | * |
154 | * Intermediate routine. Allows the kernel mechanism |
155 | * to be informed that the notification request IS |
156 | * being processed by the user-level daemon, and how |
157 | * to identify that request. |
158 | */ |
159 | kern_return_t |
160 | UNDNotificationCreated_rpc( |
161 | UNDReplyRef reply, |
162 | int userLandNotificationKey) |
163 | { |
164 | if (reply == UND_REPLY_NULL) { |
165 | return KERN_INVALID_ARGUMENT; |
166 | } |
167 | |
168 | UNDReply_lock(reply); |
169 | if (reply->inprogress || reply->userLandNotificationKey != -1) { |
170 | UNDReply_unlock(reply); |
171 | return KERN_INVALID_ARGUMENT; |
172 | } |
173 | reply->userLandNotificationKey = userLandNotificationKey; |
174 | UNDReply_unlock(reply); |
175 | return KERN_SUCCESS; |
176 | } |
177 | |
178 | /* |
179 | * KUNC Functions |
180 | */ |
181 | |
182 | |
183 | KUNCUserNotificationID |
184 | KUNCGetNotificationID(void) |
185 | { |
186 | UNDReplyRef reply; |
187 | |
188 | reply = kalloc_type(struct UNDReply, Z_WAITOK | Z_ZERO | Z_NOFAIL); |
189 | reply->self_port = ipc_kobject_alloc_port(kobject: (ipc_kobject_t)reply, |
190 | type: IKOT_UND_REPLY, options: IPC_KOBJECT_ALLOC_NSREQUEST); |
191 | lck_mtx_init(lck: &reply->lock, grp: &UNDLckGrp, LCK_ATTR_NULL); |
192 | reply->userLandNotificationKey = -1; |
193 | reply->inprogress = FALSE; |
194 | |
195 | return (KUNCUserNotificationID) reply; |
196 | } |
197 | |
198 | static void |
199 | UNDReply_no_senders(ipc_port_t port, mach_port_mscount_t mscount) |
200 | { |
201 | UNDReplyRef reply; |
202 | |
203 | reply = ipc_kobject_dealloc_port(port, mscount, type: IKOT_UND_REPLY); |
204 | lck_mtx_destroy(lck: &reply->lock, grp: &UNDLckGrp); |
205 | kfree_type(struct UNDReply, reply); |
206 | } |
207 | |
208 | kern_return_t |
209 | KUNCExecute(char executionPath[1024], int uid, int gid) |
210 | { |
211 | UNDServerRef UNDServer; |
212 | |
213 | UNDServer = UNDServer_reference(); |
214 | if (IP_VALID(UNDServer)) { |
215 | kern_return_t kr; |
216 | kr = UNDExecute_rpc(server: UNDServer, executionPath, uid, gid); |
217 | UNDServer_deallocate(UNDServer); |
218 | return kr; |
219 | } |
220 | return MACH_SEND_INVALID_DEST; |
221 | } |
222 | |
223 | kern_return_t |
224 | KUNCUserNotificationDisplayNotice( |
225 | int noticeTimeout, |
226 | unsigned flags, |
227 | char *iconPath, |
228 | char *soundPath, |
229 | char *localizationPath, |
230 | char *, |
231 | char *alertMessage, |
232 | char *defaultButtonTitle) |
233 | { |
234 | UNDServerRef UNDServer; |
235 | |
236 | UNDServer = UNDServer_reference(); |
237 | if (IP_VALID(UNDServer)) { |
238 | kern_return_t kr; |
239 | kr = UNDDisplayNoticeSimple_rpc(server: UNDServer, |
240 | rpctimeout: noticeTimeout, |
241 | flags, |
242 | iconPath, |
243 | soundPath, |
244 | localizationPath, |
245 | header: alertHeader, |
246 | message: alertMessage, |
247 | defaultButtonTitle); |
248 | UNDServer_deallocate(UNDServer); |
249 | return kr; |
250 | } |
251 | return MACH_SEND_INVALID_DEST; |
252 | } |
253 | |
254 | kern_return_t |
255 | KUNCUserNotificationDisplayAlert( |
256 | int alertTimeout, |
257 | unsigned flags, |
258 | char *iconPath, |
259 | char *soundPath, |
260 | char *localizationPath, |
261 | char *, |
262 | char *alertMessage, |
263 | char *defaultButtonTitle, |
264 | char *alternateButtonTitle, |
265 | char *otherButtonTitle, |
266 | unsigned *responseFlags) |
267 | { |
268 | UNDServerRef UNDServer; |
269 | |
270 | UNDServer = UNDServer_reference(); |
271 | if (IP_VALID(UNDServer)) { |
272 | kern_return_t kr; |
273 | kr = UNDDisplayAlertSimple_rpc(server: UNDServer, |
274 | rpctimeout: alertTimeout, |
275 | flags, |
276 | iconPath, |
277 | soundPath, |
278 | localizationPath, |
279 | header: alertHeader, |
280 | message: alertMessage, |
281 | defaultButtonTitle, |
282 | alternateButtonTitle, |
283 | otherButtonTitle, |
284 | response: responseFlags); |
285 | UNDServer_deallocate(UNDServer); |
286 | return kr; |
287 | } |
288 | return MACH_SEND_INVALID_DEST; |
289 | } |
290 | |
291 | kern_return_t |
292 | KUNCUserNotificationDisplayFromBundle( |
293 | KUNCUserNotificationID id, |
294 | char *bundlePath, |
295 | char *fileName, |
296 | char *fileExtension, |
297 | char *messageKey, |
298 | char *tokenString, |
299 | KUNCUserNotificationCallBack callback, |
300 | __unused int contextKey) |
301 | { |
302 | UNDReplyRef reply = (UNDReplyRef)id; |
303 | UNDServerRef UNDServer; |
304 | ipc_port_t reply_port; |
305 | |
306 | if (reply == UND_REPLY_NULL) { |
307 | return KERN_INVALID_ARGUMENT; |
308 | } |
309 | UNDReply_lock(reply); |
310 | if (reply->inprogress == TRUE || reply->userLandNotificationKey != -1) { |
311 | UNDReply_unlock(reply); |
312 | return KERN_INVALID_ARGUMENT; |
313 | } |
314 | reply->inprogress = TRUE; |
315 | reply->callback = callback; |
316 | reply_port = ipc_kobject_make_send(port: reply->self_port, kobject: reply, kotype: IKOT_UND_REPLY); |
317 | UNDReply_unlock(reply); |
318 | |
319 | UNDServer = UNDServer_reference(); |
320 | if (IP_VALID(UNDServer)) { |
321 | kern_return_t kr; |
322 | |
323 | kr = UNDDisplayCustomFromBundle_rpc(server: UNDServer, |
324 | reply: reply_port, |
325 | bundlePath, |
326 | fileName, |
327 | fileExtension, |
328 | messageKey, |
329 | tokenKey: tokenString); |
330 | UNDServer_deallocate(UNDServer); |
331 | return kr; |
332 | } |
333 | return MACH_SEND_INVALID_DEST; |
334 | } |
335 | |
336 | /* |
337 | * Routine: convert_port_to_UNDReply |
338 | * |
339 | * MIG helper routine to convert from a mach port to a |
340 | * UNDReply object. |
341 | * |
342 | * Assumptions: |
343 | * Nothing locked. |
344 | */ |
345 | UNDReplyRef |
346 | convert_port_to_UNDReply( |
347 | ipc_port_t port) |
348 | { |
349 | UNDReplyRef reply = NULL; |
350 | if (IP_VALID(port)) { |
351 | reply = ipc_kobject_get_stable(port, type: IKOT_UND_REPLY); |
352 | } |
353 | |
354 | return reply; |
355 | } |
356 | #endif |
357 | |
358 | /* |
359 | * User interface for setting the host UserNotification Daemon port. |
360 | */ |
361 | |
362 | kern_return_t |
363 | host_set_UNDServer( |
364 | host_priv_t host_priv, |
365 | UNDServerRef server) |
366 | { |
367 | #if CONFIG_USER_NOTIFICATION |
368 | return host_set_user_notification_port(host_priv, server); |
369 | #else |
370 | #pragma unused(host_priv, server) |
371 | return KERN_NOT_SUPPORTED; |
372 | #endif |
373 | } |
374 | |
375 | /* |
376 | * User interface for retrieving the UserNotification Daemon port. |
377 | */ |
378 | |
379 | kern_return_t |
380 | host_get_UNDServer( |
381 | host_priv_t host_priv, |
382 | UNDServerRef *serverp) |
383 | { |
384 | #if CONFIG_USER_NOTIFICATION |
385 | return host_get_user_notification_port(host_priv, serverp); |
386 | #else |
387 | #pragma unused(host_priv, serverp) |
388 | return KERN_NOT_SUPPORTED; |
389 | #endif |
390 | } |
391 | |