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_types.h> |
29 | #include <mach/notify.h> |
30 | #include <ipc/ipc_port.h> |
31 | #include <kern/ipc_kobject.h> |
32 | #include <kern/audit_sessionport.h> |
33 | #include <libkern/OSAtomic.h> |
34 | |
35 | #if CONFIG_AUDIT |
36 | /* |
37 | * audit_session_mksend |
38 | * |
39 | * Description: Obtain a send right for given audit session. |
40 | * |
41 | * Parameters: *aia_p Audit session information to assosiate with |
42 | * the new port. |
43 | * *sessionport Pointer to the current session port. This may |
44 | * actually be set to IPC_PORT_NULL. |
45 | * |
46 | * Returns: !NULL Resulting send right. |
47 | * NULL Failed to allocate port (due to lack of memory |
48 | * resources). |
49 | |
50 | * Assumptions: Caller holds a reference on the session during the call. |
51 | * If there were no outstanding send rights against the port, |
52 | * hold a reference on the session and arm a new no-senders |
53 | * notification to determine when to release that reference. |
54 | * Otherwise, by creating an additional send right, we share |
55 | * the port's reference until all send rights go away. |
56 | */ |
57 | ipc_port_t |
58 | audit_session_mksend(struct auditinfo_addr *aia_p, ipc_port_t *sessionport) |
59 | { |
60 | ipc_port_t sendport = IPC_PORT_NULL; |
61 | ipc_port_t port; |
62 | |
63 | /* |
64 | * If we don't have an existing session port, then create one. |
65 | */ |
66 | port = *sessionport; |
67 | if (!IP_VALID(port)) { |
68 | ipc_port_t new_port = ipc_port_alloc_kernel(); |
69 | if (!IP_VALID(new_port)) |
70 | return new_port; |
71 | ipc_kobject_set(new_port, (ipc_kobject_t)aia_p, IKOT_AU_SESSIONPORT); |
72 | if (!OSCompareAndSwapPtr(port, new_port, sessionport)) |
73 | ipc_port_dealloc_kernel(new_port); |
74 | port = *sessionport; |
75 | } |
76 | |
77 | assert(ip_active(port) && IKOT_AU_SESSIONPORT == ip_kotype(port)); |
78 | sendport = ipc_port_make_send(port); |
79 | |
80 | /* |
81 | * If we don't have a no-senders notification outstanding against |
82 | * the port, take a reference on the session and request one. |
83 | */ |
84 | if (IP_NULL == port->ip_nsrequest) { |
85 | ipc_port_t notifyport; |
86 | |
87 | audit_session_aiaref(aia_p); |
88 | |
89 | |
90 | ip_lock(port); |
91 | /* Need a send-once right for the target of the notification */ |
92 | notifyport = ipc_port_make_sonce_locked(port); |
93 | /* Request a no-senders notification (at the new make-send threshold) */ |
94 | ipc_port_nsrequest(port, port->ip_mscount, notifyport, ¬ifyport); |
95 | /* port unlocked */ |
96 | |
97 | if (IP_NULL != notifyport) { |
98 | /* race requesting notification */ |
99 | audit_session_aiaunref(aia_p); |
100 | ipc_port_release_sonce(notifyport); |
101 | } |
102 | } |
103 | |
104 | return (sendport); |
105 | } |
106 | |
107 | |
108 | /* |
109 | * audit_session_porttoaia |
110 | * |
111 | * Description: Obtain the audit session info associated with the given port. |
112 | |
113 | * Parameters: port A Mach port. |
114 | * |
115 | * Returns: NULL The given Mach port did not reference audit |
116 | * session info. |
117 | * !NULL The audit session info that is associated with |
118 | * the Mach port. |
119 | * |
120 | * Notes: The caller must have a reference on the sessionport. |
121 | */ |
122 | struct auditinfo_addr * |
123 | audit_session_porttoaia(ipc_port_t port) |
124 | { |
125 | struct auditinfo_addr *aia_p = NULL; |
126 | |
127 | if (IP_VALID(port)) { |
128 | ip_lock(port); |
129 | if (IKOT_AU_SESSIONPORT == ip_kotype(port)) { |
130 | assert(ip_active(port)); |
131 | aia_p = (struct auditinfo_addr *)port->ip_kobject; |
132 | } |
133 | ip_unlock(port); |
134 | } |
135 | |
136 | return (aia_p); |
137 | } |
138 | |
139 | |
140 | /* |
141 | * audit_session_nosenders |
142 | * |
143 | * Description: Handle a no-senders notification for a sessionport. |
144 | * |
145 | * Parameters: msg A Mach no-senders notification message. |
146 | * |
147 | * Notes: It is possible that new send rights are created after a |
148 | * no-senders notification has been sent (i.e. via audit_session_mksend). |
149 | * We check the port's mscount against the notification's not_count |
150 | * to detect when this happens, and re-arm the notification in that |
151 | * case. |
152 | * |
153 | * In the normal case (no new senders), we first mark the port |
154 | * as dying by setting its object type to IKOT_NONE so that |
155 | * audit_session_mksend will no longer use it to create |
156 | * additional send rights. We can then safely call |
157 | * audit_session_port_destroy with no locks. |
158 | */ |
159 | void |
160 | audit_session_nosenders(mach_msg_header_t *msg) |
161 | { |
162 | mach_no_senders_notification_t *notification = (void *)msg; |
163 | ipc_port_t port = notification->not_header.msgh_remote_port; |
164 | ipc_port_t notifyport; |
165 | struct auditinfo_addr *port_aia_p = NULL; |
166 | |
167 | assert(IKOT_AU_SESSIONPORT == ip_kotype(port)); |
168 | ip_lock(port); |
169 | assert(ip_active(port)); |
170 | port_aia_p = (struct auditinfo_addr *)port->ip_kobject; |
171 | assert(NULL != port_aia_p); |
172 | |
173 | /* |
174 | * if new send rights have been made since the last notify |
175 | * request, re-arm the notification with the new threshold. |
176 | */ |
177 | if (port->ip_mscount > notification->not_count) { |
178 | notifyport = ipc_port_make_sonce_locked(port); |
179 | ipc_port_nsrequest(port, port->ip_mscount, notifyport, ¬ifyport); |
180 | /* port unlocked */ |
181 | |
182 | if (IP_NULL != notifyport) { |
183 | /* race re-arming the notification */ |
184 | ipc_port_release_sonce(notifyport); |
185 | audit_session_aiaunref(port_aia_p); |
186 | } |
187 | return; |
188 | } |
189 | |
190 | /* |
191 | * Otherwise, no more extant send rights, so release the |
192 | * reference held on the session by those send rights. |
193 | */ |
194 | ip_unlock(port); |
195 | audit_session_aiaunref(port_aia_p); |
196 | } |
197 | |
198 | void |
199 | audit_session_portdestroy(ipc_port_t *sessionport) |
200 | { |
201 | ipc_port_t port = *sessionport; |
202 | |
203 | if (IP_VALID(port)) { |
204 | assert (ip_active(port)); |
205 | assert(IKOT_AU_SESSIONPORT == ip_kotype(port)); |
206 | ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE); |
207 | ipc_port_dealloc_kernel(port); |
208 | *sessionport = IP_NULL; |
209 | } |
210 | } |
211 | #endif /* CONFIG_AUDIT */ |
212 | |