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