1 | /* |
2 | * Copyright (c) 2013-2022 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 <string.h> |
30 | #include <sys/systm.h> |
31 | #include <sys/types.h> |
32 | #include <sys/queue.h> |
33 | #include <sys/malloc.h> |
34 | #include <sys/kernel.h> |
35 | #include <sys/kern_control.h> |
36 | #include <sys/mbuf.h> |
37 | #include <sys/kpi_mbuf.h> |
38 | #include <sys/proc_uuid_policy.h> |
39 | #include <net/if.h> |
40 | #include <sys/domain.h> |
41 | #include <sys/protosw.h> |
42 | #include <sys/socket.h> |
43 | #include <sys/socketvar.h> |
44 | #include <sys/coalition.h> |
45 | #include <sys/ubc.h> |
46 | #include <sys/codesign.h> |
47 | #include <kern/cs_blobs.h> |
48 | #include <netinet/ip.h> |
49 | #include <netinet/ip6.h> |
50 | #include <netinet/tcp.h> |
51 | #include <netinet/tcp_var.h> |
52 | #include <netinet/tcp_cache.h> |
53 | #include <netinet/udp.h> |
54 | #include <netinet/in_pcb.h> |
55 | #include <netinet/in_tclass.h> |
56 | #include <netinet6/esp.h> |
57 | #include <net/flowhash.h> |
58 | #include <net/bloom_filter.h> |
59 | #include <net/if_var.h> |
60 | #include <net/pfvar.h> |
61 | #if SKYWALK && defined(XNU_TARGET_OS_OSX) |
62 | #include <skywalk/lib/net_filter_event.h> |
63 | #endif /* defined(SKYWALK) && defined(XNU_TARGET_OS_OSX) */ |
64 | #include <sys/kauth.h> |
65 | #include <sys/sysctl.h> |
66 | #include <sys/sysproto.h> |
67 | #include <sys/priv.h> |
68 | #include <sys/kern_event.h> |
69 | #include <sys/file_internal.h> |
70 | #include <IOKit/IOBSD.h> |
71 | #include <libkern/crypto/rand.h> |
72 | #include <corecrypto/cchmac.h> |
73 | #include <corecrypto/ccsha2.h> |
74 | #include <os/refcnt.h> |
75 | #include <mach-o/loader.h> |
76 | #include <net/network_agent.h> |
77 | #include <net/necp.h> |
78 | #include <netinet/flow_divert_proto.h> |
79 | |
80 | /* |
81 | * NECP - Network Extension Control Policy database |
82 | * ------------------------------------------------ |
83 | * The goal of this module is to allow clients connecting via a |
84 | * policy file descriptor to create high-level policy sessions, which |
85 | * are ingested into low-level kernel policies that control and tag |
86 | * traffic at the application, socket, and IP layers. |
87 | * |
88 | * ------------------------------------------------ |
89 | * Sessions |
90 | * ------------------------------------------------ |
91 | * Each session owns a list of session policies, each of which can |
92 | * specify any combination of conditions and a single result. Each |
93 | * session also has a priority level (such as High, Default, or Low) |
94 | * which is requested by the client. Based on the requested level, |
95 | * a session order value is assigned to the session, which will be used |
96 | * to sort kernel policies generated by the session. The session client |
97 | * can specify the sub-order for each policy it creates which will be |
98 | * used to further sort the kernel policies. |
99 | * |
100 | * Policy fd --> 1 necp_session --> list of necp_session_policy structs |
101 | * |
102 | * ------------------------------------------------ |
103 | * Kernel Policies |
104 | * ------------------------------------------------ |
105 | * Whenever a session send the Apply command, its policies are ingested |
106 | * and generate kernel policies. There are two phases of kernel policy |
107 | * ingestion. |
108 | * |
109 | * 1. The session policy is parsed to create kernel policies at the socket |
110 | * and IP layers, when applicable. For example, a policy that requires |
111 | * all traffic from App1 to Pass will generate a socket kernel policy to |
112 | * match App1 and mark packets with ID1, and also an IP policy to match |
113 | * ID1 and let the packet pass. This is handled in necp_apply_policy. The |
114 | * resulting kernel policies are added to the global socket and IP layer |
115 | * policy lists. |
116 | * necp_session_policy --> necp_kernel_socket_policy and necp_kernel_ip_output_policy |
117 | * || || |
118 | * \/ \/ |
119 | * necp_kernel_socket_policies necp_kernel_ip_output_policies |
120 | * |
121 | * 2. Once the global lists of kernel policies have been filled out, each |
122 | * list is traversed to create optimized sub-lists ("Maps") which are used during |
123 | * data-path evaluation. IP policies are sent into necp_kernel_ip_output_policies_map, |
124 | * which hashes incoming packets based on marked socket-layer policies, and removes |
125 | * duplicate or overlapping policies. Socket policies are sent into two maps, |
126 | * necp_kernel_socket_policies_map and necp_kernel_socket_policies_app_layer_map. |
127 | * The app layer map is used for policy checks coming in from user space, and is one |
128 | * list with duplicate and overlapping policies removed. The socket map hashes based |
129 | * on app UUID, and removes duplicate and overlapping policies. |
130 | * necp_kernel_socket_policy --> necp_kernel_socket_policies_app_layer_map |
131 | * |-> necp_kernel_socket_policies_map |
132 | * |
133 | * necp_kernel_ip_output_policies --> necp_kernel_ip_output_policies_map |
134 | * |
135 | * ------------------------------------------------ |
136 | * Drop All Level |
137 | * ------------------------------------------------ |
138 | * The Drop All Level is a sysctl that controls the level at which policies are allowed |
139 | * to override a global drop rule. If the value is 0, no drop rule is applied. If the value |
140 | * is 1, all traffic is dropped. If the value is greater than 1, all kernel policies created |
141 | * by a session with a priority level better than (numerically less than) the |
142 | * Drop All Level will allow matching traffic to not be dropped. The Drop All Level is |
143 | * dynamically interpreted into necp_drop_all_order, which specifies the equivalent assigned |
144 | * session orders to be dropped. |
145 | */ |
146 | |
147 | u_int32_t necp_drop_all_order = 0; |
148 | u_int32_t necp_drop_all_level = 0; |
149 | |
150 | u_int32_t necp_pass_loopback = NECP_LOOPBACK_PASS_ALL; |
151 | u_int32_t necp_pass_keepalives = 1; // 0=Off, 1=On |
152 | u_int32_t necp_pass_interpose = 1; // 0=Off, 1=On |
153 | u_int32_t necp_restrict_multicast = 1; // 0=Off, 1=On |
154 | u_int32_t necp_dedup_policies = 0; // 0=Off, 1=On |
155 | |
156 | u_int32_t necp_drop_unentitled_order = 0; |
157 | #ifdef XNU_TARGET_OS_WATCH |
158 | u_int32_t necp_drop_unentitled_level = NECP_SESSION_PRIORITY_CONTROL + 1; // Block all unentitled traffic from policies below control level |
159 | #else // XNU_TARGET_OS_WATCH |
160 | u_int32_t necp_drop_unentitled_level = 0; |
161 | #endif // XNU_TARGET_OS_WATCH |
162 | |
163 | u_int32_t necp_drop_management_order = 0; |
164 | u_int32_t necp_drop_management_level = NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL; |
165 | |
166 | u_int32_t necp_debug = 0; // 0=None, 1=Basic, 2=EveryMatch |
167 | |
168 | os_log_t necp_log_handle = NULL; |
169 | os_log_t necp_data_trace_log_handle = NULL; |
170 | |
171 | u_int32_t necp_session_count = 0; |
172 | |
173 | static KALLOC_TYPE_DEFINE(necp_session_policy_zone, |
174 | struct necp_session_policy, NET_KT_DEFAULT); |
175 | static KALLOC_TYPE_DEFINE(necp_socket_policy_zone, |
176 | struct necp_kernel_socket_policy, NET_KT_DEFAULT); |
177 | static KALLOC_TYPE_DEFINE(necp_ip_policy_zone, |
178 | struct necp_kernel_ip_output_policy, NET_KT_DEFAULT); |
179 | |
180 | #define LIST_INSERT_SORTED_ASCENDING(head, elm, field, sortfield, tmpelm) do { \ |
181 | if (LIST_EMPTY((head)) || (LIST_FIRST(head)->sortfield >= (elm)->sortfield)) { \ |
182 | LIST_INSERT_HEAD((head), elm, field); \ |
183 | } else { \ |
184 | LIST_FOREACH(tmpelm, head, field) { \ |
185 | if (LIST_NEXT(tmpelm, field) == NULL || LIST_NEXT(tmpelm, field)->sortfield >= (elm)->sortfield) { \ |
186 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
187 | break; \ |
188 | } \ |
189 | } \ |
190 | } \ |
191 | } while (0) |
192 | |
193 | #define LIST_INSERT_SORTED_TWICE_ASCENDING(head, elm, field, firstsortfield, secondsortfield, tmpelm) do { \ |
194 | if (LIST_EMPTY((head)) || (LIST_FIRST(head)->firstsortfield > (elm)->firstsortfield) || ((LIST_FIRST(head)->firstsortfield == (elm)->firstsortfield) && (LIST_FIRST(head)->secondsortfield >= (elm)->secondsortfield))) { \ |
195 | LIST_INSERT_HEAD((head), elm, field); \ |
196 | } else { \ |
197 | LIST_FOREACH(tmpelm, head, field) { \ |
198 | if (LIST_NEXT(tmpelm, field) == NULL || (LIST_NEXT(tmpelm, field)->firstsortfield > (elm)->firstsortfield) || ((LIST_NEXT(tmpelm, field)->firstsortfield == (elm)->firstsortfield) && (LIST_NEXT(tmpelm, field)->secondsortfield >= (elm)->secondsortfield))) { \ |
199 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
200 | break; \ |
201 | } \ |
202 | } \ |
203 | } \ |
204 | } while (0) |
205 | |
206 | #define LIST_INSERT_SORTED_THRICE_ASCENDING(head, elm, field, firstsortfield, secondsortfield, thirdsortfield, tmpelm) do { \ |
207 | if (LIST_EMPTY((head)) || (LIST_FIRST(head)->firstsortfield > (elm)->firstsortfield) || ((LIST_FIRST(head)->firstsortfield == (elm)->firstsortfield) && (LIST_FIRST(head)->secondsortfield >= (elm)->secondsortfield)) || ((LIST_FIRST(head)->firstsortfield == (elm)->firstsortfield) && (LIST_FIRST(head)->secondsortfield == (elm)->secondsortfield) && (LIST_FIRST(head)->thirdsortfield >= (elm)->thirdsortfield))) { \ |
208 | LIST_INSERT_HEAD((head), elm, field); \ |
209 | } else { \ |
210 | LIST_FOREACH(tmpelm, head, field) { \ |
211 | if (LIST_NEXT(tmpelm, field) == NULL || (LIST_NEXT(tmpelm, field)->firstsortfield > (elm)->firstsortfield) || ((LIST_NEXT(tmpelm, field)->firstsortfield == (elm)->firstsortfield) && (LIST_NEXT(tmpelm, field)->secondsortfield >= (elm)->secondsortfield)) || ((LIST_NEXT(tmpelm, field)->firstsortfield == (elm)->firstsortfield) && (LIST_NEXT(tmpelm, field)->secondsortfield == (elm)->secondsortfield) && (LIST_NEXT(tmpelm, field)->thirdsortfield >= (elm)->thirdsortfield))) { \ |
212 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
213 | break; \ |
214 | } \ |
215 | } \ |
216 | } \ |
217 | } while (0) |
218 | |
219 | #define IS_NECP_ROUTE_RULE_DENY(x) ((x) == NECP_ROUTE_RULE_DENY_INTERFACE || (x) == NECP_ROUTE_RULE_DENY_INTERFACE_WITH_TYPE) |
220 | |
221 | #define IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(x) (IS_NECP_ROUTE_RULE_DENY(x) || (x) == NECP_ROUTE_RULE_ALLOW_INTERFACE) |
222 | |
223 | #define IS_NECP_DEST_IN_LOCAL_NETWORKS(rt) \ |
224 | ((rt) != NULL && !((rt)->rt_flags & RTF_GATEWAY) && ((rt)->rt_ifa && (rt)->rt_ifa->ifa_ifp && !((rt)->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) |
225 | |
226 | #define NECP_KERNEL_CONDITION_ALL_INTERFACES 0x000001 |
227 | #define NECP_KERNEL_CONDITION_BOUND_INTERFACE 0x000002 |
228 | #define NECP_KERNEL_CONDITION_PROTOCOL 0x000004 |
229 | #define NECP_KERNEL_CONDITION_LOCAL_START 0x000008 |
230 | #define NECP_KERNEL_CONDITION_LOCAL_END 0x000010 |
231 | #define NECP_KERNEL_CONDITION_LOCAL_PREFIX 0x000020 |
232 | #define NECP_KERNEL_CONDITION_REMOTE_START 0x000040 |
233 | #define NECP_KERNEL_CONDITION_REMOTE_END 0x000080 |
234 | #define NECP_KERNEL_CONDITION_REMOTE_PREFIX 0x000100 |
235 | #define NECP_KERNEL_CONDITION_APP_ID 0x000200 |
236 | #define NECP_KERNEL_CONDITION_REAL_APP_ID 0x000400 |
237 | #define NECP_KERNEL_CONDITION_DOMAIN 0x000800 |
238 | #define NECP_KERNEL_CONDITION_ACCOUNT_ID 0x001000 |
239 | #define NECP_KERNEL_CONDITION_POLICY_ID 0x002000 |
240 | #define NECP_KERNEL_CONDITION_PID 0x004000 |
241 | #define NECP_KERNEL_CONDITION_UID 0x008000 |
242 | #define NECP_KERNEL_CONDITION_LAST_INTERFACE 0x010000 // Only set from packets looping between interfaces |
243 | #define NECP_KERNEL_CONDITION_TRAFFIC_CLASS 0x020000 |
244 | #define NECP_KERNEL_CONDITION_ENTITLEMENT 0x040000 |
245 | #define NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT 0x080000 |
246 | #define NECP_KERNEL_CONDITION_AGENT_TYPE 0x100000 |
247 | #define NECP_KERNEL_CONDITION_HAS_CLIENT 0x200000 |
248 | #define NECP_KERNEL_CONDITION_LOCAL_NETWORKS 0x400000 |
249 | #define NECP_KERNEL_CONDITION_CLIENT_FLAGS 0x800000 |
250 | #define NECP_KERNEL_CONDITION_LOCAL_EMPTY 0x1000000 |
251 | #define NECP_KERNEL_CONDITION_REMOTE_EMPTY 0x2000000 |
252 | #define NECP_KERNEL_CONDITION_PLATFORM_BINARY 0x4000000 |
253 | #define NECP_KERNEL_CONDITION_SDK_VERSION 0x8000000 |
254 | #define NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER 0x10000000 |
255 | #define NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS 0x20000000 |
256 | #define NECP_KERNEL_CONDITION_IS_LOOPBACK 0x40000000 |
257 | #define NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY 0x80000000 |
258 | #define NECP_KERNEL_CONDITION_SCHEME_PORT 0x100000000 |
259 | #define NECP_KERNEL_CONDITION_DOMAIN_FILTER 0x200000000 |
260 | #define NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT 0x400000000 |
261 | #define NECP_KERNEL_CONDITION_EXACT_DOMAIN 0x800000000 |
262 | #define NECP_KERNEL_CONDITION_REAL_UID 0x1000000000 |
263 | #define NECP_KERNEL_CONDITION_URL 0x2000000000 |
264 | #define NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS 0x4000000000 |
265 | |
266 | #define NECP_MAX_POLICY_RESULT_SIZE 512 |
267 | #define NECP_MAX_ROUTE_RULES_ARRAY_SIZE 1024 |
268 | #define NECP_MAX_CONDITIONS_ARRAY_SIZE 4096 |
269 | #define NECP_MAX_POLICY_LIST_COUNT 1024 |
270 | |
271 | #define NECP_MAX_DOMAIN_FILTER_SIZE 65536 // Allows room for 100K domains |
272 | |
273 | typedef enum { |
274 | NECP_BYPASS_TYPE_NONE = 0, |
275 | NECP_BYPASS_TYPE_INTCOPROC = 1, |
276 | NECP_BYPASS_TYPE_LOOPBACK = 2, |
277 | } necp_socket_bypass_type_t; |
278 | |
279 | // Cap the policy size at the max result + conditions size, with room for extra TLVs |
280 | #define NECP_MAX_POLICY_SIZE (1024 + NECP_MAX_POLICY_RESULT_SIZE + NECP_MAX_CONDITIONS_ARRAY_SIZE) |
281 | |
282 | struct necp_service_registration { |
283 | LIST_ENTRY(necp_service_registration) session_chain; |
284 | LIST_ENTRY(necp_service_registration) kernel_chain; |
285 | u_int32_t service_id; |
286 | }; |
287 | |
288 | struct necp_domain_filter { |
289 | LIST_ENTRY(necp_domain_filter) owner_chain; |
290 | LIST_ENTRY(necp_domain_filter) chain; |
291 | u_int32_t id; |
292 | struct net_bloom_filter *filter; |
293 | os_refcnt_t refcount; |
294 | }; |
295 | static LIST_HEAD(necp_domain_filter_list, necp_domain_filter) necp_global_domain_filter_list; |
296 | |
297 | struct necp_session { |
298 | u_int8_t necp_fd_type; |
299 | u_int32_t control_unit; |
300 | u_int32_t session_priority; // Descriptive priority rating |
301 | u_int32_t session_order; |
302 | |
303 | necp_policy_id last_policy_id; |
304 | |
305 | decl_lck_mtx_data(, lock); |
306 | |
307 | bool proc_locked; // Messages must come from proc_uuid |
308 | uuid_t proc_uuid; |
309 | int proc_pid; |
310 | |
311 | bool dirty; |
312 | LIST_HEAD(_policies, necp_session_policy) policies; |
313 | |
314 | LIST_HEAD(_services, necp_service_registration) services; |
315 | struct necp_domain_filter_list domain_filters; |
316 | |
317 | TAILQ_ENTRY(necp_session) chain; |
318 | }; |
319 | |
320 | #define NECP_SESSION_LOCK(_s) lck_mtx_lock(&_s->lock) |
321 | #define NECP_SESSION_UNLOCK(_s) lck_mtx_unlock(&_s->lock) |
322 | |
323 | static TAILQ_HEAD(_necp_session_list, necp_session) necp_session_list; |
324 | |
325 | struct necp_socket_info { |
326 | pid_t pid; |
327 | int32_t pid_version; |
328 | uid_t uid; |
329 | uid_t real_uid; |
330 | union necp_sockaddr_union local_addr; |
331 | union necp_sockaddr_union remote_addr; |
332 | u_int32_t bound_interface_index; |
333 | u_int32_t bound_interface_flags; |
334 | u_int32_t bound_interface_eflags; |
335 | u_int32_t bound_interface_xflags; |
336 | u_int32_t traffic_class; |
337 | u_int16_t protocol; |
338 | u_int16_t scheme_port; |
339 | u_int32_t application_id; |
340 | u_int32_t real_application_id; |
341 | u_int32_t account_id; |
342 | u_int32_t drop_order; |
343 | u_int32_t client_flags; |
344 | char *domain; |
345 | char *url; |
346 | unsigned is_entitled : 1; |
347 | unsigned has_client : 1; |
348 | unsigned has_system_signed_result : 1; |
349 | unsigned is_platform_binary : 1; |
350 | unsigned used_responsible_pid : 1; |
351 | unsigned is_loopback : 1; |
352 | unsigned real_is_platform_binary : 1; |
353 | unsigned is_delegated : 1; |
354 | unsigned is_local : 1; |
355 | unsigned __pad_bits : 7; |
356 | }; |
357 | |
358 | static LCK_GRP_DECLARE(necp_kernel_policy_mtx_grp, NECP_CONTROL_NAME); |
359 | static LCK_ATTR_DECLARE(necp_kernel_policy_mtx_attr, 0, 0); |
360 | static LCK_RW_DECLARE_ATTR(necp_kernel_policy_lock, &necp_kernel_policy_mtx_grp, |
361 | &necp_kernel_policy_mtx_attr); |
362 | |
363 | static LCK_GRP_DECLARE(necp_route_rule_mtx_grp, "necp_route_rule" ); |
364 | static LCK_RW_DECLARE(necp_route_rule_lock, &necp_route_rule_mtx_grp); |
365 | |
366 | os_refgrp_decl(static, necp_refgrp, "NECPRefGroup" , NULL); |
367 | |
368 | /* |
369 | * On modification, invalidate cached lookups by bumping the generation count. |
370 | * Other calls will need to take the slowpath of taking |
371 | * the subsystem lock. |
372 | */ |
373 | static volatile int32_t necp_kernel_socket_policies_gencount; |
374 | #define BUMP_KERNEL_SOCKET_POLICIES_GENERATION_COUNT() do { \ |
375 | if (OSIncrementAtomic(&necp_kernel_socket_policies_gencount) == (INT32_MAX - 1)) { \ |
376 | necp_kernel_socket_policies_gencount = 1; \ |
377 | } \ |
378 | } while (0) |
379 | |
380 | /* |
381 | * Drop-all Bypass: |
382 | * Allow priviledged processes to bypass the default drop-all |
383 | * via entitlement check. For OSX, since entitlement check is |
384 | * not supported for configd, configd signing identity is checked |
385 | * instead. |
386 | */ |
387 | #define SIGNING_ID_CONFIGD "com.apple.configd" |
388 | #define SIGNING_ID_CONFIGD_LEN (sizeof(SIGNING_ID_CONFIGD) - 1) |
389 | |
390 | typedef enum { |
391 | NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE = 0, |
392 | NECP_DROP_ALL_BYPASS_CHECK_RESULT_TRUE = 1, |
393 | NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE = 2, |
394 | } necp_drop_all_bypass_check_result_t; |
395 | |
396 | static u_int64_t necp_kernel_application_policies_condition_mask; |
397 | static size_t necp_kernel_application_policies_count; |
398 | static u_int64_t necp_kernel_socket_policies_condition_mask; |
399 | static size_t necp_kernel_socket_policies_count; |
400 | static size_t necp_kernel_socket_policies_non_app_count; |
401 | static LIST_HEAD(_necpkernelsocketconnectpolicies, necp_kernel_socket_policy) necp_kernel_socket_policies; |
402 | #define NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS 5 |
403 | #define NECP_SOCKET_MAP_APP_ID_TO_BUCKET(appid) (appid ? (appid%(NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS - 1) + 1) : 0) |
404 | static size_t necp_kernel_socket_policies_map_counts[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
405 | static struct necp_kernel_socket_policy **necp_kernel_socket_policies_map[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
406 | static size_t necp_kernel_socket_policies_app_layer_map_count; |
407 | static struct necp_kernel_socket_policy **necp_kernel_socket_policies_app_layer_map; |
408 | /* |
409 | * A note on policy 'maps': these are used for boosting efficiency when matching policies. For each dimension of the map, |
410 | * such as an ID, the 0 bucket is reserved for sockets/packets that do not have this parameter, while the other |
411 | * buckets lead to an array of policy pointers that form the list applicable when the (parameter%(NUM_BUCKETS - 1) + 1) == bucket_index. |
412 | * |
413 | * For example, a packet with policy ID of 7, when there are 4 ID buckets, will map to bucket (7%3 + 1) = 2. |
414 | */ |
415 | |
416 | static u_int64_t necp_kernel_ip_output_policies_condition_mask; |
417 | static size_t necp_kernel_ip_output_policies_count; |
418 | static size_t necp_kernel_ip_output_policies_non_id_count; |
419 | static LIST_HEAD(_necpkernelipoutputpolicies, necp_kernel_ip_output_policy) necp_kernel_ip_output_policies; |
420 | #define NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS 5 |
421 | #define NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(id) (id ? (id%(NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS - 1) + 1) : 0) |
422 | static size_t necp_kernel_ip_output_policies_map_counts[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
423 | static struct necp_kernel_ip_output_policy **necp_kernel_ip_output_policies_map[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
424 | static struct necp_kernel_socket_policy pass_policy = |
425 | { |
426 | .id = NECP_KERNEL_POLICY_ID_NO_MATCH, |
427 | .result = NECP_KERNEL_POLICY_RESULT_PASS, |
428 | }; |
429 | |
430 | static struct necp_session *necp_create_session(void); |
431 | static void necp_delete_session(struct necp_session *session); |
432 | |
433 | static necp_policy_id necp_handle_policy_add(struct necp_session *session, |
434 | u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *error); |
435 | static int necp_handle_policy_dump_all(user_addr_t out_buffer, size_t out_buffer_length); |
436 | |
437 | #define MAX_RESULT_STRING_LEN 64 |
438 | static inline const char * necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
439 | |
440 | static struct necp_session_policy *necp_policy_create(struct necp_session *session, necp_policy_order order, u_int8_t *conditions_array, u_int32_t conditions_array_size, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, u_int8_t *result, u_int32_t result_size); |
441 | static struct necp_session_policy *necp_policy_find(struct necp_session *session, necp_policy_id policy_id); |
442 | static bool necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy); |
443 | static bool necp_policy_mark_all_for_deletion(struct necp_session *session); |
444 | static bool necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy); |
445 | static void necp_policy_apply_all(struct necp_session *session); |
446 | |
447 | static necp_kernel_policy_id necp_kernel_socket_policy_add(necp_policy_order order, u_int32_t session_order, int session_pid, u_int64_t condition_mask, u_int64_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, char *cond_custom_entitlement, u_int32_t cond_account_id, char *cond_domain, u_int32_t cond_domain_filter, char *cond_url, pid_t cond_pid, int32_t cond_pidversion, uid_t cond_uid, uid_t cond_real_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, struct necp_policy_condition_agent_type *cond_agent_type, struct necp_policy_condition_sdk_version *cond_sdk_version, u_int32_t cond_client_flags, char *cond_signing_identifier, u_int16_t cond_packet_filter_tags, u_int16_t cond_scheme_port, u_int32_t cond_bound_interface_flags, u_int32_t cond_bound_interface_eflags, u_int32_t cond_bound_interface_xflags, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
448 | static bool necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id); |
449 | static bool necp_kernel_socket_policies_reprocess(void); |
450 | static bool necp_kernel_socket_policies_update_uuid_table(void); |
451 | static inline struct necp_kernel_socket_policy *necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id_array, size_t *return_route_rule_id_array_count, size_t route_rule_id_array_count, necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, u_int32_t *return_netagent_array, u_int32_t *return_netagent_use_flags_array, size_t netagent_array_count, struct necp_client_parameter_netagent_type *required_agent_types, u_int32_t num_required_agent_types, proc_t proc, u_int16_t pf_tag, necp_kernel_policy_id *skip_policy_id, struct rtentry *rt, necp_kernel_policy_result *return_drop_dest_policy_result, necp_drop_all_bypass_check_result_t *return_drop_all_bypass, u_int32_t *return_flow_divert_aggregate_unit, struct socket *, int debug); |
452 | |
453 | static necp_kernel_policy_id necp_kernel_ip_output_policy_add(necp_policy_order order, necp_policy_order suborder, u_int32_t session_order, int session_pid, u_int64_t condition_mask, u_int64_t condition_negated_mask, necp_kernel_policy_id cond_policy_id, ifnet_t cond_bound_interface, u_int32_t cond_last_interface_index, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, u_int16_t cond_packet_filter_tags, u_int16_t cond_scheme_port, u_int32_t cond_bound_interface_flags, u_int32_t cond_bound_interface_eflags, u_int32_t cond_bound_interface_xflags, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
454 | static bool necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id); |
455 | static bool necp_kernel_ip_output_policies_reprocess(void); |
456 | |
457 | static bool necp_is_addr_in_range(struct sockaddr *addr, struct sockaddr *range_start, struct sockaddr *range_end); |
458 | static bool necp_is_range_in_range(struct sockaddr *inner_range_start, struct sockaddr *inner_range_end, struct sockaddr *range_start, struct sockaddr *range_end); |
459 | static bool necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subnet_addr, u_int8_t subnet_prefix); |
460 | static int necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port); |
461 | static bool necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits); |
462 | static bool necp_addr_is_empty(struct sockaddr *addr); |
463 | static bool necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet, u_int32_t bound_interface_index); |
464 | static bool necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet); |
465 | |
466 | struct necp_uuid_id_mapping { |
467 | LIST_ENTRY(necp_uuid_id_mapping) chain; |
468 | uuid_t uuid; |
469 | u_int32_t id; |
470 | os_refcnt_t refcount; |
471 | u_int32_t table_usecount; // Add to UUID policy table count |
472 | }; |
473 | static size_t necp_num_uuid_app_id_mappings; |
474 | static bool necp_uuid_app_id_mappings_dirty; |
475 | #define NECP_UUID_APP_ID_HASH_SIZE 64 |
476 | static u_long necp_uuid_app_id_hash_mask; |
477 | static u_long necp_uuid_app_id_hash_num_buckets; |
478 | static LIST_HEAD(necp_uuid_id_mapping_head, necp_uuid_id_mapping) * necp_uuid_app_id_hashtbl, necp_uuid_service_id_list; // App map is real hash table, service map is just mapping |
479 | #define APPUUIDHASH(uuid) (&necp_uuid_app_id_hashtbl[uuid[0] & necp_uuid_app_id_hash_mask]) // Assume first byte of UUIDs are evenly distributed |
480 | static u_int32_t necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table); |
481 | static bool necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table); |
482 | static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id); |
483 | |
484 | static struct necp_uuid_id_mapping *necp_uuid_lookup_service_id_locked(uuid_t uuid); |
485 | static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id); |
486 | static u_int32_t necp_create_uuid_service_id_mapping(uuid_t uuid); |
487 | static bool necp_remove_uuid_service_id_mapping(uuid_t uuid); |
488 | static bool necp_remove_uuid_service_id_mapping_with_service_id(u_int32_t service_id); |
489 | |
490 | struct necp_string_id_mapping { |
491 | LIST_ENTRY(necp_string_id_mapping) chain; |
492 | char *string; |
493 | necp_app_id id; |
494 | os_refcnt_t refcount; |
495 | }; |
496 | static LIST_HEAD(necp_string_id_mapping_list, necp_string_id_mapping) necp_account_id_list; |
497 | static u_int32_t necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain); |
498 | static bool necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain); |
499 | static struct necp_string_id_mapping *necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id); |
500 | |
501 | static u_int32_t necp_create_domain_filter(struct necp_domain_filter_list *list, struct necp_domain_filter_list *owner_list, struct net_bloom_filter *filter); |
502 | static bool necp_remove_domain_filter(struct necp_domain_filter_list *list, struct necp_domain_filter_list *owner_list, u_int32_t filter_id); |
503 | static struct necp_domain_filter *necp_lookup_domain_filter(struct necp_domain_filter_list *list, u_int32_t filter_id); |
504 | |
505 | static struct necp_kernel_socket_policy *necp_kernel_socket_policy_find(necp_kernel_policy_id policy_id); |
506 | static struct necp_kernel_ip_output_policy *necp_kernel_ip_output_policy_find(necp_kernel_policy_id policy_id); |
507 | |
508 | static LIST_HEAD(_necp_kernel_service_list, necp_service_registration) necp_registered_service_list; |
509 | |
510 | static char *necp_create_trimmed_domain(char *string, size_t length); |
511 | static inline int necp_count_dots(char *string, size_t length); |
512 | |
513 | static char *necp_copy_string(char *string, size_t length); |
514 | static bool necp_update_qos_marking(struct ifnet *ifp, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id); |
515 | |
516 | #define ROUTE_RULE_IS_AGGREGATE(ruleid) (ruleid >= UINT16_MAX) |
517 | |
518 | #define MAX_ROUTE_RULE_INTERFACES 10 |
519 | struct necp_route_rule { |
520 | LIST_ENTRY(necp_route_rule) chain; |
521 | u_int32_t id; |
522 | u_int32_t netagent_id; |
523 | u_int32_t control_unit; |
524 | u_int32_t match_netagent_id; |
525 | u_int32_t effective_type; |
526 | u_int8_t default_action; |
527 | u_int8_t cellular_action; |
528 | u_int8_t wifi_action; |
529 | u_int8_t wired_action; |
530 | u_int8_t expensive_action; |
531 | u_int8_t constrained_action; |
532 | u_int8_t companion_action; |
533 | u_int exception_if_indices[MAX_ROUTE_RULE_INTERFACES]; |
534 | u_int8_t exception_if_actions[MAX_ROUTE_RULE_INTERFACES]; |
535 | os_refcnt_t refcount; |
536 | }; |
537 | static LIST_HEAD(necp_route_rule_list, necp_route_rule) necp_route_rules; |
538 | static u_int32_t necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, bool *has_socket_only_actions); |
539 | static bool necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id); |
540 | static bool necp_route_is_interface_type_allowed(struct rtentry *route, struct ifnet *ifp, proc_t proc, struct inpcb *inp); |
541 | static bool necp_route_is_allowed(struct rtentry *route, ifnet_t interface, u_int32_t *netagent_array, size_t netagent_array_count, |
542 | u_int32_t route_rule_id, u_int32_t *interface_type_denied); |
543 | static uint32_t necp_route_get_netagent(struct rtentry *route, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id, bool *remove); |
544 | static bool necp_route_rule_matches_agents(u_int32_t route_rule_id); |
545 | static uint32_t necp_route_get_flow_divert(struct rtentry *route, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id, u_int32_t *flow_divert_aggregate_unit); |
546 | static struct necp_route_rule *necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id); |
547 | static inline void necp_get_parent_is_entitled(task_t task, struct necp_socket_info *info); |
548 | |
549 | #define MAX_AGGREGATE_ROUTE_RULES 16 |
550 | struct necp_aggregate_route_rule { |
551 | LIST_ENTRY(necp_aggregate_route_rule) chain; |
552 | u_int32_t id; |
553 | u_int32_t rule_ids[MAX_AGGREGATE_ROUTE_RULES]; |
554 | }; |
555 | static LIST_HEAD(necp_aggregate_route_rule_list, necp_aggregate_route_rule) necp_aggregate_route_rules; |
556 | static u_int32_t necp_create_aggregate_route_rule(u_int32_t *rule_ids); |
557 | |
558 | // Sysctl definitions |
559 | static int sysctl_handle_necp_level SYSCTL_HANDLER_ARGS; |
560 | static int sysctl_handle_necp_unentitled_level SYSCTL_HANDLER_ARGS; |
561 | static int sysctl_handle_necp_management_level SYSCTL_HANDLER_ARGS; |
562 | |
563 | SYSCTL_NODE(_net, OID_AUTO, necp, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "NECP" ); |
564 | SYSCTL_INT(_net_necp, NECPCTL_DEDUP_POLICIES, dedup_policies, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_dedup_policies, 0, "" ); |
565 | SYSCTL_INT(_net_necp, NECPCTL_RESTRICT_MULTICAST, restrict_multicast, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_restrict_multicast, 0, "" ); |
566 | SYSCTL_INT(_net_necp, NECPCTL_PASS_LOOPBACK, pass_loopback, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_loopback, 0, "" ); |
567 | SYSCTL_INT(_net_necp, NECPCTL_PASS_KEEPALIVES, pass_keepalives, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_keepalives, 0, "" ); |
568 | SYSCTL_INT(_net_necp, NECPCTL_PASS_INTERPOSE, pass_interpose, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_interpose, 0, "" ); |
569 | SYSCTL_INT(_net_necp, NECPCTL_DEBUG, debug, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_debug, 0, "" ); |
570 | SYSCTL_PROC(_net_necp, NECPCTL_DROP_UNENTITLED_LEVEL, drop_unentitled_level, CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_unentitled_level, 0, &sysctl_handle_necp_unentitled_level, "IU" , "" ); |
571 | SYSCTL_PROC(_net_necp, NECPCTL_DROP_MANAGEMENT_LEVEL, drop_management_level, CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_management_level, 0, &sysctl_handle_necp_management_level, "IU" , "" ); |
572 | SYSCTL_PROC(_net_necp, NECPCTL_DROP_ALL_LEVEL, drop_all_level, CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_all_level, 0, &sysctl_handle_necp_level, "IU" , "" ); |
573 | SYSCTL_LONG(_net_necp, NECPCTL_SOCKET_POLICY_COUNT, socket_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_socket_policies_count, "" ); |
574 | SYSCTL_LONG(_net_necp, NECPCTL_SOCKET_NON_APP_POLICY_COUNT, socket_non_app_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_socket_policies_non_app_count, "" ); |
575 | SYSCTL_LONG(_net_necp, NECPCTL_IP_POLICY_COUNT, ip_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_ip_output_policies_count, "" ); |
576 | SYSCTL_INT(_net_necp, NECPCTL_SESSION_COUNT, session_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_session_count, 0, "" ); |
577 | |
578 | static struct necp_drop_dest_policy necp_drop_dest_policy; |
579 | static int necp_drop_dest_debug = 0; // 0: off, 1: match, >1: every evaluation |
580 | SYSCTL_INT(_net_necp, OID_AUTO, drop_dest_debug, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_dest_debug, 0, "" ); |
581 | |
582 | static int sysctl_handle_necp_drop_dest_level SYSCTL_HANDLER_ARGS; |
583 | SYSCTL_PROC(_net_necp, OID_AUTO, drop_dest_level, CTLTYPE_STRUCT | CTLFLAG_LOCKED | CTLFLAG_ANYBODY | CTLFLAG_RW, |
584 | 0, 0, &sysctl_handle_necp_drop_dest_level, "S,necp_drop_dest_level" , "" ); |
585 | |
586 | static bool necp_address_matches_drop_dest_policy(union necp_sockaddr_union *, u_int32_t); |
587 | |
588 | /* |
589 | * data tracing control - |
590 | * |
591 | * necp_data_tracing_level : 1 for brief trace, 2 for policy details, 3 for condition details |
592 | * necp_data_tracing_port : match traffic with specified port |
593 | * necp_data_tracing_proto : match traffic with specified protocol |
594 | * necp_data_tracing_pid : match traffic with specified pid (only applied at socket level) |
595 | * necp_data_tracing_ifindex : match traffic on specified ifindex |
596 | * necp_data_tracing_match_all: trace traffic only if ALL specified attributes matched. Default is 0 to trace traffic if any specified attributes matched. |
597 | * data_tracing_session_order : match policies in the specified session - log traffic that hit these policies |
598 | * necp_data_tracing_policy_order : match specified policy - log traffic that hit this policy |
599 | */ |
600 | static int necp_data_tracing_level = 0; |
601 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_level, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_level, 0, "" ); |
602 | |
603 | static int necp_data_tracing_port = 0; |
604 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_port, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_port, 0, "" ); |
605 | |
606 | static int necp_data_tracing_proto = 0; |
607 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_proto, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_proto, 0, "" ); |
608 | |
609 | static int necp_data_tracing_pid = 0; |
610 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_pid, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_pid, 0, "" ); |
611 | |
612 | static int necp_data_tracing_ifindex = 0; |
613 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_ifindex, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_ifindex, 0, "" ); |
614 | |
615 | static int necp_data_tracing_match_all = 0; |
616 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_match_all, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_match_all, 0, "" ); |
617 | |
618 | static int necp_data_tracing_session_order = 0; |
619 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_session_order, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_session_order, 0, "" ); |
620 | |
621 | static int necp_data_tracing_policy_order = 0; |
622 | SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_policy_order, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_policy_order, 0, "" ); |
623 | |
624 | #define NECP_DATA_TRACE_LEVEL_BRIEF 1 |
625 | #define NECP_DATA_TRACE_LEVEL_POLICY 2 |
626 | #define NECP_DATA_TRACE_LEVEL_CONDITION 3 |
627 | |
628 | #define NECP_DATA_TRACE_PID_MATCHED(pid) \ |
629 | (pid == necp_data_tracing_pid) |
630 | #define NECP_DATA_TRACE_PROTO_MATCHED(protocol) \ |
631 | (protocol == necp_data_tracing_proto) |
632 | #define NECP_DATA_TRACE_LOCAL_PORT_MATCHED(local_addr) \ |
633 | (local_addr && (ntohs(local_addr->sin.sin_port) == necp_data_tracing_port || ntohs(local_addr->sin6.sin6_port) == necp_data_tracing_port)) |
634 | #define NECP_DATA_TRACE_REMOTE_ORT_MATCHED(remote_addr) \ |
635 | (remote_addr && (ntohs(remote_addr->sin.sin_port) == necp_data_tracing_port || ntohs(remote_addr->sin6.sin6_port) == necp_data_tracing_port)) |
636 | #define NECP_DATA_TRACE_IFINDEX_MATCHED(ifindex) \ |
637 | (ifindex == necp_data_tracing_ifindex) |
638 | |
639 | #define NECP_ENABLE_DATA_TRACE_OR(local_addr, remote_addr, protocol, pid, ifindex) \ |
640 | ((necp_data_tracing_level && \ |
641 | ((necp_data_tracing_pid && (!pid || NECP_DATA_TRACE_PID_MATCHED(pid))) || \ |
642 | (necp_data_tracing_proto && NECP_DATA_TRACE_PROTO_MATCHED(protocol)) || \ |
643 | (necp_data_tracing_ifindex && NECP_DATA_TRACE_IFINDEX_MATCHED(ifindex)) || \ |
644 | (necp_data_tracing_port && (NECP_DATA_TRACE_LOCAL_PORT_MATCHED(local_addr) || NECP_DATA_TRACE_REMOTE_ORT_MATCHED(remote_addr))))) ? necp_data_tracing_level : 0) |
645 | |
646 | #define NECP_ENABLE_DATA_TRACE_AND(local_addr, remote_addr, protocol, pid, ifindex) \ |
647 | ((necp_data_tracing_level && \ |
648 | ((!necp_data_tracing_pid || !pid || NECP_DATA_TRACE_PID_MATCHED(pid)) && \ |
649 | (!necp_data_tracing_proto || NECP_DATA_TRACE_PROTO_MATCHED(protocol)) && \ |
650 | (!necp_data_tracing_ifindex || NECP_DATA_TRACE_IFINDEX_MATCHED(ifindex)) && \ |
651 | (!necp_data_tracing_port || (NECP_DATA_TRACE_LOCAL_PORT_MATCHED(local_addr) || NECP_DATA_TRACE_REMOTE_ORT_MATCHED(remote_addr))))) ? necp_data_tracing_level : 0) |
652 | |
653 | #define NECP_ENABLE_DATA_TRACE(local_addr, remote_addr, protocol, pid, ifindex) \ |
654 | (necp_data_tracing_match_all ? \ |
655 | NECP_ENABLE_DATA_TRACE_AND(local_addr, remote_addr, protocol, pid, ifindex) : \ |
656 | NECP_ENABLE_DATA_TRACE_OR(local_addr, remote_addr, protocol, pid, ifindex)) |
657 | |
658 | #define NECP_DATA_TRACE_ON(debug) (debug) |
659 | #define NECP_DATA_TRACE_POLICY_ON(debug) (debug > NECP_DATA_TRACE_LEVEL_BRIEF) |
660 | #define NECP_DATA_TRACE_CONDITION_ON(debug) (debug > NECP_DATA_TRACE_LEVEL_POLICY) |
661 | |
662 | const char* necp_get_address_string(union necp_sockaddr_union *address, char addr_str[MAX_IPv6_STR_LEN]); |
663 | |
664 | #define NECP_DATA_TRACE_LOG_APP_LEVEL(debug, caller, log_msg, policy_id, skip_policy_id) \ |
665 | if (NECP_DATA_TRACE_ON(debug)) { \ |
666 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
667 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
668 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <pid=%d Application %d Real Application %d BoundInterface %d> <policy_id %d skip_policy_id %d>", \ |
669 | caller, log_msg, info.local_addr.sin.sin_family, info.protocol, ntohs(info.local_addr.sin.sin_port), ntohs(info.local_addr.sin6.sin6_port), ntohs(info.remote_addr.sin.sin_port), ntohs(info.remote_addr.sin6.sin6_port), necp_get_address_string(&info.local_addr, laddr_str), necp_get_address_string(&info.remote_addr, raddr_str), necp_drop_all_order, info.pid, info.application_id, info.real_application_id, info.bound_interface_index, policy_id, skip_policy_id); \ |
670 | } |
671 | |
672 | #define NECP_DATA_TRACE_LOG_SOCKET(debug, socket, caller, log_msg, policy_id, skip_policy_id) \ |
673 | if (NECP_DATA_TRACE_ON(debug)) { \ |
674 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
675 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
676 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s %llx>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <pid=%d Application %d Real Application %d BoundInterface %d> <policy_id %d skip_policy_id %d>", \ |
677 | caller, (unsigned long long)socket, log_msg, info.local_addr.sin.sin_family, info.protocol, ntohs(info.local_addr.sin.sin_port), ntohs(info.local_addr.sin6.sin6_port), ntohs(info.remote_addr.sin.sin_port), ntohs(info.remote_addr.sin6.sin6_port), necp_get_address_string(&info.local_addr, laddr_str), necp_get_address_string(&info.remote_addr, raddr_str), necp_drop_all_order, info.pid, info.application_id, info.real_application_id, info.bound_interface_index, policy_id, skip_policy_id); \ |
678 | } |
679 | |
680 | #define NECP_DATA_TRACE_LOG_SOCKET_RESULT(debug, socket, caller, log_msg) \ |
681 | if (NECP_DATA_TRACE_ON(debug)) { \ |
682 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
683 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
684 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s %llx>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <pid=%d Application %d Real Application %d BoundInterface %d> (policy id=%d session_order=%d policy_order=%d result=%s)", \ |
685 | caller, (unsigned long long)socket, log_msg, info->local_addr.sin.sin_family, info->protocol, ntohs(info->local_addr.sin.sin_port), ntohs(info->local_addr.sin6.sin6_port), ntohs(info->remote_addr.sin.sin_port), ntohs(info->remote_addr.sin6.sin6_port), necp_get_address_string(&info->local_addr, laddr_str), necp_get_address_string(&info->remote_addr, raddr_str), necp_drop_all_order, info->pid, info->application_id, info->real_application_id, info->bound_interface_index, policy_search_array[i]->id, policy_search_array[i]->session_order, policy_search_array[i]->order, resultString[policy_search_array[i]->result]); \ |
686 | } |
687 | |
688 | #define NECP_DATA_TRACE_LOG_IP4(debug, caller, log_msg) \ |
689 | if (NECP_DATA_TRACE_ON(debug)) { \ |
690 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
691 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
692 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <BoundInterface %d> <socket policy id %d socket skip id %d> <mbuf %X len %d %d>", \ |
693 | caller, log_msg, local_addr.sin.sin_family, protocol, ntohs(local_addr.sin.sin_port), ntohs(local_addr.sin6.sin6_port), ntohs(remote_addr.sin.sin_port), ntohs(remote_addr.sin6.sin6_port), necp_get_address_string(&local_addr, laddr_str), necp_get_address_string(&remote_addr, raddr_str), necp_drop_all_order, bound_interface_index, socket_policy_id, socket_skip_policy_id, (unsigned int)packet, ip->ip_len, ntohs(ip->ip_len)); \ |
694 | } |
695 | |
696 | #define NECP_DATA_TRACE_LOG_IP6(debug, caller, log_msg) \ |
697 | if (NECP_DATA_TRACE_ON(debug)) { \ |
698 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
699 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
700 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <BoundInterface %d> <socket policy id %d socket skip id %d> <mbuf %X len %d %d>", \ |
701 | caller, log_msg, local_addr.sin.sin_family, protocol, ntohs(local_addr.sin.sin_port), ntohs(local_addr.sin6.sin6_port), ntohs(remote_addr.sin.sin_port), ntohs(remote_addr.sin6.sin6_port), necp_get_address_string(&local_addr, laddr_str), necp_get_address_string(&remote_addr, raddr_str), necp_drop_all_order, bound_interface_index, socket_policy_id, socket_skip_policy_id, (unsigned int)packet, ip6->ip6_plen, ntohs(ip6->ip6_plen)); \ |
702 | } |
703 | |
704 | #define NECP_DATA_TRACE_LOG_IP_RESULT(debug, caller, log_msg) \ |
705 | if (NECP_DATA_TRACE_ON(debug)) { \ |
706 | char laddr_str[MAX_IPv6_STR_LEN]; \ |
707 | char raddr_str[MAX_IPv6_STR_LEN]; \ |
708 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: %s - fam %d proto %d port <local %d/%d remote %d/%d> <local %s remote %s> <drop-all order %d> <BoundInterface %d> (policy id=%d session_order=%d policy_order=%d result=%s)", \ |
709 | caller, log_msg, local_addr->sin.sin_family, protocol, ntohs(local_addr->sin.sin_port), ntohs(local_addr->sin6.sin6_port), ntohs(remote_addr->sin.sin_port), ntohs(remote_addr->sin6.sin6_port), necp_get_address_string(local_addr, laddr_str), necp_get_address_string(remote_addr, raddr_str), necp_drop_all_order, bound_interface_index, policy_search_array[i]->id, policy_search_array[i]->session_order, policy_search_array[i]->order, resultString[policy_search_array[i]->result]); \ |
710 | } |
711 | |
712 | #define NECP_DATA_TRACE_LOG_POLICY(debug, caller, log_msg) \ |
713 | if (NECP_DATA_TRACE_POLICY_ON(debug)) { \ |
714 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: %s - policy id=%d session_order=%d policy_order=%d result=%s (cond_policy_id %d) (skip_session_order %d skip_order %d)", \ |
715 | caller, log_msg, policy_search_array[i]->id, policy_search_array[i]->session_order, policy_search_array[i]->order, resultString[policy_search_array[i]->result], policy_search_array[i]->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID ? policy_search_array[i]->cond_policy_id : 0, skip_session_order, skip_order); \ |
716 | } |
717 | |
718 | #define NECP_DATA_TRACE_LOG_CONDITION3(debug, caller, negate, name, val1, val2, val3, input1, input2, input3) \ |
719 | if (NECP_DATA_TRACE_CONDITION_ON(debug)) { \ |
720 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: ------ %smatching <%s> <value (%d / 0x%X) (%d / 0x%X) (%d / 0x%X) input (%d / 0x%X) (%d / 0x%X) (%d / 0x%X)>", \ |
721 | caller, negate ? "!":"", name, val1, val1, val2, val2, val3, val3, input1, input1, input2, input2, input3, input3); \ |
722 | } |
723 | |
724 | #define NECP_DATA_TRACE_LOG_CONDITION_STR3(debug, caller, negate, name, val1, val2, val3, input1, input2, input3) \ |
725 | if (NECP_DATA_TRACE_CONDITION_ON(debug)) { \ |
726 | NECPDATATRACELOG(LOG_ERR, "DATA-TRACE <%s>: ------ %smatching <%s> <value %s %s %s input %s %s %s>", \ |
727 | caller, negate ? "!":"", name, val1 != NULL ? val1 : "null", val2 != NULL ? val2 : "null", val3 != NULL ? val3 : "null", \ |
728 | input1 != NULL ? input1 : "null", input2 != NULL ? input2 : "null", input3 != NULL ? input3 : "null"); \ |
729 | } |
730 | |
731 | #define NECP_DATA_TRACE_LOG_CONDITION(debug, caller, negate, name, val, input) \ |
732 | NECP_DATA_TRACE_LOG_CONDITION3(debug, caller, negate, name, val, 0, 0, input, 0, 0) |
733 | |
734 | #define NECP_DATA_TRACE_LOG_CONDITION_STR(debug, caller, negate, name, val, input) \ |
735 | NECP_DATA_TRACE_LOG_CONDITION_STR3(debug, caller, negate, name, val, "n/a", "n/a", input, "n/a", "n/a") |
736 | |
737 | #define NECP_IS_INTCOPROC_ADDRESS(addrv6) \ |
738 | (IN6_IS_ADDR_LINKLOCAL(addrv6) && \ |
739 | addrv6->s6_addr32[2] == ntohl(0xaede48ff) && addrv6->s6_addr32[3] == ntohl(0xfe334455)) |
740 | |
741 | const char* resultString[NECP_POLICY_RESULT_MAX + 1] = { |
742 | "INVALID" , |
743 | "PASS" , |
744 | "SKIP" , |
745 | "DROP" , |
746 | "SOCKET_DIVERT" , |
747 | "SOCKET_FILTER" , |
748 | "IP_TUNNEL" , |
749 | "IP_FILTER" , |
750 | "TRIGGER" , |
751 | "TRIGGER_IF_NEEDED" , |
752 | "TRIGGER_SCOPED" , |
753 | "NO_TRIGGER_SCOPED" , |
754 | "SOCKET_SCOPED" , |
755 | "ROUTE_RULES" , |
756 | "USE_NETAGENT" , |
757 | "NETAGENT_SCOPED" , |
758 | "SCOPED_DIRECT" , |
759 | "ALLOW_UNENTITLED" , |
760 | "REMOVE_NETAGENT" |
761 | }; |
762 | |
763 | // Session order allocation |
764 | static u_int32_t |
765 | necp_allocate_new_session_order(u_int32_t priority, u_int32_t control_unit) |
766 | { |
767 | u_int32_t new_order = 0; |
768 | |
769 | // For now, just allocate 1000 orders for each priority |
770 | if (priority == NECP_SESSION_PRIORITY_UNKNOWN || priority > NECP_SESSION_NUM_PRIORITIES) { |
771 | priority = NECP_SESSION_PRIORITY_DEFAULT; |
772 | } |
773 | |
774 | // Use the control unit to decide the offset into the priority list |
775 | new_order = (control_unit) + ((priority - 1) * 1000); |
776 | |
777 | return new_order; |
778 | } |
779 | |
780 | static inline u_int32_t |
781 | necp_get_first_order_for_priority(u_int32_t priority) |
782 | { |
783 | if (priority == 0) { |
784 | return 0; |
785 | } |
786 | return ((priority - 1) * 1000) + 1; |
787 | } |
788 | |
789 | // Sysctl handler |
790 | static int |
791 | sysctl_handle_necp_level SYSCTL_HANDLER_ARGS |
792 | { |
793 | #pragma unused(arg1, arg2) |
794 | int error = sysctl_handle_int(oidp, arg1: oidp->oid_arg1, arg2: oidp->oid_arg2, req); |
795 | necp_drop_all_order = necp_get_first_order_for_priority(priority: necp_drop_all_level); |
796 | return error; |
797 | } |
798 | |
799 | static int |
800 | sysctl_handle_necp_unentitled_level SYSCTL_HANDLER_ARGS |
801 | { |
802 | #pragma unused(arg1, arg2) |
803 | int error = sysctl_handle_int(oidp, arg1: oidp->oid_arg1, arg2: oidp->oid_arg2, req); |
804 | necp_drop_unentitled_order = necp_get_first_order_for_priority(priority: necp_drop_unentitled_level); |
805 | return error; |
806 | } |
807 | |
808 | // Use a macro here to avoid computing the kauth_cred_t when necp_drop_unentitled_level is 0 |
809 | static inline u_int32_t |
810 | _necp_process_drop_order_inner(kauth_cred_t cred) |
811 | { |
812 | if (priv_check_cred(cred, PRIV_NET_PRIVILEGED_CLIENT_ACCESS, flags: 0) != 0 && |
813 | priv_check_cred(cred, PRIV_NET_PRIVILEGED_SERVER_ACCESS, flags: 0) != 0) { |
814 | return necp_drop_unentitled_order; |
815 | } else { |
816 | return 0; |
817 | } |
818 | } |
819 | |
820 | #define necp_process_drop_order(_cred) (necp_drop_unentitled_order != 0 ? _necp_process_drop_order_inner(_cred) : necp_drop_unentitled_order) |
821 | #pragma GCC poison _necp_process_drop_order_inner |
822 | |
823 | static int |
824 | sysctl_handle_necp_management_level SYSCTL_HANDLER_ARGS |
825 | { |
826 | #pragma unused(arg1, arg2) |
827 | int error = sysctl_handle_int(oidp, arg1: oidp->oid_arg1, arg2: oidp->oid_arg2, req); |
828 | necp_drop_management_order = necp_get_first_order_for_priority(priority: necp_drop_management_level); |
829 | return error; |
830 | } |
831 | |
832 | // Session fd |
833 | |
834 | static int necp_session_op_close(struct fileglob *, vfs_context_t); |
835 | |
836 | static const struct fileops necp_session_fd_ops = { |
837 | .fo_type = DTYPE_NETPOLICY, |
838 | .fo_read = fo_no_read, |
839 | .fo_write = fo_no_write, |
840 | .fo_ioctl = fo_no_ioctl, |
841 | .fo_select = fo_no_select, |
842 | .fo_close = necp_session_op_close, |
843 | .fo_drain = fo_no_drain, |
844 | .fo_kqfilter = fo_no_kqfilter, |
845 | }; |
846 | |
847 | static inline int |
848 | necp_is_platform_binary(proc_t proc) |
849 | { |
850 | return (proc != NULL) ? (csproc_get_platform_binary(proc) && cs_valid(proc)) : 0; |
851 | } |
852 | |
853 | static inline necp_drop_all_bypass_check_result_t |
854 | necp_check_drop_all_bypass_result(proc_t proc) |
855 | { |
856 | if (proc == NULL) { |
857 | proc = current_proc(); |
858 | if (proc == NULL) { |
859 | return NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE; |
860 | } |
861 | } |
862 | |
863 | #if defined(XNU_TARGET_OS_OSX) |
864 | const char *signing_id = NULL; |
865 | const bool isConfigd = (necp_is_platform_binary(proc) && |
866 | (signing_id = cs_identity_get(proc)) && |
867 | (strlen(s: signing_id) == SIGNING_ID_CONFIGD_LEN) && |
868 | (memcmp(s1: signing_id, SIGNING_ID_CONFIGD, SIGNING_ID_CONFIGD_LEN) == 0)); |
869 | if (isConfigd) { |
870 | return NECP_DROP_ALL_BYPASS_CHECK_RESULT_TRUE; |
871 | } |
872 | #endif |
873 | |
874 | const task_t task = proc_task(proc); |
875 | if (task == NULL || !IOTaskHasEntitlement(task, entitlement: "com.apple.private.necp.drop_all_bypass" )) { |
876 | return NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE; |
877 | } else { |
878 | return NECP_DROP_ALL_BYPASS_CHECK_RESULT_TRUE; |
879 | } |
880 | } |
881 | |
882 | int |
883 | necp_session_open(struct proc *p, struct necp_session_open_args *uap, int *retval) |
884 | { |
885 | #pragma unused(uap) |
886 | int error = 0; |
887 | struct necp_session *session = NULL; |
888 | struct fileproc *fp = NULL; |
889 | int fd = -1; |
890 | uid_t uid = kauth_cred_getuid(cred: kauth_cred_get()); |
891 | |
892 | if (!necp_is_platform_binary(proc: p)) { |
893 | NECPLOG0(LOG_ERR, "Only platform-signed binaries can open NECP sessions" ); |
894 | error = EACCES; |
895 | goto done; |
896 | } |
897 | |
898 | if (uid != 0 && priv_check_cred(cred: kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, flags: 0) != 0) { |
899 | NECPLOG0(LOG_ERR, "Process does not hold necessary entitlement to open NECP session" ); |
900 | error = EACCES; |
901 | goto done; |
902 | } |
903 | |
904 | error = falloc(p, &fp, &fd); |
905 | if (error != 0) { |
906 | goto done; |
907 | } |
908 | |
909 | session = necp_create_session(); |
910 | if (session == NULL) { |
911 | error = ENOMEM; |
912 | goto done; |
913 | } |
914 | |
915 | fp->fp_flags |= FP_CLOEXEC | FP_CLOFORK; |
916 | fp->fp_glob->fg_flag = 0; |
917 | fp->fp_glob->fg_ops = &necp_session_fd_ops; |
918 | fp_set_data(fp, fg_data: session); |
919 | |
920 | proc_fdlock(p); |
921 | procfdtbl_releasefd(p, fd, NULL); |
922 | fp_drop(p, fd, fp, locked: 1); |
923 | proc_fdunlock(p); |
924 | |
925 | *retval = fd; |
926 | done: |
927 | if (error != 0) { |
928 | if (fp != NULL) { |
929 | fp_free(p, fd, fp); |
930 | fp = NULL; |
931 | } |
932 | } |
933 | |
934 | return error; |
935 | } |
936 | |
937 | static int |
938 | necp_session_op_close(struct fileglob *fg, vfs_context_t ctx) |
939 | { |
940 | #pragma unused(ctx) |
941 | struct necp_session *session = (struct necp_session *)fg_get_data(fg); |
942 | fg_set_data(fg, NULL); |
943 | |
944 | if (session != NULL) { |
945 | necp_policy_mark_all_for_deletion(session); |
946 | necp_policy_apply_all(session); |
947 | necp_delete_session(session); |
948 | return 0; |
949 | } else { |
950 | return ENOENT; |
951 | } |
952 | } |
953 | |
954 | static int |
955 | necp_session_find_from_fd(struct proc *p, int fd, |
956 | struct fileproc **fpp, struct necp_session **session) |
957 | { |
958 | struct fileproc *fp = NULL; |
959 | int error = fp_get_ftype(p, fd, ftype: DTYPE_NETPOLICY, ENODEV, fpp: &fp); |
960 | |
961 | if (error == 0) { |
962 | *fpp = fp; |
963 | *session = (struct necp_session *)fp_get_data(fp); |
964 | if ((*session)->necp_fd_type != necp_fd_type_session) { |
965 | // Not a client fd, ignore |
966 | fp_drop(p, fd, fp, locked: 0); |
967 | error = EINVAL; |
968 | } |
969 | } |
970 | |
971 | return error; |
972 | } |
973 | |
974 | static int |
975 | necp_session_add_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
976 | { |
977 | int error = 0; |
978 | u_int8_t *tlv_buffer = NULL; |
979 | |
980 | if (uap->in_buffer_length == 0 || uap->in_buffer_length > NECP_MAX_POLICY_SIZE || uap->in_buffer == 0) { |
981 | NECPLOG(LOG_ERR, "necp_session_add_policy invalid input (%zu)" , (size_t)uap->in_buffer_length); |
982 | error = EINVAL; |
983 | goto done; |
984 | } |
985 | |
986 | if (uap->out_buffer_length < sizeof(necp_policy_id) || uap->out_buffer == 0) { |
987 | NECPLOG(LOG_ERR, "necp_session_add_policy invalid output buffer (%zu)" , (size_t)uap->out_buffer_length); |
988 | error = EINVAL; |
989 | goto done; |
990 | } |
991 | |
992 | if ((tlv_buffer = (u_int8_t *)kalloc_data(uap->in_buffer_length, Z_WAITOK | Z_ZERO)) == NULL) { |
993 | error = ENOMEM; |
994 | goto done; |
995 | } |
996 | |
997 | error = copyin(uap->in_buffer, tlv_buffer, uap->in_buffer_length); |
998 | if (error != 0) { |
999 | NECPLOG(LOG_ERR, "necp_session_add_policy tlv copyin error (%d)" , error); |
1000 | goto done; |
1001 | } |
1002 | |
1003 | necp_policy_id new_policy_id = necp_handle_policy_add(session, tlv_buffer, tlv_buffer_length: uap->in_buffer_length, offset: 0, error: &error); |
1004 | if (error != 0) { |
1005 | NECPLOG(LOG_ERR, "necp_session_add_policy failed to add policy (%d)" , error); |
1006 | goto done; |
1007 | } |
1008 | |
1009 | error = copyout(&new_policy_id, uap->out_buffer, sizeof(new_policy_id)); |
1010 | if (error != 0) { |
1011 | NECPLOG(LOG_ERR, "necp_session_add_policy policy_id copyout error (%d)" , error); |
1012 | goto done; |
1013 | } |
1014 | |
1015 | done: |
1016 | if (tlv_buffer != NULL) { |
1017 | kfree_data(tlv_buffer, uap->in_buffer_length); |
1018 | tlv_buffer = NULL; |
1019 | } |
1020 | *retval = error; |
1021 | |
1022 | return error; |
1023 | } |
1024 | |
1025 | static int |
1026 | necp_session_get_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1027 | { |
1028 | int error = 0; |
1029 | u_int8_t *response = NULL; |
1030 | |
1031 | if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) { |
1032 | NECPLOG(LOG_ERR, "necp_session_get_policy invalid input (%zu)" , (size_t)uap->in_buffer_length); |
1033 | error = EINVAL; |
1034 | goto done; |
1035 | } |
1036 | |
1037 | necp_policy_id policy_id = 0; |
1038 | error = copyin(uap->in_buffer, &policy_id, sizeof(policy_id)); |
1039 | if (error != 0) { |
1040 | NECPLOG(LOG_ERR, "necp_session_get_policy policy_id copyin error (%d)" , error); |
1041 | goto done; |
1042 | } |
1043 | |
1044 | struct necp_session_policy *policy = necp_policy_find(session, policy_id); |
1045 | if (policy == NULL || policy->pending_deletion) { |
1046 | NECPLOG(LOG_ERR, "Failed to find policy with id %d" , policy_id); |
1047 | error = ENOENT; |
1048 | goto done; |
1049 | } |
1050 | |
1051 | u_int32_t order_tlv_size = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_order); |
1052 | u_int32_t result_tlv_size = (policy->result_size ? (sizeof(u_int8_t) + sizeof(u_int32_t) + policy->result_size) : 0); |
1053 | u_int32_t response_size = order_tlv_size + result_tlv_size + policy->conditions_size; |
1054 | |
1055 | if (uap->out_buffer_length < response_size || uap->out_buffer == 0) { |
1056 | NECPLOG(LOG_ERR, "necp_session_get_policy buffer not large enough (%zu < %u)" , (size_t)uap->out_buffer_length, response_size); |
1057 | error = EINVAL; |
1058 | goto done; |
1059 | } |
1060 | |
1061 | if (response_size > NECP_MAX_POLICY_SIZE) { |
1062 | NECPLOG(LOG_ERR, "necp_session_get_policy size too large to copy (%u)" , response_size); |
1063 | error = EINVAL; |
1064 | goto done; |
1065 | } |
1066 | |
1067 | response = (u_int8_t *)kalloc_data(response_size, Z_WAITOK | Z_ZERO); |
1068 | if (response == NULL) { |
1069 | error = ENOMEM; |
1070 | goto done; |
1071 | } |
1072 | |
1073 | u_int8_t *cursor = response; |
1074 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, length: sizeof(necp_policy_order), value: &policy->order, buffer: response, buffer_length: response_size); |
1075 | if (result_tlv_size) { |
1076 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, length: policy->result_size, value: &policy->result, buffer: response, buffer_length: response_size); |
1077 | } |
1078 | if (policy->conditions_size) { |
1079 | memcpy(dst: ((u_int8_t *)(void *)(cursor)), src: policy->conditions, n: policy->conditions_size); |
1080 | } |
1081 | |
1082 | error = copyout(response, uap->out_buffer, response_size); |
1083 | if (error != 0) { |
1084 | NECPLOG(LOG_ERR, "necp_session_get_policy TLV copyout error (%d)" , error); |
1085 | goto done; |
1086 | } |
1087 | |
1088 | done: |
1089 | if (response != NULL) { |
1090 | kfree_data(response, response_size); |
1091 | response = NULL; |
1092 | } |
1093 | *retval = error; |
1094 | |
1095 | return error; |
1096 | } |
1097 | |
1098 | static int |
1099 | necp_session_delete_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1100 | { |
1101 | int error = 0; |
1102 | |
1103 | if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) { |
1104 | NECPLOG(LOG_ERR, "necp_session_delete_policy invalid input (%zu)" , (size_t)uap->in_buffer_length); |
1105 | error = EINVAL; |
1106 | goto done; |
1107 | } |
1108 | |
1109 | necp_policy_id delete_policy_id = 0; |
1110 | error = copyin(uap->in_buffer, &delete_policy_id, sizeof(delete_policy_id)); |
1111 | if (error != 0) { |
1112 | NECPLOG(LOG_ERR, "necp_session_delete_policy policy_id copyin error (%d)" , error); |
1113 | goto done; |
1114 | } |
1115 | |
1116 | struct necp_session_policy *policy = necp_policy_find(session, policy_id: delete_policy_id); |
1117 | if (policy == NULL || policy->pending_deletion) { |
1118 | NECPLOG(LOG_ERR, "necp_session_delete_policy failed to find policy with id %u" , delete_policy_id); |
1119 | error = ENOENT; |
1120 | goto done; |
1121 | } |
1122 | |
1123 | necp_policy_mark_for_deletion(session, policy); |
1124 | done: |
1125 | *retval = error; |
1126 | return error; |
1127 | } |
1128 | |
1129 | static int |
1130 | necp_session_apply_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1131 | { |
1132 | #pragma unused(uap) |
1133 | necp_policy_apply_all(session); |
1134 | *retval = 0; |
1135 | return 0; |
1136 | } |
1137 | |
1138 | static int |
1139 | necp_session_list_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1140 | { |
1141 | u_int32_t tlv_size = (sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_id)); |
1142 | u_int32_t response_size = 0; |
1143 | u_int8_t *response = NULL; |
1144 | int num_policies = 0; |
1145 | int cur_policy_index = 0; |
1146 | int error = 0; |
1147 | struct necp_session_policy *policy; |
1148 | |
1149 | LIST_FOREACH(policy, &session->policies, chain) { |
1150 | if (!policy->pending_deletion) { |
1151 | num_policies++; |
1152 | } |
1153 | } |
1154 | |
1155 | if (num_policies > NECP_MAX_POLICY_LIST_COUNT) { |
1156 | NECPLOG(LOG_ERR, "necp_session_list_all size too large to copy (%u policies)" , num_policies); |
1157 | error = EINVAL; |
1158 | goto done; |
1159 | } |
1160 | |
1161 | response_size = num_policies * tlv_size; |
1162 | if (uap->out_buffer_length < response_size || uap->out_buffer == 0) { |
1163 | NECPLOG(LOG_ERR, "necp_session_list_all buffer not large enough (%zu < %u)" , (size_t)uap->out_buffer_length, response_size); |
1164 | error = EINVAL; |
1165 | goto done; |
1166 | } |
1167 | |
1168 | // Create a response with one Policy ID TLV for each policy |
1169 | response = (u_int8_t *)kalloc_data(response_size, Z_WAITOK | Z_ZERO); |
1170 | if (response == NULL) { |
1171 | error = ENOMEM; |
1172 | goto done; |
1173 | } |
1174 | |
1175 | u_int8_t *cursor = response; |
1176 | LIST_FOREACH(policy, &session->policies, chain) { |
1177 | if (!policy->pending_deletion && cur_policy_index < num_policies) { |
1178 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, length: sizeof(u_int32_t), value: &policy->local_id, buffer: response, buffer_length: response_size); |
1179 | cur_policy_index++; |
1180 | } |
1181 | } |
1182 | |
1183 | error = copyout(response, uap->out_buffer, response_size); |
1184 | if (error != 0) { |
1185 | NECPLOG(LOG_ERR, "necp_session_list_all TLV copyout error (%d)" , error); |
1186 | goto done; |
1187 | } |
1188 | |
1189 | done: |
1190 | if (response != NULL) { |
1191 | kfree_data(response, response_size); |
1192 | response = NULL; |
1193 | } |
1194 | *retval = error; |
1195 | |
1196 | return error; |
1197 | } |
1198 | |
1199 | |
1200 | static int |
1201 | necp_session_delete_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1202 | { |
1203 | #pragma unused(uap) |
1204 | necp_policy_mark_all_for_deletion(session); |
1205 | *retval = 0; |
1206 | return 0; |
1207 | } |
1208 | |
1209 | static int |
1210 | necp_session_set_session_priority(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1211 | { |
1212 | int error = 0; |
1213 | struct necp_session_policy *policy = NULL; |
1214 | struct necp_session_policy *temp_policy = NULL; |
1215 | |
1216 | if (uap->in_buffer_length < sizeof(necp_session_priority) || uap->in_buffer == 0) { |
1217 | NECPLOG(LOG_ERR, "necp_session_set_session_priority invalid input (%zu)" , (size_t)uap->in_buffer_length); |
1218 | error = EINVAL; |
1219 | goto done; |
1220 | } |
1221 | |
1222 | necp_session_priority requested_session_priority = 0; |
1223 | error = copyin(uap->in_buffer, &requested_session_priority, sizeof(requested_session_priority)); |
1224 | if (error != 0) { |
1225 | NECPLOG(LOG_ERR, "necp_session_set_session_priority priority copyin error (%d)" , error); |
1226 | goto done; |
1227 | } |
1228 | |
1229 | // Enforce special session priorities with entitlements |
1230 | if (requested_session_priority == NECP_SESSION_PRIORITY_CONTROL || |
1231 | requested_session_priority == NECP_SESSION_PRIORITY_CONTROL_1 || |
1232 | requested_session_priority == NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL || |
1233 | requested_session_priority == NECP_SESSION_PRIORITY_HIGH_RESTRICTED) { |
1234 | errno_t cred_result = priv_check_cred(cred: kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, flags: 0); |
1235 | if (cred_result != 0) { |
1236 | NECPLOG(LOG_ERR, "Session does not hold necessary entitlement to claim priority level %d" , requested_session_priority); |
1237 | error = EPERM; |
1238 | goto done; |
1239 | } |
1240 | } |
1241 | |
1242 | if (session->session_priority != requested_session_priority) { |
1243 | session->session_priority = requested_session_priority; |
1244 | session->session_order = necp_allocate_new_session_order(priority: session->session_priority, control_unit: session->control_unit); |
1245 | session->dirty = TRUE; |
1246 | |
1247 | // Mark all policies as needing updates |
1248 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
1249 | policy->pending_update = TRUE; |
1250 | } |
1251 | } |
1252 | |
1253 | done: |
1254 | *retval = error; |
1255 | return error; |
1256 | } |
1257 | |
1258 | static int |
1259 | necp_session_lock_to_process(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1260 | { |
1261 | #pragma unused(uap) |
1262 | session->proc_locked = TRUE; |
1263 | *retval = 0; |
1264 | return 0; |
1265 | } |
1266 | |
1267 | static int |
1268 | necp_session_register_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1269 | { |
1270 | int error = 0; |
1271 | struct necp_service_registration *new_service = NULL; |
1272 | |
1273 | if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) { |
1274 | NECPLOG(LOG_ERR, "necp_session_register_service invalid input (%zu)" , (size_t)uap->in_buffer_length); |
1275 | error = EINVAL; |
1276 | goto done; |
1277 | } |
1278 | |
1279 | uuid_t service_uuid; |
1280 | error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid)); |
1281 | if (error != 0) { |
1282 | NECPLOG(LOG_ERR, "necp_session_register_service uuid copyin error (%d)" , error); |
1283 | goto done; |
1284 | } |
1285 | |
1286 | new_service = kalloc_type(struct necp_service_registration, |
1287 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
1288 | |
1289 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
1290 | new_service->service_id = necp_create_uuid_service_id_mapping(uuid: service_uuid); |
1291 | LIST_INSERT_HEAD(&session->services, new_service, session_chain); |
1292 | LIST_INSERT_HEAD(&necp_registered_service_list, new_service, kernel_chain); |
1293 | lck_rw_done(lck: &necp_kernel_policy_lock); |
1294 | |
1295 | done: |
1296 | *retval = error; |
1297 | return error; |
1298 | } |
1299 | |
1300 | static int |
1301 | necp_session_unregister_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1302 | { |
1303 | int error = 0; |
1304 | struct necp_service_registration *service = NULL; |
1305 | struct necp_service_registration *temp_service = NULL; |
1306 | struct necp_uuid_id_mapping *mapping = NULL; |
1307 | |
1308 | if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) { |
1309 | NECPLOG(LOG_ERR, "necp_session_unregister_service invalid input (%zu)" , (size_t)uap->in_buffer_length); |
1310 | error = EINVAL; |
1311 | goto done; |
1312 | } |
1313 | |
1314 | uuid_t service_uuid; |
1315 | error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid)); |
1316 | if (error != 0) { |
1317 | NECPLOG(LOG_ERR, "necp_session_unregister_service uuid copyin error (%d)" , error); |
1318 | goto done; |
1319 | } |
1320 | |
1321 | // Remove all matching services for this session |
1322 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
1323 | mapping = necp_uuid_lookup_service_id_locked(uuid: service_uuid); |
1324 | if (mapping != NULL) { |
1325 | LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) { |
1326 | if (service->service_id == mapping->id) { |
1327 | LIST_REMOVE(service, session_chain); |
1328 | LIST_REMOVE(service, kernel_chain); |
1329 | kfree_type(struct necp_service_registration, service); |
1330 | } |
1331 | } |
1332 | necp_remove_uuid_service_id_mapping(uuid: service_uuid); |
1333 | } |
1334 | lck_rw_done(lck: &necp_kernel_policy_lock); |
1335 | |
1336 | done: |
1337 | *retval = error; |
1338 | return error; |
1339 | } |
1340 | |
1341 | static int |
1342 | necp_session_dump_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1343 | { |
1344 | #pragma unused(session) |
1345 | int error = 0; |
1346 | |
1347 | if (uap->out_buffer_length == 0 || uap->out_buffer == 0) { |
1348 | NECPLOG(LOG_ERR, "necp_session_dump_all invalid output buffer (%zu)" , (size_t)uap->out_buffer_length); |
1349 | error = EINVAL; |
1350 | goto done; |
1351 | } |
1352 | |
1353 | error = necp_handle_policy_dump_all(out_buffer: uap->out_buffer, out_buffer_length: uap->out_buffer_length); |
1354 | done: |
1355 | *retval = error; |
1356 | return error; |
1357 | } |
1358 | |
1359 | static int |
1360 | necp_session_add_domain_filter(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1361 | { |
1362 | int error = 0; |
1363 | struct net_bloom_filter *bloom_filter = NULL; |
1364 | const size_t in_buffer_length = (size_t)uap->in_buffer_length; |
1365 | const size_t out_buffer_length = (size_t)uap->out_buffer_length; |
1366 | |
1367 | if (in_buffer_length < sizeof(struct net_bloom_filter) || |
1368 | in_buffer_length > NECP_MAX_DOMAIN_FILTER_SIZE || |
1369 | uap->in_buffer == 0) { |
1370 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter invalid input (%zu)" , (size_t)in_buffer_length); |
1371 | error = EINVAL; |
1372 | goto done; |
1373 | } |
1374 | |
1375 | if (out_buffer_length < sizeof(u_int32_t) || uap->out_buffer == 0) { |
1376 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter buffer not large enough (%zu)" , (size_t)out_buffer_length); |
1377 | error = EINVAL; |
1378 | goto done; |
1379 | } |
1380 | |
1381 | bloom_filter = (struct net_bloom_filter *)kalloc_data(in_buffer_length, Z_WAITOK | Z_ZERO); |
1382 | if (bloom_filter == NULL) { |
1383 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter allocate filter error (%zu)" , in_buffer_length); |
1384 | error = ENOMEM; |
1385 | goto done; |
1386 | } |
1387 | |
1388 | error = copyin(uap->in_buffer, bloom_filter, in_buffer_length); |
1389 | if (error != 0) { |
1390 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter filter copyin error (%d)" , error); |
1391 | goto done; |
1392 | } |
1393 | |
1394 | size_t expected_filter_size = net_bloom_filter_get_size(num_bits: bloom_filter->b_table_num_bits); |
1395 | if (expected_filter_size != in_buffer_length) { |
1396 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter size mismatch (%zu != %zu)" , expected_filter_size, in_buffer_length); |
1397 | error = EINVAL; |
1398 | goto done; |
1399 | } |
1400 | |
1401 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
1402 | u_int32_t filter_id = necp_create_domain_filter(list: &necp_global_domain_filter_list, owner_list: &session->domain_filters, filter: bloom_filter); |
1403 | lck_rw_done(lck: &necp_kernel_policy_lock); |
1404 | |
1405 | if (filter_id == 0) { |
1406 | error = ENOMEM; |
1407 | } else { |
1408 | // Bloom filter is taken over by the new filter entry, clear the local pointer |
1409 | bloom_filter = NULL; |
1410 | |
1411 | error = copyout(&filter_id, uap->out_buffer, sizeof(filter_id)); |
1412 | if (error != 0) { |
1413 | NECPLOG(LOG_ERR, "necp_session_add_domain_filter ID copyout error (%d)" , error); |
1414 | goto done; |
1415 | } |
1416 | } |
1417 | |
1418 | done: |
1419 | *retval = error; |
1420 | if (error != 0 && bloom_filter != NULL) { |
1421 | uint8_t *filter_buffer = (uint8_t *)bloom_filter; |
1422 | kfree_data(filter_buffer, in_buffer_length); |
1423 | bloom_filter = NULL; |
1424 | } |
1425 | return error; |
1426 | } |
1427 | |
1428 | static int |
1429 | necp_session_remove_domain_filter(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1430 | { |
1431 | int error = 0; |
1432 | |
1433 | const size_t in_buffer_length = (size_t)uap->in_buffer_length; |
1434 | if (in_buffer_length < sizeof(u_int32_t) || uap->in_buffer == 0) { |
1435 | NECPLOG(LOG_ERR, "necp_session_remove_domain_filter invalid input (%zu)" , (size_t)in_buffer_length); |
1436 | error = EINVAL; |
1437 | goto done; |
1438 | } |
1439 | |
1440 | u_int32_t filter_id; |
1441 | error = copyin(uap->in_buffer, &filter_id, sizeof(filter_id)); |
1442 | if (error != 0) { |
1443 | NECPLOG(LOG_ERR, "necp_session_remove_domain_filter uuid copyin error (%d)" , error); |
1444 | goto done; |
1445 | } |
1446 | |
1447 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
1448 | bool removed = necp_remove_domain_filter(list: &necp_global_domain_filter_list, owner_list: &session->domain_filters, filter_id); |
1449 | if (!removed) { |
1450 | error = ENOENT; |
1451 | } |
1452 | lck_rw_done(lck: &necp_kernel_policy_lock); |
1453 | |
1454 | done: |
1455 | *retval = error; |
1456 | return error; |
1457 | } |
1458 | |
1459 | static int |
1460 | necp_session_remove_all_domain_filters(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1461 | { |
1462 | #pragma unused(uap) |
1463 | |
1464 | struct necp_domain_filter *filter = NULL; |
1465 | struct necp_domain_filter *temp_filter = NULL; |
1466 | LIST_FOREACH_SAFE(filter, &session->domain_filters, owner_chain, temp_filter) { |
1467 | if (os_ref_release_locked(rc: &filter->refcount) == 0) { |
1468 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
1469 | LIST_REMOVE(filter, chain); |
1470 | lck_rw_done(lck: &necp_kernel_policy_lock); |
1471 | LIST_REMOVE(filter, owner_chain); |
1472 | net_bloom_filter_destroy(filter: filter->filter); |
1473 | kfree_type(struct necp_domain_filter, filter); |
1474 | } |
1475 | } |
1476 | |
1477 | *retval = 0; |
1478 | return 0; |
1479 | } |
1480 | |
1481 | int |
1482 | necp_session_action(struct proc *p, struct necp_session_action_args *uap, int *retval) |
1483 | { |
1484 | struct fileproc *fp; |
1485 | int error = 0; |
1486 | int return_value = 0; |
1487 | struct necp_session *session = NULL; |
1488 | |
1489 | error = necp_session_find_from_fd(p, fd: uap->necp_fd, fpp: &fp, session: &session); |
1490 | if (error != 0) { |
1491 | NECPLOG(LOG_ERR, "necp_session_action find fd error (%d)" , error); |
1492 | return error; |
1493 | } |
1494 | |
1495 | NECP_SESSION_LOCK(session); |
1496 | |
1497 | if (session->proc_locked) { |
1498 | // Verify that the calling process is allowed to do actions |
1499 | uuid_t proc_uuid; |
1500 | proc_getexecutableuuid(current_proc(), proc_uuid, sizeof(proc_uuid)); |
1501 | if (uuid_compare(uu1: proc_uuid, uu2: session->proc_uuid) != 0) { |
1502 | error = EPERM; |
1503 | goto done; |
1504 | } |
1505 | } else { |
1506 | // If not locked, update the proc_uuid and proc_pid of the session |
1507 | proc_getexecutableuuid(current_proc(), session->proc_uuid, sizeof(session->proc_uuid)); |
1508 | session->proc_pid = proc_pid(current_proc()); |
1509 | } |
1510 | |
1511 | u_int32_t action = uap->action; |
1512 | switch (action) { |
1513 | case NECP_SESSION_ACTION_POLICY_ADD: { |
1514 | return_value = necp_session_add_policy(session, uap, retval); |
1515 | break; |
1516 | } |
1517 | case NECP_SESSION_ACTION_POLICY_GET: { |
1518 | return_value = necp_session_get_policy(session, uap, retval); |
1519 | break; |
1520 | } |
1521 | case NECP_SESSION_ACTION_POLICY_DELETE: { |
1522 | return_value = necp_session_delete_policy(session, uap, retval); |
1523 | break; |
1524 | } |
1525 | case NECP_SESSION_ACTION_POLICY_APPLY_ALL: { |
1526 | return_value = necp_session_apply_all(session, uap, retval); |
1527 | break; |
1528 | } |
1529 | case NECP_SESSION_ACTION_POLICY_LIST_ALL: { |
1530 | return_value = necp_session_list_all(session, uap, retval); |
1531 | break; |
1532 | } |
1533 | case NECP_SESSION_ACTION_POLICY_DELETE_ALL: { |
1534 | return_value = necp_session_delete_all(session, uap, retval); |
1535 | break; |
1536 | } |
1537 | case NECP_SESSION_ACTION_SET_SESSION_PRIORITY: { |
1538 | return_value = necp_session_set_session_priority(session, uap, retval); |
1539 | break; |
1540 | } |
1541 | case NECP_SESSION_ACTION_LOCK_SESSION_TO_PROC: { |
1542 | return_value = necp_session_lock_to_process(session, uap, retval); |
1543 | break; |
1544 | } |
1545 | case NECP_SESSION_ACTION_REGISTER_SERVICE: { |
1546 | return_value = necp_session_register_service(session, uap, retval); |
1547 | break; |
1548 | } |
1549 | case NECP_SESSION_ACTION_UNREGISTER_SERVICE: { |
1550 | return_value = necp_session_unregister_service(session, uap, retval); |
1551 | break; |
1552 | } |
1553 | case NECP_SESSION_ACTION_POLICY_DUMP_ALL: { |
1554 | return_value = necp_session_dump_all(session, uap, retval); |
1555 | break; |
1556 | } |
1557 | case NECP_SESSION_ACTION_ADD_DOMAIN_FILTER: { |
1558 | return_value = necp_session_add_domain_filter(session, uap, retval); |
1559 | break; |
1560 | } |
1561 | case NECP_SESSION_ACTION_REMOVE_DOMAIN_FILTER: { |
1562 | return_value = necp_session_remove_domain_filter(session, uap, retval); |
1563 | break; |
1564 | } |
1565 | case NECP_SESSION_ACTION_REMOVE_ALL_DOMAIN_FILTERS: { |
1566 | return_value = necp_session_remove_all_domain_filters(session, uap, retval); |
1567 | break; |
1568 | } |
1569 | default: { |
1570 | NECPLOG(LOG_ERR, "necp_session_action unknown action (%u)" , action); |
1571 | return_value = EINVAL; |
1572 | break; |
1573 | } |
1574 | } |
1575 | |
1576 | done: |
1577 | NECP_SESSION_UNLOCK(session); |
1578 | fp_drop(p, fd: uap->necp_fd, fp, locked: 0); |
1579 | return return_value; |
1580 | } |
1581 | |
1582 | struct necp_resolver_key_state { |
1583 | const struct ccdigest_info *digest_info; |
1584 | uint8_t key[CCSHA256_OUTPUT_SIZE]; |
1585 | }; |
1586 | static struct necp_resolver_key_state s_necp_resolver_key_state; |
1587 | |
1588 | static void |
1589 | necp_generate_resolver_key(void) |
1590 | { |
1591 | s_necp_resolver_key_state.digest_info = ccsha256_di(); |
1592 | cc_rand_generate(out: s_necp_resolver_key_state.key, outlen: sizeof(s_necp_resolver_key_state.key)); |
1593 | } |
1594 | |
1595 | static void |
1596 | necp_sign_update_context(const struct ccdigest_info *di, |
1597 | cchmac_ctx_t ctx, |
1598 | uuid_t client_id, |
1599 | u_int32_t sign_type, |
1600 | u_int8_t *data, |
1601 | size_t data_length) |
1602 | { |
1603 | const uint8_t context[32] = {[0 ... 31] = 0x20}; // 0x20 repeated 32 times |
1604 | const char *context_string = "NECP Resolver Binder" ; |
1605 | uint8_t separator = 0; |
1606 | cchmac_update(di, ctx, data_len: sizeof(context), data: context); |
1607 | cchmac_update(di, ctx, data_len: strlen(s: context_string), data: context_string); |
1608 | cchmac_update(di, ctx, data_len: sizeof(separator), data: &separator); |
1609 | cchmac_update(di, ctx, data_len: sizeof(uuid_t), data: client_id); |
1610 | cchmac_update(di, ctx, data_len: sizeof(sign_type), data: &sign_type); |
1611 | cchmac_update(di, ctx, data_len: data_length, data); |
1612 | } |
1613 | |
1614 | int |
1615 | necp_sign_resolver_answer(uuid_t client_id, u_int32_t sign_type, |
1616 | u_int8_t *data, size_t data_length, |
1617 | u_int8_t *tag, size_t *out_tag_length) |
1618 | { |
1619 | if (s_necp_resolver_key_state.digest_info == NULL) { |
1620 | return EINVAL; |
1621 | } |
1622 | |
1623 | if (data == NULL || |
1624 | data_length == 0 || |
1625 | tag == NULL || |
1626 | out_tag_length == NULL) { |
1627 | return EINVAL; |
1628 | } |
1629 | |
1630 | size_t required_tag_length = s_necp_resolver_key_state.digest_info->output_size; |
1631 | if (*out_tag_length < required_tag_length) { |
1632 | return ERANGE; |
1633 | } |
1634 | |
1635 | *out_tag_length = required_tag_length; |
1636 | |
1637 | cchmac_ctx_decl(s_necp_resolver_key_state.digest_info->state_size, |
1638 | s_necp_resolver_key_state.digest_info->block_size, ctx); |
1639 | cchmac_init(di: s_necp_resolver_key_state.digest_info, ctx, |
1640 | key_len: sizeof(s_necp_resolver_key_state.key), |
1641 | key: s_necp_resolver_key_state.key); |
1642 | necp_sign_update_context(di: s_necp_resolver_key_state.digest_info, |
1643 | ctx, client_id, sign_type, data, data_length); |
1644 | cchmac_final(di: s_necp_resolver_key_state.digest_info, ctx, mac: tag); |
1645 | |
1646 | return 0; |
1647 | } |
1648 | |
1649 | bool |
1650 | necp_validate_resolver_answer(uuid_t client_id, u_int32_t sign_type, |
1651 | u_int8_t *data, size_t data_length, |
1652 | u_int8_t *tag, size_t tag_length) |
1653 | { |
1654 | if (s_necp_resolver_key_state.digest_info == NULL) { |
1655 | return false; |
1656 | } |
1657 | |
1658 | if (data == NULL || |
1659 | data_length == 0 || |
1660 | tag == NULL || |
1661 | tag_length == 0) { |
1662 | return false; |
1663 | } |
1664 | |
1665 | size_t required_tag_length = s_necp_resolver_key_state.digest_info->output_size; |
1666 | if (tag_length != required_tag_length) { |
1667 | return false; |
1668 | } |
1669 | |
1670 | uint8_t actual_tag[required_tag_length]; |
1671 | |
1672 | cchmac_ctx_decl(s_necp_resolver_key_state.digest_info->state_size, |
1673 | s_necp_resolver_key_state.digest_info->block_size, ctx); |
1674 | cchmac_init(di: s_necp_resolver_key_state.digest_info, ctx, |
1675 | key_len: sizeof(s_necp_resolver_key_state.key), |
1676 | key: s_necp_resolver_key_state.key); |
1677 | necp_sign_update_context(di: s_necp_resolver_key_state.digest_info, |
1678 | ctx, client_id, sign_type, data, data_length); |
1679 | cchmac_final(di: s_necp_resolver_key_state.digest_info, ctx, mac: actual_tag); |
1680 | |
1681 | return cc_cmp_safe(num: s_necp_resolver_key_state.digest_info->output_size, ptr1: tag, ptr2: actual_tag) == 0; |
1682 | } |
1683 | |
1684 | struct necp_application_id_key_state { |
1685 | const struct ccdigest_info *digest_info; |
1686 | uint8_t key[CCSHA256_OUTPUT_SIZE]; |
1687 | }; |
1688 | static struct necp_application_id_key_state s_necp_application_id_key_state; |
1689 | |
1690 | static void |
1691 | necp_generate_application_id_key(void) |
1692 | { |
1693 | s_necp_application_id_key_state.digest_info = ccsha256_di(); |
1694 | cc_rand_generate(out: s_necp_application_id_key_state.key, outlen: sizeof(s_necp_application_id_key_state.key)); |
1695 | } |
1696 | |
1697 | static void |
1698 | necp_sign_application_id_update_context(const struct ccdigest_info *di, |
1699 | cchmac_ctx_t ctx, |
1700 | uuid_t client_id, |
1701 | u_int32_t sign_type) |
1702 | { |
1703 | const uint8_t context[32] = {[0 ... 31] = 0x20}; // 0x20 repeated 32 times |
1704 | const char *context_string = "NECP Application ID" ; |
1705 | uint8_t separator = 0; |
1706 | cchmac_update(di, ctx, data_len: sizeof(context), data: context); |
1707 | cchmac_update(di, ctx, data_len: strlen(s: context_string), data: context_string); |
1708 | cchmac_update(di, ctx, data_len: sizeof(separator), data: &separator); |
1709 | cchmac_update(di, ctx, data_len: sizeof(uuid_t), data: client_id); |
1710 | cchmac_update(di, ctx, data_len: sizeof(sign_type), data: &sign_type); |
1711 | } |
1712 | |
1713 | int |
1714 | necp_sign_application_id(uuid_t client_id, u_int32_t sign_type, |
1715 | u_int8_t *tag, size_t *out_tag_length) |
1716 | { |
1717 | if (s_necp_application_id_key_state.digest_info == NULL) { |
1718 | return EINVAL; |
1719 | } |
1720 | |
1721 | if (tag == NULL || |
1722 | out_tag_length == NULL) { |
1723 | return EINVAL; |
1724 | } |
1725 | |
1726 | size_t required_tag_length = s_necp_application_id_key_state.digest_info->output_size; |
1727 | if (*out_tag_length < required_tag_length) { |
1728 | return ERANGE; |
1729 | } |
1730 | |
1731 | *out_tag_length = required_tag_length; |
1732 | |
1733 | cchmac_ctx_decl(s_necp_application_id_key_state.digest_info->state_size, |
1734 | s_necp_application_id_key_state.digest_info->block_size, ctx); |
1735 | cchmac_init(di: s_necp_application_id_key_state.digest_info, ctx, |
1736 | key_len: sizeof(s_necp_application_id_key_state.key), |
1737 | key: s_necp_application_id_key_state.key); |
1738 | necp_sign_application_id_update_context(di: s_necp_application_id_key_state.digest_info, |
1739 | ctx, client_id, sign_type); |
1740 | cchmac_final(di: s_necp_application_id_key_state.digest_info, ctx, mac: tag); |
1741 | |
1742 | return 0; |
1743 | } |
1744 | |
1745 | bool |
1746 | necp_validate_application_id(uuid_t client_id, u_int32_t sign_type, |
1747 | u_int8_t *tag, size_t tag_length) |
1748 | { |
1749 | if (s_necp_application_id_key_state.digest_info == NULL) { |
1750 | return false; |
1751 | } |
1752 | |
1753 | if (tag == NULL || |
1754 | tag_length == 0) { |
1755 | return false; |
1756 | } |
1757 | |
1758 | size_t required_tag_length = s_necp_application_id_key_state.digest_info->output_size; |
1759 | if (tag_length != required_tag_length) { |
1760 | return false; |
1761 | } |
1762 | |
1763 | uint8_t actual_tag[required_tag_length]; |
1764 | |
1765 | cchmac_ctx_decl(s_necp_application_id_key_state.digest_info->state_size, |
1766 | s_necp_application_id_key_state.digest_info->block_size, ctx); |
1767 | cchmac_init(di: s_necp_application_id_key_state.digest_info, ctx, |
1768 | key_len: sizeof(s_necp_application_id_key_state.key), |
1769 | key: s_necp_application_id_key_state.key); |
1770 | necp_sign_application_id_update_context(di: s_necp_application_id_key_state.digest_info, |
1771 | ctx, client_id, sign_type); |
1772 | cchmac_final(di: s_necp_application_id_key_state.digest_info, ctx, mac: actual_tag); |
1773 | |
1774 | return cc_cmp_safe(num: s_necp_application_id_key_state.digest_info->output_size, ptr1: tag, ptr2: actual_tag) == 0; |
1775 | } |
1776 | |
1777 | void |
1778 | necp_init(void) |
1779 | { |
1780 | necp_log_handle = os_log_create(subsystem: "com.apple.xnu.net.necp" , category: "necp" ); |
1781 | necp_data_trace_log_handle = os_log_create(subsystem: "com.apple.xnu.net.necp" , category: "necp-data-trace" ); |
1782 | |
1783 | necp_client_init(); |
1784 | |
1785 | TAILQ_INIT(&necp_session_list); |
1786 | |
1787 | LIST_INIT(&necp_kernel_socket_policies); |
1788 | LIST_INIT(&necp_kernel_ip_output_policies); |
1789 | |
1790 | LIST_INIT(&necp_account_id_list); |
1791 | |
1792 | LIST_INIT(&necp_uuid_service_id_list); |
1793 | |
1794 | LIST_INIT(&necp_registered_service_list); |
1795 | |
1796 | LIST_INIT(&necp_route_rules); |
1797 | LIST_INIT(&necp_aggregate_route_rules); |
1798 | |
1799 | LIST_INIT(&necp_global_domain_filter_list); |
1800 | |
1801 | necp_generate_resolver_key(); |
1802 | necp_generate_application_id_key(); |
1803 | |
1804 | necp_uuid_app_id_hashtbl = hashinit(NECP_UUID_APP_ID_HASH_SIZE, M_NECP, hashmask: &necp_uuid_app_id_hash_mask); |
1805 | necp_uuid_app_id_hash_num_buckets = necp_uuid_app_id_hash_mask + 1; |
1806 | necp_num_uuid_app_id_mappings = 0; |
1807 | necp_uuid_app_id_mappings_dirty = FALSE; |
1808 | |
1809 | necp_kernel_application_policies_condition_mask = 0; |
1810 | necp_kernel_socket_policies_condition_mask = 0; |
1811 | necp_kernel_ip_output_policies_condition_mask = 0; |
1812 | |
1813 | necp_kernel_application_policies_count = 0; |
1814 | necp_kernel_socket_policies_count = 0; |
1815 | necp_kernel_socket_policies_non_app_count = 0; |
1816 | necp_kernel_ip_output_policies_count = 0; |
1817 | necp_kernel_ip_output_policies_non_id_count = 0; |
1818 | |
1819 | necp_kernel_socket_policies_gencount = 1; |
1820 | |
1821 | memset(s: &necp_kernel_socket_policies_map, c: 0, n: sizeof(necp_kernel_socket_policies_map)); |
1822 | memset(s: &necp_kernel_ip_output_policies_map, c: 0, n: sizeof(necp_kernel_ip_output_policies_map)); |
1823 | necp_kernel_socket_policies_app_layer_map = NULL; |
1824 | |
1825 | necp_drop_unentitled_order = necp_get_first_order_for_priority(priority: necp_drop_unentitled_level); |
1826 | necp_drop_management_order = necp_get_first_order_for_priority(priority: necp_drop_management_level); |
1827 | } |
1828 | |
1829 | static void |
1830 | necp_post_change_event(struct kev_necp_policies_changed_data *necp_event_data) |
1831 | { |
1832 | struct kev_msg ev_msg; |
1833 | memset(s: &ev_msg, c: 0, n: sizeof(ev_msg)); |
1834 | |
1835 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
1836 | ev_msg.kev_class = KEV_NETWORK_CLASS; |
1837 | ev_msg.kev_subclass = KEV_NECP_SUBCLASS; |
1838 | ev_msg.event_code = KEV_NECP_POLICIES_CHANGED; |
1839 | |
1840 | ev_msg.dv[0].data_ptr = necp_event_data; |
1841 | ev_msg.dv[0].data_length = sizeof(necp_event_data->changed_count); |
1842 | ev_msg.dv[1].data_length = 0; |
1843 | |
1844 | kev_post_msg(event: &ev_msg); |
1845 | } |
1846 | |
1847 | static inline bool |
1848 | necp_buffer_write_tlv_validate(u_int8_t *cursor, u_int8_t type, u_int32_t length, |
1849 | u_int8_t *buffer, u_int32_t buffer_length) |
1850 | { |
1851 | if (cursor < buffer || (uintptr_t)(cursor - buffer) > buffer_length) { |
1852 | NECPLOG0(LOG_ERR, "Cannot write TLV in buffer (invalid cursor)" ); |
1853 | return false; |
1854 | } |
1855 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1856 | if (next_tlv <= buffer || // make sure the next TLV start doesn't overflow |
1857 | (uintptr_t)(next_tlv - buffer) > buffer_length) { // make sure the next TLV has enough room in buffer |
1858 | NECPLOG(LOG_ERR, "Cannot write TLV in buffer (TLV length %u, buffer length %u)" , |
1859 | length, buffer_length); |
1860 | return false; |
1861 | } |
1862 | return true; |
1863 | } |
1864 | |
1865 | u_int8_t * |
1866 | necp_buffer_write_tlv_if_different(u_int8_t *cursor, u_int8_t type, |
1867 | u_int32_t length, const void *value, bool *updated, |
1868 | u_int8_t *buffer, u_int32_t buffer_length) |
1869 | { |
1870 | if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) { |
1871 | // If we can't fit this TLV, return the current cursor |
1872 | return cursor; |
1873 | } |
1874 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1875 | if (*updated || *(u_int8_t *)(cursor) != type) { |
1876 | *(u_int8_t *)(cursor) = type; |
1877 | *updated = TRUE; |
1878 | } |
1879 | if (*updated || *(u_int32_t *)(void *)(cursor + sizeof(type)) != length) { |
1880 | *(u_int32_t *)(void *)(cursor + sizeof(type)) = length; |
1881 | *updated = TRUE; |
1882 | } |
1883 | if (length > 0) { |
1884 | if (*updated || memcmp(s1: (u_int8_t *)(cursor + sizeof(type) + sizeof(length)), s2: value, n: length) != 0) { |
1885 | memcpy(dst: (u_int8_t *)(cursor + sizeof(type) + sizeof(length)), src: value, n: length); |
1886 | *updated = TRUE; |
1887 | } |
1888 | } |
1889 | return next_tlv; |
1890 | } |
1891 | |
1892 | u_int8_t * |
1893 | necp_buffer_write_tlv(u_int8_t *cursor, u_int8_t type, |
1894 | u_int32_t length, const void *value, |
1895 | u_int8_t *buffer, u_int32_t buffer_length) |
1896 | { |
1897 | if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) { |
1898 | return NULL; |
1899 | } |
1900 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1901 | *(u_int8_t *)(cursor) = type; |
1902 | *(u_int32_t *)(void *)(cursor + sizeof(type)) = length; |
1903 | if (length > 0) { |
1904 | memcpy(dst: (u_int8_t *)(cursor + sizeof(type) + sizeof(length)), src: value, n: length); |
1905 | } |
1906 | |
1907 | return next_tlv; |
1908 | } |
1909 | |
1910 | u_int8_t |
1911 | necp_buffer_get_tlv_type(u_int8_t *buffer, int tlv_offset) |
1912 | { |
1913 | u_int8_t *type = NULL; |
1914 | |
1915 | if (buffer == NULL) { |
1916 | return 0; |
1917 | } |
1918 | |
1919 | type = (u_int8_t *)((u_int8_t *)buffer + tlv_offset); |
1920 | return type ? *type : 0; |
1921 | } |
1922 | |
1923 | u_int32_t |
1924 | necp_buffer_get_tlv_length(u_int8_t *buffer, int tlv_offset) |
1925 | { |
1926 | u_int32_t *length = NULL; |
1927 | |
1928 | if (buffer == NULL) { |
1929 | return 0; |
1930 | } |
1931 | |
1932 | length = (u_int32_t *)(void *)((u_int8_t *)buffer + tlv_offset + sizeof(u_int8_t)); |
1933 | return length ? *length : 0; |
1934 | } |
1935 | |
1936 | u_int8_t * |
1937 | necp_buffer_get_tlv_value(u_int8_t *buffer, int tlv_offset, u_int32_t *value_size) |
1938 | { |
1939 | u_int8_t *value = NULL; |
1940 | u_int32_t length = necp_buffer_get_tlv_length(buffer, tlv_offset); |
1941 | if (length == 0) { |
1942 | return value; |
1943 | } |
1944 | |
1945 | if (value_size) { |
1946 | *value_size = length; |
1947 | } |
1948 | |
1949 | value = (u_int8_t *)((u_int8_t *)buffer + tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t)); |
1950 | return value; |
1951 | } |
1952 | |
1953 | int |
1954 | necp_buffer_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int *err, int next) |
1955 | { |
1956 | if (err != NULL) { |
1957 | *err = ENOENT; |
1958 | } |
1959 | if (offset < 0) { |
1960 | if (err != NULL) { |
1961 | *err = EINVAL; |
1962 | } |
1963 | return -1; |
1964 | } |
1965 | int cursor = offset; |
1966 | int next_cursor; |
1967 | u_int32_t curr_length; |
1968 | u_int8_t curr_type; |
1969 | |
1970 | while (TRUE) { |
1971 | if ((((u_int32_t)cursor) + sizeof(curr_type) + sizeof(curr_length)) > buffer_length) { |
1972 | return -1; |
1973 | } |
1974 | if (!next) { |
1975 | curr_type = necp_buffer_get_tlv_type(buffer, tlv_offset: cursor); |
1976 | } else { |
1977 | next = 0; |
1978 | curr_type = NECP_TLV_NIL; |
1979 | } |
1980 | curr_length = necp_buffer_get_tlv_length(buffer, tlv_offset: cursor); |
1981 | if (curr_length > buffer_length - ((u_int32_t)cursor + sizeof(curr_type) + sizeof(curr_length))) { |
1982 | return -1; |
1983 | } |
1984 | |
1985 | next_cursor = (cursor + sizeof(curr_type) + sizeof(curr_length) + curr_length); |
1986 | if (curr_type == type) { |
1987 | // check if entire TLV fits inside buffer |
1988 | if (((u_int32_t)next_cursor) <= buffer_length) { |
1989 | if (err != NULL) { |
1990 | *err = 0; |
1991 | } |
1992 | return cursor; |
1993 | } else { |
1994 | return -1; |
1995 | } |
1996 | } |
1997 | cursor = next_cursor; |
1998 | } |
1999 | } |
2000 | |
2001 | static int |
2002 | necp_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int *err, int next) |
2003 | { |
2004 | int cursor = -1; |
2005 | if (buffer != NULL) { |
2006 | cursor = necp_buffer_find_tlv(buffer, buffer_length, offset, type, err, next); |
2007 | } |
2008 | return cursor; |
2009 | } |
2010 | |
2011 | static int |
2012 | necp_get_tlv_at_offset(u_int8_t *buffer, u_int32_t buffer_length, |
2013 | int tlv_offset, u_int32_t out_buffer_length, void *out_buffer, u_int32_t *value_size) |
2014 | { |
2015 | if (buffer == NULL) { |
2016 | NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset buffer is NULL" ); |
2017 | return EINVAL; |
2018 | } |
2019 | |
2020 | // Handle buffer parsing |
2021 | |
2022 | // Validate that buffer has enough room for any TLV |
2023 | if (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) > buffer_length) { |
2024 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV (%u < %lu)" , |
2025 | buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t)); |
2026 | return EINVAL; |
2027 | } |
2028 | |
2029 | // Validate that buffer has enough room for this TLV |
2030 | u_int32_t tlv_length = necp_buffer_get_tlv_length(buffer, tlv_offset); |
2031 | if (tlv_length > buffer_length - (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t))) { |
2032 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV of length %u (%u < %lu)" , |
2033 | tlv_length, buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) + tlv_length); |
2034 | return EINVAL; |
2035 | } |
2036 | |
2037 | if (out_buffer != NULL && out_buffer_length > 0) { |
2038 | // Validate that out buffer is large enough for value |
2039 | if (out_buffer_length < tlv_length) { |
2040 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset out_buffer_length is too small for TLV value (%u < %u)" , |
2041 | out_buffer_length, tlv_length); |
2042 | return EINVAL; |
2043 | } |
2044 | |
2045 | // Get value pointer |
2046 | u_int8_t *tlv_value = necp_buffer_get_tlv_value(buffer, tlv_offset, NULL); |
2047 | if (tlv_value == NULL) { |
2048 | NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset tlv_value is NULL" ); |
2049 | return ENOENT; |
2050 | } |
2051 | |
2052 | // Copy value |
2053 | memcpy(dst: out_buffer, src: tlv_value, n: tlv_length); |
2054 | } |
2055 | |
2056 | // Copy out length |
2057 | if (value_size != NULL) { |
2058 | *value_size = tlv_length; |
2059 | } |
2060 | |
2061 | return 0; |
2062 | } |
2063 | |
2064 | static int |
2065 | necp_get_tlv(u_int8_t *buffer, u_int32_t buffer_length, |
2066 | int offset, u_int8_t type, u_int32_t buff_len, void *buff, u_int32_t *value_size) |
2067 | { |
2068 | int error = 0; |
2069 | |
2070 | int tlv_offset = necp_find_tlv(buffer, buffer_length, offset, type, err: &error, next: 0); |
2071 | if (tlv_offset < 0) { |
2072 | return error; |
2073 | } |
2074 | |
2075 | return necp_get_tlv_at_offset(buffer, buffer_length, tlv_offset, out_buffer_length: buff_len, out_buffer: buff, value_size); |
2076 | } |
2077 | |
2078 | // Session Management |
2079 | |
2080 | static struct necp_session * |
2081 | necp_create_session(void) |
2082 | { |
2083 | struct necp_session *new_session = NULL; |
2084 | |
2085 | new_session = kalloc_type(struct necp_session, |
2086 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
2087 | |
2088 | new_session->necp_fd_type = necp_fd_type_session; |
2089 | new_session->session_priority = NECP_SESSION_PRIORITY_UNKNOWN; |
2090 | new_session->dirty = FALSE; |
2091 | LIST_INIT(&new_session->policies); |
2092 | LIST_INIT(&new_session->services); |
2093 | LIST_INIT(&new_session->domain_filters); |
2094 | lck_mtx_init(lck: &new_session->lock, grp: &necp_kernel_policy_mtx_grp, attr: &necp_kernel_policy_mtx_attr); |
2095 | |
2096 | // Take the lock |
2097 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
2098 | |
2099 | // Find the next available control unit |
2100 | u_int32_t control_unit = 1; |
2101 | struct necp_session *next_session = NULL; |
2102 | TAILQ_FOREACH(next_session, &necp_session_list, chain) { |
2103 | if (next_session->control_unit > control_unit) { |
2104 | // Found a gap, grab this control unit |
2105 | break; |
2106 | } |
2107 | |
2108 | // Try the next control unit, loop around |
2109 | control_unit = next_session->control_unit + 1; |
2110 | } |
2111 | |
2112 | new_session->control_unit = control_unit; |
2113 | new_session->session_order = necp_allocate_new_session_order(priority: new_session->session_priority, control_unit); |
2114 | |
2115 | if (next_session != NULL) { |
2116 | TAILQ_INSERT_BEFORE(next_session, new_session, chain); |
2117 | } else { |
2118 | TAILQ_INSERT_TAIL(&necp_session_list, new_session, chain); |
2119 | } |
2120 | |
2121 | necp_session_count++; |
2122 | lck_rw_done(lck: &necp_kernel_policy_lock); |
2123 | |
2124 | if (necp_debug) { |
2125 | NECPLOG(LOG_DEBUG, "Created NECP session, control unit %d" , control_unit); |
2126 | } |
2127 | |
2128 | return new_session; |
2129 | } |
2130 | |
2131 | static void |
2132 | necp_delete_session(struct necp_session *session) |
2133 | { |
2134 | if (session != NULL) { |
2135 | struct necp_service_registration *service = NULL; |
2136 | struct necp_service_registration *temp_service = NULL; |
2137 | LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) { |
2138 | LIST_REMOVE(service, session_chain); |
2139 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
2140 | LIST_REMOVE(service, kernel_chain); |
2141 | lck_rw_done(lck: &necp_kernel_policy_lock); |
2142 | kfree_type(struct necp_service_registration, service); |
2143 | } |
2144 | struct necp_domain_filter *filter = NULL; |
2145 | struct necp_domain_filter *temp_filter = NULL; |
2146 | LIST_FOREACH_SAFE(filter, &session->domain_filters, owner_chain, temp_filter) { |
2147 | if (os_ref_release_locked(rc: &filter->refcount) == 0) { |
2148 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
2149 | LIST_REMOVE(filter, chain); |
2150 | lck_rw_done(lck: &necp_kernel_policy_lock); |
2151 | LIST_REMOVE(filter, owner_chain); |
2152 | net_bloom_filter_destroy(filter: filter->filter); |
2153 | kfree_type(struct necp_domain_filter, filter); |
2154 | } |
2155 | } |
2156 | if (necp_debug) { |
2157 | NECPLOG0(LOG_DEBUG, "Deleted NECP session" ); |
2158 | } |
2159 | |
2160 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
2161 | TAILQ_REMOVE(&necp_session_list, session, chain); |
2162 | necp_session_count--; |
2163 | lck_rw_done(lck: &necp_kernel_policy_lock); |
2164 | |
2165 | lck_mtx_destroy(lck: &session->lock, grp: &necp_kernel_policy_mtx_grp); |
2166 | kfree_type(struct necp_session, session); |
2167 | } |
2168 | } |
2169 | |
2170 | // Session Policy Management |
2171 | |
2172 | static inline u_int8_t |
2173 | necp_policy_result_get_type_from_buffer(u_int8_t *buffer, u_int32_t length) |
2174 | { |
2175 | return (buffer && length >= sizeof(u_int8_t)) ? buffer[0] : 0; |
2176 | } |
2177 | |
2178 | static inline u_int32_t |
2179 | necp_policy_result_get_parameter_length_from_buffer(u_int8_t *buffer, u_int32_t length) |
2180 | { |
2181 | return (buffer && length > sizeof(u_int8_t)) ? (length - sizeof(u_int8_t)) : 0; |
2182 | } |
2183 | |
2184 | static inline u_int8_t * |
2185 | necp_policy_result_get_parameter_pointer_from_buffer(u_int8_t *buffer, u_int32_t length) |
2186 | { |
2187 | return (buffer && length > sizeof(u_int8_t)) ? (buffer + sizeof(u_int8_t)) : NULL; |
2188 | } |
2189 | |
2190 | static bool |
2191 | necp_policy_result_requires_route_rules(u_int8_t *buffer, u_int32_t length) |
2192 | { |
2193 | u_int8_t type = necp_policy_result_get_type_from_buffer(buffer, length); |
2194 | if (type == NECP_POLICY_RESULT_ROUTE_RULES) { |
2195 | return TRUE; |
2196 | } |
2197 | return FALSE; |
2198 | } |
2199 | |
2200 | static inline bool |
2201 | _necp_address_is_valid(struct sockaddr *address) |
2202 | { |
2203 | if (address->sa_family == AF_INET) { |
2204 | return address->sa_len == sizeof(struct sockaddr_in); |
2205 | } else if (address->sa_family == AF_INET6) { |
2206 | return address->sa_len == sizeof(struct sockaddr_in6); |
2207 | } else { |
2208 | return FALSE; |
2209 | } |
2210 | } |
2211 | |
2212 | #define necp_address_is_valid(S) _necp_address_is_valid(SA(S)) |
2213 | |
2214 | static bool |
2215 | necp_policy_result_is_valid(u_int8_t *buffer, u_int32_t length, bool *is_pass_skip) |
2216 | { |
2217 | bool validated = FALSE; |
2218 | u_int8_t type = necp_policy_result_get_type_from_buffer(buffer, length); |
2219 | u_int32_t parameter_length = necp_policy_result_get_parameter_length_from_buffer(buffer, length); |
2220 | *is_pass_skip = FALSE; |
2221 | switch (type) { |
2222 | case NECP_POLICY_RESULT_PASS: { |
2223 | *is_pass_skip = TRUE; |
2224 | if (parameter_length == 0 || parameter_length == sizeof(u_int32_t)) { |
2225 | validated = TRUE; |
2226 | } |
2227 | break; |
2228 | } |
2229 | case NECP_POLICY_RESULT_DROP: { |
2230 | if (parameter_length == 0 || parameter_length == sizeof(u_int32_t)) { |
2231 | validated = TRUE; |
2232 | } |
2233 | break; |
2234 | } |
2235 | case NECP_POLICY_RESULT_ROUTE_RULES: |
2236 | case NECP_POLICY_RESULT_SCOPED_DIRECT: |
2237 | case NECP_POLICY_RESULT_ALLOW_UNENTITLED: { |
2238 | validated = TRUE; |
2239 | break; |
2240 | } |
2241 | case NECP_POLICY_RESULT_SKIP: |
2242 | *is_pass_skip = TRUE; |
2243 | case NECP_POLICY_RESULT_SOCKET_DIVERT: |
2244 | case NECP_POLICY_RESULT_SOCKET_FILTER: { |
2245 | if (parameter_length >= sizeof(u_int32_t)) { |
2246 | validated = TRUE; |
2247 | } |
2248 | break; |
2249 | } |
2250 | case NECP_POLICY_RESULT_IP_TUNNEL: { |
2251 | if (parameter_length > sizeof(u_int32_t)) { |
2252 | validated = TRUE; |
2253 | } |
2254 | break; |
2255 | } |
2256 | case NECP_POLICY_RESULT_SOCKET_SCOPED: { |
2257 | if (parameter_length > 0) { |
2258 | validated = TRUE; |
2259 | } |
2260 | break; |
2261 | } |
2262 | case NECP_POLICY_RESULT_USE_NETAGENT: |
2263 | case NECP_POLICY_RESULT_NETAGENT_SCOPED: |
2264 | case NECP_POLICY_RESULT_REMOVE_NETAGENT: { |
2265 | if (parameter_length >= sizeof(uuid_t)) { |
2266 | validated = TRUE; |
2267 | } |
2268 | break; |
2269 | } |
2270 | default: { |
2271 | validated = FALSE; |
2272 | break; |
2273 | } |
2274 | } |
2275 | |
2276 | if (necp_debug) { |
2277 | NECPLOG(LOG_DEBUG, "Policy result type %d, valid %d" , type, validated); |
2278 | } |
2279 | |
2280 | return validated; |
2281 | } |
2282 | |
2283 | static inline u_int8_t |
2284 | necp_policy_condition_get_type_from_buffer(u_int8_t *buffer, u_int32_t length) |
2285 | { |
2286 | return (buffer && length >= sizeof(u_int8_t)) ? buffer[0] : 0; |
2287 | } |
2288 | |
2289 | static inline u_int8_t |
2290 | necp_policy_condition_get_flags_from_buffer(u_int8_t *buffer, u_int32_t length) |
2291 | { |
2292 | return (buffer && length >= (2 * sizeof(u_int8_t))) ? buffer[1] : 0; |
2293 | } |
2294 | |
2295 | static inline u_int32_t |
2296 | necp_policy_condition_get_value_length_from_buffer(u_int8_t *buffer, u_int32_t length) |
2297 | { |
2298 | return (buffer && length >= (2 * sizeof(u_int8_t))) ? (length - (2 * sizeof(u_int8_t))) : 0; |
2299 | } |
2300 | |
2301 | static inline u_int8_t * |
2302 | necp_policy_condition_get_value_pointer_from_buffer(u_int8_t *buffer, u_int32_t length) |
2303 | { |
2304 | return (buffer && length > (2 * sizeof(u_int8_t))) ? (buffer + (2 * sizeof(u_int8_t))) : NULL; |
2305 | } |
2306 | |
2307 | static inline bool |
2308 | necp_policy_condition_is_default(u_int8_t *buffer, u_int32_t length) |
2309 | { |
2310 | return necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_DEFAULT; |
2311 | } |
2312 | |
2313 | static inline bool |
2314 | necp_policy_condition_is_application(u_int8_t *buffer, u_int32_t length) |
2315 | { |
2316 | return necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_APPLICATION; |
2317 | } |
2318 | |
2319 | static inline bool |
2320 | necp_policy_condition_is_real_application(u_int8_t *buffer, u_int32_t length) |
2321 | { |
2322 | return necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_REAL_APPLICATION; |
2323 | } |
2324 | |
2325 | static inline bool |
2326 | necp_policy_condition_requires_application(u_int8_t *buffer, u_int32_t length) |
2327 | { |
2328 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2329 | return type == NECP_POLICY_CONDITION_REAL_APPLICATION; |
2330 | } |
2331 | |
2332 | static inline bool |
2333 | necp_policy_condition_is_kernel_pid(u_int8_t *buffer, u_int32_t length) |
2334 | { |
2335 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2336 | u_int32_t condition_length = 0; |
2337 | pid_t *condition_value = NULL; |
2338 | |
2339 | if (type == NECP_POLICY_CONDITION_PID) { |
2340 | condition_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2341 | if (condition_length >= sizeof(pid_t)) { |
2342 | condition_value = (pid_t *)(void *)necp_policy_condition_get_value_pointer_from_buffer(buffer, length); |
2343 | return *condition_value == 0; |
2344 | } |
2345 | } |
2346 | return false; |
2347 | } |
2348 | |
2349 | static bool |
2350 | necp_policy_condition_is_valid(u_int8_t *buffer, u_int32_t length, u_int8_t policy_result_type) |
2351 | { |
2352 | bool validated = FALSE; |
2353 | bool result_cannot_have_ip_layer = (policy_result_type == NECP_POLICY_RESULT_SOCKET_DIVERT || |
2354 | policy_result_type == NECP_POLICY_RESULT_SOCKET_FILTER || |
2355 | policy_result_type == NECP_POLICY_RESULT_SOCKET_SCOPED || |
2356 | policy_result_type == NECP_POLICY_RESULT_ROUTE_RULES || |
2357 | policy_result_type == NECP_POLICY_RESULT_USE_NETAGENT || |
2358 | policy_result_type == NECP_POLICY_RESULT_NETAGENT_SCOPED || |
2359 | policy_result_type == NECP_POLICY_RESULT_SCOPED_DIRECT || |
2360 | policy_result_type == NECP_POLICY_RESULT_ALLOW_UNENTITLED || |
2361 | policy_result_type == NECP_POLICY_RESULT_REMOVE_NETAGENT) ? TRUE : FALSE; |
2362 | u_int32_t condition_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2363 | u_int8_t *condition_value = necp_policy_condition_get_value_pointer_from_buffer(buffer, length); |
2364 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2365 | u_int8_t flags = necp_policy_condition_get_flags_from_buffer(buffer, length); |
2366 | switch (type) { |
2367 | case NECP_POLICY_CONDITION_APPLICATION: |
2368 | case NECP_POLICY_CONDITION_REAL_APPLICATION: { |
2369 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE) && |
2370 | condition_length >= sizeof(uuid_t) && |
2371 | condition_value != NULL && |
2372 | !uuid_is_null(uu: condition_value)) { |
2373 | validated = TRUE; |
2374 | } |
2375 | break; |
2376 | } |
2377 | case NECP_POLICY_CONDITION_DOMAIN: |
2378 | case NECP_POLICY_CONDITION_ACCOUNT: |
2379 | case NECP_POLICY_CONDITION_BOUND_INTERFACE: |
2380 | case NECP_POLICY_CONDITION_SIGNING_IDENTIFIER: |
2381 | case NECP_POLICY_CONDITION_URL: { |
2382 | if (condition_length > 0) { |
2383 | validated = TRUE; |
2384 | } |
2385 | break; |
2386 | } |
2387 | case NECP_POLICY_CONDITION_TRAFFIC_CLASS: { |
2388 | if (condition_length >= sizeof(struct necp_policy_condition_tc_range)) { |
2389 | validated = TRUE; |
2390 | } |
2391 | break; |
2392 | } |
2393 | case NECP_POLICY_CONDITION_DEFAULT: |
2394 | case NECP_POLICY_CONDITION_ALL_INTERFACES: |
2395 | case NECP_POLICY_CONDITION_ENTITLEMENT: |
2396 | case NECP_POLICY_CONDITION_PLATFORM_BINARY: |
2397 | case NECP_POLICY_CONDITION_HAS_CLIENT: |
2398 | case NECP_POLICY_CONDITION_SYSTEM_SIGNED_RESULT: |
2399 | case NECP_POLICY_CONDITION_LOCAL_NETWORKS: { |
2400 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE)) { |
2401 | validated = TRUE; |
2402 | } |
2403 | break; |
2404 | } |
2405 | case NECP_POLICY_CONDITION_SDK_VERSION: { |
2406 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE) && |
2407 | condition_length >= sizeof(struct necp_policy_condition_sdk_version)) { |
2408 | validated = TRUE; |
2409 | } |
2410 | break; |
2411 | } |
2412 | case NECP_POLICY_CONDITION_IP_PROTOCOL: { |
2413 | if (condition_length >= sizeof(u_int16_t)) { |
2414 | validated = TRUE; |
2415 | } |
2416 | break; |
2417 | } |
2418 | case NECP_POLICY_CONDITION_PID: { |
2419 | if (condition_length >= sizeof(pid_t) && |
2420 | condition_value != NULL) { |
2421 | validated = TRUE; |
2422 | } |
2423 | break; |
2424 | } |
2425 | case NECP_POLICY_CONDITION_DOMAIN_FILTER: { |
2426 | if (condition_length >= sizeof(u_int32_t)) { |
2427 | validated = TRUE; |
2428 | } |
2429 | break; |
2430 | } |
2431 | case NECP_POLICY_CONDITION_UID: |
2432 | case NECP_POLICY_CONDITION_REAL_UID: { |
2433 | if (condition_length >= sizeof(uid_t)) { |
2434 | validated = TRUE; |
2435 | } |
2436 | break; |
2437 | } |
2438 | case NECP_POLICY_CONDITION_LOCAL_ADDR: |
2439 | case NECP_POLICY_CONDITION_REMOTE_ADDR: { |
2440 | if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr) && |
2441 | necp_address_is_valid(&((struct necp_policy_condition_addr *)(void *)condition_value)->address.sa)) { |
2442 | validated = TRUE; |
2443 | } |
2444 | break; |
2445 | } |
2446 | case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: |
2447 | case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: { |
2448 | if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr_range) && |
2449 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->start_address.sa) && |
2450 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->end_address.sa)) { |
2451 | validated = TRUE; |
2452 | } |
2453 | break; |
2454 | } |
2455 | case NECP_POLICY_CONDITION_AGENT_TYPE: { |
2456 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE) && |
2457 | condition_length >= sizeof(struct necp_policy_condition_agent_type)) { |
2458 | validated = TRUE; |
2459 | } |
2460 | break; |
2461 | } |
2462 | case NECP_POLICY_CONDITION_FLOW_IP_PROTOCOL: { |
2463 | if (condition_length >= sizeof(u_int16_t)) { |
2464 | validated = TRUE; |
2465 | } |
2466 | break; |
2467 | } |
2468 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR: |
2469 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR: { |
2470 | if (condition_length >= sizeof(struct necp_policy_condition_addr) && |
2471 | necp_address_is_valid(&((struct necp_policy_condition_addr *)(void *)condition_value)->address.sa)) { |
2472 | validated = TRUE; |
2473 | } |
2474 | break; |
2475 | } |
2476 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_RANGE: |
2477 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_RANGE: { |
2478 | if (condition_length >= sizeof(struct necp_policy_condition_addr_range) && |
2479 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->start_address.sa) && |
2480 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->end_address.sa)) { |
2481 | validated = TRUE; |
2482 | } |
2483 | break; |
2484 | } |
2485 | case NECP_POLICY_CONDITION_CLIENT_FLAGS: { |
2486 | if (condition_length == 0 || condition_length >= sizeof(u_int32_t)) { |
2487 | validated = TRUE; |
2488 | } |
2489 | break; |
2490 | } |
2491 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_EMPTY: { |
2492 | validated = TRUE; |
2493 | break; |
2494 | } |
2495 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_EMPTY: { |
2496 | validated = TRUE; |
2497 | break; |
2498 | } |
2499 | case NECP_POLICY_CONDITION_PACKET_FILTER_TAGS: { |
2500 | if (condition_length >= sizeof(u_int16_t)) { |
2501 | u_int16_t packet_filter_tags = *(u_int16_t *)(void *)condition_value; |
2502 | if (packet_filter_tags > 0 && packet_filter_tags <= NECP_POLICY_CONDITION_PACKET_FILTER_TAG_MAX) { |
2503 | validated = TRUE; |
2504 | } |
2505 | } |
2506 | break; |
2507 | } |
2508 | case NECP_POLICY_CONDITION_FLOW_IS_LOOPBACK: { |
2509 | validated = TRUE; |
2510 | break; |
2511 | } |
2512 | case NECP_POLICY_CONDITION_DELEGATE_IS_PLATFORM_BINARY: { |
2513 | validated = TRUE; |
2514 | break; |
2515 | } |
2516 | case NECP_POLICY_CONDITION_SCHEME_PORT: { |
2517 | if (condition_length >= sizeof(u_int16_t)) { |
2518 | validated = TRUE; |
2519 | } |
2520 | break; |
2521 | } |
2522 | case NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS: { |
2523 | if (condition_length >= sizeof(u_int32_t) * NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_MAX) { |
2524 | validated = TRUE; |
2525 | } |
2526 | break; |
2527 | } |
2528 | default: { |
2529 | validated = FALSE; |
2530 | break; |
2531 | } |
2532 | } |
2533 | |
2534 | if (necp_debug) { |
2535 | NECPLOG(LOG_DEBUG, "Policy condition type %d, valid %d" , type, validated); |
2536 | } |
2537 | |
2538 | return validated; |
2539 | } |
2540 | |
2541 | static bool |
2542 | necp_policy_route_rule_is_default(u_int8_t *buffer, u_int32_t length) |
2543 | { |
2544 | return necp_policy_condition_get_value_length_from_buffer(buffer, length) == 0 && |
2545 | necp_policy_condition_get_flags_from_buffer(buffer, length) == 0; |
2546 | } |
2547 | |
2548 | static bool |
2549 | necp_policy_route_rule_is_valid(u_int8_t *buffer, u_int32_t length) |
2550 | { |
2551 | bool validated = FALSE; |
2552 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2553 | switch (type) { |
2554 | case NECP_ROUTE_RULE_ALLOW_INTERFACE: { |
2555 | validated = TRUE; |
2556 | break; |
2557 | } |
2558 | case NECP_ROUTE_RULE_DENY_INTERFACE: { |
2559 | validated = TRUE; |
2560 | break; |
2561 | } |
2562 | case NECP_ROUTE_RULE_DENY_INTERFACE_WITH_TYPE: { |
2563 | u_int32_t rule_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2564 | validated = (rule_length >= sizeof(u_int32_t)); |
2565 | break; |
2566 | } |
2567 | case NECP_ROUTE_RULE_QOS_MARKING: { |
2568 | validated = TRUE; |
2569 | break; |
2570 | } |
2571 | case NECP_ROUTE_RULE_DENY_LQM_ABORT: { |
2572 | validated = TRUE; |
2573 | break; |
2574 | } |
2575 | case NECP_ROUTE_RULE_USE_NETAGENT: |
2576 | case NECP_ROUTE_RULE_REMOVE_NETAGENT: { |
2577 | u_int32_t rule_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2578 | validated = (rule_length >= sizeof(uuid_t)); |
2579 | break; |
2580 | } |
2581 | case NECP_ROUTE_RULE_DIVERT_SOCKET: { |
2582 | u_int32_t rule_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2583 | validated = (rule_length >= sizeof(uint32_t)); |
2584 | break; |
2585 | } |
2586 | default: { |
2587 | validated = FALSE; |
2588 | break; |
2589 | } |
2590 | } |
2591 | |
2592 | if (necp_debug) { |
2593 | NECPLOG(LOG_DEBUG, "Policy route rule type %d, valid %d" , type, validated); |
2594 | } |
2595 | |
2596 | return validated; |
2597 | } |
2598 | |
2599 | static int |
2600 | necp_get_posix_error_for_necp_error(int response_error) |
2601 | { |
2602 | switch (response_error) { |
2603 | case NECP_ERROR_UNKNOWN_PACKET_TYPE: |
2604 | case NECP_ERROR_INVALID_TLV: |
2605 | case NECP_ERROR_POLICY_RESULT_INVALID: |
2606 | case NECP_ERROR_POLICY_CONDITIONS_INVALID: |
2607 | case NECP_ERROR_ROUTE_RULES_INVALID: { |
2608 | return EINVAL; |
2609 | } |
2610 | case NECP_ERROR_POLICY_ID_NOT_FOUND: { |
2611 | return ENOENT; |
2612 | } |
2613 | case NECP_ERROR_INVALID_PROCESS: { |
2614 | return EPERM; |
2615 | } |
2616 | case NECP_ERROR_INTERNAL: |
2617 | default: { |
2618 | return ENOMEM; |
2619 | } |
2620 | } |
2621 | } |
2622 | |
2623 | static necp_policy_id |
2624 | necp_handle_policy_add(struct necp_session *session, |
2625 | u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *return_error) |
2626 | { |
2627 | bool has_default_condition = FALSE; |
2628 | bool has_non_default_condition = FALSE; |
2629 | bool has_application_condition = FALSE; |
2630 | bool has_real_application_condition = FALSE; |
2631 | bool requires_application_condition = FALSE; |
2632 | bool has_kernel_pid = FALSE; |
2633 | bool is_pass_skip = FALSE; |
2634 | u_int8_t *conditions_array = NULL; |
2635 | u_int32_t conditions_array_size = 0; |
2636 | int conditions_array_cursor; |
2637 | |
2638 | bool has_default_route_rule = FALSE; |
2639 | u_int8_t *route_rules_array = NULL; |
2640 | u_int32_t route_rules_array_size = 0; |
2641 | int route_rules_array_cursor; |
2642 | |
2643 | int cursor; |
2644 | int error = 0; |
2645 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2646 | |
2647 | necp_policy_order order = 0; |
2648 | struct necp_session_policy *policy = NULL; |
2649 | u_int8_t *policy_result = NULL; |
2650 | u_int32_t policy_result_size = 0; |
2651 | |
2652 | // Read policy order |
2653 | error = necp_get_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_POLICY_ORDER, buff_len: sizeof(order), buff: &order, NULL); |
2654 | if (error) { |
2655 | NECPLOG(LOG_ERR, "Failed to get policy order: %d" , error); |
2656 | response_error = NECP_ERROR_INVALID_TLV; |
2657 | goto fail; |
2658 | } |
2659 | |
2660 | // Read policy result |
2661 | cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_POLICY_RESULT, err: &error, next: 0); |
2662 | if (error || cursor < 0) { |
2663 | NECPLOG(LOG_ERR, "Failed to find policy result TLV: %d" , error); |
2664 | response_error = NECP_ERROR_INVALID_TLV; |
2665 | goto fail; |
2666 | } |
2667 | error = necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: 0, NULL, value_size: &policy_result_size); |
2668 | if (error || policy_result_size == 0) { |
2669 | NECPLOG(LOG_ERR, "Failed to get policy result length: %d" , error); |
2670 | response_error = NECP_ERROR_INVALID_TLV; |
2671 | goto fail; |
2672 | } |
2673 | if (policy_result_size > NECP_MAX_POLICY_RESULT_SIZE) { |
2674 | NECPLOG(LOG_ERR, "Policy result length too large: %u" , policy_result_size); |
2675 | response_error = NECP_ERROR_INVALID_TLV; |
2676 | goto fail; |
2677 | } |
2678 | policy_result = (u_int8_t *)kalloc_data(policy_result_size, Z_WAITOK); |
2679 | if (policy_result == NULL) { |
2680 | NECPLOG(LOG_ERR, "Failed to allocate a policy result buffer (size %d)" , policy_result_size); |
2681 | response_error = NECP_ERROR_INTERNAL; |
2682 | goto fail; |
2683 | } |
2684 | error = necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: policy_result_size, out_buffer: policy_result, NULL); |
2685 | if (error) { |
2686 | NECPLOG(LOG_ERR, "Failed to get policy result: %d" , error); |
2687 | response_error = NECP_ERROR_POLICY_RESULT_INVALID; |
2688 | goto fail; |
2689 | } |
2690 | if (!necp_policy_result_is_valid(buffer: policy_result, length: policy_result_size, is_pass_skip: &is_pass_skip)) { |
2691 | NECPLOG0(LOG_ERR, "Failed to validate policy result" ); |
2692 | response_error = NECP_ERROR_POLICY_RESULT_INVALID; |
2693 | goto fail; |
2694 | } |
2695 | |
2696 | if (necp_policy_result_requires_route_rules(buffer: policy_result, length: policy_result_size)) { |
2697 | // Read route rules conditions |
2698 | for (cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, err: &error, next: 0); |
2699 | cursor >= 0; |
2700 | cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset: cursor, NECP_TLV_ROUTE_RULE, err: &error, next: 1)) { |
2701 | u_int32_t route_rule_size = 0; |
2702 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: 0, NULL, value_size: &route_rule_size); |
2703 | if (os_add_overflow(route_rules_array_size, |
2704 | (sizeof(u_int8_t) + sizeof(u_int32_t) + route_rule_size), |
2705 | &route_rules_array_size)) { |
2706 | NECPLOG0(LOG_ERR, "Route rules size overflowed, too large" ); |
2707 | response_error = NECP_ERROR_INVALID_TLV; |
2708 | goto fail; |
2709 | } |
2710 | } |
2711 | |
2712 | if (route_rules_array_size == 0) { |
2713 | NECPLOG0(LOG_ERR, "Failed to get policy route rules" ); |
2714 | response_error = NECP_ERROR_INVALID_TLV; |
2715 | goto fail; |
2716 | } |
2717 | if (route_rules_array_size > NECP_MAX_ROUTE_RULES_ARRAY_SIZE) { |
2718 | NECPLOG(LOG_ERR, "Route rules length too large: %u" , route_rules_array_size); |
2719 | response_error = NECP_ERROR_INVALID_TLV; |
2720 | goto fail; |
2721 | } |
2722 | route_rules_array = (u_int8_t *)kalloc_data(route_rules_array_size, Z_WAITOK); |
2723 | if (route_rules_array == NULL) { |
2724 | NECPLOG(LOG_ERR, "Failed to allocate a policy route rules array (size %d)" , route_rules_array_size); |
2725 | response_error = NECP_ERROR_INTERNAL; |
2726 | goto fail; |
2727 | } |
2728 | |
2729 | route_rules_array_cursor = 0; |
2730 | for (cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, err: &error, next: 0); |
2731 | cursor >= 0; |
2732 | cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset: cursor, NECP_TLV_ROUTE_RULE, err: &error, next: 1)) { |
2733 | u_int8_t route_rule_type = NECP_TLV_ROUTE_RULE; |
2734 | u_int32_t route_rule_size = 0; |
2735 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: 0, NULL, value_size: &route_rule_size); |
2736 | if (route_rule_size > 0 && |
2737 | (sizeof(route_rule_type) + sizeof(route_rule_size) + route_rule_size) <= (route_rules_array_size - route_rules_array_cursor)) { |
2738 | // Add type |
2739 | memcpy(dst: (route_rules_array + route_rules_array_cursor), src: &route_rule_type, n: sizeof(route_rule_type)); |
2740 | route_rules_array_cursor += sizeof(route_rule_type); |
2741 | |
2742 | // Add length |
2743 | memcpy(dst: (route_rules_array + route_rules_array_cursor), src: &route_rule_size, n: sizeof(route_rule_size)); |
2744 | route_rules_array_cursor += sizeof(route_rule_size); |
2745 | |
2746 | // Add value |
2747 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: route_rule_size, out_buffer: (route_rules_array + route_rules_array_cursor), NULL); |
2748 | |
2749 | if (!necp_policy_route_rule_is_valid(buffer: (route_rules_array + route_rules_array_cursor), length: route_rule_size)) { |
2750 | NECPLOG0(LOG_ERR, "Failed to validate policy route rule" ); |
2751 | response_error = NECP_ERROR_ROUTE_RULES_INVALID; |
2752 | goto fail; |
2753 | } |
2754 | |
2755 | if (necp_policy_route_rule_is_default(buffer: (route_rules_array + route_rules_array_cursor), length: route_rule_size)) { |
2756 | if (has_default_route_rule) { |
2757 | NECPLOG0(LOG_ERR, "Failed to validate route rule; contained multiple default route rules" ); |
2758 | response_error = NECP_ERROR_ROUTE_RULES_INVALID; |
2759 | goto fail; |
2760 | } |
2761 | has_default_route_rule = TRUE; |
2762 | } |
2763 | |
2764 | route_rules_array_cursor += route_rule_size; |
2765 | } |
2766 | } |
2767 | } |
2768 | |
2769 | // Read policy conditions |
2770 | for (cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, err: &error, next: 0); |
2771 | cursor >= 0; |
2772 | cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset: cursor, NECP_TLV_POLICY_CONDITION, err: &error, next: 1)) { |
2773 | u_int32_t condition_size = 0; |
2774 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: 0, NULL, value_size: &condition_size); |
2775 | |
2776 | if (condition_size > 0) { |
2777 | if (os_add_overflow(conditions_array_size, |
2778 | (sizeof(u_int8_t) + sizeof(u_int32_t) + condition_size), |
2779 | &conditions_array_size)) { |
2780 | NECPLOG0(LOG_ERR, "Conditions size overflowed, too large" ); |
2781 | response_error = NECP_ERROR_INVALID_TLV; |
2782 | goto fail; |
2783 | } |
2784 | } |
2785 | } |
2786 | |
2787 | if (conditions_array_size == 0) { |
2788 | NECPLOG0(LOG_ERR, "Failed to get policy conditions" ); |
2789 | response_error = NECP_ERROR_INVALID_TLV; |
2790 | goto fail; |
2791 | } |
2792 | if (conditions_array_size > NECP_MAX_CONDITIONS_ARRAY_SIZE) { |
2793 | NECPLOG(LOG_ERR, "Conditions length too large: %u" , conditions_array_size); |
2794 | response_error = NECP_ERROR_INVALID_TLV; |
2795 | goto fail; |
2796 | } |
2797 | conditions_array = (u_int8_t *)kalloc_data(conditions_array_size, Z_WAITOK); |
2798 | if (conditions_array == NULL) { |
2799 | NECPLOG(LOG_ERR, "Failed to allocate a policy conditions array (size %d)" , conditions_array_size); |
2800 | response_error = NECP_ERROR_INTERNAL; |
2801 | goto fail; |
2802 | } |
2803 | |
2804 | conditions_array_cursor = 0; |
2805 | for (cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, err: &error, next: 0); |
2806 | cursor >= 0; |
2807 | cursor = necp_find_tlv(buffer: tlv_buffer, buffer_length: tlv_buffer_length, offset: cursor, NECP_TLV_POLICY_CONDITION, err: &error, next: 1)) { |
2808 | u_int8_t condition_type = NECP_TLV_POLICY_CONDITION; |
2809 | u_int32_t condition_size = 0; |
2810 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: 0, NULL, value_size: &condition_size); |
2811 | if (condition_size > 0 && |
2812 | (sizeof(condition_type) + sizeof(condition_size) + condition_size) <= (conditions_array_size - conditions_array_cursor)) { |
2813 | // Add type |
2814 | memcpy(dst: (conditions_array + conditions_array_cursor), src: &condition_type, n: sizeof(condition_type)); |
2815 | conditions_array_cursor += sizeof(condition_type); |
2816 | |
2817 | // Add length |
2818 | memcpy(dst: (conditions_array + conditions_array_cursor), src: &condition_size, n: sizeof(condition_size)); |
2819 | conditions_array_cursor += sizeof(condition_size); |
2820 | |
2821 | // Add value |
2822 | necp_get_tlv_at_offset(buffer: tlv_buffer, buffer_length: tlv_buffer_length, tlv_offset: cursor, out_buffer_length: condition_size, out_buffer: (conditions_array + conditions_array_cursor), NULL); |
2823 | if (!necp_policy_condition_is_valid(buffer: (conditions_array + conditions_array_cursor), length: condition_size, policy_result_type: necp_policy_result_get_type_from_buffer(buffer: policy_result, length: policy_result_size))) { |
2824 | NECPLOG0(LOG_ERR, "Failed to validate policy condition" ); |
2825 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2826 | goto fail; |
2827 | } |
2828 | |
2829 | if (necp_policy_condition_is_default(buffer: (conditions_array + conditions_array_cursor), length: condition_size)) { |
2830 | has_default_condition = TRUE; |
2831 | } else { |
2832 | has_non_default_condition = TRUE; |
2833 | } |
2834 | if (has_default_condition && has_non_default_condition) { |
2835 | NECPLOG0(LOG_ERR, "Failed to validate conditions; contained default and non-default conditions" ); |
2836 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2837 | goto fail; |
2838 | } |
2839 | |
2840 | if (necp_policy_condition_is_application(buffer: (conditions_array + conditions_array_cursor), length: condition_size)) { |
2841 | has_application_condition = TRUE; |
2842 | } |
2843 | |
2844 | if (necp_policy_condition_is_real_application(buffer: (conditions_array + conditions_array_cursor), length: condition_size)) { |
2845 | has_real_application_condition = TRUE; |
2846 | } |
2847 | |
2848 | if (necp_policy_condition_requires_application(buffer: (conditions_array + conditions_array_cursor), length: condition_size)) { |
2849 | requires_application_condition = TRUE; |
2850 | } |
2851 | |
2852 | if (necp_policy_condition_is_kernel_pid(buffer: (conditions_array + conditions_array_cursor), length: condition_size)) { |
2853 | has_kernel_pid = TRUE; |
2854 | } |
2855 | |
2856 | conditions_array_cursor += condition_size; |
2857 | } |
2858 | } |
2859 | |
2860 | if (requires_application_condition && !has_application_condition) { |
2861 | NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain application condition" ); |
2862 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2863 | goto fail; |
2864 | } |
2865 | |
2866 | if (has_kernel_pid && !is_pass_skip) { |
2867 | NECPLOG0(LOG_ERR, "Failed to validate conditions; kernel pid (0) condition allows only Pass/Skip result" ); |
2868 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2869 | goto fail; |
2870 | } |
2871 | |
2872 | if ((policy = necp_policy_create(session, order, conditions_array, conditions_array_size, route_rules_array, route_rules_array_size, result: policy_result, result_size: policy_result_size)) == NULL) { |
2873 | response_error = NECP_ERROR_INTERNAL; |
2874 | goto fail; |
2875 | } |
2876 | |
2877 | return policy->local_id; |
2878 | |
2879 | fail: |
2880 | if (policy_result != NULL) { |
2881 | kfree_data(policy_result, policy_result_size); |
2882 | } |
2883 | if (conditions_array != NULL) { |
2884 | kfree_data(conditions_array, conditions_array_size); |
2885 | } |
2886 | if (route_rules_array != NULL) { |
2887 | kfree_data(route_rules_array, route_rules_array_size); |
2888 | } |
2889 | |
2890 | if (return_error != NULL) { |
2891 | *return_error = necp_get_posix_error_for_necp_error(response_error); |
2892 | } |
2893 | return 0; |
2894 | } |
2895 | |
2896 | static necp_policy_id |
2897 | necp_policy_get_new_id(struct necp_session *session) |
2898 | { |
2899 | session->last_policy_id++; |
2900 | if (session->last_policy_id < 1) { |
2901 | session->last_policy_id = 1; |
2902 | } |
2903 | |
2904 | necp_policy_id newid = session->last_policy_id; |
2905 | |
2906 | if (newid == 0) { |
2907 | NECPLOG0(LOG_ERR, "Allocate policy id failed.\n" ); |
2908 | return 0; |
2909 | } |
2910 | |
2911 | return newid; |
2912 | } |
2913 | |
2914 | /* |
2915 | * For the policy dump response this is the structure: |
2916 | * |
2917 | * <NECP_PACKET_HEADER> |
2918 | * { |
2919 | * type : NECP_TLV_POLICY_DUMP |
2920 | * length : ... |
2921 | * value : |
2922 | * { |
2923 | * { |
2924 | * type : NECP_TLV_POLICY_ID |
2925 | * len : ... |
2926 | * value : ... |
2927 | * } |
2928 | * { |
2929 | * type : NECP_TLV_POLICY_ORDER |
2930 | * len : ... |
2931 | * value : ... |
2932 | * } |
2933 | * { |
2934 | * type : NECP_TLV_POLICY_RESULT_STRING |
2935 | * len : ... |
2936 | * value : ... |
2937 | * } |
2938 | * { |
2939 | * type : NECP_TLV_POLICY_OWNER |
2940 | * len : ... |
2941 | * value : ... |
2942 | * } |
2943 | * { |
2944 | * type : NECP_TLV_POLICY_CONDITION |
2945 | * len : ... |
2946 | * value : |
2947 | * { |
2948 | * { |
2949 | * type : NECP_POLICY_CONDITION_ALL_INTERFACES |
2950 | * len : ... |
2951 | * value : ... |
2952 | * } |
2953 | * { |
2954 | * type : NECP_POLICY_CONDITION_BOUND_INTERFACES |
2955 | * len : ... |
2956 | * value : ... |
2957 | * } |
2958 | * ... |
2959 | * } |
2960 | * } |
2961 | * } |
2962 | * } |
2963 | * { |
2964 | * type : NECP_TLV_POLICY_DUMP |
2965 | * length : ... |
2966 | * value : |
2967 | * { |
2968 | * { |
2969 | * type : NECP_TLV_POLICY_ID |
2970 | * len : ... |
2971 | * value : ... |
2972 | * } |
2973 | * { |
2974 | * type : NECP_TLV_POLICY_ORDER |
2975 | * len : ... |
2976 | * value : ... |
2977 | * } |
2978 | * { |
2979 | * type : NECP_TLV_POLICY_RESULT_STRING |
2980 | * len : ... |
2981 | * value : ... |
2982 | * } |
2983 | * { |
2984 | * type : NECP_TLV_POLICY_OWNER |
2985 | * len : ... |
2986 | * value : ... |
2987 | * } |
2988 | * { |
2989 | * type : NECP_TLV_POLICY_CONDITION |
2990 | * len : ... |
2991 | * value : |
2992 | * { |
2993 | * { |
2994 | * type : NECP_POLICY_CONDITION_ALL_INTERFACES |
2995 | * len : ... |
2996 | * value : ... |
2997 | * } |
2998 | * { |
2999 | * type : NECP_POLICY_CONDITION_BOUND_INTERFACES |
3000 | * len : ... |
3001 | * value : ... |
3002 | * } |
3003 | * ... |
3004 | * } |
3005 | * } |
3006 | * } |
3007 | * } |
3008 | * ... |
3009 | */ |
3010 | static int |
3011 | necp_handle_policy_dump_all(user_addr_t out_buffer, size_t out_buffer_length) |
3012 | { |
3013 | struct necp_kernel_socket_policy *policy = NULL; |
3014 | int policy_i; |
3015 | int policy_count = 0; |
3016 | u_int8_t **tlv_buffer_pointers = NULL; |
3017 | u_int32_t *tlv_buffer_lengths = NULL; |
3018 | u_int32_t total_tlv_len = 0; |
3019 | u_int8_t *result_buf = NULL; |
3020 | u_int8_t *result_buf_cursor = result_buf; |
3021 | char result_string[MAX_RESULT_STRING_LEN]; |
3022 | char proc_name_string[MAXCOMLEN + 1]; |
3023 | |
3024 | int error_code = 0; |
3025 | bool error_occured = false; |
3026 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
3027 | |
3028 | #define REPORT_ERROR(error) error_occured = true; \ |
3029 | response_error = error; \ |
3030 | goto done |
3031 | |
3032 | #define UNLOCK_AND_REPORT_ERROR(lock, error) lck_rw_done(lock); \ |
3033 | REPORT_ERROR(error) |
3034 | |
3035 | errno_t cred_result = priv_check_cred(cred: kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, flags: 0); |
3036 | if (cred_result != 0) { |
3037 | NECPLOG0(LOG_ERR, "Session does not hold the necessary entitlement to get Network Extension Policy information" ); |
3038 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3039 | } |
3040 | |
3041 | // LOCK |
3042 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
3043 | |
3044 | if (necp_debug) { |
3045 | NECPLOG0(LOG_DEBUG, "Gathering policies" ); |
3046 | } |
3047 | |
3048 | policy_count = necp_kernel_application_policies_count; |
3049 | |
3050 | tlv_buffer_pointers = kalloc_type(u_int8_t *, policy_count, M_WAITOK | Z_ZERO); |
3051 | if (tlv_buffer_pointers == NULL) { |
3052 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_pointers (%lu bytes)" , sizeof(u_int8_t *) * policy_count); |
3053 | UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL); |
3054 | } |
3055 | |
3056 | tlv_buffer_lengths = (u_int32_t *)kalloc_data(sizeof(u_int32_t) * policy_count, Z_NOWAIT | Z_ZERO); |
3057 | if (tlv_buffer_lengths == NULL) { |
3058 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_lengths (%lu bytes)" , sizeof(u_int32_t) * policy_count); |
3059 | UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL); |
3060 | } |
3061 | |
3062 | for (policy_i = 0; necp_kernel_socket_policies_app_layer_map != NULL && necp_kernel_socket_policies_app_layer_map[policy_i] != NULL; policy_i++) { |
3063 | policy = necp_kernel_socket_policies_app_layer_map[policy_i]; |
3064 | |
3065 | memset(s: result_string, c: 0, MAX_RESULT_STRING_LEN); |
3066 | memset(s: proc_name_string, c: 0, MAXCOMLEN + 1); |
3067 | |
3068 | necp_get_result_description(result_string, result: policy->result, result_parameter: policy->result_parameter); |
3069 | proc_name(pid: policy->session_pid, buf: proc_name_string, MAXCOMLEN); |
3070 | |
3071 | u_int16_t proc_name_len = strlen(s: proc_name_string) + 1; |
3072 | u_int16_t result_string_len = strlen(s: result_string) + 1; |
3073 | |
3074 | if (necp_debug) { |
3075 | NECPLOG(LOG_DEBUG, "Policy: process: %s, result: %s" , proc_name_string, result_string); |
3076 | } |
3077 | |
3078 | u_int32_t total_allocated_bytes = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->id) + // NECP_TLV_POLICY_ID |
3079 | sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->order) + // NECP_TLV_POLICY_ORDER |
3080 | sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->session_order) + // NECP_TLV_POLICY_SESSION_ORDER |
3081 | sizeof(u_int8_t) + sizeof(u_int32_t) + result_string_len + // NECP_TLV_POLICY_RESULT_STRING |
3082 | sizeof(u_int8_t) + sizeof(u_int32_t) + proc_name_len + // NECP_TLV_POLICY_OWNER |
3083 | sizeof(u_int8_t) + sizeof(u_int32_t); // NECP_TLV_POLICY_CONDITION |
3084 | |
3085 | // We now traverse the condition_mask to see how much space we need to allocate |
3086 | u_int64_t condition_mask = policy->condition_mask; |
3087 | u_int8_t num_conditions = 0; |
3088 | struct necp_string_id_mapping *account_id_entry = NULL; |
3089 | char if_name[IFXNAMSIZ]; |
3090 | u_int32_t condition_tlv_length = 0; |
3091 | memset(s: if_name, c: 0, n: sizeof(if_name)); |
3092 | |
3093 | if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) { |
3094 | num_conditions++; |
3095 | } else { |
3096 | if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) { |
3097 | num_conditions++; |
3098 | } |
3099 | if (condition_mask & NECP_KERNEL_CONDITION_HAS_CLIENT) { |
3100 | num_conditions++; |
3101 | } |
3102 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
3103 | snprintf(if_name, IFXNAMSIZ, "%s%d" , ifnet_name(interface: policy->cond_bound_interface), ifnet_unit(interface: policy->cond_bound_interface)); |
3104 | condition_tlv_length += strlen(s: if_name) + 1; |
3105 | num_conditions++; |
3106 | } |
3107 | if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
3108 | condition_tlv_length += sizeof(policy->cond_protocol); |
3109 | num_conditions++; |
3110 | } |
3111 | if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
3112 | condition_tlv_length += sizeof(uuid_t); |
3113 | num_conditions++; |
3114 | } |
3115 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
3116 | condition_tlv_length += sizeof(uuid_t); |
3117 | num_conditions++; |
3118 | } |
3119 | if ((condition_mask & NECP_KERNEL_CONDITION_DOMAIN) || |
3120 | (condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN)) { |
3121 | u_int32_t domain_len = strlen(s: policy->cond_domain) + 1; |
3122 | condition_tlv_length += domain_len; |
3123 | num_conditions++; |
3124 | } |
3125 | if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER) { |
3126 | condition_tlv_length += sizeof(u_int32_t); |
3127 | num_conditions++; |
3128 | } |
3129 | if (condition_mask & NECP_KERNEL_CONDITION_URL) { |
3130 | u_int32_t url_len = strlen(s: policy->cond_url) + 1; |
3131 | condition_tlv_length += url_len; |
3132 | num_conditions++; |
3133 | } |
3134 | if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
3135 | account_id_entry = necp_lookup_string_with_id_locked(list: &necp_account_id_list, local_id: policy->cond_account_id); |
3136 | u_int32_t account_id_len = 0; |
3137 | if (account_id_entry) { |
3138 | account_id_len = account_id_entry->string ? strlen(s: account_id_entry->string) + 1 : 0; |
3139 | } |
3140 | condition_tlv_length += account_id_len; |
3141 | num_conditions++; |
3142 | } |
3143 | if (condition_mask & NECP_KERNEL_CONDITION_PID) { |
3144 | condition_tlv_length += (sizeof(pid_t) + sizeof(int32_t)); |
3145 | num_conditions++; |
3146 | } |
3147 | if (condition_mask & NECP_KERNEL_CONDITION_UID) { |
3148 | condition_tlv_length += sizeof(uid_t); |
3149 | num_conditions++; |
3150 | } |
3151 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
3152 | condition_tlv_length += sizeof(uid_t); |
3153 | num_conditions++; |
3154 | } |
3155 | if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
3156 | condition_tlv_length += sizeof(struct necp_policy_condition_tc_range); |
3157 | num_conditions++; |
3158 | } |
3159 | if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
3160 | num_conditions++; |
3161 | } |
3162 | if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
3163 | u_int32_t entitlement_len = strlen(s: policy->cond_custom_entitlement) + 1; |
3164 | condition_tlv_length += entitlement_len; |
3165 | num_conditions++; |
3166 | } |
3167 | if (condition_mask & NECP_KERNEL_CONDITION_PLATFORM_BINARY) { |
3168 | num_conditions++; |
3169 | } |
3170 | if (condition_mask & NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT) { |
3171 | num_conditions++; |
3172 | } |
3173 | if (condition_mask & NECP_KERNEL_CONDITION_SDK_VERSION) { |
3174 | condition_tlv_length += sizeof(struct necp_policy_condition_sdk_version); |
3175 | num_conditions++; |
3176 | } |
3177 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) { |
3178 | num_conditions++; |
3179 | } |
3180 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
3181 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
3182 | condition_tlv_length += sizeof(struct necp_policy_condition_addr_range); |
3183 | } else { |
3184 | condition_tlv_length += sizeof(struct necp_policy_condition_addr); |
3185 | } |
3186 | num_conditions++; |
3187 | } |
3188 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
3189 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
3190 | condition_tlv_length += sizeof(struct necp_policy_condition_addr_range); |
3191 | } else { |
3192 | condition_tlv_length += sizeof(struct necp_policy_condition_addr); |
3193 | } |
3194 | num_conditions++; |
3195 | } |
3196 | if (condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
3197 | condition_tlv_length += sizeof(struct necp_policy_condition_agent_type); |
3198 | num_conditions++; |
3199 | } |
3200 | if (condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
3201 | condition_tlv_length += sizeof(u_int32_t); |
3202 | num_conditions++; |
3203 | } |
3204 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY) { |
3205 | num_conditions++; |
3206 | } |
3207 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY) { |
3208 | num_conditions++; |
3209 | } |
3210 | if (condition_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER) { |
3211 | u_int32_t identifier_len = strlen(s: policy->cond_signing_identifier) + 1; |
3212 | condition_tlv_length += identifier_len; |
3213 | num_conditions++; |
3214 | } |
3215 | if (condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
3216 | condition_tlv_length += sizeof(u_int16_t); |
3217 | num_conditions++; |
3218 | } |
3219 | if (condition_mask & NECP_KERNEL_CONDITION_IS_LOOPBACK) { |
3220 | num_conditions++; |
3221 | } |
3222 | if (condition_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY) { |
3223 | num_conditions++; |
3224 | } |
3225 | if (condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
3226 | condition_tlv_length += sizeof(u_int16_t); |
3227 | num_conditions++; |
3228 | } |
3229 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
3230 | condition_tlv_length += (sizeof(u_int32_t) * NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_MAX); |
3231 | num_conditions++; |
3232 | } |
3233 | } |
3234 | |
3235 | condition_tlv_length += num_conditions * (sizeof(u_int8_t) + sizeof(u_int32_t)); // These are for the condition TLVs. The space for "value" is already accounted for above. |
3236 | total_allocated_bytes += condition_tlv_length; |
3237 | |
3238 | u_int8_t *tlv_buffer; |
3239 | tlv_buffer = (u_int8_t *)kalloc_data(total_allocated_bytes, Z_NOWAIT | Z_ZERO); |
3240 | if (tlv_buffer == NULL) { |
3241 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer (%u bytes)" , total_allocated_bytes); |
3242 | continue; |
3243 | } |
3244 | |
3245 | u_int8_t *cursor = tlv_buffer; |
3246 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, length: sizeof(policy->id), value: &policy->id, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3247 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, length: sizeof(necp_policy_order), value: &policy->order, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3248 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_SESSION_ORDER, length: sizeof(policy->session_order), value: &policy->session_order, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3249 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT_STRING, length: result_string_len, value: result_string, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3250 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_OWNER, length: proc_name_len, value: proc_name_string, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3251 | |
3252 | #define N_QUICK 256 |
3253 | u_int8_t q_cond_buf[N_QUICK]; // Minor optimization |
3254 | |
3255 | u_int8_t *cond_buf; // To be used for condition TLVs |
3256 | if (condition_tlv_length <= N_QUICK) { |
3257 | cond_buf = q_cond_buf; |
3258 | } else { |
3259 | cond_buf = (u_int8_t *)kalloc_data(condition_tlv_length, Z_NOWAIT); |
3260 | if (cond_buf == NULL) { |
3261 | NECPLOG(LOG_DEBUG, "Failed to allocate cond_buffer (%u bytes)" , condition_tlv_length); |
3262 | kfree_data(tlv_buffer, total_allocated_bytes); |
3263 | continue; |
3264 | } |
3265 | } |
3266 | |
3267 | memset(s: cond_buf, c: 0, n: condition_tlv_length); |
3268 | u_int8_t *cond_buf_cursor = cond_buf; |
3269 | if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) { |
3270 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_DEFAULT, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3271 | } else { |
3272 | if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) { |
3273 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_ALL_INTERFACES, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3274 | } |
3275 | if (condition_mask & NECP_KERNEL_CONDITION_HAS_CLIENT) { |
3276 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_HAS_CLIENT, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3277 | } |
3278 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) { |
3279 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_NETWORKS, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3280 | } |
3281 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
3282 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_BOUND_INTERFACE, length: strlen(s: if_name) + 1, |
3283 | value: if_name, buffer: cond_buf, buffer_length: condition_tlv_length); |
3284 | } |
3285 | if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
3286 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_IP_PROTOCOL, length: sizeof(policy->cond_protocol), value: &policy->cond_protocol, |
3287 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3288 | } |
3289 | if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
3290 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(local_id: policy->cond_app_id); |
3291 | if (entry != NULL) { |
3292 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_APPLICATION, length: sizeof(entry->uuid), value: entry->uuid, |
3293 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3294 | } |
3295 | } |
3296 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
3297 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(local_id: policy->cond_real_app_id); |
3298 | if (entry != NULL) { |
3299 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_REAL_APPLICATION, length: sizeof(entry->uuid), value: entry->uuid, |
3300 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3301 | } |
3302 | } |
3303 | if ((condition_mask & NECP_KERNEL_CONDITION_DOMAIN) || |
3304 | (condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN)) { |
3305 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_DOMAIN, length: strlen(s: policy->cond_domain) + 1, value: policy->cond_domain, |
3306 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3307 | } |
3308 | if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER) { |
3309 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_DOMAIN_FILTER, length: sizeof(policy->cond_domain_filter), value: &policy->cond_domain_filter, |
3310 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3311 | } |
3312 | if (condition_mask & NECP_KERNEL_CONDITION_URL) { |
3313 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_URL, length: strlen(s: policy->cond_url) + 1, value: policy->cond_url, |
3314 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3315 | } |
3316 | if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
3317 | if (account_id_entry != NULL) { |
3318 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_ACCOUNT, length: strlen(s: account_id_entry->string) + 1, value: account_id_entry->string, |
3319 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3320 | } |
3321 | } |
3322 | if (condition_mask & NECP_KERNEL_CONDITION_PID) { |
3323 | uint8_t pid_buffer[sizeof(policy->cond_pid) + sizeof(policy->cond_pid_version)] = { }; |
3324 | memcpy(dst: pid_buffer, src: &policy->cond_pid, n: sizeof(policy->cond_pid)); |
3325 | memcpy(dst: pid_buffer + sizeof(policy->cond_pid), src: &policy->cond_pid_version, n: sizeof(policy->cond_pid_version)); |
3326 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_PID, length: sizeof(pid_buffer), value: &pid_buffer, |
3327 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3328 | } |
3329 | if (condition_mask & NECP_KERNEL_CONDITION_UID) { |
3330 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_UID, length: sizeof(policy->cond_uid), value: &policy->cond_uid, |
3331 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3332 | } |
3333 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
3334 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_REAL_UID, length: sizeof(policy->cond_real_uid), value: &policy->cond_real_uid, |
3335 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3336 | } |
3337 | if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
3338 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_TRAFFIC_CLASS, length: sizeof(policy->cond_traffic_class), value: &policy->cond_traffic_class, |
3339 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3340 | } |
3341 | if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
3342 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, length: 0, value: "" , |
3343 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3344 | } |
3345 | if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
3346 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, length: strlen(s: policy->cond_custom_entitlement) + 1, value: policy->cond_custom_entitlement, |
3347 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3348 | } |
3349 | if (condition_mask & NECP_KERNEL_CONDITION_PLATFORM_BINARY) { |
3350 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_PLATFORM_BINARY, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3351 | } |
3352 | if (condition_mask & NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT) { |
3353 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_SYSTEM_SIGNED_RESULT, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3354 | } |
3355 | if (condition_mask & NECP_KERNEL_CONDITION_SDK_VERSION) { |
3356 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_SDK_VERSION, |
3357 | length: sizeof(policy->cond_sdk_version), value: &policy->cond_sdk_version, |
3358 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3359 | } |
3360 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
3361 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
3362 | struct necp_policy_condition_addr_range range; |
3363 | memcpy(dst: &range.start_address, src: &policy->cond_local_start, n: sizeof(policy->cond_local_start)); |
3364 | memcpy(dst: &range.end_address, src: &policy->cond_local_end, n: sizeof(policy->cond_local_end)); |
3365 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE, length: sizeof(range), value: &range, |
3366 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3367 | } else { |
3368 | struct necp_policy_condition_addr addr; |
3369 | addr.prefix = policy->cond_local_prefix; |
3370 | memcpy(dst: &addr.address, src: &policy->cond_local_start, n: sizeof(policy->cond_local_start)); |
3371 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR, length: sizeof(addr), value: &addr, |
3372 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3373 | } |
3374 | } |
3375 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
3376 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
3377 | struct necp_policy_condition_addr_range range; |
3378 | memcpy(dst: &range.start_address, src: &policy->cond_remote_start, n: sizeof(policy->cond_remote_start)); |
3379 | memcpy(dst: &range.end_address, src: &policy->cond_remote_end, n: sizeof(policy->cond_remote_end)); |
3380 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE, length: sizeof(range), value: &range, |
3381 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3382 | } else { |
3383 | struct necp_policy_condition_addr addr; |
3384 | addr.prefix = policy->cond_remote_prefix; |
3385 | memcpy(dst: &addr.address, src: &policy->cond_remote_start, n: sizeof(policy->cond_remote_start)); |
3386 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR, length: sizeof(addr), value: &addr, |
3387 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3388 | } |
3389 | } |
3390 | if (condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
3391 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_AGENT_TYPE, |
3392 | length: sizeof(policy->cond_agent_type), value: &policy->cond_agent_type, |
3393 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3394 | } |
3395 | if (condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
3396 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_CLIENT_FLAGS, length: sizeof(policy->cond_client_flags), value: &policy->cond_client_flags, buffer: cond_buf, buffer_length: condition_tlv_length); |
3397 | } |
3398 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY) { |
3399 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_EMPTY, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3400 | } |
3401 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY) { |
3402 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_EMPTY, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3403 | } |
3404 | if (condition_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER) { |
3405 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_SIGNING_IDENTIFIER, length: strlen(s: policy->cond_signing_identifier) + 1, value: policy->cond_signing_identifier, |
3406 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3407 | } |
3408 | if (condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
3409 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_PACKET_FILTER_TAGS, length: sizeof(policy->cond_packet_filter_tags), value: &policy->cond_packet_filter_tags, buffer: cond_buf, buffer_length: condition_tlv_length); |
3410 | } |
3411 | if (condition_mask & NECP_KERNEL_CONDITION_IS_LOOPBACK) { |
3412 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_FLOW_IS_LOOPBACK, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3413 | } |
3414 | if (condition_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY) { |
3415 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_DELEGATE_IS_PLATFORM_BINARY, length: 0, value: "" , buffer: cond_buf, buffer_length: condition_tlv_length); |
3416 | } |
3417 | if (condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
3418 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_SCHEME_PORT, length: sizeof(policy->cond_scheme_port), value: &policy->cond_scheme_port, buffer: cond_buf, buffer_length: condition_tlv_length); |
3419 | } |
3420 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
3421 | uint32_t flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_MAX] = {}; |
3422 | flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_FLAGS] = policy->cond_bound_interface_flags; |
3423 | flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_EFLAGS] = policy->cond_bound_interface_eflags; |
3424 | flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_XFLAGS] = policy->cond_bound_interface_xflags; |
3425 | cond_buf_cursor = necp_buffer_write_tlv(cursor: cond_buf_cursor, NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS, length: sizeof(flags), value: &flags, |
3426 | buffer: cond_buf, buffer_length: condition_tlv_length); |
3427 | } |
3428 | } |
3429 | |
3430 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_CONDITION, length: cond_buf_cursor - cond_buf, value: cond_buf, buffer: tlv_buffer, buffer_length: total_allocated_bytes); |
3431 | if (cond_buf != q_cond_buf) { |
3432 | kfree_data(cond_buf, condition_tlv_length); |
3433 | } |
3434 | |
3435 | tlv_buffer_pointers[policy_i] = tlv_buffer; |
3436 | tlv_buffer_lengths[policy_i] = (cursor - tlv_buffer); |
3437 | |
3438 | // This is the length of the TLV for NECP_TLV_POLICY_DUMP |
3439 | total_tlv_len += sizeof(u_int8_t) + sizeof(u_int32_t) + (cursor - tlv_buffer); |
3440 | } |
3441 | |
3442 | // UNLOCK |
3443 | lck_rw_done(lck: &necp_kernel_policy_lock); |
3444 | |
3445 | // Copy out |
3446 | if (out_buffer != 0) { |
3447 | if (out_buffer_length < total_tlv_len + sizeof(u_int32_t)) { |
3448 | NECPLOG(LOG_DEBUG, "out_buffer_length too small (%lu < %lu)" , out_buffer_length, total_tlv_len + sizeof(u_int32_t)); |
3449 | REPORT_ERROR(NECP_ERROR_INVALID_TLV); |
3450 | } |
3451 | |
3452 | // Allow malloc to wait, since the total buffer may be large and we are not holding any locks |
3453 | result_buf = (u_int8_t *)kalloc_data(total_tlv_len + sizeof(u_int32_t), Z_WAITOK | Z_ZERO); |
3454 | if (result_buf == NULL) { |
3455 | NECPLOG(LOG_DEBUG, "Failed to allocate result_buffer (%lu bytes)" , total_tlv_len + sizeof(u_int32_t)); |
3456 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3457 | } |
3458 | |
3459 | // Add four bytes for total length at the start |
3460 | memcpy(dst: result_buf, src: &total_tlv_len, n: sizeof(u_int32_t)); |
3461 | |
3462 | // Copy the TLVs |
3463 | result_buf_cursor = result_buf + sizeof(u_int32_t); |
3464 | for (int i = 0; i < policy_count; i++) { |
3465 | if (tlv_buffer_pointers[i] != NULL) { |
3466 | result_buf_cursor = necp_buffer_write_tlv(cursor: result_buf_cursor, NECP_TLV_POLICY_DUMP, length: tlv_buffer_lengths[i], value: tlv_buffer_pointers[i], |
3467 | buffer: result_buf, buffer_length: total_tlv_len + sizeof(u_int32_t)); |
3468 | } |
3469 | } |
3470 | |
3471 | int copy_error = copyout(result_buf, out_buffer, total_tlv_len + sizeof(u_int32_t)); |
3472 | if (copy_error) { |
3473 | NECPLOG(LOG_DEBUG, "Failed to copy out result_buffer (%lu bytes)" , total_tlv_len + sizeof(u_int32_t)); |
3474 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3475 | } |
3476 | } |
3477 | |
3478 | done: |
3479 | |
3480 | if (error_occured) { |
3481 | error_code = necp_get_posix_error_for_necp_error(response_error); |
3482 | } |
3483 | |
3484 | if (result_buf != NULL) { |
3485 | kfree_data(result_buf, total_tlv_len + sizeof(u_int32_t)); |
3486 | } |
3487 | |
3488 | if (tlv_buffer_pointers != NULL) { |
3489 | for (int i = 0; i < policy_count; i++) { |
3490 | if (tlv_buffer_pointers[i] != NULL) { |
3491 | kfree_data_addr(tlv_buffer_pointers[i]); |
3492 | tlv_buffer_pointers[i] = NULL; |
3493 | } |
3494 | } |
3495 | kfree_type(u_int8_t *, policy_count, tlv_buffer_pointers); |
3496 | } |
3497 | |
3498 | if (tlv_buffer_lengths != NULL) { |
3499 | kfree_data(tlv_buffer_lengths, sizeof(*tlv_buffer_lengths) * policy_count); |
3500 | } |
3501 | #undef N_QUICK |
3502 | #undef RESET_COND_BUF |
3503 | #undef REPORT_ERROR |
3504 | #undef UNLOCK_AND_REPORT_ERROR |
3505 | |
3506 | return error_code; |
3507 | } |
3508 | |
3509 | static struct necp_session_policy * |
3510 | necp_policy_create(struct necp_session *session, necp_policy_order order, u_int8_t *conditions_array, u_int32_t conditions_array_size, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, u_int8_t *result, u_int32_t result_size) |
3511 | { |
3512 | struct necp_session_policy *new_policy = NULL; |
3513 | struct necp_session_policy *tmp_policy = NULL; |
3514 | |
3515 | if (session == NULL || conditions_array == NULL || result == NULL || result_size == 0) { |
3516 | goto done; |
3517 | } |
3518 | |
3519 | new_policy = zalloc_flags(necp_session_policy_zone, Z_WAITOK | Z_ZERO); |
3520 | new_policy->applied = FALSE; |
3521 | new_policy->pending_deletion = FALSE; |
3522 | new_policy->pending_update = FALSE; |
3523 | new_policy->order = order; |
3524 | new_policy->conditions = conditions_array; |
3525 | new_policy->conditions_size = conditions_array_size; |
3526 | new_policy->route_rules = route_rules_array; |
3527 | new_policy->route_rules_size = route_rules_array_size; |
3528 | new_policy->result = result; |
3529 | new_policy->result_size = result_size; |
3530 | new_policy->local_id = necp_policy_get_new_id(session); |
3531 | |
3532 | LIST_INSERT_SORTED_ASCENDING(&session->policies, new_policy, chain, order, tmp_policy); |
3533 | |
3534 | session->dirty = TRUE; |
3535 | |
3536 | if (necp_debug) { |
3537 | NECPLOG(LOG_DEBUG, "Created NECP policy, order %d" , order); |
3538 | } |
3539 | done: |
3540 | return new_policy; |
3541 | } |
3542 | |
3543 | static struct necp_session_policy * |
3544 | necp_policy_find(struct necp_session *session, necp_policy_id policy_id) |
3545 | { |
3546 | struct necp_session_policy *policy = NULL; |
3547 | if (policy_id == 0) { |
3548 | return NULL; |
3549 | } |
3550 | |
3551 | LIST_FOREACH(policy, &session->policies, chain) { |
3552 | if (policy->local_id == policy_id) { |
3553 | return policy; |
3554 | } |
3555 | } |
3556 | |
3557 | return NULL; |
3558 | } |
3559 | |
3560 | static inline u_int8_t |
3561 | necp_policy_get_result_type(struct necp_session_policy *policy) |
3562 | { |
3563 | return policy ? necp_policy_result_get_type_from_buffer(buffer: policy->result, length: policy->result_size) : 0; |
3564 | } |
3565 | |
3566 | static inline u_int32_t |
3567 | necp_policy_get_result_parameter_length(struct necp_session_policy *policy) |
3568 | { |
3569 | return policy ? necp_policy_result_get_parameter_length_from_buffer(buffer: policy->result, length: policy->result_size) : 0; |
3570 | } |
3571 | |
3572 | static bool |
3573 | necp_policy_get_result_parameter(struct necp_session_policy *policy, u_int8_t *parameter_buffer, u_int32_t parameter_buffer_length) |
3574 | { |
3575 | if (policy) { |
3576 | u_int32_t parameter_length = necp_policy_result_get_parameter_length_from_buffer(buffer: policy->result, length: policy->result_size); |
3577 | if (parameter_buffer_length >= parameter_length) { |
3578 | u_int8_t *parameter = necp_policy_result_get_parameter_pointer_from_buffer(buffer: policy->result, length: policy->result_size); |
3579 | if (parameter && parameter_buffer) { |
3580 | memcpy(dst: parameter_buffer, src: parameter, n: parameter_length); |
3581 | return TRUE; |
3582 | } |
3583 | } |
3584 | } |
3585 | |
3586 | return FALSE; |
3587 | } |
3588 | |
3589 | static bool |
3590 | necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy) |
3591 | { |
3592 | if (session == NULL || policy == NULL) { |
3593 | return FALSE; |
3594 | } |
3595 | |
3596 | policy->pending_deletion = TRUE; |
3597 | session->dirty = TRUE; |
3598 | |
3599 | if (necp_debug) { |
3600 | NECPLOG0(LOG_DEBUG, "Marked NECP policy for removal" ); |
3601 | } |
3602 | return TRUE; |
3603 | } |
3604 | |
3605 | static bool |
3606 | necp_policy_mark_all_for_deletion(struct necp_session *session) |
3607 | { |
3608 | struct necp_session_policy *policy = NULL; |
3609 | struct necp_session_policy *temp_policy = NULL; |
3610 | |
3611 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
3612 | necp_policy_mark_for_deletion(session, policy); |
3613 | } |
3614 | |
3615 | return TRUE; |
3616 | } |
3617 | |
3618 | static bool |
3619 | necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy) |
3620 | { |
3621 | if (session == NULL || policy == NULL) { |
3622 | return FALSE; |
3623 | } |
3624 | |
3625 | LIST_REMOVE(policy, chain); |
3626 | |
3627 | if (policy->result) { |
3628 | kfree_data(policy->result, policy->result_size); |
3629 | policy->result = NULL; |
3630 | } |
3631 | |
3632 | if (policy->conditions) { |
3633 | kfree_data(policy->conditions, policy->conditions_size); |
3634 | policy->conditions = NULL; |
3635 | } |
3636 | |
3637 | if (policy->route_rules) { |
3638 | kfree_data(policy->route_rules, policy->route_rules_size); |
3639 | policy->route_rules = NULL; |
3640 | } |
3641 | |
3642 | zfree(necp_session_policy_zone, policy); |
3643 | |
3644 | if (necp_debug) { |
3645 | NECPLOG0(LOG_DEBUG, "Removed NECP policy" ); |
3646 | } |
3647 | return TRUE; |
3648 | } |
3649 | |
3650 | static bool |
3651 | necp_policy_unapply(struct necp_session_policy *policy) |
3652 | { |
3653 | int i = 0; |
3654 | if (policy == NULL) { |
3655 | return FALSE; |
3656 | } |
3657 | |
3658 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
3659 | |
3660 | // Release local uuid mappings |
3661 | if (!uuid_is_null(uu: policy->applied_app_uuid)) { |
3662 | bool removed_mapping = FALSE; |
3663 | if (necp_remove_uuid_app_id_mapping(uuid: policy->applied_app_uuid, removed_mapping: &removed_mapping, TRUE) && removed_mapping) { |
3664 | necp_uuid_app_id_mappings_dirty = TRUE; |
3665 | necp_num_uuid_app_id_mappings--; |
3666 | } |
3667 | uuid_clear(uu: policy->applied_app_uuid); |
3668 | } |
3669 | if (!uuid_is_null(uu: policy->applied_real_app_uuid)) { |
3670 | necp_remove_uuid_app_id_mapping(uuid: policy->applied_real_app_uuid, NULL, FALSE); |
3671 | uuid_clear(uu: policy->applied_real_app_uuid); |
3672 | } |
3673 | if (!uuid_is_null(uu: policy->applied_result_uuid)) { |
3674 | necp_remove_uuid_service_id_mapping(uuid: policy->applied_result_uuid); |
3675 | uuid_clear(uu: policy->applied_result_uuid); |
3676 | } |
3677 | |
3678 | // Release string mappings |
3679 | if (policy->applied_account != NULL) { |
3680 | necp_remove_string_to_id_mapping(list: &necp_account_id_list, domain: policy->applied_account); |
3681 | kfree_data(policy->applied_account, policy->applied_account_size); |
3682 | policy->applied_account = NULL; |
3683 | } |
3684 | |
3685 | // Release route rule |
3686 | if (policy->applied_route_rules_id != 0) { |
3687 | necp_remove_route_rule(list: &necp_route_rules, route_rule_id: policy->applied_route_rules_id); |
3688 | policy->applied_route_rules_id = 0; |
3689 | } |
3690 | |
3691 | // Remove socket policies |
3692 | for (i = 0; i < MAX_KERNEL_SOCKET_POLICIES; i++) { |
3693 | if (policy->kernel_socket_policies[i] != 0) { |
3694 | necp_kernel_socket_policy_delete(policy_id: policy->kernel_socket_policies[i]); |
3695 | policy->kernel_socket_policies[i] = 0; |
3696 | } |
3697 | } |
3698 | |
3699 | // Remove IP output policies |
3700 | for (i = 0; i < MAX_KERNEL_IP_OUTPUT_POLICIES; i++) { |
3701 | if (policy->kernel_ip_output_policies[i] != 0) { |
3702 | necp_kernel_ip_output_policy_delete(policy_id: policy->kernel_ip_output_policies[i]); |
3703 | policy->kernel_ip_output_policies[i] = 0; |
3704 | } |
3705 | } |
3706 | |
3707 | policy->applied = FALSE; |
3708 | |
3709 | return TRUE; |
3710 | } |
3711 | |
3712 | #define NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION 0 |
3713 | #define NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION 1 |
3714 | #define NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION 2 |
3715 | #define NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS 3 |
3716 | struct necp_policy_result_ip_tunnel { |
3717 | u_int32_t secondary_result; |
3718 | char interface_name[IFXNAMSIZ]; |
3719 | } __attribute__((__packed__)); |
3720 | |
3721 | struct necp_policy_result_service { |
3722 | uuid_t identifier; |
3723 | u_int32_t data; |
3724 | } __attribute__((__packed__)); |
3725 | |
3726 | static bool |
3727 | necp_policy_apply(struct necp_session *session, struct necp_session_policy *policy) |
3728 | { |
3729 | bool socket_only_conditions = FALSE; |
3730 | bool socket_ip_conditions = FALSE; |
3731 | |
3732 | bool socket_layer_non_id_conditions = FALSE; |
3733 | bool ip_output_layer_non_id_conditions = FALSE; |
3734 | bool ip_output_layer_non_id_only = FALSE; |
3735 | bool ip_output_layer_id_condition = FALSE; |
3736 | bool ip_output_layer_tunnel_condition_from_id = FALSE; |
3737 | bool ip_output_layer_tunnel_condition_from_non_id = FALSE; |
3738 | necp_kernel_policy_id cond_ip_output_layer_id = NECP_KERNEL_POLICY_ID_NONE; |
3739 | |
3740 | u_int64_t master_condition_mask = 0; |
3741 | u_int64_t master_condition_negated_mask = 0; |
3742 | ifnet_t cond_bound_interface = NULL; |
3743 | u_int32_t cond_account_id = 0; |
3744 | char *cond_domain = NULL; |
3745 | u_int32_t cond_domain_filter = 0; |
3746 | char *cond_url = NULL; |
3747 | char *cond_custom_entitlement = NULL; |
3748 | char *cond_signing_identifier = NULL; |
3749 | pid_t cond_pid = 0; |
3750 | int32_t cond_pid_version = 0; |
3751 | uid_t cond_uid = 0; |
3752 | uid_t cond_real_uid = 0; |
3753 | necp_app_id cond_app_id = 0; |
3754 | necp_app_id cond_real_app_id = 0; |
3755 | struct necp_policy_condition_tc_range cond_traffic_class; |
3756 | cond_traffic_class.start_tc = 0; |
3757 | cond_traffic_class.end_tc = 0; |
3758 | u_int16_t cond_protocol = 0; |
3759 | union necp_sockaddr_union cond_local_start; |
3760 | union necp_sockaddr_union cond_local_end; |
3761 | u_int8_t cond_local_prefix = 0; |
3762 | union necp_sockaddr_union cond_remote_start; |
3763 | union necp_sockaddr_union cond_remote_end; |
3764 | u_int8_t cond_remote_prefix = 0; |
3765 | u_int32_t cond_client_flags = 0; |
3766 | u_int32_t offset = 0; |
3767 | u_int8_t ultimate_result = 0; |
3768 | u_int32_t secondary_result = 0; |
3769 | struct necp_policy_condition_agent_type cond_agent_type = {}; |
3770 | struct necp_policy_condition_sdk_version cond_sdk_version = {}; |
3771 | u_int16_t cond_packet_filter_tags = 0; |
3772 | u_int16_t cond_scheme_port = 0; |
3773 | u_int32_t cond_bound_interface_flags = 0; |
3774 | u_int32_t cond_bound_interface_eflags = 0; |
3775 | u_int32_t cond_bound_interface_xflags = 0; |
3776 | necp_kernel_policy_result_parameter secondary_result_parameter; |
3777 | memset(s: &secondary_result_parameter, c: 0, n: sizeof(secondary_result_parameter)); |
3778 | u_int32_t cond_last_interface_index = 0; |
3779 | necp_kernel_policy_result_parameter ultimate_result_parameter; |
3780 | memset(s: &ultimate_result_parameter, c: 0, n: sizeof(ultimate_result_parameter)); |
3781 | |
3782 | if (policy == NULL) { |
3783 | return FALSE; |
3784 | } |
3785 | |
3786 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
3787 | |
3788 | // Process conditions |
3789 | while (offset < policy->conditions_size) { |
3790 | u_int32_t length = 0; |
3791 | u_int8_t *value = necp_buffer_get_tlv_value(buffer: policy->conditions, tlv_offset: offset, value_size: &length); |
3792 | |
3793 | u_int8_t condition_type = necp_policy_condition_get_type_from_buffer(buffer: value, length); |
3794 | u_int8_t condition_flags = necp_policy_condition_get_flags_from_buffer(buffer: value, length); |
3795 | bool condition_is_negative = condition_flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE; |
3796 | u_int32_t condition_length = necp_policy_condition_get_value_length_from_buffer(buffer: value, length); |
3797 | u_int8_t *condition_value = necp_policy_condition_get_value_pointer_from_buffer(buffer: value, length); |
3798 | switch (condition_type) { |
3799 | case NECP_POLICY_CONDITION_DEFAULT: { |
3800 | socket_ip_conditions = TRUE; |
3801 | break; |
3802 | } |
3803 | case NECP_POLICY_CONDITION_ALL_INTERFACES: { |
3804 | master_condition_mask |= NECP_KERNEL_CONDITION_ALL_INTERFACES; |
3805 | socket_ip_conditions = TRUE; |
3806 | break; |
3807 | } |
3808 | case NECP_POLICY_CONDITION_HAS_CLIENT: { |
3809 | master_condition_mask |= NECP_KERNEL_CONDITION_HAS_CLIENT; |
3810 | socket_only_conditions = TRUE; |
3811 | break; |
3812 | } |
3813 | case NECP_POLICY_CONDITION_ENTITLEMENT: { |
3814 | if (condition_length > 0) { |
3815 | if (cond_custom_entitlement == NULL) { |
3816 | cond_custom_entitlement = necp_copy_string(string: (char *)condition_value, length: condition_length); |
3817 | if (cond_custom_entitlement != NULL) { |
3818 | master_condition_mask |= NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT; |
3819 | socket_only_conditions = TRUE; |
3820 | } |
3821 | } |
3822 | } else { |
3823 | master_condition_mask |= NECP_KERNEL_CONDITION_ENTITLEMENT; |
3824 | socket_only_conditions = TRUE; |
3825 | } |
3826 | break; |
3827 | } |
3828 | case NECP_POLICY_CONDITION_PLATFORM_BINARY: { |
3829 | master_condition_mask |= NECP_KERNEL_CONDITION_PLATFORM_BINARY; |
3830 | socket_only_conditions = TRUE; |
3831 | break; |
3832 | } |
3833 | case NECP_POLICY_CONDITION_SYSTEM_SIGNED_RESULT: { |
3834 | master_condition_mask |= NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT; |
3835 | socket_only_conditions = TRUE; |
3836 | break; |
3837 | } |
3838 | case NECP_POLICY_CONDITION_SDK_VERSION: { |
3839 | if (condition_length >= sizeof(cond_sdk_version)) { |
3840 | master_condition_mask |= NECP_KERNEL_CONDITION_SDK_VERSION; |
3841 | memcpy(dst: &cond_sdk_version, src: condition_value, n: sizeof(cond_sdk_version)); |
3842 | socket_only_conditions = TRUE; |
3843 | } |
3844 | break; |
3845 | } |
3846 | case NECP_POLICY_CONDITION_DOMAIN: { |
3847 | // Make sure there is only one such rule |
3848 | if (condition_length > 0 && cond_domain == NULL) { |
3849 | const bool condition_is_exact = condition_flags & NECP_POLICY_CONDITION_FLAGS_EXACT; |
3850 | |
3851 | u_int64_t mask_value = condition_is_exact ? NECP_KERNEL_CONDITION_EXACT_DOMAIN : NECP_KERNEL_CONDITION_DOMAIN; |
3852 | cond_domain = necp_create_trimmed_domain(string: (char *)condition_value, length: condition_length); |
3853 | if (cond_domain != NULL) { |
3854 | master_condition_mask |= mask_value; |
3855 | if (condition_is_negative) { |
3856 | master_condition_negated_mask |= mask_value; |
3857 | } |
3858 | socket_only_conditions = TRUE; |
3859 | } |
3860 | } |
3861 | break; |
3862 | } |
3863 | case NECP_POLICY_CONDITION_DOMAIN_FILTER: { |
3864 | // Make sure there is only one such rule |
3865 | if (condition_length >= sizeof(cond_domain_filter) && cond_domain_filter == 0) { |
3866 | memcpy(dst: &cond_domain_filter, src: condition_value, n: sizeof(cond_domain_filter)); |
3867 | if (cond_domain_filter != 0) { |
3868 | master_condition_mask |= NECP_KERNEL_CONDITION_DOMAIN_FILTER; |
3869 | if (condition_is_negative) { |
3870 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_DOMAIN_FILTER; |
3871 | } |
3872 | socket_only_conditions = TRUE; |
3873 | } |
3874 | } |
3875 | break; |
3876 | } |
3877 | case NECP_POLICY_CONDITION_URL: { |
3878 | // Make sure there is only one such rule |
3879 | if (condition_length > 0 && cond_url == NULL) { |
3880 | u_int64_t mask_value = NECP_KERNEL_CONDITION_URL; |
3881 | cond_url = necp_create_trimmed_domain(string: (char *)condition_value, length: condition_length); |
3882 | if (cond_url != NULL) { |
3883 | master_condition_mask |= mask_value; |
3884 | if (condition_is_negative) { |
3885 | master_condition_negated_mask |= mask_value; |
3886 | } |
3887 | socket_only_conditions = TRUE; |
3888 | } |
3889 | } |
3890 | break; |
3891 | } |
3892 | case NECP_POLICY_CONDITION_ACCOUNT: { |
3893 | // Make sure there is only one such rule |
3894 | if (condition_length > 0 && condition_length < UINT32_MAX && cond_account_id == 0 && policy->applied_account == NULL) { |
3895 | char *string = NULL; |
3896 | string = (char *)kalloc_data(condition_length + 1, Z_WAITOK); |
3897 | if (string != NULL) { |
3898 | memcpy(dst: string, src: condition_value, n: condition_length); |
3899 | string[condition_length] = 0; |
3900 | cond_account_id = necp_create_string_to_id_mapping(list: &necp_account_id_list, domain: string); |
3901 | if (cond_account_id != 0) { |
3902 | policy->applied_account = string; // Save the string in parent policy |
3903 | policy->applied_account_size = condition_length + 1; |
3904 | master_condition_mask |= NECP_KERNEL_CONDITION_ACCOUNT_ID; |
3905 | if (condition_is_negative) { |
3906 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_ACCOUNT_ID; |
3907 | } |
3908 | socket_only_conditions = TRUE; |
3909 | } else { |
3910 | kfree_data(string, condition_length + 1); |
3911 | } |
3912 | } |
3913 | } |
3914 | break; |
3915 | } |
3916 | case NECP_POLICY_CONDITION_APPLICATION: { |
3917 | // Make sure there is only one such rule, because we save the uuid in the policy |
3918 | if (condition_length >= sizeof(uuid_t) && cond_app_id == 0) { |
3919 | bool allocated_mapping = FALSE; |
3920 | uuid_t application_uuid; |
3921 | memcpy(dst: application_uuid, src: condition_value, n: sizeof(uuid_t)); |
3922 | cond_app_id = necp_create_uuid_app_id_mapping(uuid: application_uuid, allocated_mapping: &allocated_mapping, TRUE); |
3923 | if (cond_app_id != 0) { |
3924 | if (allocated_mapping) { |
3925 | necp_uuid_app_id_mappings_dirty = TRUE; |
3926 | necp_num_uuid_app_id_mappings++; |
3927 | } |
3928 | uuid_copy(dst: policy->applied_app_uuid, src: application_uuid); |
3929 | master_condition_mask |= NECP_KERNEL_CONDITION_APP_ID; |
3930 | if (condition_is_negative) { |
3931 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_APP_ID; |
3932 | } |
3933 | socket_only_conditions = TRUE; |
3934 | } |
3935 | } |
3936 | break; |
3937 | } |
3938 | case NECP_POLICY_CONDITION_REAL_APPLICATION: { |
3939 | // Make sure there is only one such rule, because we save the uuid in the policy |
3940 | if (condition_length >= sizeof(uuid_t) && cond_real_app_id == 0) { |
3941 | uuid_t real_application_uuid; |
3942 | memcpy(dst: real_application_uuid, src: condition_value, n: sizeof(uuid_t)); |
3943 | cond_real_app_id = necp_create_uuid_app_id_mapping(uuid: real_application_uuid, NULL, FALSE); |
3944 | if (cond_real_app_id != 0) { |
3945 | uuid_copy(dst: policy->applied_real_app_uuid, src: real_application_uuid); |
3946 | master_condition_mask |= NECP_KERNEL_CONDITION_REAL_APP_ID; |
3947 | if (condition_is_negative) { |
3948 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REAL_APP_ID; |
3949 | } |
3950 | socket_only_conditions = TRUE; |
3951 | } |
3952 | } |
3953 | break; |
3954 | } |
3955 | case NECP_POLICY_CONDITION_PID: { |
3956 | if (condition_length >= sizeof(pid_t)) { |
3957 | master_condition_mask |= NECP_KERNEL_CONDITION_PID; |
3958 | if (condition_is_negative) { |
3959 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_PID; |
3960 | } |
3961 | memcpy(dst: &cond_pid, src: condition_value, n: sizeof(cond_pid)); |
3962 | if (condition_length >= (sizeof(pid_t) + sizeof(cond_pid_version))) { |
3963 | memcpy(dst: &cond_pid_version, src: (condition_value + sizeof(pid_t)), n: sizeof(cond_pid_version)); |
3964 | } |
3965 | socket_only_conditions = TRUE; |
3966 | } |
3967 | break; |
3968 | } |
3969 | case NECP_POLICY_CONDITION_UID: { |
3970 | if (condition_length >= sizeof(uid_t)) { |
3971 | master_condition_mask |= NECP_KERNEL_CONDITION_UID; |
3972 | if (condition_is_negative) { |
3973 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_UID; |
3974 | } |
3975 | memcpy(dst: &cond_uid, src: condition_value, n: sizeof(cond_uid)); |
3976 | socket_only_conditions = TRUE; |
3977 | } |
3978 | break; |
3979 | } |
3980 | case NECP_POLICY_CONDITION_REAL_UID: { |
3981 | if (condition_length >= sizeof(uid_t)) { |
3982 | master_condition_mask |= NECP_KERNEL_CONDITION_REAL_UID; |
3983 | if (condition_is_negative) { |
3984 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REAL_UID; |
3985 | } |
3986 | memcpy(dst: &cond_real_uid, src: condition_value, n: sizeof(cond_real_uid)); |
3987 | socket_only_conditions = TRUE; |
3988 | } |
3989 | break; |
3990 | } |
3991 | case NECP_POLICY_CONDITION_TRAFFIC_CLASS: { |
3992 | if (condition_length >= sizeof(struct necp_policy_condition_tc_range)) { |
3993 | master_condition_mask |= NECP_KERNEL_CONDITION_TRAFFIC_CLASS; |
3994 | if (condition_is_negative) { |
3995 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_TRAFFIC_CLASS; |
3996 | } |
3997 | memcpy(dst: &cond_traffic_class, src: condition_value, n: sizeof(cond_traffic_class)); |
3998 | socket_only_conditions = TRUE; |
3999 | } |
4000 | break; |
4001 | } |
4002 | case NECP_POLICY_CONDITION_BOUND_INTERFACE: { |
4003 | if (condition_length <= IFXNAMSIZ && condition_length > 0) { |
4004 | char interface_name[IFXNAMSIZ]; |
4005 | memcpy(dst: interface_name, src: condition_value, n: condition_length); |
4006 | interface_name[condition_length - 1] = 0; // Make sure the string is NULL terminated |
4007 | if (ifnet_find_by_name(ifname: interface_name, interface: &cond_bound_interface) == 0) { |
4008 | master_condition_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
4009 | if (condition_is_negative) { |
4010 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
4011 | } |
4012 | } |
4013 | socket_ip_conditions = TRUE; |
4014 | } |
4015 | break; |
4016 | } |
4017 | case NECP_POLICY_CONDITION_IP_PROTOCOL: |
4018 | case NECP_POLICY_CONDITION_FLOW_IP_PROTOCOL: { |
4019 | if (condition_length >= sizeof(u_int16_t)) { |
4020 | master_condition_mask |= NECP_KERNEL_CONDITION_PROTOCOL; |
4021 | if (condition_is_negative) { |
4022 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_PROTOCOL; |
4023 | } |
4024 | memcpy(dst: &cond_protocol, src: condition_value, n: sizeof(cond_protocol)); |
4025 | if (condition_type == NECP_POLICY_CONDITION_FLOW_IP_PROTOCOL) { |
4026 | socket_only_conditions = TRUE; |
4027 | } else { |
4028 | socket_ip_conditions = TRUE; |
4029 | } |
4030 | } |
4031 | break; |
4032 | } |
4033 | case NECP_POLICY_CONDITION_LOCAL_NETWORKS: { |
4034 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_NETWORKS; |
4035 | socket_ip_conditions = TRUE; |
4036 | break; |
4037 | } |
4038 | case NECP_POLICY_CONDITION_LOCAL_ADDR: |
4039 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR: { |
4040 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value; |
4041 | if (!necp_address_is_valid(&address_struct->address.sa)) { |
4042 | break; |
4043 | } |
4044 | |
4045 | cond_local_prefix = address_struct->prefix; |
4046 | memcpy(dst: &cond_local_start, src: &address_struct->address, n: sizeof(address_struct->address)); |
4047 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
4048 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
4049 | if (condition_is_negative) { |
4050 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
4051 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
4052 | } |
4053 | if (condition_type == NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR) { |
4054 | socket_only_conditions = TRUE; |
4055 | } else { |
4056 | socket_ip_conditions = TRUE; |
4057 | } |
4058 | break; |
4059 | } |
4060 | case NECP_POLICY_CONDITION_REMOTE_ADDR: |
4061 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR: { |
4062 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value; |
4063 | if (!necp_address_is_valid(&address_struct->address.sa)) { |
4064 | break; |
4065 | } |
4066 | |
4067 | cond_remote_prefix = address_struct->prefix; |
4068 | memcpy(dst: &cond_remote_start, src: &address_struct->address, n: sizeof(address_struct->address)); |
4069 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
4070 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
4071 | if (condition_is_negative) { |
4072 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
4073 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
4074 | } |
4075 | if (condition_type == NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR) { |
4076 | socket_only_conditions = TRUE; |
4077 | } else { |
4078 | socket_ip_conditions = TRUE; |
4079 | } |
4080 | break; |
4081 | } |
4082 | case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: |
4083 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_RANGE: { |
4084 | struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value; |
4085 | if (!necp_address_is_valid(&address_struct->start_address.sa) || |
4086 | !necp_address_is_valid(&address_struct->end_address.sa)) { |
4087 | break; |
4088 | } |
4089 | |
4090 | memcpy(dst: &cond_local_start, src: &address_struct->start_address, n: sizeof(address_struct->start_address)); |
4091 | memcpy(dst: &cond_local_end, src: &address_struct->end_address, n: sizeof(address_struct->end_address)); |
4092 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
4093 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_END; |
4094 | if (condition_is_negative) { |
4095 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
4096 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_END; |
4097 | } |
4098 | if (condition_type == NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_RANGE) { |
4099 | socket_only_conditions = TRUE; |
4100 | } else { |
4101 | socket_ip_conditions = TRUE; |
4102 | } |
4103 | break; |
4104 | } |
4105 | case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: |
4106 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_RANGE: { |
4107 | struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value; |
4108 | if (!necp_address_is_valid(&address_struct->start_address.sa) || |
4109 | !necp_address_is_valid(&address_struct->end_address.sa)) { |
4110 | break; |
4111 | } |
4112 | |
4113 | memcpy(dst: &cond_remote_start, src: &address_struct->start_address, n: sizeof(address_struct->start_address)); |
4114 | memcpy(dst: &cond_remote_end, src: &address_struct->end_address, n: sizeof(address_struct->end_address)); |
4115 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
4116 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_END; |
4117 | if (condition_is_negative) { |
4118 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
4119 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_END; |
4120 | } |
4121 | if (condition_type == NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_RANGE) { |
4122 | socket_only_conditions = TRUE; |
4123 | } else { |
4124 | socket_ip_conditions = TRUE; |
4125 | } |
4126 | break; |
4127 | } |
4128 | case NECP_POLICY_CONDITION_AGENT_TYPE: { |
4129 | if (condition_length >= sizeof(cond_agent_type)) { |
4130 | master_condition_mask |= NECP_KERNEL_CONDITION_AGENT_TYPE; |
4131 | memcpy(dst: &cond_agent_type, src: condition_value, n: sizeof(cond_agent_type)); |
4132 | socket_only_conditions = TRUE; |
4133 | } |
4134 | break; |
4135 | } |
4136 | case NECP_POLICY_CONDITION_CLIENT_FLAGS: { |
4137 | if (condition_is_negative) { |
4138 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_CLIENT_FLAGS; |
4139 | } |
4140 | master_condition_mask |= NECP_KERNEL_CONDITION_CLIENT_FLAGS; |
4141 | socket_only_conditions = TRUE; |
4142 | if (condition_length >= sizeof(u_int32_t)) { |
4143 | memcpy(dst: &cond_client_flags, src: condition_value, n: sizeof(cond_client_flags)); |
4144 | } else { |
4145 | // Empty means match on fallback traffic |
4146 | cond_client_flags = NECP_CLIENT_PARAMETER_FLAG_FALLBACK_TRAFFIC; |
4147 | } |
4148 | break; |
4149 | } |
4150 | case NECP_POLICY_CONDITION_FLOW_LOCAL_ADDR_EMPTY: { |
4151 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_EMPTY; |
4152 | if (condition_is_negative) { |
4153 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_EMPTY; |
4154 | } |
4155 | socket_only_conditions = TRUE; |
4156 | break; |
4157 | } |
4158 | case NECP_POLICY_CONDITION_FLOW_REMOTE_ADDR_EMPTY: { |
4159 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_EMPTY; |
4160 | if (condition_is_negative) { |
4161 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_EMPTY; |
4162 | } |
4163 | socket_only_conditions = TRUE; |
4164 | break; |
4165 | } |
4166 | case NECP_POLICY_CONDITION_SCHEME_PORT: { |
4167 | master_condition_mask |= NECP_KERNEL_CONDITION_SCHEME_PORT; |
4168 | if (condition_is_negative) { |
4169 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_SCHEME_PORT; |
4170 | } |
4171 | memcpy(dst: &cond_scheme_port, src: condition_value, n: sizeof(cond_scheme_port)); |
4172 | socket_ip_conditions = TRUE; |
4173 | break; |
4174 | } |
4175 | case NECP_POLICY_CONDITION_SIGNING_IDENTIFIER: { |
4176 | if (condition_length > 0) { |
4177 | if (cond_signing_identifier == NULL) { |
4178 | cond_signing_identifier = necp_copy_string(string: (char *)condition_value, length: condition_length); |
4179 | if (cond_signing_identifier != NULL) { |
4180 | master_condition_mask |= NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER; |
4181 | socket_only_conditions = TRUE; |
4182 | if (condition_is_negative) { |
4183 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER; |
4184 | } |
4185 | } |
4186 | } |
4187 | } |
4188 | break; |
4189 | } |
4190 | case NECP_POLICY_CONDITION_PACKET_FILTER_TAGS: { |
4191 | if (condition_length >= sizeof(u_int16_t)) { |
4192 | master_condition_mask |= NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS; |
4193 | if (condition_is_negative) { |
4194 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS; |
4195 | } |
4196 | memcpy(dst: &cond_packet_filter_tags, src: condition_value, n: sizeof(cond_packet_filter_tags)); |
4197 | socket_ip_conditions = TRUE; |
4198 | } |
4199 | break; |
4200 | } |
4201 | case NECP_POLICY_CONDITION_FLOW_IS_LOOPBACK: { |
4202 | master_condition_mask |= NECP_KERNEL_CONDITION_IS_LOOPBACK; |
4203 | if (condition_is_negative) { |
4204 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_IS_LOOPBACK; |
4205 | } |
4206 | socket_only_conditions = TRUE; |
4207 | break; |
4208 | } |
4209 | case NECP_POLICY_CONDITION_DELEGATE_IS_PLATFORM_BINARY: { |
4210 | master_condition_mask |= NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY; |
4211 | if (condition_is_negative) { |
4212 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY; |
4213 | } |
4214 | socket_only_conditions = TRUE; |
4215 | break; |
4216 | } |
4217 | case NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS: { |
4218 | if (condition_length <= (sizeof(u_int32_t) * NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_MAX) && condition_length > 0) { |
4219 | u_int32_t flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_MAX] = {}; |
4220 | memcpy(dst: &flags, src: condition_value, n: sizeof(flags)); |
4221 | cond_bound_interface_flags = flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_FLAGS]; |
4222 | cond_bound_interface_eflags = flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_EFLAGS]; |
4223 | cond_bound_interface_xflags = flags[NECP_POLICY_CONDITION_BOUND_INTERFACE_FLAGS_IDX_XFLAGS]; |
4224 | master_condition_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS; |
4225 | if (condition_is_negative) { |
4226 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS; |
4227 | } |
4228 | socket_ip_conditions = TRUE; |
4229 | } |
4230 | break; |
4231 | } |
4232 | default: { |
4233 | break; |
4234 | } |
4235 | } |
4236 | |
4237 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
4238 | } |
4239 | |
4240 | // Process result |
4241 | ultimate_result = necp_policy_get_result_type(policy); |
4242 | switch (ultimate_result) { |
4243 | case NECP_POLICY_RESULT_PASS: { |
4244 | u_int32_t pass_flags = 0; |
4245 | if (necp_policy_result_get_parameter_length_from_buffer(buffer: policy->result, length: policy->result_size) > 0) { |
4246 | if (necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&pass_flags, parameter_buffer_length: sizeof(pass_flags))) { |
4247 | ultimate_result_parameter.pass_flags = pass_flags; |
4248 | } |
4249 | } |
4250 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
4251 | socket_layer_non_id_conditions = TRUE; |
4252 | ip_output_layer_id_condition = TRUE; |
4253 | } else if (socket_ip_conditions) { |
4254 | socket_layer_non_id_conditions = TRUE; |
4255 | ip_output_layer_id_condition = TRUE; |
4256 | ip_output_layer_non_id_conditions = TRUE; |
4257 | } |
4258 | break; |
4259 | } |
4260 | case NECP_POLICY_RESULT_DROP: { |
4261 | u_int32_t drop_flags = 0; |
4262 | if (necp_policy_result_get_parameter_length_from_buffer(buffer: policy->result, length: policy->result_size) > 0) { |
4263 | if (necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&drop_flags, parameter_buffer_length: sizeof(drop_flags))) { |
4264 | ultimate_result_parameter.drop_flags = drop_flags; |
4265 | } |
4266 | } |
4267 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
4268 | socket_layer_non_id_conditions = TRUE; |
4269 | } else if (socket_ip_conditions) { |
4270 | socket_layer_non_id_conditions = TRUE; |
4271 | ip_output_layer_non_id_conditions = TRUE; |
4272 | ip_output_layer_non_id_only = TRUE; // Only apply drop to packets that didn't go through socket layer |
4273 | } |
4274 | break; |
4275 | } |
4276 | case NECP_POLICY_RESULT_SKIP: { |
4277 | u_int32_t skip_policy_order = 0; |
4278 | if (necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&skip_policy_order, parameter_buffer_length: sizeof(skip_policy_order))) { |
4279 | ultimate_result_parameter.skip_policy_order = skip_policy_order; |
4280 | } |
4281 | |
4282 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
4283 | socket_layer_non_id_conditions = TRUE; |
4284 | ip_output_layer_id_condition = TRUE; |
4285 | } else if (socket_ip_conditions) { |
4286 | socket_layer_non_id_conditions = TRUE; |
4287 | ip_output_layer_non_id_conditions = TRUE; |
4288 | } |
4289 | break; |
4290 | } |
4291 | case NECP_POLICY_RESULT_SOCKET_DIVERT: |
4292 | case NECP_POLICY_RESULT_SOCKET_FILTER: { |
4293 | u_int32_t control_unit = 0; |
4294 | if (necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&control_unit, parameter_buffer_length: sizeof(control_unit))) { |
4295 | ultimate_result_parameter.flow_divert_control_unit = control_unit; |
4296 | } |
4297 | socket_layer_non_id_conditions = TRUE; |
4298 | break; |
4299 | } |
4300 | case NECP_POLICY_RESULT_IP_TUNNEL: { |
4301 | struct necp_policy_result_ip_tunnel tunnel_parameters; |
4302 | u_int32_t tunnel_parameters_length = necp_policy_get_result_parameter_length(policy); |
4303 | if (tunnel_parameters_length > sizeof(u_int32_t) && |
4304 | tunnel_parameters_length <= sizeof(struct necp_policy_result_ip_tunnel) && |
4305 | necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&tunnel_parameters, parameter_buffer_length: sizeof(tunnel_parameters))) { |
4306 | ifnet_t tunnel_interface = NULL; |
4307 | tunnel_parameters.interface_name[tunnel_parameters_length - sizeof(u_int32_t) - 1] = 0; // Make sure the string is NULL terminated |
4308 | if (ifnet_find_by_name(ifname: tunnel_parameters.interface_name, interface: &tunnel_interface) == 0) { |
4309 | ultimate_result_parameter.tunnel_interface_index = tunnel_interface->if_index; |
4310 | ifnet_release(interface: tunnel_interface); |
4311 | } |
4312 | |
4313 | secondary_result = tunnel_parameters.secondary_result; |
4314 | if (secondary_result) { |
4315 | cond_last_interface_index = ultimate_result_parameter.tunnel_interface_index; |
4316 | } |
4317 | } |
4318 | |
4319 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
4320 | socket_layer_non_id_conditions = TRUE; |
4321 | ip_output_layer_id_condition = TRUE; |
4322 | if (secondary_result) { |
4323 | ip_output_layer_tunnel_condition_from_id = TRUE; |
4324 | } |
4325 | } else if (socket_ip_conditions) { |
4326 | socket_layer_non_id_conditions = TRUE; |
4327 | ip_output_layer_id_condition = TRUE; |
4328 | ip_output_layer_non_id_conditions = TRUE; |
4329 | if (secondary_result) { |
4330 | ip_output_layer_tunnel_condition_from_id = TRUE; |
4331 | ip_output_layer_tunnel_condition_from_non_id = TRUE; |
4332 | } |
4333 | } |
4334 | break; |
4335 | } |
4336 | case NECP_POLICY_RESULT_USE_NETAGENT: |
4337 | case NECP_POLICY_RESULT_NETAGENT_SCOPED: |
4338 | case NECP_POLICY_RESULT_REMOVE_NETAGENT: { |
4339 | uuid_t netagent_uuid; |
4340 | if (necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)&netagent_uuid, parameter_buffer_length: sizeof(netagent_uuid))) { |
4341 | ultimate_result_parameter.netagent_id = necp_create_uuid_service_id_mapping(uuid: netagent_uuid); |
4342 | if (ultimate_result_parameter.netagent_id != 0) { |
4343 | uuid_copy(dst: policy->applied_result_uuid, src: netagent_uuid); |
4344 | socket_layer_non_id_conditions = TRUE; |
4345 | } |
4346 | } |
4347 | break; |
4348 | } |
4349 | case NECP_POLICY_RESULT_SOCKET_SCOPED: { |
4350 | u_int32_t interface_name_length = necp_policy_get_result_parameter_length(policy); |
4351 | if (interface_name_length <= IFXNAMSIZ && interface_name_length > 0) { |
4352 | char interface_name[IFXNAMSIZ]; |
4353 | ifnet_t scope_interface = NULL; |
4354 | necp_policy_get_result_parameter(policy, parameter_buffer: (u_int8_t *)interface_name, parameter_buffer_length: interface_name_length); |
4355 | interface_name[interface_name_length - 1] = 0; // Make sure the string is NULL terminated |
4356 | if (ifnet_find_by_name(ifname: interface_name, interface: &scope_interface) == 0) { |
4357 | ultimate_result_parameter.scoped_interface_index = scope_interface->if_index; |
4358 | socket_layer_non_id_conditions = TRUE; |
4359 | ifnet_release(interface: scope_interface); |
4360 | } |
4361 | } |
4362 | break; |
4363 | } |
4364 | case NECP_POLICY_RESULT_SCOPED_DIRECT: { |
4365 | socket_layer_non_id_conditions = TRUE; |
4366 | break; |
4367 | } |
4368 | case NECP_POLICY_RESULT_ALLOW_UNENTITLED: { |
4369 | socket_layer_non_id_conditions = TRUE; |
4370 | break; |
4371 | } |
4372 | case NECP_POLICY_RESULT_ROUTE_RULES: { |
4373 | if (policy->route_rules != NULL && policy->route_rules_size > 0) { |
4374 | bool has_socket_only_actions = FALSE; |
4375 | u_int32_t route_rule_id = necp_create_route_rule(list: &necp_route_rules, route_rules_array: policy->route_rules, route_rules_array_size: policy->route_rules_size, has_socket_only_actions: &has_socket_only_actions); |
4376 | if (route_rule_id > 0) { |
4377 | policy->applied_route_rules_id = route_rule_id; |
4378 | ultimate_result_parameter.route_rule_id = route_rule_id; |
4379 | if (socket_only_conditions || has_socket_only_actions) { // socket_ip_conditions can be TRUE or FALSE |
4380 | socket_layer_non_id_conditions = TRUE; |
4381 | } else if (socket_ip_conditions) { |
4382 | socket_layer_non_id_conditions = TRUE; |
4383 | ip_output_layer_non_id_conditions = TRUE; |
4384 | ip_output_layer_non_id_only = TRUE; // Only apply route rules to packets that didn't go through socket layer |
4385 | } |
4386 | } |
4387 | } |
4388 | break; |
4389 | } |
4390 | default: { |
4391 | break; |
4392 | } |
4393 | } |
4394 | |
4395 | if (socket_layer_non_id_conditions) { |
4396 | necp_kernel_policy_id policy_id = necp_kernel_socket_policy_add(order: policy->order, session_order: session->session_order, session_pid: session->proc_pid, condition_mask: master_condition_mask, condition_negated_mask: master_condition_negated_mask, cond_app_id, cond_real_app_id, cond_custom_entitlement, cond_account_id, cond_domain, cond_domain_filter, cond_url, cond_pid, cond_pidversion: cond_pid_version, cond_uid, cond_real_uid, cond_bound_interface, cond_traffic_class, cond_protocol, cond_local_start: &cond_local_start, cond_local_end: &cond_local_end, cond_local_prefix, cond_remote_start: &cond_remote_start, cond_remote_end: &cond_remote_end, cond_remote_prefix, cond_agent_type: &cond_agent_type, cond_sdk_version: &cond_sdk_version, cond_client_flags, cond_signing_identifier, cond_packet_filter_tags, cond_scheme_port, cond_bound_interface_flags, cond_bound_interface_eflags, cond_bound_interface_xflags, result: ultimate_result, result_parameter: ultimate_result_parameter); |
4397 | |
4398 | if (policy_id == 0) { |
4399 | NECPLOG0(LOG_DEBUG, "Error applying socket kernel policy" ); |
4400 | goto fail; |
4401 | } |
4402 | |
4403 | cond_ip_output_layer_id = policy_id; |
4404 | policy->kernel_socket_policies[0] = policy_id; |
4405 | } |
4406 | |
4407 | if (ip_output_layer_non_id_conditions) { |
4408 | u_int64_t condition_mask = master_condition_mask; |
4409 | if (ip_output_layer_non_id_only) { |
4410 | condition_mask |= NECP_KERNEL_CONDITION_POLICY_ID; |
4411 | } |
4412 | |
4413 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(order: policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS, session_order: session->session_order, session_pid: session->proc_pid, condition_mask, condition_negated_mask: master_condition_negated_mask, NECP_KERNEL_POLICY_ID_NONE, cond_bound_interface, cond_last_interface_index: 0, cond_protocol, cond_local_start: &cond_local_start, cond_local_end: &cond_local_end, cond_local_prefix, cond_remote_start: &cond_remote_start, cond_remote_end: &cond_remote_end, cond_remote_prefix, cond_packet_filter_tags, cond_scheme_port, cond_bound_interface_flags, cond_bound_interface_eflags, cond_bound_interface_xflags, result: ultimate_result, result_parameter: ultimate_result_parameter); |
4414 | |
4415 | if (policy_id == 0) { |
4416 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4417 | goto fail; |
4418 | } |
4419 | |
4420 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS] = policy_id; |
4421 | } |
4422 | |
4423 | if (ip_output_layer_id_condition) { |
4424 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(order: policy->order, NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION, session_order: session->session_order, session_pid: session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_ALL_INTERFACES, condition_negated_mask: 0, cond_policy_id: cond_ip_output_layer_id, NULL, cond_last_interface_index: 0, cond_protocol: 0, NULL, NULL, cond_local_prefix: 0, NULL, NULL, cond_remote_prefix: 0, cond_packet_filter_tags: 0, cond_scheme_port: 0, cond_bound_interface_flags, cond_bound_interface_eflags, cond_bound_interface_xflags, result: ultimate_result, result_parameter: ultimate_result_parameter); |
4425 | |
4426 | if (policy_id == 0) { |
4427 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4428 | goto fail; |
4429 | } |
4430 | |
4431 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION] = policy_id; |
4432 | } |
4433 | |
4434 | // Extra policies for IP Output tunnels for when packets loop back |
4435 | if (ip_output_layer_tunnel_condition_from_id) { |
4436 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(order: policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION, session_order: session->session_order, session_pid: session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE | NECP_KERNEL_CONDITION_ALL_INTERFACES, condition_negated_mask: 0, cond_policy_id: policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS], NULL, cond_last_interface_index, cond_protocol: 0, NULL, NULL, cond_local_prefix: 0, NULL, NULL, cond_remote_prefix: 0, cond_packet_filter_tags: 0, cond_scheme_port: 0, cond_bound_interface_flags, cond_bound_interface_eflags, cond_bound_interface_xflags, result: secondary_result, result_parameter: secondary_result_parameter); |
4437 | |
4438 | if (policy_id == 0) { |
4439 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4440 | goto fail; |
4441 | } |
4442 | |
4443 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION] = policy_id; |
4444 | } |
4445 | |
4446 | if (ip_output_layer_tunnel_condition_from_id) { |
4447 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(order: policy->order, NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION, session_order: session->session_order, session_pid: session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE | NECP_KERNEL_CONDITION_ALL_INTERFACES, condition_negated_mask: 0, cond_policy_id: policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION], NULL, cond_last_interface_index, cond_protocol: 0, NULL, NULL, cond_local_prefix: 0, NULL, NULL, cond_remote_prefix: 0, cond_packet_filter_tags: 0, cond_scheme_port: 0, cond_bound_interface_flags, cond_bound_interface_eflags, cond_bound_interface_xflags, result: secondary_result, result_parameter: secondary_result_parameter); |
4448 | |
4449 | if (policy_id == 0) { |
4450 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4451 | goto fail; |
4452 | } |
4453 | |
4454 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION] = policy_id; |
4455 | } |
4456 | |
4457 | policy->applied = TRUE; |
4458 | policy->pending_update = FALSE; |
4459 | return TRUE; |
4460 | |
4461 | fail: |
4462 | return FALSE; |
4463 | } |
4464 | |
4465 | static void |
4466 | necp_policy_apply_all(struct necp_session *session) |
4467 | { |
4468 | struct necp_session_policy *policy = NULL; |
4469 | struct necp_session_policy *temp_policy = NULL; |
4470 | struct kev_necp_policies_changed_data kev_data; |
4471 | kev_data.changed_count = 0; |
4472 | |
4473 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
4474 | |
4475 | // Remove exisiting applied policies |
4476 | if (session->dirty) { |
4477 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
4478 | if (policy->pending_deletion) { |
4479 | if (policy->applied) { |
4480 | necp_policy_unapply(policy); |
4481 | } |
4482 | // Delete the policy |
4483 | necp_policy_delete(session, policy); |
4484 | } else if (!policy->applied) { |
4485 | necp_policy_apply(session, policy); |
4486 | } else if (policy->pending_update) { |
4487 | // Must have been applied, but needs an update. Remove and re-add. |
4488 | necp_policy_unapply(policy); |
4489 | necp_policy_apply(session, policy); |
4490 | } |
4491 | } |
4492 | |
4493 | necp_kernel_socket_policies_update_uuid_table(); |
4494 | necp_kernel_socket_policies_reprocess(); |
4495 | necp_kernel_ip_output_policies_reprocess(); |
4496 | |
4497 | // Clear dirty bit flags |
4498 | session->dirty = FALSE; |
4499 | } |
4500 | |
4501 | lck_rw_done(lck: &necp_kernel_policy_lock); |
4502 | |
4503 | necp_update_all_clients(); |
4504 | necp_post_change_event(necp_event_data: &kev_data); |
4505 | |
4506 | if (necp_debug) { |
4507 | NECPLOG0(LOG_DEBUG, "Applied NECP policies" ); |
4508 | } |
4509 | } |
4510 | |
4511 | // Kernel Policy Management |
4512 | // --------------------- |
4513 | // Kernel policies are derived from session policies |
4514 | static necp_kernel_policy_id |
4515 | necp_kernel_policy_get_new_id(bool socket_level) |
4516 | { |
4517 | static necp_kernel_policy_id necp_last_kernel_socket_policy_id = 0; |
4518 | static necp_kernel_policy_id necp_last_kernel_ip_policy_id = 0; |
4519 | |
4520 | necp_kernel_policy_id newid = NECP_KERNEL_POLICY_ID_NONE; |
4521 | |
4522 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4523 | |
4524 | if (socket_level) { |
4525 | bool wrapped = FALSE; |
4526 | do { |
4527 | necp_last_kernel_socket_policy_id++; |
4528 | if (necp_last_kernel_socket_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET || |
4529 | necp_last_kernel_socket_policy_id >= NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) { |
4530 | if (wrapped) { |
4531 | // Already wrapped, give up |
4532 | NECPLOG0(LOG_ERR, "Failed to find a free socket kernel policy ID.\n" ); |
4533 | return NECP_KERNEL_POLICY_ID_NONE; |
4534 | } |
4535 | necp_last_kernel_socket_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET; |
4536 | wrapped = TRUE; |
4537 | } |
4538 | newid = necp_last_kernel_socket_policy_id; |
4539 | } while (necp_kernel_socket_policy_find(policy_id: newid) != NULL); // If already used, keep trying |
4540 | } else { |
4541 | bool wrapped = FALSE; |
4542 | do { |
4543 | necp_last_kernel_ip_policy_id++; |
4544 | if (necp_last_kernel_ip_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) { |
4545 | if (wrapped) { |
4546 | // Already wrapped, give up |
4547 | NECPLOG0(LOG_ERR, "Failed to find a free IP kernel policy ID.\n" ); |
4548 | return NECP_KERNEL_POLICY_ID_NONE; |
4549 | } |
4550 | necp_last_kernel_ip_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_IP; |
4551 | wrapped = TRUE; |
4552 | } |
4553 | newid = necp_last_kernel_ip_policy_id; |
4554 | } while (necp_kernel_ip_output_policy_find(policy_id: newid) != NULL); // If already used, keep trying |
4555 | } |
4556 | |
4557 | if (newid == NECP_KERNEL_POLICY_ID_NONE) { |
4558 | NECPLOG0(LOG_ERR, "Allocate kernel policy id failed.\n" ); |
4559 | return NECP_KERNEL_POLICY_ID_NONE; |
4560 | } |
4561 | |
4562 | return newid; |
4563 | } |
4564 | |
4565 | #define NECP_KERNEL_VALID_SOCKET_CONDITIONS (NECP_KERNEL_CONDITION_APP_ID | NECP_KERNEL_CONDITION_REAL_APP_ID | NECP_KERNEL_CONDITION_DOMAIN | NECP_KERNEL_CONDITION_ACCOUNT_ID | NECP_KERNEL_CONDITION_PID | NECP_KERNEL_CONDITION_UID | NECP_KERNEL_CONDITION_REAL_UID | NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_TRAFFIC_CLASS | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_ENTITLEMENT | NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT | NECP_KERNEL_CONDITION_AGENT_TYPE | NECP_KERNEL_CONDITION_HAS_CLIENT | NECP_KERNEL_CONDITION_LOCAL_NETWORKS | NECP_KERNEL_CONDITION_CLIENT_FLAGS | NECP_KERNEL_CONDITION_LOCAL_EMPTY | NECP_KERNEL_CONDITION_REMOTE_EMPTY | NECP_KERNEL_CONDITION_PLATFORM_BINARY | NECP_KERNEL_CONDITION_SDK_VERSION | NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER | NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS | NECP_KERNEL_CONDITION_IS_LOOPBACK | NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY | NECP_KERNEL_CONDITION_SCHEME_PORT | NECP_KERNEL_CONDITION_DOMAIN_FILTER | NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT | NECP_KERNEL_CONDITION_EXACT_DOMAIN | NECP_KERNEL_CONDITION_URL | NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) |
4566 | |
4567 | static necp_kernel_policy_id |
4568 | necp_kernel_socket_policy_add(necp_policy_order order, u_int32_t session_order, int session_pid, u_int64_t condition_mask, u_int64_t condition_negated_mask, necp_app_id cond_app_id, necp_app_id cond_real_app_id, char *cond_custom_entitlement, u_int32_t cond_account_id, char *cond_domain, u_int32_t cond_domain_filter, char *cond_url, pid_t cond_pid, int32_t cond_pid_version, uid_t cond_uid, uid_t cond_real_uid, ifnet_t cond_bound_interface, struct necp_policy_condition_tc_range cond_traffic_class, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, struct necp_policy_condition_agent_type *cond_agent_type, struct necp_policy_condition_sdk_version *cond_sdk_version, u_int32_t cond_client_flags, char *cond_signing_identifier, u_int16_t cond_packet_filter_tags, u_int16_t cond_scheme_port, u_int32_t cond_bound_interface_flags, u_int32_t cond_bound_interface_eflags, u_int32_t cond_bound_interface_xflags, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
4569 | { |
4570 | struct necp_kernel_socket_policy *new_kernel_policy = NULL; |
4571 | struct necp_kernel_socket_policy *tmp_kernel_policy = NULL; |
4572 | |
4573 | new_kernel_policy = zalloc_flags(necp_socket_policy_zone, Z_WAITOK | Z_ZERO); |
4574 | |
4575 | new_kernel_policy->id = necp_kernel_policy_get_new_id(true); |
4576 | new_kernel_policy->order = order; |
4577 | new_kernel_policy->session_order = session_order; |
4578 | new_kernel_policy->session_pid = session_pid; |
4579 | |
4580 | // Sanitize condition mask |
4581 | new_kernel_policy->condition_mask = (condition_mask & NECP_KERNEL_VALID_SOCKET_CONDITIONS); |
4582 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE)) { |
4583 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
4584 | } |
4585 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS)) { |
4586 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS; |
4587 | } |
4588 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) && !(new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID)) { |
4589 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REAL_APP_ID; |
4590 | } |
4591 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX)) { |
4592 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
4593 | } |
4594 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX)) { |
4595 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
4596 | } |
4597 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY) { |
4598 | new_kernel_policy->condition_mask &= ~(NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_LOCAL_END); |
4599 | } |
4600 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY)) { |
4601 | new_kernel_policy->condition_mask &= ~(NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_REMOTE_END); |
4602 | } |
4603 | new_kernel_policy->condition_negated_mask = condition_negated_mask & new_kernel_policy->condition_mask; |
4604 | |
4605 | // Set condition values |
4606 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
4607 | new_kernel_policy->cond_app_id = cond_app_id; |
4608 | } |
4609 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
4610 | new_kernel_policy->cond_real_app_id = cond_real_app_id; |
4611 | } |
4612 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
4613 | new_kernel_policy->cond_custom_entitlement = cond_custom_entitlement; |
4614 | } |
4615 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
4616 | new_kernel_policy->cond_account_id = cond_account_id; |
4617 | } |
4618 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN) || |
4619 | (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN)) { |
4620 | new_kernel_policy->cond_domain = cond_domain; |
4621 | new_kernel_policy->cond_domain_dot_count = necp_count_dots(string: cond_domain, length: strlen(s: cond_domain)); |
4622 | } |
4623 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER) { |
4624 | new_kernel_policy->cond_domain_filter = cond_domain_filter; |
4625 | } |
4626 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_URL) { |
4627 | new_kernel_policy->cond_url = cond_url; |
4628 | } |
4629 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PID) { |
4630 | new_kernel_policy->cond_pid = cond_pid; |
4631 | new_kernel_policy->cond_pid_version = cond_pid_version; |
4632 | } |
4633 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_UID) { |
4634 | new_kernel_policy->cond_uid = cond_uid; |
4635 | } |
4636 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
4637 | new_kernel_policy->cond_real_uid = cond_real_uid; |
4638 | } |
4639 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
4640 | if (cond_bound_interface) { |
4641 | ifnet_reference(interface: cond_bound_interface); |
4642 | } |
4643 | new_kernel_policy->cond_bound_interface = cond_bound_interface; |
4644 | } |
4645 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
4646 | new_kernel_policy->cond_traffic_class = cond_traffic_class; |
4647 | } |
4648 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
4649 | new_kernel_policy->cond_protocol = cond_protocol; |
4650 | } |
4651 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
4652 | memcpy(dst: &new_kernel_policy->cond_local_start, src: cond_local_start, n: cond_local_start->sa.sa_len); |
4653 | } |
4654 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
4655 | memcpy(dst: &new_kernel_policy->cond_local_end, src: cond_local_end, n: cond_local_end->sa.sa_len); |
4656 | } |
4657 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
4658 | new_kernel_policy->cond_local_prefix = cond_local_prefix; |
4659 | } |
4660 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
4661 | memcpy(dst: &new_kernel_policy->cond_remote_start, src: cond_remote_start, n: cond_remote_start->sa.sa_len); |
4662 | } |
4663 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
4664 | memcpy(dst: &new_kernel_policy->cond_remote_end, src: cond_remote_end, n: cond_remote_end->sa.sa_len); |
4665 | } |
4666 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
4667 | new_kernel_policy->cond_remote_prefix = cond_remote_prefix; |
4668 | } |
4669 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
4670 | memcpy(dst: &new_kernel_policy->cond_agent_type, src: cond_agent_type, n: sizeof(*cond_agent_type)); |
4671 | } |
4672 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SDK_VERSION) { |
4673 | memcpy(dst: &new_kernel_policy->cond_sdk_version, src: cond_sdk_version, n: sizeof(*cond_sdk_version)); |
4674 | } |
4675 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
4676 | new_kernel_policy->cond_client_flags = cond_client_flags; |
4677 | } |
4678 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER) { |
4679 | new_kernel_policy->cond_signing_identifier = cond_signing_identifier; |
4680 | } |
4681 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
4682 | new_kernel_policy->cond_packet_filter_tags = cond_packet_filter_tags; |
4683 | } |
4684 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
4685 | new_kernel_policy->cond_scheme_port = cond_scheme_port; |
4686 | } |
4687 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
4688 | new_kernel_policy->cond_bound_interface_flags = cond_bound_interface_flags; |
4689 | new_kernel_policy->cond_bound_interface_eflags = cond_bound_interface_eflags; |
4690 | new_kernel_policy->cond_bound_interface_xflags = cond_bound_interface_xflags; |
4691 | } |
4692 | |
4693 | new_kernel_policy->result = result; |
4694 | memcpy(dst: &new_kernel_policy->result_parameter, src: &result_parameter, n: sizeof(result_parameter)); |
4695 | |
4696 | if (necp_debug) { |
4697 | NECPLOG(LOG_DEBUG, "Added kernel policy: socket, id=%d, mask=%llx\n" , new_kernel_policy->id, new_kernel_policy->condition_mask); |
4698 | } |
4699 | LIST_INSERT_SORTED_TWICE_ASCENDING(&necp_kernel_socket_policies, new_kernel_policy, chain, session_order, order, tmp_kernel_policy); |
4700 | |
4701 | return new_kernel_policy ? new_kernel_policy->id : 0; |
4702 | } |
4703 | |
4704 | static struct necp_kernel_socket_policy * |
4705 | necp_kernel_socket_policy_find(necp_kernel_policy_id policy_id) |
4706 | { |
4707 | struct necp_kernel_socket_policy *kernel_policy = NULL; |
4708 | struct necp_kernel_socket_policy *tmp_kernel_policy = NULL; |
4709 | |
4710 | if (policy_id == 0) { |
4711 | return NULL; |
4712 | } |
4713 | |
4714 | LIST_FOREACH_SAFE(kernel_policy, &necp_kernel_socket_policies, chain, tmp_kernel_policy) { |
4715 | if (kernel_policy->id == policy_id) { |
4716 | return kernel_policy; |
4717 | } |
4718 | } |
4719 | |
4720 | return NULL; |
4721 | } |
4722 | |
4723 | static bool |
4724 | necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id) |
4725 | { |
4726 | struct necp_kernel_socket_policy *policy = NULL; |
4727 | |
4728 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4729 | |
4730 | policy = necp_kernel_socket_policy_find(policy_id); |
4731 | if (policy) { |
4732 | LIST_REMOVE(policy, chain); |
4733 | |
4734 | if (policy->cond_bound_interface) { |
4735 | ifnet_release(interface: policy->cond_bound_interface); |
4736 | policy->cond_bound_interface = NULL; |
4737 | } |
4738 | |
4739 | if (policy->cond_domain) { |
4740 | kfree_data_addr(policy->cond_domain); |
4741 | policy->cond_domain = NULL; |
4742 | } |
4743 | |
4744 | if (policy->cond_url) { |
4745 | kfree_data_addr(policy->cond_url); |
4746 | policy->cond_url = NULL; |
4747 | } |
4748 | |
4749 | if (policy->cond_custom_entitlement) { |
4750 | kfree_data_addr(policy->cond_custom_entitlement); |
4751 | policy->cond_custom_entitlement = NULL; |
4752 | } |
4753 | |
4754 | if (policy->cond_signing_identifier) { |
4755 | kfree_data_addr(policy->cond_signing_identifier); |
4756 | policy->cond_signing_identifier = NULL; |
4757 | } |
4758 | |
4759 | zfree(necp_socket_policy_zone, policy); |
4760 | return TRUE; |
4761 | } |
4762 | |
4763 | return FALSE; |
4764 | } |
4765 | |
4766 | static inline const char * |
4767 | necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
4768 | { |
4769 | uuid_string_t uuid_string; |
4770 | switch (result) { |
4771 | case NECP_KERNEL_POLICY_RESULT_NONE: { |
4772 | snprintf(result_string, MAX_RESULT_STRING_LEN, "None" ); |
4773 | break; |
4774 | } |
4775 | case NECP_KERNEL_POLICY_RESULT_PASS: { |
4776 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Pass (%X)" , result_parameter.pass_flags); |
4777 | break; |
4778 | } |
4779 | case NECP_KERNEL_POLICY_RESULT_SKIP: { |
4780 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Skip (%u)" , result_parameter.skip_policy_order); |
4781 | break; |
4782 | } |
4783 | case NECP_KERNEL_POLICY_RESULT_DROP: { |
4784 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Drop (%X)" , result_parameter.drop_flags); |
4785 | break; |
4786 | } |
4787 | case NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT: { |
4788 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketDivert (%d)" , result_parameter.flow_divert_control_unit); |
4789 | break; |
4790 | } |
4791 | case NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER: { |
4792 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketFilter (%d)" , result_parameter.filter_control_unit); |
4793 | break; |
4794 | } |
4795 | case NECP_KERNEL_POLICY_RESULT_IP_TUNNEL: { |
4796 | ifnet_t interface = ifindex2ifnet[result_parameter.tunnel_interface_index]; |
4797 | snprintf(result_string, MAX_RESULT_STRING_LEN, "IPTunnel (%s%d)" , ifnet_name(interface), ifnet_unit(interface)); |
4798 | break; |
4799 | } |
4800 | case NECP_KERNEL_POLICY_RESULT_IP_FILTER: { |
4801 | snprintf(result_string, MAX_RESULT_STRING_LEN, "IPFilter" ); |
4802 | break; |
4803 | } |
4804 | case NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED: { |
4805 | ifnet_t interface = ifindex2ifnet[result_parameter.scoped_interface_index]; |
4806 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketScoped (%s%d)" , ifnet_name(interface), ifnet_unit(interface)); |
4807 | break; |
4808 | } |
4809 | case NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT: { |
4810 | snprintf(result_string, MAX_RESULT_STRING_LEN, "ScopedDirect" ); |
4811 | break; |
4812 | } |
4813 | case NECP_KERNEL_POLICY_RESULT_ALLOW_UNENTITLED: { |
4814 | snprintf(result_string, MAX_RESULT_STRING_LEN, "AllowUnentitled" ); |
4815 | break; |
4816 | } |
4817 | case NECP_KERNEL_POLICY_RESULT_ROUTE_RULES: { |
4818 | int index = 0; |
4819 | char interface_names[MAX_ROUTE_RULE_INTERFACES][IFXNAMSIZ]; |
4820 | struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id: result_parameter.route_rule_id); |
4821 | if (route_rule != NULL) { |
4822 | for (index = 0; index < MAX_ROUTE_RULE_INTERFACES; index++) { |
4823 | if (route_rule->exception_if_indices[index] != 0) { |
4824 | ifnet_t interface = ifindex2ifnet[route_rule->exception_if_indices[index]]; |
4825 | snprintf(interface_names[index], IFXNAMSIZ, "%s%d" , ifnet_name(interface), ifnet_unit(interface)); |
4826 | } else { |
4827 | memset(s: interface_names[index], c: 0, IFXNAMSIZ); |
4828 | } |
4829 | } |
4830 | switch (route_rule->default_action) { |
4831 | case NECP_ROUTE_RULE_DENY_INTERFACE: |
4832 | case NECP_ROUTE_RULE_DENY_INTERFACE_WITH_TYPE: |
4833 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Only %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4834 | (route_rule->cellular_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Cell " : "" , |
4835 | (route_rule->wifi_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "WiFi " : "" , |
4836 | (route_rule->wired_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Wired " : "" , |
4837 | (route_rule->expensive_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Exp " : "" , |
4838 | (route_rule->constrained_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Constrained " : "" , |
4839 | (route_rule->companion_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Companion " : "" , |
4840 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[0] : "" , |
4841 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4842 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[1] : "" , |
4843 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4844 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[2] : "" , |
4845 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4846 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[3] : "" , |
4847 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4848 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[4] : "" , |
4849 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4850 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[5] : "" , |
4851 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4852 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[6] : "" , |
4853 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4854 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[7] : "" , |
4855 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4856 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[8] : "" , |
4857 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4858 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[9] : "" ); |
4859 | break; |
4860 | case NECP_ROUTE_RULE_ALLOW_INTERFACE: |
4861 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4862 | IS_NECP_ROUTE_RULE_DENY(route_rule->cellular_action) ? "!Cell " : "" , |
4863 | IS_NECP_ROUTE_RULE_DENY(route_rule->wifi_action) ? "!WiFi " : "" , |
4864 | IS_NECP_ROUTE_RULE_DENY(route_rule->wired_action) ? "!Wired " : "" , |
4865 | IS_NECP_ROUTE_RULE_DENY(route_rule->expensive_action) ? "!Exp " : "" , |
4866 | IS_NECP_ROUTE_RULE_DENY(route_rule->constrained_action) ? "!Constrained " : "" , |
4867 | IS_NECP_ROUTE_RULE_DENY(route_rule->companion_action) ? "!Companion " : "" , |
4868 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[0]) ? "!" : "" , |
4869 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[0]) ? interface_names[0] : "" , |
4870 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[1]) ? "!" : "" , |
4871 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[1]) ? interface_names[1] : "" , |
4872 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[2]) ? "!" : "" , |
4873 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[2]) ? interface_names[2] : "" , |
4874 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[3]) ? "!" : "" , |
4875 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[3]) ? interface_names[3] : "" , |
4876 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[4]) ? "!" : "" , |
4877 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[4]) ? interface_names[4] : "" , |
4878 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[5]) ? "!" : "" , |
4879 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[5]) ? interface_names[5] : "" , |
4880 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[6]) ? "!" : "" , |
4881 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[6]) ? interface_names[6] : "" , |
4882 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[7]) ? "!" : "" , |
4883 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[7]) ? interface_names[7] : "" , |
4884 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[8]) ? "!" : "" , |
4885 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[8]) ? interface_names[8] : "" , |
4886 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[9]) ? "!" : "" , |
4887 | IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[9]) ? interface_names[9] : "" ); |
4888 | break; |
4889 | case NECP_ROUTE_RULE_QOS_MARKING: |
4890 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (QoSMarking %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4891 | (route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Cell " : "" , |
4892 | (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING) ? "WiFi " : "" , |
4893 | (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Wired " : "" , |
4894 | (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Exp " : "" , |
4895 | (route_rule->constrained_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Constrained " : "" , |
4896 | (route_rule->companion_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Companion " : "" , |
4897 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[0] : "" , |
4898 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4899 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[1] : "" , |
4900 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4901 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[2] : "" , |
4902 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4903 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[3] : "" , |
4904 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4905 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[4] : "" , |
4906 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4907 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[5] : "" , |
4908 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4909 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[6] : "" , |
4910 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4911 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[7] : "" , |
4912 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4913 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[8] : "" , |
4914 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4915 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[9] : "" ); |
4916 | break; |
4917 | default: |
4918 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Unknown)" ); |
4919 | break; |
4920 | } |
4921 | } |
4922 | break; |
4923 | } |
4924 | case NECP_KERNEL_POLICY_RESULT_USE_NETAGENT: { |
4925 | bool found_mapping = FALSE; |
4926 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: result_parameter.netagent_id); |
4927 | if (mapping != NULL) { |
4928 | uuid_unparse(uu: mapping->uuid, out: uuid_string); |
4929 | found_mapping = TRUE; |
4930 | } |
4931 | snprintf(result_string, MAX_RESULT_STRING_LEN, "UseNetAgent (%s)" , found_mapping ? uuid_string : "Unknown" ); |
4932 | break; |
4933 | } |
4934 | case NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED: { |
4935 | bool found_mapping = FALSE; |
4936 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: result_parameter.netagent_id); |
4937 | if (mapping != NULL) { |
4938 | uuid_unparse(uu: mapping->uuid, out: uuid_string); |
4939 | found_mapping = TRUE; |
4940 | } |
4941 | snprintf(result_string, MAX_RESULT_STRING_LEN, "NetAgentScoped (%s)" , found_mapping ? uuid_string : "Unknown" ); |
4942 | break; |
4943 | } |
4944 | case NECP_KERNEL_POLICY_RESULT_REMOVE_NETAGENT: { |
4945 | bool found_mapping = FALSE; |
4946 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: result_parameter.netagent_id); |
4947 | if (mapping != NULL) { |
4948 | uuid_unparse(uu: mapping->uuid, out: uuid_string); |
4949 | found_mapping = TRUE; |
4950 | } |
4951 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RemoveNetAgent (%s)" , found_mapping ? uuid_string : "Unknown" ); |
4952 | break; |
4953 | } |
4954 | default: { |
4955 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Unknown %d (%d)" , result, result_parameter.tunnel_interface_index); |
4956 | break; |
4957 | } |
4958 | } |
4959 | return result_string; |
4960 | } |
4961 | |
4962 | static void |
4963 | necp_kernel_socket_policies_dump_all(void) |
4964 | { |
4965 | if (necp_debug) { |
4966 | struct necp_kernel_socket_policy *policy = NULL; |
4967 | int policy_i; |
4968 | int app_i; |
4969 | char result_string[MAX_RESULT_STRING_LEN]; |
4970 | char proc_name_string[MAXCOMLEN + 1]; |
4971 | memset(s: result_string, c: 0, MAX_RESULT_STRING_LEN); |
4972 | memset(s: proc_name_string, c: 0, MAXCOMLEN + 1); |
4973 | |
4974 | NECPLOG0(LOG_DEBUG, "NECP Application Policies:\n" ); |
4975 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4976 | for (policy_i = 0; necp_kernel_socket_policies_app_layer_map != NULL && necp_kernel_socket_policies_app_layer_map[policy_i] != NULL; policy_i++) { |
4977 | policy = necp_kernel_socket_policies_app_layer_map[policy_i]; |
4978 | proc_name(pid: policy->session_pid, buf: proc_name_string, MAXCOMLEN); |
4979 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d\tMask: %llx\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
4980 | } |
4981 | if (necp_kernel_socket_policies_app_layer_map[0] != NULL) { |
4982 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4983 | } |
4984 | |
4985 | NECPLOG0(LOG_DEBUG, "NECP Socket Policies:\n" ); |
4986 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4987 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4988 | NECPLOG(LOG_DEBUG, "\tApp Bucket: %d\n" , app_i); |
4989 | for (policy_i = 0; necp_kernel_socket_policies_map[app_i] != NULL && (necp_kernel_socket_policies_map[app_i])[policy_i] != NULL; policy_i++) { |
4990 | policy = (necp_kernel_socket_policies_map[app_i])[policy_i]; |
4991 | proc_name(pid: policy->session_pid, buf: proc_name_string, MAXCOMLEN); |
4992 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d\tMask: %llx\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
4993 | } |
4994 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4995 | } |
4996 | } |
4997 | } |
4998 | |
4999 | static inline bool |
5000 | necp_kernel_socket_policy_results_overlap(struct necp_kernel_socket_policy *upper_policy, struct necp_kernel_socket_policy *lower_policy) |
5001 | { |
5002 | if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_DROP) { |
5003 | // Drop always cancels out lower policies |
5004 | return TRUE; |
5005 | } else if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER || |
5006 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES || |
5007 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT || |
5008 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED || |
5009 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_ALLOW_UNENTITLED || |
5010 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_REMOVE_NETAGENT) { |
5011 | // Filters and route rules never cancel out lower policies |
5012 | return FALSE; |
5013 | } else if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5014 | if (upper_policy->session_order != lower_policy->session_order) { |
5015 | // A skip cannot override a policy of a different session |
5016 | return FALSE; |
5017 | } else { |
5018 | if (upper_policy->result_parameter.skip_policy_order == 0 || |
5019 | lower_policy->order >= upper_policy->result_parameter.skip_policy_order) { |
5020 | // This policy is beyond the skip |
5021 | return FALSE; |
5022 | } else { |
5023 | // This policy is inside the skip |
5024 | return TRUE; |
5025 | } |
5026 | } |
5027 | } |
5028 | |
5029 | // A hard pass, flow divert, tunnel, or scope will currently block out lower policies |
5030 | return TRUE; |
5031 | } |
5032 | |
5033 | static bool |
5034 | necp_kernel_socket_policy_is_unnecessary(struct necp_kernel_socket_policy *policy, struct necp_kernel_socket_policy **policy_array, int valid_indices) |
5035 | { |
5036 | bool can_skip = FALSE; |
5037 | u_int32_t highest_skip_session_order = 0; |
5038 | u_int32_t highest_skip_order = 0; |
5039 | int i; |
5040 | for (i = 0; i < valid_indices; i++) { |
5041 | struct necp_kernel_socket_policy *compared_policy = policy_array[i]; |
5042 | |
5043 | // For policies in a skip window, we can't mark conflicting policies as unnecessary |
5044 | if (can_skip) { |
5045 | if (highest_skip_session_order != compared_policy->session_order || |
5046 | (highest_skip_order != 0 && compared_policy->order >= highest_skip_order)) { |
5047 | // If we've moved on to the next session, or passed the skip window |
5048 | highest_skip_session_order = 0; |
5049 | highest_skip_order = 0; |
5050 | can_skip = FALSE; |
5051 | } else { |
5052 | // If this policy is also a skip, in can increase the skip window |
5053 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5054 | if (compared_policy->result_parameter.skip_policy_order > highest_skip_order) { |
5055 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
5056 | } |
5057 | } |
5058 | continue; |
5059 | } |
5060 | } |
5061 | |
5062 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5063 | // This policy is a skip. Set the skip window accordingly |
5064 | can_skip = TRUE; |
5065 | highest_skip_session_order = compared_policy->session_order; |
5066 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
5067 | } |
5068 | |
5069 | // The result of the compared policy must be able to block out this policy result |
5070 | if (!necp_kernel_socket_policy_results_overlap(upper_policy: compared_policy, lower_policy: policy)) { |
5071 | continue; |
5072 | } |
5073 | |
5074 | // If new policy matches All Interfaces, compared policy must also |
5075 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
5076 | continue; |
5077 | } |
5078 | |
5079 | // If new policy matches Local Networks, compared policy must also |
5080 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS)) { |
5081 | continue; |
5082 | } |
5083 | |
5084 | // Default makes lower policies unecessary always |
5085 | if (compared_policy->condition_mask == 0) { |
5086 | return TRUE; |
5087 | } |
5088 | |
5089 | // Compared must be more general than policy, and include only conditions within policy |
5090 | if ((policy->condition_mask & compared_policy->condition_mask) != compared_policy->condition_mask) { |
5091 | continue; |
5092 | } |
5093 | |
5094 | // Negative conditions must match for the overlapping conditions |
5095 | if ((policy->condition_negated_mask & compared_policy->condition_mask) != (compared_policy->condition_negated_mask & compared_policy->condition_mask)) { |
5096 | continue; |
5097 | } |
5098 | |
5099 | if ((compared_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN || |
5100 | compared_policy->condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN) && |
5101 | strcmp(s1: compared_policy->cond_domain, s2: policy->cond_domain) != 0) { |
5102 | continue; |
5103 | } |
5104 | |
5105 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER && |
5106 | compared_policy->cond_domain_filter != policy->cond_domain_filter) { |
5107 | continue; |
5108 | } |
5109 | |
5110 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_URL && |
5111 | strcmp(s1: compared_policy->cond_url, s2: policy->cond_url) != 0) { |
5112 | continue; |
5113 | } |
5114 | |
5115 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT && |
5116 | strcmp(s1: compared_policy->cond_custom_entitlement, s2: policy->cond_custom_entitlement) != 0) { |
5117 | continue; |
5118 | } |
5119 | |
5120 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && |
5121 | compared_policy->cond_account_id != policy->cond_account_id) { |
5122 | continue; |
5123 | } |
5124 | |
5125 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID && |
5126 | compared_policy->cond_policy_id != policy->cond_policy_id) { |
5127 | continue; |
5128 | } |
5129 | |
5130 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID && |
5131 | compared_policy->cond_app_id != policy->cond_app_id) { |
5132 | continue; |
5133 | } |
5134 | |
5135 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID && |
5136 | compared_policy->cond_real_app_id != policy->cond_real_app_id) { |
5137 | continue; |
5138 | } |
5139 | |
5140 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PID && |
5141 | (compared_policy->cond_pid != policy->cond_pid || compared_policy->cond_pid_version != policy->cond_pid_version)) { |
5142 | continue; |
5143 | } |
5144 | |
5145 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_UID && |
5146 | compared_policy->cond_uid != policy->cond_uid) { |
5147 | continue; |
5148 | } |
5149 | |
5150 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_UID && |
5151 | compared_policy->cond_real_uid != policy->cond_real_uid) { |
5152 | continue; |
5153 | } |
5154 | |
5155 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE && |
5156 | compared_policy->cond_bound_interface != policy->cond_bound_interface) { |
5157 | continue; |
5158 | } |
5159 | |
5160 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL && |
5161 | compared_policy->cond_protocol != policy->cond_protocol) { |
5162 | continue; |
5163 | } |
5164 | |
5165 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS && |
5166 | compared_policy->cond_client_flags != policy->cond_client_flags) { |
5167 | continue; |
5168 | } |
5169 | |
5170 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS && |
5171 | !(compared_policy->cond_traffic_class.start_tc <= policy->cond_traffic_class.start_tc && |
5172 | compared_policy->cond_traffic_class.end_tc >= policy->cond_traffic_class.end_tc)) { |
5173 | continue; |
5174 | } |
5175 | |
5176 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
5177 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
5178 | if (!necp_is_range_in_range(inner_range_start: (struct sockaddr *)&policy->cond_local_start, inner_range_end: (struct sockaddr *)&policy->cond_local_end, range_start: (struct sockaddr *)&compared_policy->cond_local_start, range_end: (struct sockaddr *)&compared_policy->cond_local_end)) { |
5179 | continue; |
5180 | } |
5181 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
5182 | if (compared_policy->cond_local_prefix > policy->cond_local_prefix || |
5183 | !necp_is_addr_in_subnet(addr: (struct sockaddr *)&policy->cond_local_start, subnet_addr: (struct sockaddr *)&compared_policy->cond_local_start, subnet_prefix: compared_policy->cond_local_prefix)) { |
5184 | continue; |
5185 | } |
5186 | } |
5187 | } |
5188 | |
5189 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
5190 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
5191 | if (!necp_is_range_in_range(inner_range_start: (struct sockaddr *)&policy->cond_remote_start, inner_range_end: (struct sockaddr *)&policy->cond_remote_end, range_start: (struct sockaddr *)&compared_policy->cond_remote_start, range_end: (struct sockaddr *)&compared_policy->cond_remote_end)) { |
5192 | continue; |
5193 | } |
5194 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
5195 | if (compared_policy->cond_remote_prefix > policy->cond_remote_prefix || |
5196 | !necp_is_addr_in_subnet(addr: (struct sockaddr *)&policy->cond_remote_start, subnet_addr: (struct sockaddr *)&compared_policy->cond_remote_start, subnet_prefix: compared_policy->cond_remote_prefix)) { |
5197 | continue; |
5198 | } |
5199 | } |
5200 | } |
5201 | |
5202 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE && |
5203 | memcmp(s1: &compared_policy->cond_agent_type, s2: &policy->cond_agent_type, n: sizeof(policy->cond_agent_type)) == 0) { |
5204 | continue; |
5205 | } |
5206 | |
5207 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_SDK_VERSION && |
5208 | memcmp(s1: &compared_policy->cond_sdk_version, s2: &policy->cond_sdk_version, n: sizeof(policy->cond_sdk_version)) == 0) { |
5209 | continue; |
5210 | } |
5211 | |
5212 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS && |
5213 | memcmp(s1: &compared_policy->cond_packet_filter_tags, s2: &policy->cond_packet_filter_tags, n: sizeof(policy->cond_packet_filter_tags)) == 0) { |
5214 | continue; |
5215 | } |
5216 | |
5217 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT && |
5218 | memcmp(s1: &compared_policy->cond_scheme_port, s2: &policy->cond_scheme_port, n: sizeof(policy->cond_scheme_port)) == 0) { |
5219 | continue; |
5220 | } |
5221 | |
5222 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS && |
5223 | (compared_policy->cond_bound_interface_flags != policy->cond_bound_interface_flags || |
5224 | compared_policy->cond_bound_interface_eflags != policy->cond_bound_interface_eflags || |
5225 | compared_policy->cond_bound_interface_xflags != policy->cond_bound_interface_xflags)) { |
5226 | continue; |
5227 | } |
5228 | |
5229 | return TRUE; |
5230 | } |
5231 | |
5232 | return FALSE; |
5233 | } |
5234 | |
5235 | static bool |
5236 | necp_kernel_socket_policies_reprocess(void) |
5237 | { |
5238 | int app_i; |
5239 | int bucket_current_free_index[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
5240 | int app_layer_current_free_index = 0; |
5241 | struct necp_kernel_socket_policy *kernel_policy = NULL; |
5242 | |
5243 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5244 | |
5245 | // Reset mask to 0 |
5246 | necp_kernel_application_policies_condition_mask = 0; |
5247 | necp_kernel_socket_policies_condition_mask = 0; |
5248 | necp_kernel_application_policies_count = 0; |
5249 | necp_kernel_socket_policies_count = 0; |
5250 | necp_kernel_socket_policies_non_app_count = 0; |
5251 | |
5252 | // Reset all maps to NULL |
5253 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
5254 | if (necp_kernel_socket_policies_map[app_i] != NULL) { |
5255 | kfree_type(struct necp_kernel_socket_policy *, |
5256 | necp_kernel_socket_policies_map_counts[app_i] + 1, |
5257 | necp_kernel_socket_policies_map[app_i]); |
5258 | necp_kernel_socket_policies_map[app_i] = NULL; |
5259 | } |
5260 | |
5261 | // Init counts |
5262 | necp_kernel_socket_policies_map_counts[app_i] = 0; |
5263 | } |
5264 | if (necp_kernel_socket_policies_app_layer_map != NULL) { |
5265 | kfree_type(struct necp_kernel_socket_policy *, |
5266 | necp_kernel_socket_policies_app_layer_map_count + 1, |
5267 | necp_kernel_socket_policies_app_layer_map); |
5268 | } |
5269 | necp_kernel_socket_policies_app_layer_map = NULL; |
5270 | necp_kernel_socket_policies_app_layer_map_count = 0; |
5271 | |
5272 | // Create masks and counts |
5273 | LIST_FOREACH(kernel_policy, &necp_kernel_socket_policies, chain) { |
5274 | // App layer mask/count |
5275 | necp_kernel_application_policies_condition_mask |= kernel_policy->condition_mask; |
5276 | necp_kernel_application_policies_count++; |
5277 | necp_kernel_socket_policies_app_layer_map_count++; |
5278 | |
5279 | if ((kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE)) { |
5280 | // Agent type conditions only apply to app layer |
5281 | continue; |
5282 | } |
5283 | |
5284 | // Update socket layer bucket mask/counts |
5285 | necp_kernel_socket_policies_condition_mask |= kernel_policy->condition_mask; |
5286 | necp_kernel_socket_policies_count++; |
5287 | |
5288 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) || |
5289 | kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
5290 | necp_kernel_socket_policies_non_app_count++; |
5291 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
5292 | necp_kernel_socket_policies_map_counts[app_i]++; |
5293 | } |
5294 | } else { |
5295 | necp_kernel_socket_policies_map_counts[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(kernel_policy->cond_app_id)]++; |
5296 | } |
5297 | } |
5298 | |
5299 | // Allocate maps |
5300 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
5301 | if (necp_kernel_socket_policies_map_counts[app_i] > 0) { |
5302 | // Allocate a NULL-terminated array of policy pointers for each bucket |
5303 | necp_kernel_socket_policies_map[app_i] = kalloc_type(struct necp_kernel_socket_policy *, |
5304 | necp_kernel_socket_policies_map_counts[app_i] + 1, Z_WAITOK | Z_ZERO); |
5305 | if (necp_kernel_socket_policies_map[app_i] == NULL) { |
5306 | goto fail; |
5307 | } |
5308 | } |
5309 | bucket_current_free_index[app_i] = 0; |
5310 | } |
5311 | necp_kernel_socket_policies_app_layer_map = kalloc_type(struct necp_kernel_socket_policy *, |
5312 | necp_kernel_socket_policies_app_layer_map_count + 1, Z_WAITOK | Z_ZERO); |
5313 | if (necp_kernel_socket_policies_app_layer_map == NULL) { |
5314 | goto fail; |
5315 | } |
5316 | |
5317 | // Fill out maps |
5318 | LIST_FOREACH(kernel_policy, &necp_kernel_socket_policies, chain) { |
5319 | // Add app layer policies |
5320 | if (!necp_dedup_policies || !necp_kernel_socket_policy_is_unnecessary(policy: kernel_policy, policy_array: necp_kernel_socket_policies_app_layer_map, valid_indices: app_layer_current_free_index)) { |
5321 | necp_kernel_socket_policies_app_layer_map[app_layer_current_free_index] = kernel_policy; |
5322 | app_layer_current_free_index++; |
5323 | necp_kernel_socket_policies_app_layer_map[app_layer_current_free_index] = NULL; |
5324 | } |
5325 | |
5326 | if ((kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE)) { |
5327 | // Agent type conditions only apply to app layer |
5328 | continue; |
5329 | } |
5330 | |
5331 | // Add socket policies |
5332 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) || |
5333 | kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
5334 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
5335 | if (!necp_dedup_policies || !necp_kernel_socket_policy_is_unnecessary(policy: kernel_policy, policy_array: necp_kernel_socket_policies_map[app_i], valid_indices: bucket_current_free_index[app_i])) { |
5336 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = kernel_policy; |
5337 | bucket_current_free_index[app_i]++; |
5338 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = NULL; |
5339 | } |
5340 | } |
5341 | } else { |
5342 | app_i = NECP_SOCKET_MAP_APP_ID_TO_BUCKET(kernel_policy->cond_app_id); |
5343 | if (!necp_dedup_policies || !necp_kernel_socket_policy_is_unnecessary(policy: kernel_policy, policy_array: necp_kernel_socket_policies_map[app_i], valid_indices: bucket_current_free_index[app_i])) { |
5344 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = kernel_policy; |
5345 | bucket_current_free_index[app_i]++; |
5346 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = NULL; |
5347 | } |
5348 | } |
5349 | } |
5350 | necp_kernel_socket_policies_dump_all(); |
5351 | BUMP_KERNEL_SOCKET_POLICIES_GENERATION_COUNT(); |
5352 | return TRUE; |
5353 | |
5354 | fail: |
5355 | // Free memory, reset masks to 0 |
5356 | necp_kernel_application_policies_condition_mask = 0; |
5357 | necp_kernel_socket_policies_condition_mask = 0; |
5358 | necp_kernel_application_policies_count = 0; |
5359 | necp_kernel_socket_policies_count = 0; |
5360 | necp_kernel_socket_policies_non_app_count = 0; |
5361 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
5362 | if (necp_kernel_socket_policies_map[app_i] != NULL) { |
5363 | kfree_type(struct necp_kernel_socket_policy *, |
5364 | necp_kernel_socket_policies_map_counts[app_i] + 1, |
5365 | necp_kernel_socket_policies_map[app_i]); |
5366 | necp_kernel_socket_policies_map[app_i] = NULL; |
5367 | } |
5368 | necp_kernel_socket_policies_map_counts[app_i] = 0; |
5369 | } |
5370 | if (necp_kernel_socket_policies_app_layer_map != NULL) { |
5371 | kfree_type(struct necp_kernel_socket_policy *, |
5372 | necp_kernel_socket_policies_app_layer_map_count + 1, |
5373 | necp_kernel_socket_policies_app_layer_map); |
5374 | necp_kernel_socket_policies_app_layer_map = NULL; |
5375 | } |
5376 | necp_kernel_socket_policies_app_layer_map_count = 0; |
5377 | return FALSE; |
5378 | } |
5379 | |
5380 | static u_int32_t |
5381 | necp_get_new_string_id(void) |
5382 | { |
5383 | static u_int32_t necp_last_string_id = 0; |
5384 | |
5385 | u_int32_t newid = 0; |
5386 | |
5387 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5388 | |
5389 | bool wrapped = FALSE; |
5390 | do { |
5391 | necp_last_string_id++; |
5392 | if (necp_last_string_id < 1) { |
5393 | if (wrapped) { |
5394 | // Already wrapped, give up |
5395 | NECPLOG0(LOG_ERR, "Failed to find a free app UUID.\n" ); |
5396 | return 0; |
5397 | } |
5398 | necp_last_string_id = 1; |
5399 | wrapped = TRUE; |
5400 | } |
5401 | newid = necp_last_string_id; |
5402 | } while (necp_lookup_string_with_id_locked(list: &necp_account_id_list, local_id: newid) != NULL); // If already used, keep trying |
5403 | |
5404 | if (newid == 0) { |
5405 | NECPLOG0(LOG_ERR, "Allocate string id failed.\n" ); |
5406 | return 0; |
5407 | } |
5408 | |
5409 | return newid; |
5410 | } |
5411 | |
5412 | static struct necp_string_id_mapping * |
5413 | necp_lookup_string_to_id_locked(struct necp_string_id_mapping_list *list, char *string) |
5414 | { |
5415 | struct necp_string_id_mapping *searchentry = NULL; |
5416 | struct necp_string_id_mapping *foundentry = NULL; |
5417 | |
5418 | LIST_FOREACH(searchentry, list, chain) { |
5419 | if (strcmp(s1: searchentry->string, s2: string) == 0) { |
5420 | foundentry = searchentry; |
5421 | break; |
5422 | } |
5423 | } |
5424 | |
5425 | return foundentry; |
5426 | } |
5427 | |
5428 | static struct necp_string_id_mapping * |
5429 | necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id) |
5430 | { |
5431 | struct necp_string_id_mapping *searchentry = NULL; |
5432 | struct necp_string_id_mapping *foundentry = NULL; |
5433 | |
5434 | LIST_FOREACH(searchentry, list, chain) { |
5435 | if (searchentry->id == local_id) { |
5436 | foundentry = searchentry; |
5437 | break; |
5438 | } |
5439 | } |
5440 | |
5441 | return foundentry; |
5442 | } |
5443 | |
5444 | static u_int32_t |
5445 | necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *string) |
5446 | { |
5447 | u_int32_t string_id = 0; |
5448 | struct necp_string_id_mapping *existing_mapping = NULL; |
5449 | |
5450 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5451 | |
5452 | existing_mapping = necp_lookup_string_to_id_locked(list, string); |
5453 | if (existing_mapping != NULL) { |
5454 | string_id = existing_mapping->id; |
5455 | os_ref_retain_locked(rc: &existing_mapping->refcount); |
5456 | } else { |
5457 | struct necp_string_id_mapping *new_mapping = NULL; |
5458 | new_mapping = kalloc_type(struct necp_string_id_mapping, |
5459 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
5460 | |
5461 | size_t length = strlen(s: string) + 1; |
5462 | new_mapping->string = (char *)kalloc_data(length, Z_WAITOK); |
5463 | if (new_mapping->string != NULL) { |
5464 | memcpy(dst: new_mapping->string, src: string, n: length); |
5465 | new_mapping->id = necp_get_new_string_id(); |
5466 | os_ref_init(&new_mapping->refcount, &necp_refgrp); |
5467 | LIST_INSERT_HEAD(list, new_mapping, chain); |
5468 | string_id = new_mapping->id; |
5469 | } else { |
5470 | kfree_type(struct necp_string_id_mapping, new_mapping); |
5471 | new_mapping = NULL; |
5472 | } |
5473 | } |
5474 | return string_id; |
5475 | } |
5476 | |
5477 | static bool |
5478 | necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *string) |
5479 | { |
5480 | struct necp_string_id_mapping *existing_mapping = NULL; |
5481 | |
5482 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5483 | |
5484 | existing_mapping = necp_lookup_string_to_id_locked(list, string); |
5485 | if (existing_mapping != NULL) { |
5486 | if (os_ref_release_locked(rc: &existing_mapping->refcount) == 0) { |
5487 | LIST_REMOVE(existing_mapping, chain); |
5488 | kfree_data_addr(existing_mapping->string); |
5489 | kfree_type(struct necp_string_id_mapping, existing_mapping); |
5490 | } |
5491 | return TRUE; |
5492 | } |
5493 | |
5494 | return FALSE; |
5495 | } |
5496 | |
5497 | static struct necp_domain_filter * |
5498 | necp_lookup_domain_filter(struct necp_domain_filter_list *list, u_int32_t filter_id) |
5499 | { |
5500 | struct necp_domain_filter *searchfilter = NULL; |
5501 | struct necp_domain_filter *foundfilter = NULL; |
5502 | |
5503 | LIST_FOREACH(searchfilter, list, chain) { |
5504 | if (searchfilter->id == filter_id) { |
5505 | foundfilter = searchfilter; |
5506 | break; |
5507 | } |
5508 | } |
5509 | |
5510 | return foundfilter; |
5511 | } |
5512 | |
5513 | static u_int32_t |
5514 | necp_get_new_domain_filter_id(void) |
5515 | { |
5516 | static u_int32_t necp_last_filter_id = 0; |
5517 | |
5518 | u_int32_t newid = 0; |
5519 | |
5520 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5521 | |
5522 | bool wrapped = FALSE; |
5523 | do { |
5524 | necp_last_filter_id++; |
5525 | if (necp_last_filter_id < 1) { |
5526 | if (wrapped) { |
5527 | // Already wrapped, give up |
5528 | NECPLOG0(LOG_ERR, "Failed to find a free filter ID.\n" ); |
5529 | return 0; |
5530 | } |
5531 | necp_last_filter_id = 1; |
5532 | wrapped = TRUE; |
5533 | } |
5534 | newid = necp_last_filter_id; |
5535 | } while (necp_lookup_domain_filter(list: &necp_global_domain_filter_list, filter_id: newid) != NULL); // If already used, keep trying |
5536 | |
5537 | if (newid == 0) { |
5538 | NECPLOG0(LOG_ERR, "Allocate filter id failed.\n" ); |
5539 | return 0; |
5540 | } |
5541 | |
5542 | return newid; |
5543 | } |
5544 | |
5545 | static u_int32_t |
5546 | necp_create_domain_filter(struct necp_domain_filter_list *list, struct necp_domain_filter_list *owner_list, struct net_bloom_filter *filter) |
5547 | { |
5548 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5549 | |
5550 | struct necp_domain_filter *new_filter = NULL; |
5551 | new_filter = kalloc_type(struct necp_domain_filter, |
5552 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
5553 | |
5554 | new_filter->filter = filter; |
5555 | new_filter->id = necp_get_new_domain_filter_id(); |
5556 | LIST_INSERT_HEAD(list, new_filter, chain); |
5557 | LIST_INSERT_HEAD(owner_list, new_filter, owner_chain); |
5558 | os_ref_init(&new_filter->refcount, &necp_refgrp); |
5559 | |
5560 | return new_filter->id; |
5561 | } |
5562 | |
5563 | static bool |
5564 | necp_remove_domain_filter(struct necp_domain_filter_list *list, __unused struct necp_domain_filter_list *owner_list, u_int32_t filter_id) |
5565 | { |
5566 | struct necp_domain_filter *existing_filter = NULL; |
5567 | |
5568 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5569 | |
5570 | existing_filter = necp_lookup_domain_filter(list, filter_id); |
5571 | if (existing_filter != NULL) { |
5572 | if (os_ref_release_locked(rc: &existing_filter->refcount) == 0) { |
5573 | LIST_REMOVE(existing_filter, chain); |
5574 | LIST_REMOVE(existing_filter, owner_chain); |
5575 | net_bloom_filter_destroy(filter: existing_filter->filter); |
5576 | kfree_type(struct necp_domain_filter, existing_filter); |
5577 | } |
5578 | return true; |
5579 | } |
5580 | |
5581 | return false; |
5582 | } |
5583 | |
5584 | #define NECP_FIRST_VALID_ROUTE_RULE_ID 1 |
5585 | #define NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID UINT16_MAX |
5586 | static u_int32_t |
5587 | necp_get_new_route_rule_id(bool aggregate) |
5588 | { |
5589 | static u_int32_t necp_last_route_rule_id = 0; |
5590 | static u_int32_t necp_last_aggregate_route_rule_id = 0; |
5591 | |
5592 | u_int32_t newid = 0; |
5593 | |
5594 | if (!aggregate) { |
5595 | // Main necp_kernel_policy_lock protects non-aggregate rule IDs |
5596 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5597 | |
5598 | bool wrapped = FALSE; |
5599 | do { |
5600 | necp_last_route_rule_id++; |
5601 | if (necp_last_route_rule_id < NECP_FIRST_VALID_ROUTE_RULE_ID || |
5602 | necp_last_route_rule_id >= NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID) { |
5603 | if (wrapped) { |
5604 | // Already wrapped, give up |
5605 | NECPLOG0(LOG_ERR, "Failed to find a free route rule id.\n" ); |
5606 | return 0; |
5607 | } |
5608 | necp_last_route_rule_id = NECP_FIRST_VALID_ROUTE_RULE_ID; |
5609 | wrapped = TRUE; |
5610 | } |
5611 | newid = necp_last_route_rule_id; |
5612 | } while (necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id: newid) != NULL); // If already used, keep trying |
5613 | } else { |
5614 | // necp_route_rule_lock protects aggregate rule IDs |
5615 | LCK_RW_ASSERT(&necp_route_rule_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5616 | |
5617 | bool wrapped = FALSE; |
5618 | do { |
5619 | necp_last_aggregate_route_rule_id++; |
5620 | if (necp_last_aggregate_route_rule_id < NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID) { |
5621 | if (wrapped) { |
5622 | // Already wrapped, give up |
5623 | NECPLOG0(LOG_ERR, "Failed to find a free aggregate route rule id.\n" ); |
5624 | return 0; |
5625 | } |
5626 | necp_last_aggregate_route_rule_id = NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID; |
5627 | wrapped = TRUE; |
5628 | } |
5629 | newid = necp_last_aggregate_route_rule_id; |
5630 | } while (necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id: newid) != NULL); // If already used, keep trying |
5631 | } |
5632 | |
5633 | if (newid == 0) { |
5634 | NECPLOG0(LOG_ERR, "Allocate route rule ID failed.\n" ); |
5635 | return 0; |
5636 | } |
5637 | |
5638 | return newid; |
5639 | } |
5640 | |
5641 | static struct necp_route_rule * |
5642 | necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id) |
5643 | { |
5644 | struct necp_route_rule *searchentry = NULL; |
5645 | struct necp_route_rule *foundentry = NULL; |
5646 | |
5647 | LIST_FOREACH(searchentry, list, chain) { |
5648 | if (searchentry->id == route_rule_id) { |
5649 | foundentry = searchentry; |
5650 | break; |
5651 | } |
5652 | } |
5653 | |
5654 | return foundentry; |
5655 | } |
5656 | |
5657 | static struct necp_route_rule * |
5658 | necp_lookup_route_rule_by_contents_locked(struct necp_route_rule_list *list, u_int8_t default_action, u_int8_t cellular_action, u_int8_t wifi_action, u_int8_t wired_action, u_int8_t expensive_action, u_int8_t constrained_action, u_int8_t companion_action, u_int32_t *if_indices, u_int8_t *if_actions, uuid_t netagent_uuid, uuid_t match_netagent_uuid, u_int32_t control_unit, u_int32_t effective_type) |
5659 | { |
5660 | struct necp_route_rule *searchentry = NULL; |
5661 | struct necp_route_rule *foundentry = NULL; |
5662 | |
5663 | LIST_FOREACH(searchentry, list, chain) { |
5664 | if (searchentry->default_action == default_action && |
5665 | searchentry->cellular_action == cellular_action && |
5666 | searchentry->wifi_action == wifi_action && |
5667 | searchentry->wired_action == wired_action && |
5668 | searchentry->expensive_action == expensive_action && |
5669 | searchentry->constrained_action == constrained_action && |
5670 | searchentry->companion_action == companion_action && |
5671 | searchentry->control_unit == control_unit && |
5672 | searchentry->effective_type == effective_type) { |
5673 | bool match_failed = FALSE; |
5674 | size_t index_a = 0; |
5675 | size_t index_b = 0; |
5676 | size_t count_a = 0; |
5677 | size_t count_b = 0; |
5678 | for (index_a = 0; index_a < MAX_ROUTE_RULE_INTERFACES; index_a++) { |
5679 | bool found_index = FALSE; |
5680 | if (searchentry->exception_if_indices[index_a] == 0) { |
5681 | break; |
5682 | } |
5683 | count_a++; |
5684 | for (index_b = 0; index_b < MAX_ROUTE_RULE_INTERFACES; index_b++) { |
5685 | if (if_indices[index_b] == 0) { |
5686 | break; |
5687 | } |
5688 | if (index_b >= count_b) { |
5689 | count_b = index_b + 1; |
5690 | } |
5691 | if (searchentry->exception_if_indices[index_a] == if_indices[index_b] && |
5692 | searchentry->exception_if_actions[index_a] == if_actions[index_b]) { |
5693 | found_index = TRUE; |
5694 | break; |
5695 | } |
5696 | } |
5697 | if (!found_index) { |
5698 | match_failed = TRUE; |
5699 | break; |
5700 | } |
5701 | } |
5702 | |
5703 | if (match_failed || count_a != count_b) { |
5704 | continue; |
5705 | } |
5706 | |
5707 | bool has_agent_a = !uuid_is_null(uu: netagent_uuid); |
5708 | bool has_agent_b = (searchentry->netagent_id != 0); |
5709 | if (has_agent_a != has_agent_b) { |
5710 | continue; |
5711 | } |
5712 | |
5713 | if (has_agent_a) { |
5714 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: searchentry->netagent_id); |
5715 | if (mapping == NULL) { |
5716 | // Bad mapping, doesn't match |
5717 | continue; |
5718 | } |
5719 | if (uuid_compare(uu1: mapping->uuid, uu2: netagent_uuid) != 0) { |
5720 | // UUIDs don't match |
5721 | continue; |
5722 | } |
5723 | } |
5724 | |
5725 | bool has_match_agent_a = !uuid_is_null(uu: match_netagent_uuid); |
5726 | bool has_match_agent_b = (searchentry->match_netagent_id != 0); |
5727 | if (has_match_agent_a != has_match_agent_b) { |
5728 | continue; |
5729 | } |
5730 | |
5731 | if (has_match_agent_a) { |
5732 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: searchentry->match_netagent_id); |
5733 | if (mapping == NULL) { |
5734 | // Bad mapping, doesn't match |
5735 | continue; |
5736 | } |
5737 | if (uuid_compare(uu1: mapping->uuid, uu2: match_netagent_uuid) != 0) { |
5738 | // UUIDs don't match |
5739 | continue; |
5740 | } |
5741 | } |
5742 | |
5743 | // Rules match! |
5744 | foundentry = searchentry; |
5745 | break; |
5746 | } |
5747 | } |
5748 | |
5749 | return foundentry; |
5750 | } |
5751 | |
5752 | static u_int32_t |
5753 | necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, |
5754 | bool *has_socket_only_actions) |
5755 | { |
5756 | size_t offset = 0; |
5757 | u_int32_t route_rule_id = 0; |
5758 | struct necp_route_rule *existing_rule = NULL; |
5759 | u_int8_t default_action = NECP_ROUTE_RULE_ALLOW_INTERFACE; |
5760 | u_int8_t cellular_action = NECP_ROUTE_RULE_NONE; |
5761 | u_int8_t wifi_action = NECP_ROUTE_RULE_NONE; |
5762 | u_int8_t wired_action = NECP_ROUTE_RULE_NONE; |
5763 | u_int8_t expensive_action = NECP_ROUTE_RULE_NONE; |
5764 | u_int8_t constrained_action = NECP_ROUTE_RULE_NONE; |
5765 | u_int8_t companion_action = NECP_ROUTE_RULE_NONE; |
5766 | u_int32_t if_indices[MAX_ROUTE_RULE_INTERFACES]; |
5767 | size_t num_valid_indices = 0; |
5768 | memset(s: &if_indices, c: 0, n: sizeof(if_indices)); |
5769 | u_int8_t if_actions[MAX_ROUTE_RULE_INTERFACES]; |
5770 | memset(s: &if_actions, c: 0, n: sizeof(if_actions)); |
5771 | |
5772 | uuid_t netagent_uuid = {}; |
5773 | |
5774 | uuid_t match_netagent_uuid = {}; |
5775 | uint32_t control_unit = 0; |
5776 | uint32_t effective_type = 0; |
5777 | |
5778 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5779 | |
5780 | if (route_rules_array == NULL || route_rules_array_size == 0 || has_socket_only_actions == NULL) { |
5781 | return 0; |
5782 | } |
5783 | |
5784 | // Process rules |
5785 | while ((offset + sizeof(u_int8_t) + sizeof(u_int32_t)) < route_rules_array_size) { |
5786 | ifnet_t rule_interface = NULL; |
5787 | char interface_name[IFXNAMSIZ]; |
5788 | u_int32_t length = 0; |
5789 | u_int8_t *value = necp_buffer_get_tlv_value(buffer: route_rules_array, tlv_offset: offset, value_size: &length); |
5790 | |
5791 | if (offset + sizeof(u_int8_t) + sizeof(u_int32_t) + length > route_rules_array_size) { |
5792 | // Invalid TLV goes beyond end of the rules array |
5793 | break; |
5794 | } |
5795 | |
5796 | // Increment offset for the next time through the loop |
5797 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
5798 | |
5799 | u_int8_t rule_action = necp_policy_condition_get_type_from_buffer(buffer: value, length); |
5800 | u_int8_t rule_flags = necp_policy_condition_get_flags_from_buffer(buffer: value, length); |
5801 | u_int32_t rule_length = necp_policy_condition_get_value_length_from_buffer(buffer: value, length); |
5802 | u_int8_t *rule_value = necp_policy_condition_get_value_pointer_from_buffer(buffer: value, length); |
5803 | |
5804 | if (rule_action == NECP_ROUTE_RULE_NONE) { |
5805 | // Don't allow an explicit rule to be None action |
5806 | continue; |
5807 | } |
5808 | |
5809 | if (rule_action == NECP_ROUTE_RULE_USE_NETAGENT || |
5810 | rule_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
5811 | if (rule_length < sizeof(uuid_t)) { |
5812 | // Too short, skip |
5813 | continue; |
5814 | } |
5815 | |
5816 | if (!uuid_is_null(uu: netagent_uuid)) { |
5817 | if (uuid_compare(uu1: netagent_uuid, uu2: rule_value) != 0) { |
5818 | // UUIDs don't match, skip |
5819 | continue; |
5820 | } |
5821 | } else { |
5822 | // Copy out agent UUID |
5823 | memcpy(dst: netagent_uuid, src: rule_value, n: sizeof(netagent_uuid)); |
5824 | } |
5825 | |
5826 | // Adjust remaining length |
5827 | rule_value += sizeof(netagent_uuid); |
5828 | rule_length -= sizeof(netagent_uuid); |
5829 | *has_socket_only_actions = true; |
5830 | } else if (rule_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
5831 | if (rule_length < sizeof(control_unit)) { |
5832 | // Too short, skip |
5833 | continue; |
5834 | } |
5835 | |
5836 | memcpy(dst: &control_unit, src: rule_value, n: sizeof(control_unit)); |
5837 | |
5838 | // Adjust remaining length |
5839 | rule_value += sizeof(control_unit); |
5840 | rule_length -= sizeof(control_unit); |
5841 | *has_socket_only_actions = true; |
5842 | } else if (rule_action == NECP_ROUTE_RULE_DENY_INTERFACE_WITH_TYPE) { |
5843 | if (rule_length < sizeof(effective_type)) { |
5844 | // Too short, skip |
5845 | continue; |
5846 | } |
5847 | |
5848 | memcpy(dst: &effective_type, src: rule_value, n: sizeof(effective_type)); |
5849 | |
5850 | // Adjust remaining length |
5851 | rule_value += sizeof(effective_type); |
5852 | rule_length -= sizeof(effective_type); |
5853 | } |
5854 | |
5855 | if (rule_length == 0) { |
5856 | if (rule_flags & NECP_ROUTE_RULE_FLAG_CELLULAR) { |
5857 | cellular_action = rule_action; |
5858 | } |
5859 | if (rule_flags & NECP_ROUTE_RULE_FLAG_WIFI) { |
5860 | wifi_action = rule_action; |
5861 | } |
5862 | if (rule_flags & NECP_ROUTE_RULE_FLAG_WIRED) { |
5863 | wired_action = rule_action; |
5864 | } |
5865 | if (rule_flags & NECP_ROUTE_RULE_FLAG_EXPENSIVE) { |
5866 | expensive_action = rule_action; |
5867 | } |
5868 | if (rule_flags & NECP_ROUTE_RULE_FLAG_CONSTRAINED) { |
5869 | constrained_action = rule_action; |
5870 | } |
5871 | if (rule_flags & NECP_ROUTE_RULE_FLAG_COMPANION) { |
5872 | companion_action = rule_action; |
5873 | } |
5874 | if (rule_flags == 0) { |
5875 | default_action = rule_action; |
5876 | } |
5877 | continue; |
5878 | } else if (rule_flags & NECP_ROUTE_RULE_FLAG_NETAGENT) { |
5879 | if (rule_length == sizeof(uuid_t)) { |
5880 | memcpy(dst: match_netagent_uuid, src: rule_value, n: sizeof(match_netagent_uuid)); |
5881 | default_action = rule_action; |
5882 | } |
5883 | continue; |
5884 | } |
5885 | |
5886 | if (num_valid_indices >= MAX_ROUTE_RULE_INTERFACES) { |
5887 | continue; |
5888 | } |
5889 | |
5890 | if (rule_length <= IFXNAMSIZ) { |
5891 | memcpy(dst: interface_name, src: rule_value, n: rule_length); |
5892 | interface_name[rule_length - 1] = 0; // Make sure the string is NULL terminated |
5893 | if (ifnet_find_by_name(ifname: interface_name, interface: &rule_interface) == 0) { |
5894 | if_actions[num_valid_indices] = rule_action; |
5895 | if_indices[num_valid_indices++] = rule_interface->if_index; |
5896 | ifnet_release(interface: rule_interface); |
5897 | } |
5898 | } |
5899 | } |
5900 | |
5901 | existing_rule = necp_lookup_route_rule_by_contents_locked(list, default_action, cellular_action, wifi_action, wired_action, expensive_action, constrained_action, companion_action, if_indices, if_actions, netagent_uuid, match_netagent_uuid, control_unit, effective_type); |
5902 | if (existing_rule != NULL) { |
5903 | route_rule_id = existing_rule->id; |
5904 | os_ref_retain_locked(rc: &existing_rule->refcount); |
5905 | } else { |
5906 | struct necp_route_rule *new_rule = NULL; |
5907 | new_rule = kalloc_type(struct necp_route_rule, |
5908 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
5909 | route_rule_id = new_rule->id = necp_get_new_route_rule_id(false); |
5910 | if (!uuid_is_null(uu: netagent_uuid)) { |
5911 | new_rule->netagent_id = necp_create_uuid_service_id_mapping(uuid: netagent_uuid); |
5912 | } |
5913 | if (!uuid_is_null(uu: match_netagent_uuid)) { |
5914 | new_rule->match_netagent_id = necp_create_uuid_service_id_mapping(uuid: match_netagent_uuid); |
5915 | } |
5916 | new_rule->effective_type = effective_type; |
5917 | new_rule->control_unit = control_unit; |
5918 | new_rule->default_action = default_action; |
5919 | new_rule->cellular_action = cellular_action; |
5920 | new_rule->wifi_action = wifi_action; |
5921 | new_rule->wired_action = wired_action; |
5922 | new_rule->expensive_action = expensive_action; |
5923 | new_rule->constrained_action = constrained_action; |
5924 | new_rule->companion_action = companion_action; |
5925 | memcpy(dst: &new_rule->exception_if_indices, src: &if_indices, n: sizeof(if_indices)); |
5926 | memcpy(dst: &new_rule->exception_if_actions, src: &if_actions, n: sizeof(if_actions)); |
5927 | os_ref_init(&new_rule->refcount, &necp_refgrp); |
5928 | LIST_INSERT_HEAD(list, new_rule, chain); |
5929 | } |
5930 | return route_rule_id; |
5931 | } |
5932 | |
5933 | static void |
5934 | necp_remove_aggregate_route_rule_for_id(u_int32_t rule_id) |
5935 | { |
5936 | if (rule_id) { |
5937 | lck_rw_lock_exclusive(lck: &necp_route_rule_lock); |
5938 | |
5939 | struct necp_aggregate_route_rule *existing_rule = NULL; |
5940 | struct necp_aggregate_route_rule *tmp_rule = NULL; |
5941 | |
5942 | LIST_FOREACH_SAFE(existing_rule, &necp_aggregate_route_rules, chain, tmp_rule) { |
5943 | int index = 0; |
5944 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
5945 | u_int32_t route_rule_id = existing_rule->rule_ids[index]; |
5946 | if (route_rule_id == rule_id) { |
5947 | LIST_REMOVE(existing_rule, chain); |
5948 | kfree_type(struct necp_aggregate_route_rule, existing_rule); |
5949 | break; |
5950 | } |
5951 | } |
5952 | } |
5953 | |
5954 | lck_rw_done(lck: &necp_route_rule_lock); |
5955 | } |
5956 | } |
5957 | |
5958 | static bool |
5959 | necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id) |
5960 | { |
5961 | struct necp_route_rule *existing_rule = NULL; |
5962 | |
5963 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5964 | |
5965 | existing_rule = necp_lookup_route_rule_locked(list, route_rule_id); |
5966 | if (existing_rule != NULL) { |
5967 | if (os_ref_release_locked(rc: &existing_rule->refcount) == 0) { |
5968 | necp_remove_aggregate_route_rule_for_id(rule_id: existing_rule->id); |
5969 | necp_remove_uuid_service_id_mapping_with_service_id(service_id: existing_rule->netagent_id); |
5970 | necp_remove_uuid_service_id_mapping_with_service_id(service_id: existing_rule->match_netagent_id); |
5971 | LIST_REMOVE(existing_rule, chain); |
5972 | kfree_type(struct necp_route_rule, existing_rule); |
5973 | } |
5974 | return TRUE; |
5975 | } |
5976 | |
5977 | return FALSE; |
5978 | } |
5979 | |
5980 | static struct necp_aggregate_route_rule * |
5981 | necp_lookup_aggregate_route_rule_locked(u_int32_t route_rule_id) |
5982 | { |
5983 | struct necp_aggregate_route_rule *searchentry = NULL; |
5984 | struct necp_aggregate_route_rule *foundentry = NULL; |
5985 | |
5986 | lck_rw_lock_shared(lck: &necp_route_rule_lock); |
5987 | |
5988 | LIST_FOREACH(searchentry, &necp_aggregate_route_rules, chain) { |
5989 | if (searchentry->id == route_rule_id) { |
5990 | foundentry = searchentry; |
5991 | break; |
5992 | } |
5993 | } |
5994 | |
5995 | lck_rw_done(lck: &necp_route_rule_lock); |
5996 | |
5997 | return foundentry; |
5998 | } |
5999 | |
6000 | static u_int32_t |
6001 | necp_create_aggregate_route_rule(u_int32_t *rule_ids) |
6002 | { |
6003 | u_int32_t aggregate_route_rule_id = 0; |
6004 | struct necp_aggregate_route_rule *new_rule = NULL; |
6005 | struct necp_aggregate_route_rule *existing_rule = NULL; |
6006 | |
6007 | lck_rw_lock_exclusive(lck: &necp_route_rule_lock); |
6008 | |
6009 | // Check if the rule already exists |
6010 | LIST_FOREACH(existing_rule, &necp_aggregate_route_rules, chain) { |
6011 | if (memcmp(s1: existing_rule->rule_ids, s2: rule_ids, n: (sizeof(u_int32_t) * MAX_AGGREGATE_ROUTE_RULES)) == 0) { |
6012 | lck_rw_done(lck: &necp_route_rule_lock); |
6013 | return existing_rule->id; |
6014 | } |
6015 | } |
6016 | |
6017 | new_rule = kalloc_type(struct necp_aggregate_route_rule, |
6018 | Z_WAITOK | Z_ZERO | Z_NOFAIL); |
6019 | aggregate_route_rule_id = new_rule->id = necp_get_new_route_rule_id(true); |
6020 | new_rule->id = aggregate_route_rule_id; |
6021 | memcpy(dst: new_rule->rule_ids, src: rule_ids, n: (sizeof(u_int32_t) * MAX_AGGREGATE_ROUTE_RULES)); |
6022 | LIST_INSERT_HEAD(&necp_aggregate_route_rules, new_rule, chain); |
6023 | lck_rw_done(lck: &necp_route_rule_lock); |
6024 | |
6025 | return aggregate_route_rule_id; |
6026 | } |
6027 | |
6028 | #define NECP_NULL_SERVICE_ID 1 |
6029 | #define NECP_FIRST_VALID_SERVICE_ID 2 |
6030 | #define NECP_FIRST_VALID_APP_ID UINT16_MAX |
6031 | static u_int32_t |
6032 | necp_get_new_uuid_id(bool service) |
6033 | { |
6034 | static u_int32_t necp_last_service_uuid_id = 0; |
6035 | static u_int32_t necp_last_app_uuid_id = 0; |
6036 | |
6037 | u_int32_t newid = 0; |
6038 | |
6039 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6040 | |
6041 | if (service) { |
6042 | bool wrapped = FALSE; |
6043 | do { |
6044 | necp_last_service_uuid_id++; |
6045 | if (necp_last_service_uuid_id < NECP_FIRST_VALID_SERVICE_ID || |
6046 | necp_last_service_uuid_id >= NECP_FIRST_VALID_APP_ID) { |
6047 | if (wrapped) { |
6048 | // Already wrapped, give up |
6049 | NECPLOG0(LOG_ERR, "Failed to find a free service UUID.\n" ); |
6050 | return NECP_NULL_SERVICE_ID; |
6051 | } |
6052 | necp_last_service_uuid_id = NECP_FIRST_VALID_SERVICE_ID; |
6053 | wrapped = TRUE; |
6054 | } |
6055 | newid = necp_last_service_uuid_id; |
6056 | } while (necp_uuid_lookup_uuid_with_service_id_locked(local_id: newid) != NULL); // If already used, keep trying |
6057 | } else { |
6058 | bool wrapped = FALSE; |
6059 | do { |
6060 | necp_last_app_uuid_id++; |
6061 | if (necp_last_app_uuid_id < NECP_FIRST_VALID_APP_ID) { |
6062 | if (wrapped) { |
6063 | // Already wrapped, give up |
6064 | NECPLOG0(LOG_ERR, "Failed to find a free app UUID.\n" ); |
6065 | return NECP_NULL_SERVICE_ID; |
6066 | } |
6067 | necp_last_app_uuid_id = NECP_FIRST_VALID_APP_ID; |
6068 | wrapped = TRUE; |
6069 | } |
6070 | newid = necp_last_app_uuid_id; |
6071 | } while (necp_uuid_lookup_uuid_with_app_id_locked(local_id: newid) != NULL); // If already used, keep trying |
6072 | } |
6073 | |
6074 | if (newid == NECP_NULL_SERVICE_ID) { |
6075 | NECPLOG0(LOG_ERR, "Allocate uuid ID failed.\n" ); |
6076 | return NECP_NULL_SERVICE_ID; |
6077 | } |
6078 | |
6079 | return newid; |
6080 | } |
6081 | |
6082 | static struct necp_uuid_id_mapping * |
6083 | necp_uuid_lookup_app_id_locked(uuid_t uuid) |
6084 | { |
6085 | struct necp_uuid_id_mapping *searchentry = NULL; |
6086 | struct necp_uuid_id_mapping *foundentry = NULL; |
6087 | |
6088 | LIST_FOREACH(searchentry, APPUUIDHASH(uuid), chain) { |
6089 | if (uuid_compare(uu1: searchentry->uuid, uu2: uuid) == 0) { |
6090 | foundentry = searchentry; |
6091 | break; |
6092 | } |
6093 | } |
6094 | |
6095 | return foundentry; |
6096 | } |
6097 | |
6098 | static struct necp_uuid_id_mapping * |
6099 | necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id) |
6100 | { |
6101 | struct necp_uuid_id_mapping *searchentry = NULL; |
6102 | struct necp_uuid_id_mapping *foundentry = NULL; |
6103 | |
6104 | struct necp_uuid_id_mapping_head *uuid_list_head = NULL; |
6105 | for (uuid_list_head = &necp_uuid_app_id_hashtbl[necp_uuid_app_id_hash_num_buckets - 1]; uuid_list_head >= necp_uuid_app_id_hashtbl; uuid_list_head--) { |
6106 | LIST_FOREACH(searchentry, uuid_list_head, chain) { |
6107 | if (searchentry->id == local_id) { |
6108 | foundentry = searchentry; |
6109 | break; |
6110 | } |
6111 | } |
6112 | } |
6113 | |
6114 | return foundentry; |
6115 | } |
6116 | |
6117 | static u_int32_t |
6118 | necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table) |
6119 | { |
6120 | u_int32_t local_id = 0; |
6121 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
6122 | |
6123 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6124 | |
6125 | if (allocated_mapping) { |
6126 | *allocated_mapping = FALSE; |
6127 | } |
6128 | |
6129 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid); |
6130 | if (existing_mapping != NULL) { |
6131 | local_id = existing_mapping->id; |
6132 | os_ref_retain_locked(rc: &existing_mapping->refcount); |
6133 | if (uuid_policy_table) { |
6134 | existing_mapping->table_usecount++; |
6135 | } |
6136 | } else { |
6137 | struct necp_uuid_id_mapping *new_mapping = NULL; |
6138 | new_mapping = kalloc_type(struct necp_uuid_id_mapping, |
6139 | Z_WAITOK | Z_NOFAIL); |
6140 | uuid_copy(dst: new_mapping->uuid, src: uuid); |
6141 | new_mapping->id = necp_get_new_uuid_id(false); |
6142 | os_ref_init(&new_mapping->refcount, &necp_refgrp); |
6143 | if (uuid_policy_table) { |
6144 | new_mapping->table_usecount = 1; |
6145 | } else { |
6146 | new_mapping->table_usecount = 0; |
6147 | } |
6148 | |
6149 | LIST_INSERT_HEAD(APPUUIDHASH(uuid), new_mapping, chain); |
6150 | |
6151 | if (allocated_mapping) { |
6152 | *allocated_mapping = TRUE; |
6153 | } |
6154 | |
6155 | local_id = new_mapping->id; |
6156 | } |
6157 | |
6158 | return local_id; |
6159 | } |
6160 | |
6161 | static bool |
6162 | necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table) |
6163 | { |
6164 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
6165 | |
6166 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6167 | |
6168 | if (removed_mapping) { |
6169 | *removed_mapping = FALSE; |
6170 | } |
6171 | |
6172 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid); |
6173 | if (existing_mapping != NULL) { |
6174 | if (uuid_policy_table) { |
6175 | existing_mapping->table_usecount--; |
6176 | } |
6177 | if (os_ref_release_locked(rc: &existing_mapping->refcount) == 0) { |
6178 | LIST_REMOVE(existing_mapping, chain); |
6179 | kfree_type(struct necp_uuid_id_mapping, existing_mapping); |
6180 | if (removed_mapping) { |
6181 | *removed_mapping = TRUE; |
6182 | } |
6183 | } |
6184 | return TRUE; |
6185 | } |
6186 | |
6187 | return FALSE; |
6188 | } |
6189 | |
6190 | static struct necp_uuid_id_mapping * |
6191 | necp_uuid_get_null_service_id_mapping(void) |
6192 | { |
6193 | static struct necp_uuid_id_mapping null_mapping; |
6194 | uuid_clear(uu: null_mapping.uuid); |
6195 | null_mapping.id = NECP_NULL_SERVICE_ID; |
6196 | |
6197 | return &null_mapping; |
6198 | } |
6199 | |
6200 | static struct necp_uuid_id_mapping * |
6201 | necp_uuid_lookup_service_id_locked(uuid_t uuid) |
6202 | { |
6203 | struct necp_uuid_id_mapping *searchentry = NULL; |
6204 | struct necp_uuid_id_mapping *foundentry = NULL; |
6205 | |
6206 | if (uuid_is_null(uu: uuid)) { |
6207 | return necp_uuid_get_null_service_id_mapping(); |
6208 | } |
6209 | |
6210 | LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) { |
6211 | if (uuid_compare(uu1: searchentry->uuid, uu2: uuid) == 0) { |
6212 | foundentry = searchentry; |
6213 | break; |
6214 | } |
6215 | } |
6216 | |
6217 | return foundentry; |
6218 | } |
6219 | |
6220 | static struct necp_uuid_id_mapping * |
6221 | necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id) |
6222 | { |
6223 | struct necp_uuid_id_mapping *searchentry = NULL; |
6224 | struct necp_uuid_id_mapping *foundentry = NULL; |
6225 | |
6226 | if (local_id == NECP_NULL_SERVICE_ID) { |
6227 | return necp_uuid_get_null_service_id_mapping(); |
6228 | } |
6229 | |
6230 | LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) { |
6231 | if (searchentry->id == local_id) { |
6232 | foundentry = searchentry; |
6233 | break; |
6234 | } |
6235 | } |
6236 | |
6237 | return foundentry; |
6238 | } |
6239 | |
6240 | static u_int32_t |
6241 | necp_create_uuid_service_id_mapping(uuid_t uuid) |
6242 | { |
6243 | u_int32_t local_id = 0; |
6244 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
6245 | |
6246 | if (uuid_is_null(uu: uuid)) { |
6247 | return NECP_NULL_SERVICE_ID; |
6248 | } |
6249 | |
6250 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6251 | |
6252 | existing_mapping = necp_uuid_lookup_service_id_locked(uuid); |
6253 | if (existing_mapping != NULL) { |
6254 | local_id = existing_mapping->id; |
6255 | os_ref_retain_locked(rc: &existing_mapping->refcount); |
6256 | } else { |
6257 | struct necp_uuid_id_mapping *new_mapping = NULL; |
6258 | new_mapping = kalloc_type(struct necp_uuid_id_mapping, |
6259 | Z_WAITOK | Z_NOFAIL); |
6260 | uuid_copy(dst: new_mapping->uuid, src: uuid); |
6261 | new_mapping->id = necp_get_new_uuid_id(true); |
6262 | os_ref_init(&new_mapping->refcount, &necp_refgrp); |
6263 | |
6264 | LIST_INSERT_HEAD(&necp_uuid_service_id_list, new_mapping, chain); |
6265 | |
6266 | local_id = new_mapping->id; |
6267 | } |
6268 | |
6269 | return local_id; |
6270 | } |
6271 | |
6272 | static bool |
6273 | necp_remove_uuid_service_id_mapping(uuid_t uuid) |
6274 | { |
6275 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
6276 | |
6277 | if (uuid_is_null(uu: uuid)) { |
6278 | return TRUE; |
6279 | } |
6280 | |
6281 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6282 | |
6283 | existing_mapping = necp_uuid_lookup_service_id_locked(uuid); |
6284 | if (existing_mapping != NULL) { |
6285 | if (os_ref_release_locked(rc: &existing_mapping->refcount) == 0) { |
6286 | LIST_REMOVE(existing_mapping, chain); |
6287 | kfree_type(struct necp_uuid_id_mapping, existing_mapping); |
6288 | } |
6289 | return TRUE; |
6290 | } |
6291 | |
6292 | return FALSE; |
6293 | } |
6294 | |
6295 | static bool |
6296 | necp_remove_uuid_service_id_mapping_with_service_id(u_int32_t service_id) |
6297 | { |
6298 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
6299 | |
6300 | if (service_id == 0) { |
6301 | return TRUE; |
6302 | } |
6303 | |
6304 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6305 | |
6306 | existing_mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: service_id); |
6307 | if (existing_mapping != NULL) { |
6308 | if (os_ref_release_locked(rc: &existing_mapping->refcount) == 0) { |
6309 | LIST_REMOVE(existing_mapping, chain); |
6310 | kfree_type(struct necp_uuid_id_mapping, existing_mapping); |
6311 | } |
6312 | return TRUE; |
6313 | } |
6314 | |
6315 | return FALSE; |
6316 | } |
6317 | |
6318 | static bool |
6319 | necp_kernel_socket_policies_update_uuid_table(void) |
6320 | { |
6321 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6322 | |
6323 | if (necp_uuid_app_id_mappings_dirty) { |
6324 | if (proc_uuid_policy_kernel(PROC_UUID_POLICY_OPERATION_CLEAR, NULL, PROC_UUID_NECP_APP_POLICY) < 0) { |
6325 | NECPLOG0(LOG_DEBUG, "Error clearing uuids from policy table\n" ); |
6326 | return FALSE; |
6327 | } |
6328 | |
6329 | if (necp_num_uuid_app_id_mappings > 0) { |
6330 | struct necp_uuid_id_mapping_head *uuid_list_head = NULL; |
6331 | for (uuid_list_head = &necp_uuid_app_id_hashtbl[necp_uuid_app_id_hash_num_buckets - 1]; uuid_list_head >= necp_uuid_app_id_hashtbl; uuid_list_head--) { |
6332 | struct necp_uuid_id_mapping *mapping = NULL; |
6333 | LIST_FOREACH(mapping, uuid_list_head, chain) { |
6334 | if (mapping->table_usecount > 0 && |
6335 | proc_uuid_policy_kernel(PROC_UUID_POLICY_OPERATION_ADD, uuid: mapping->uuid, PROC_UUID_NECP_APP_POLICY) < 0) { |
6336 | NECPLOG0(LOG_DEBUG, "Error adding uuid to policy table\n" ); |
6337 | } |
6338 | } |
6339 | } |
6340 | } |
6341 | |
6342 | necp_uuid_app_id_mappings_dirty = FALSE; |
6343 | } |
6344 | |
6345 | return TRUE; |
6346 | } |
6347 | |
6348 | #define NECP_KERNEL_VALID_IP_OUTPUT_CONDITIONS (NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE | NECP_KERNEL_CONDITION_LOCAL_NETWORKS | NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS | NECP_KERNEL_CONDITION_SCHEME_PORT | NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) |
6349 | static necp_kernel_policy_id |
6350 | necp_kernel_ip_output_policy_add(necp_policy_order order, necp_policy_order suborder, u_int32_t session_order, int session_pid, u_int64_t condition_mask, u_int64_t condition_negated_mask, necp_kernel_policy_id cond_policy_id, ifnet_t cond_bound_interface, u_int32_t cond_last_interface_index, u_int16_t cond_protocol, union necp_sockaddr_union *cond_local_start, union necp_sockaddr_union *cond_local_end, u_int8_t cond_local_prefix, union necp_sockaddr_union *cond_remote_start, union necp_sockaddr_union *cond_remote_end, u_int8_t cond_remote_prefix, u_int16_t cond_packet_filter_tags, u_int16_t cond_scheme_port, u_int32_t cond_bound_interface_flags, u_int32_t cond_bound_interface_eflags, u_int32_t cond_bound_interface_xflags, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
6351 | { |
6352 | struct necp_kernel_ip_output_policy *new_kernel_policy = NULL; |
6353 | struct necp_kernel_ip_output_policy *tmp_kernel_policy = NULL; |
6354 | |
6355 | new_kernel_policy = zalloc_flags(necp_ip_policy_zone, Z_WAITOK | Z_ZERO); |
6356 | new_kernel_policy->id = necp_kernel_policy_get_new_id(false); |
6357 | new_kernel_policy->suborder = suborder; |
6358 | new_kernel_policy->order = order; |
6359 | new_kernel_policy->session_order = session_order; |
6360 | new_kernel_policy->session_pid = session_pid; |
6361 | |
6362 | // Sanitize condition mask |
6363 | new_kernel_policy->condition_mask = (condition_mask & NECP_KERNEL_VALID_IP_OUTPUT_CONDITIONS); |
6364 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE)) { |
6365 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
6366 | } |
6367 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS)) { |
6368 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS; |
6369 | } |
6370 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX)) { |
6371 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
6372 | } |
6373 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX)) { |
6374 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
6375 | } |
6376 | new_kernel_policy->condition_negated_mask = condition_negated_mask & new_kernel_policy->condition_mask; |
6377 | |
6378 | // Set condition values |
6379 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) { |
6380 | new_kernel_policy->cond_policy_id = cond_policy_id; |
6381 | } |
6382 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
6383 | if (cond_bound_interface) { |
6384 | ifnet_reference(interface: cond_bound_interface); |
6385 | } |
6386 | new_kernel_policy->cond_bound_interface = cond_bound_interface; |
6387 | } |
6388 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LAST_INTERFACE) { |
6389 | new_kernel_policy->cond_last_interface_index = cond_last_interface_index; |
6390 | } |
6391 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
6392 | new_kernel_policy->cond_protocol = cond_protocol; |
6393 | } |
6394 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
6395 | memcpy(dst: &new_kernel_policy->cond_local_start, src: cond_local_start, n: cond_local_start->sa.sa_len); |
6396 | } |
6397 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
6398 | memcpy(dst: &new_kernel_policy->cond_local_end, src: cond_local_end, n: cond_local_end->sa.sa_len); |
6399 | } |
6400 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
6401 | new_kernel_policy->cond_local_prefix = cond_local_prefix; |
6402 | } |
6403 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
6404 | memcpy(dst: &new_kernel_policy->cond_remote_start, src: cond_remote_start, n: cond_remote_start->sa.sa_len); |
6405 | } |
6406 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
6407 | memcpy(dst: &new_kernel_policy->cond_remote_end, src: cond_remote_end, n: cond_remote_end->sa.sa_len); |
6408 | } |
6409 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
6410 | new_kernel_policy->cond_remote_prefix = cond_remote_prefix; |
6411 | } |
6412 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
6413 | new_kernel_policy->cond_packet_filter_tags = cond_packet_filter_tags; |
6414 | } |
6415 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
6416 | new_kernel_policy->cond_scheme_port = cond_scheme_port; |
6417 | } |
6418 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
6419 | new_kernel_policy->cond_bound_interface_flags = cond_bound_interface_flags; |
6420 | new_kernel_policy->cond_bound_interface_eflags = cond_bound_interface_eflags; |
6421 | new_kernel_policy->cond_bound_interface_xflags = cond_bound_interface_xflags; |
6422 | } |
6423 | new_kernel_policy->result = result; |
6424 | memcpy(dst: &new_kernel_policy->result_parameter, src: &result_parameter, n: sizeof(result_parameter)); |
6425 | |
6426 | if (necp_debug) { |
6427 | NECPLOG(LOG_DEBUG, "Added kernel policy: ip output, id=%d, mask=%llx\n" , new_kernel_policy->id, new_kernel_policy->condition_mask); |
6428 | } |
6429 | LIST_INSERT_SORTED_THRICE_ASCENDING(&necp_kernel_ip_output_policies, new_kernel_policy, chain, session_order, order, suborder, tmp_kernel_policy); |
6430 | |
6431 | return new_kernel_policy ? new_kernel_policy->id : 0; |
6432 | } |
6433 | |
6434 | static struct necp_kernel_ip_output_policy * |
6435 | necp_kernel_ip_output_policy_find(necp_kernel_policy_id policy_id) |
6436 | { |
6437 | struct necp_kernel_ip_output_policy *kernel_policy = NULL; |
6438 | struct necp_kernel_ip_output_policy *tmp_kernel_policy = NULL; |
6439 | |
6440 | if (policy_id == 0) { |
6441 | return NULL; |
6442 | } |
6443 | |
6444 | LIST_FOREACH_SAFE(kernel_policy, &necp_kernel_ip_output_policies, chain, tmp_kernel_policy) { |
6445 | if (kernel_policy->id == policy_id) { |
6446 | return kernel_policy; |
6447 | } |
6448 | } |
6449 | |
6450 | return NULL; |
6451 | } |
6452 | |
6453 | static bool |
6454 | necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id) |
6455 | { |
6456 | struct necp_kernel_ip_output_policy *policy = NULL; |
6457 | |
6458 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6459 | |
6460 | policy = necp_kernel_ip_output_policy_find(policy_id); |
6461 | if (policy) { |
6462 | LIST_REMOVE(policy, chain); |
6463 | |
6464 | if (policy->cond_bound_interface) { |
6465 | ifnet_release(interface: policy->cond_bound_interface); |
6466 | policy->cond_bound_interface = NULL; |
6467 | } |
6468 | |
6469 | zfree(necp_ip_policy_zone, policy); |
6470 | return TRUE; |
6471 | } |
6472 | |
6473 | return FALSE; |
6474 | } |
6475 | |
6476 | static void |
6477 | necp_kernel_ip_output_policies_dump_all(void) |
6478 | { |
6479 | if (necp_debug) { |
6480 | struct necp_kernel_ip_output_policy *policy = NULL; |
6481 | int policy_i; |
6482 | int id_i; |
6483 | char result_string[MAX_RESULT_STRING_LEN]; |
6484 | char proc_name_string[MAXCOMLEN + 1]; |
6485 | memset(s: result_string, c: 0, MAX_RESULT_STRING_LEN); |
6486 | memset(s: proc_name_string, c: 0, MAXCOMLEN + 1); |
6487 | |
6488 | NECPLOG0(LOG_DEBUG, "NECP IP Output Policies:\n" ); |
6489 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
6490 | for (id_i = 0; id_i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; id_i++) { |
6491 | NECPLOG(LOG_DEBUG, " ID Bucket: %d\n" , id_i); |
6492 | for (policy_i = 0; necp_kernel_ip_output_policies_map[id_i] != NULL && (necp_kernel_ip_output_policies_map[id_i])[policy_i] != NULL; policy_i++) { |
6493 | policy = (necp_kernel_ip_output_policies_map[id_i])[policy_i]; |
6494 | proc_name(pid: policy->session_pid, buf: proc_name_string, MAXCOMLEN); |
6495 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d.%d\tMask: %llx\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->suborder, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
6496 | } |
6497 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
6498 | } |
6499 | } |
6500 | } |
6501 | |
6502 | static inline bool |
6503 | necp_kernel_ip_output_policy_results_overlap(struct necp_kernel_ip_output_policy *upper_policy, struct necp_kernel_ip_output_policy *lower_policy) |
6504 | { |
6505 | if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6506 | if (upper_policy->session_order != lower_policy->session_order) { |
6507 | // A skip cannot override a policy of a different session |
6508 | return FALSE; |
6509 | } else { |
6510 | if (upper_policy->result_parameter.skip_policy_order == 0 || |
6511 | lower_policy->order >= upper_policy->result_parameter.skip_policy_order) { |
6512 | // This policy is beyond the skip |
6513 | return FALSE; |
6514 | } else { |
6515 | // This policy is inside the skip |
6516 | return TRUE; |
6517 | } |
6518 | } |
6519 | } |
6520 | |
6521 | // All other IP Output policy results (drop, tunnel, hard pass) currently overlap |
6522 | return TRUE; |
6523 | } |
6524 | |
6525 | static bool |
6526 | necp_kernel_ip_output_policy_is_unnecessary(struct necp_kernel_ip_output_policy *policy, struct necp_kernel_ip_output_policy **policy_array, int valid_indices) |
6527 | { |
6528 | bool can_skip = FALSE; |
6529 | u_int32_t highest_skip_session_order = 0; |
6530 | u_int32_t highest_skip_order = 0; |
6531 | int i; |
6532 | for (i = 0; i < valid_indices; i++) { |
6533 | struct necp_kernel_ip_output_policy *compared_policy = policy_array[i]; |
6534 | |
6535 | // For policies in a skip window, we can't mark conflicting policies as unnecessary |
6536 | if (can_skip) { |
6537 | if (highest_skip_session_order != compared_policy->session_order || |
6538 | (highest_skip_order != 0 && compared_policy->order >= highest_skip_order)) { |
6539 | // If we've moved on to the next session, or passed the skip window |
6540 | highest_skip_session_order = 0; |
6541 | highest_skip_order = 0; |
6542 | can_skip = FALSE; |
6543 | } else { |
6544 | // If this policy is also a skip, in can increase the skip window |
6545 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6546 | if (compared_policy->result_parameter.skip_policy_order > highest_skip_order) { |
6547 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
6548 | } |
6549 | } |
6550 | continue; |
6551 | } |
6552 | } |
6553 | |
6554 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6555 | // This policy is a skip. Set the skip window accordingly |
6556 | can_skip = TRUE; |
6557 | highest_skip_session_order = compared_policy->session_order; |
6558 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
6559 | } |
6560 | |
6561 | // The result of the compared policy must be able to block out this policy result |
6562 | if (!necp_kernel_ip_output_policy_results_overlap(upper_policy: compared_policy, lower_policy: policy)) { |
6563 | continue; |
6564 | } |
6565 | |
6566 | // If new policy matches All Interfaces, compared policy must also |
6567 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
6568 | continue; |
6569 | } |
6570 | |
6571 | // If new policy matches Local Networks, compared policy must also |
6572 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS)) { |
6573 | continue; |
6574 | } |
6575 | |
6576 | // Default makes lower policies unnecessary always |
6577 | if (compared_policy->condition_mask == 0) { |
6578 | return TRUE; |
6579 | } |
6580 | |
6581 | // Compared must be more general than policy, and include only conditions within policy |
6582 | if ((policy->condition_mask & compared_policy->condition_mask) != compared_policy->condition_mask) { |
6583 | continue; |
6584 | } |
6585 | |
6586 | // Negative conditions must match for the overlapping conditions |
6587 | if ((policy->condition_negated_mask & compared_policy->condition_mask) != (compared_policy->condition_negated_mask & compared_policy->condition_mask)) { |
6588 | continue; |
6589 | } |
6590 | |
6591 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID && |
6592 | compared_policy->cond_policy_id != policy->cond_policy_id) { |
6593 | continue; |
6594 | } |
6595 | |
6596 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE && |
6597 | compared_policy->cond_bound_interface != policy->cond_bound_interface) { |
6598 | continue; |
6599 | } |
6600 | |
6601 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL && |
6602 | compared_policy->cond_protocol != policy->cond_protocol) { |
6603 | continue; |
6604 | } |
6605 | |
6606 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
6607 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
6608 | if (!necp_is_range_in_range(inner_range_start: (struct sockaddr *)&policy->cond_local_start, inner_range_end: (struct sockaddr *)&policy->cond_local_end, range_start: (struct sockaddr *)&compared_policy->cond_local_start, range_end: (struct sockaddr *)&compared_policy->cond_local_end)) { |
6609 | continue; |
6610 | } |
6611 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
6612 | if (compared_policy->cond_local_prefix > policy->cond_local_prefix || |
6613 | !necp_is_addr_in_subnet(addr: (struct sockaddr *)&policy->cond_local_start, subnet_addr: (struct sockaddr *)&compared_policy->cond_local_start, subnet_prefix: compared_policy->cond_local_prefix)) { |
6614 | continue; |
6615 | } |
6616 | } |
6617 | } |
6618 | |
6619 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
6620 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
6621 | if (!necp_is_range_in_range(inner_range_start: (struct sockaddr *)&policy->cond_remote_start, inner_range_end: (struct sockaddr *)&policy->cond_remote_end, range_start: (struct sockaddr *)&compared_policy->cond_remote_start, range_end: (struct sockaddr *)&compared_policy->cond_remote_end)) { |
6622 | continue; |
6623 | } |
6624 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
6625 | if (compared_policy->cond_remote_prefix > policy->cond_remote_prefix || |
6626 | !necp_is_addr_in_subnet(addr: (struct sockaddr *)&policy->cond_remote_start, subnet_addr: (struct sockaddr *)&compared_policy->cond_remote_start, subnet_prefix: compared_policy->cond_remote_prefix)) { |
6627 | continue; |
6628 | } |
6629 | } |
6630 | } |
6631 | |
6632 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT && |
6633 | compared_policy->cond_scheme_port != policy->cond_scheme_port) { |
6634 | continue; |
6635 | } |
6636 | |
6637 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS && |
6638 | (compared_policy->cond_bound_interface_flags != policy->cond_bound_interface_flags || |
6639 | compared_policy->cond_bound_interface_eflags != policy->cond_bound_interface_eflags || |
6640 | compared_policy->cond_bound_interface_xflags != policy->cond_bound_interface_xflags)) { |
6641 | continue; |
6642 | } |
6643 | |
6644 | return TRUE; |
6645 | } |
6646 | |
6647 | return FALSE; |
6648 | } |
6649 | |
6650 | static bool |
6651 | necp_kernel_ip_output_policies_reprocess(void) |
6652 | { |
6653 | int i; |
6654 | int bucket_current_free_index[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
6655 | struct necp_kernel_ip_output_policy *kernel_policy = NULL; |
6656 | |
6657 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
6658 | |
6659 | // Reset mask to 0 |
6660 | necp_kernel_ip_output_policies_condition_mask = 0; |
6661 | necp_kernel_ip_output_policies_count = 0; |
6662 | necp_kernel_ip_output_policies_non_id_count = 0; |
6663 | |
6664 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6665 | if (necp_kernel_ip_output_policies_map[i] != NULL) { |
6666 | kfree_type(struct necp_kernel_ip_output_policy *, |
6667 | necp_kernel_ip_output_policies_map_counts[i] + 1, |
6668 | necp_kernel_ip_output_policies_map[i]); |
6669 | necp_kernel_ip_output_policies_map[i] = NULL; |
6670 | } |
6671 | |
6672 | // Init counts |
6673 | necp_kernel_ip_output_policies_map_counts[i] = 0; |
6674 | } |
6675 | |
6676 | LIST_FOREACH(kernel_policy, &necp_kernel_ip_output_policies, chain) { |
6677 | // Update mask |
6678 | necp_kernel_ip_output_policies_condition_mask |= kernel_policy->condition_mask; |
6679 | necp_kernel_ip_output_policies_count++; |
6680 | |
6681 | /* Update bucket counts: |
6682 | * Non-id and SKIP policies will be added to all buckets |
6683 | * Add local networks policy to all buckets for incoming IP |
6684 | */ |
6685 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) || |
6686 | (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) || |
6687 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6688 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6689 | necp_kernel_ip_output_policies_map_counts[i]++; |
6690 | } |
6691 | } |
6692 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID)) { |
6693 | necp_kernel_ip_output_policies_non_id_count++; |
6694 | } else { |
6695 | necp_kernel_ip_output_policies_map_counts[NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(kernel_policy->cond_policy_id)]++; |
6696 | } |
6697 | } |
6698 | |
6699 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6700 | if (necp_kernel_ip_output_policies_map_counts[i] > 0) { |
6701 | // Allocate a NULL-terminated array of policy pointers for each bucket |
6702 | necp_kernel_ip_output_policies_map[i] = kalloc_type(struct necp_kernel_ip_output_policy *, |
6703 | necp_kernel_ip_output_policies_map_counts[i] + 1, Z_WAITOK | Z_ZERO); |
6704 | if (necp_kernel_ip_output_policies_map[i] == NULL) { |
6705 | goto fail; |
6706 | } |
6707 | } |
6708 | bucket_current_free_index[i] = 0; |
6709 | } |
6710 | |
6711 | u_int32_t current_session_order = 0; |
6712 | u_int32_t current_session_last_non_skip_policy = 0; |
6713 | |
6714 | LIST_FOREACH(kernel_policy, &necp_kernel_ip_output_policies, chain) { |
6715 | // For each new session, find the last non-skip policy. We can |
6716 | // avoid adding any skip policies that don't actually skip over |
6717 | // any non-skip policies. |
6718 | if (current_session_order != kernel_policy->session_order) { |
6719 | current_session_order = kernel_policy->session_order; |
6720 | current_session_last_non_skip_policy = 0; |
6721 | |
6722 | struct necp_kernel_ip_output_policy *inner_policy = NULL; |
6723 | LIST_FOREACH(inner_policy, &necp_kernel_ip_output_policies, chain) { |
6724 | if (inner_policy->session_order < current_session_order) { |
6725 | continue; |
6726 | } |
6727 | if (inner_policy->session_order > current_session_order) { |
6728 | break; |
6729 | } |
6730 | if (inner_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6731 | continue; |
6732 | } |
6733 | |
6734 | current_session_last_non_skip_policy = inner_policy->order; |
6735 | } |
6736 | } |
6737 | |
6738 | if (kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6739 | if (current_session_last_non_skip_policy == 0) { |
6740 | // No useful policies to skip over, don't add |
6741 | continue; |
6742 | } |
6743 | if (kernel_policy->order >= current_session_last_non_skip_policy) { |
6744 | // Skip policy is after the last useful policy, don't add |
6745 | continue; |
6746 | } |
6747 | } |
6748 | |
6749 | // Insert pointers into map |
6750 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) || |
6751 | (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) || |
6752 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6753 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6754 | if (!necp_dedup_policies || !necp_kernel_ip_output_policy_is_unnecessary(policy: kernel_policy, policy_array: necp_kernel_ip_output_policies_map[i], valid_indices: bucket_current_free_index[i])) { |
6755 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = kernel_policy; |
6756 | bucket_current_free_index[i]++; |
6757 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = NULL; |
6758 | } |
6759 | } |
6760 | } else { |
6761 | i = NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(kernel_policy->cond_policy_id); |
6762 | if (!necp_dedup_policies || !necp_kernel_ip_output_policy_is_unnecessary(policy: kernel_policy, policy_array: necp_kernel_ip_output_policies_map[i], valid_indices: bucket_current_free_index[i])) { |
6763 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = kernel_policy; |
6764 | bucket_current_free_index[i]++; |
6765 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = NULL; |
6766 | } |
6767 | } |
6768 | } |
6769 | |
6770 | if (bucket_current_free_index[0] == 0) { |
6771 | // No non-id policies were actually added |
6772 | necp_kernel_ip_output_policies_non_id_count = 0; |
6773 | |
6774 | // Also check if no policies at all were added |
6775 | bool policies_added = FALSE; |
6776 | for (i = 1; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6777 | if (bucket_current_free_index[i] != 0) { |
6778 | policies_added = TRUE; |
6779 | break; |
6780 | } |
6781 | } |
6782 | if (!policies_added) { |
6783 | necp_kernel_ip_output_policies_condition_mask = 0; |
6784 | necp_kernel_ip_output_policies_count = 0; |
6785 | } |
6786 | } |
6787 | |
6788 | necp_kernel_ip_output_policies_dump_all(); |
6789 | return TRUE; |
6790 | |
6791 | fail: |
6792 | // Free memory, reset mask to 0 |
6793 | necp_kernel_ip_output_policies_condition_mask = 0; |
6794 | necp_kernel_ip_output_policies_count = 0; |
6795 | necp_kernel_ip_output_policies_non_id_count = 0; |
6796 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6797 | if (necp_kernel_ip_output_policies_map[i] != NULL) { |
6798 | kfree_type(struct necp_kernel_ip_output_policy *, |
6799 | necp_kernel_ip_output_policies_map_counts[i] + 1, |
6800 | necp_kernel_ip_output_policies_map[i]); |
6801 | necp_kernel_ip_output_policies_map[i] = NULL; |
6802 | } |
6803 | } |
6804 | return FALSE; |
6805 | } |
6806 | |
6807 | // Outbound Policy Matching |
6808 | // --------------------- |
6809 | struct substring { |
6810 | char *string; |
6811 | size_t length; |
6812 | }; |
6813 | |
6814 | static struct substring |
6815 | necp_trim_dots_and_stars(char *string, size_t length) |
6816 | { |
6817 | struct substring sub; |
6818 | sub.string = string; |
6819 | sub.length = string ? length : 0; |
6820 | |
6821 | while (sub.length && (sub.string[0] == '.' || sub.string[0] == '*')) { |
6822 | sub.string++; |
6823 | sub.length--; |
6824 | } |
6825 | |
6826 | while (sub.length && (sub.string[sub.length - 1] == '.' || sub.string[sub.length - 1] == '*')) { |
6827 | sub.length--; |
6828 | } |
6829 | |
6830 | return sub; |
6831 | } |
6832 | |
6833 | static char * |
6834 | necp_create_trimmed_domain(char *string, size_t length) |
6835 | { |
6836 | char *trimmed_domain = NULL; |
6837 | struct substring sub = necp_trim_dots_and_stars(string, length); |
6838 | |
6839 | trimmed_domain = (char *)kalloc_data(sub.length + 1, Z_WAITOK); |
6840 | if (trimmed_domain == NULL) { |
6841 | return NULL; |
6842 | } |
6843 | |
6844 | memcpy(dst: trimmed_domain, src: sub.string, n: sub.length); |
6845 | trimmed_domain[sub.length] = 0; |
6846 | |
6847 | return trimmed_domain; |
6848 | } |
6849 | |
6850 | static inline int |
6851 | necp_count_dots(char *string, size_t length) |
6852 | { |
6853 | int dot_count = 0; |
6854 | size_t i = 0; |
6855 | |
6856 | for (i = 0; i < length; i++) { |
6857 | if (string[i] == '.') { |
6858 | dot_count++; |
6859 | } |
6860 | } |
6861 | |
6862 | return dot_count; |
6863 | } |
6864 | |
6865 | static bool |
6866 | necp_check_suffix(struct substring parent, struct substring suffix, bool require_dot_before_suffix) |
6867 | { |
6868 | if (parent.length <= suffix.length) { |
6869 | return FALSE; |
6870 | } |
6871 | |
6872 | size_t length_difference = (parent.length - suffix.length); |
6873 | |
6874 | if (require_dot_before_suffix) { |
6875 | if (((char *)(parent.string + length_difference - 1))[0] != '.') { |
6876 | return FALSE; |
6877 | } |
6878 | } |
6879 | |
6880 | // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters) |
6881 | return strncasecmp(s1: parent.string + length_difference, s2: suffix.string, n: suffix.length) == 0; |
6882 | } |
6883 | |
6884 | static bool |
6885 | necp_hostname_matches_domain(struct substring hostname_substring, u_int8_t hostname_dot_count, char *domain, u_int8_t domain_dot_count) |
6886 | { |
6887 | if (hostname_substring.string == NULL || domain == NULL) { |
6888 | return hostname_substring.string == domain; |
6889 | } |
6890 | |
6891 | struct substring domain_substring; |
6892 | domain_substring.string = domain; |
6893 | domain_substring.length = strlen(s: domain); |
6894 | |
6895 | if (hostname_dot_count == domain_dot_count) { |
6896 | // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters) |
6897 | if (hostname_substring.length == domain_substring.length && |
6898 | strncasecmp(s1: hostname_substring.string, s2: domain_substring.string, n: hostname_substring.length) == 0) { |
6899 | return TRUE; |
6900 | } |
6901 | } else if (domain_dot_count < hostname_dot_count) { |
6902 | if (necp_check_suffix(parent: hostname_substring, suffix: domain_substring, TRUE)) { |
6903 | return TRUE; |
6904 | } |
6905 | } |
6906 | |
6907 | return FALSE; |
6908 | } |
6909 | |
6910 | bool |
6911 | net_domain_contains_hostname(char *hostname_string, char *domain_string) |
6912 | { |
6913 | if (hostname_string == NULL || |
6914 | domain_string == NULL) { |
6915 | return false; |
6916 | } |
6917 | |
6918 | struct substring hostname_substring; |
6919 | hostname_substring.string = hostname_string; |
6920 | hostname_substring.length = strlen(s: hostname_string); |
6921 | |
6922 | return necp_hostname_matches_domain(hostname_substring, |
6923 | hostname_dot_count: necp_count_dots(string: hostname_string, length: hostname_substring.length), |
6924 | domain: domain_string, |
6925 | domain_dot_count: necp_count_dots(string: domain_string, length: strlen(s: domain_string))); |
6926 | } |
6927 | |
6928 | #define NECP_MAX_STRING_LEN 1024 |
6929 | |
6930 | static char * |
6931 | necp_copy_string(char *string, size_t length) |
6932 | { |
6933 | char *copied_string = NULL; |
6934 | |
6935 | if (length > NECP_MAX_STRING_LEN) { |
6936 | return NULL; |
6937 | } |
6938 | |
6939 | copied_string = (char *)kalloc_data(length + 1, Z_WAITOK); |
6940 | if (copied_string == NULL) { |
6941 | return NULL; |
6942 | } |
6943 | |
6944 | memcpy(dst: copied_string, src: string, n: length); |
6945 | copied_string[length] = 0; |
6946 | |
6947 | return copied_string; |
6948 | } |
6949 | |
6950 | static u_int32_t |
6951 | necp_get_primary_direct_interface_index(void) |
6952 | { |
6953 | u_int32_t interface_index = IFSCOPE_NONE; |
6954 | |
6955 | ifnet_head_lock_shared(); |
6956 | struct ifnet *ordered_interface = NULL; |
6957 | TAILQ_FOREACH(ordered_interface, &ifnet_ordered_head, if_ordered_link) { |
6958 | const u_int8_t functional_type = if_functional_type(ordered_interface, TRUE); |
6959 | if (functional_type != IFRTYPE_FUNCTIONAL_UNKNOWN && |
6960 | functional_type != IFRTYPE_FUNCTIONAL_LOOPBACK) { |
6961 | // All known, non-loopback functional types represent direct physical interfaces (Wi-Fi, Cellular, Wired) |
6962 | interface_index = ordered_interface->if_index; |
6963 | break; |
6964 | } |
6965 | } |
6966 | ifnet_head_done(); |
6967 | |
6968 | return interface_index; |
6969 | } |
6970 | |
6971 | static inline bool |
6972 | necp_task_has_match_entitlement(task_t task) |
6973 | { |
6974 | return task != NULL && |
6975 | (IOTaskHasEntitlement(task, entitlement: "com.apple.private.necp.match" ) || |
6976 | IOTaskHasEntitlement(task, entitlement: "com.apple.developer.CaptiveNetworkPlugin" )); |
6977 | } |
6978 | |
6979 | static inline void |
6980 | necp_get_parent_is_entitled(task_t task, struct necp_socket_info *info) |
6981 | { |
6982 | coalition_t coal = task_get_coalition(task, COALITION_TYPE_JETSAM); |
6983 | |
6984 | if (coal == COALITION_NULL || coalition_is_leader(task, coal)) { |
6985 | // No parent, nothing to do |
6986 | return; |
6987 | } |
6988 | |
6989 | task_t lead_task = coalition_get_leader(coal); |
6990 | if (lead_task != NULL) { |
6991 | info->is_entitled = necp_task_has_match_entitlement(task: lead_task); |
6992 | task_deallocate(lead_task); |
6993 | } |
6994 | } |
6995 | |
6996 | // Some processes, due to particular entitlements, require using an NECP client to |
6997 | // access networking. Returns true if the result should be a Drop. |
6998 | static inline bool |
6999 | necp_check_missing_client_drop(proc_t proc, struct necp_socket_info *info) |
7000 | { |
7001 | if (necp_is_platform_binary(proc)) { |
7002 | // This check is currently for the "on-demand-install-capable" |
7003 | // entitlement, which by definition cannot be a built-in platform |
7004 | // binary. |
7005 | return false; |
7006 | } |
7007 | |
7008 | task_t task = proc_task(proc ? proc : current_proc()); |
7009 | |
7010 | if (!info->has_client && |
7011 | task != NULL && |
7012 | IOTaskHasEntitlement(task, entitlement: "com.apple.developer.on-demand-install-capable" )) { |
7013 | // Drop connections that don't use NECP clients and have the |
7014 | // com.apple.developer.on-demand-install-capable entitlement. |
7015 | // This effectively restricts those processes to only using |
7016 | // an NECP-aware path for networking. |
7017 | return true; |
7018 | } else { |
7019 | return false; |
7020 | } |
7021 | } |
7022 | |
7023 | static inline bool |
7024 | necp_check_restricted_multicast_drop(proc_t proc, struct necp_socket_info *info, bool check_minor_version) |
7025 | { |
7026 | if (!necp_restrict_multicast || proc == NULL) { |
7027 | return false; |
7028 | } |
7029 | |
7030 | // Check for multicast/broadcast here |
7031 | if (info->remote_addr.sa.sa_family == AF_INET) { |
7032 | if (!IN_MULTICAST(ntohl(info->remote_addr.sin.sin_addr.s_addr)) && |
7033 | info->remote_addr.sin.sin_addr.s_addr != INADDR_BROADCAST) { |
7034 | return false; |
7035 | } |
7036 | } else if (info->remote_addr.sa.sa_family == AF_INET6) { |
7037 | if (!IN6_IS_ADDR_MULTICAST(&info->remote_addr.sin6.sin6_addr)) { |
7038 | return false; |
7039 | } |
7040 | } else { |
7041 | // Not IPv4/IPv6 |
7042 | return false; |
7043 | } |
7044 | |
7045 | if (necp_is_platform_binary(proc)) { |
7046 | return false; |
7047 | } |
7048 | |
7049 | const uint32_t platform = proc_platform(proc); |
7050 | const uint32_t sdk = proc_sdk(proc); |
7051 | |
7052 | // Enforce for iOS, linked on or after version 14 |
7053 | // If the caller set `check_minor_version`, only enforce starting at 14.5 |
7054 | if ((platform != PLATFORM_IOS || |
7055 | sdk == 0 || |
7056 | (sdk >> 16) < 14 || |
7057 | (check_minor_version && (sdk >> 16) == 14 && ((sdk >> 8) & 0xff) < 5))) { |
7058 | return false; |
7059 | } |
7060 | |
7061 | // Allow entitled processes to use multicast |
7062 | task_t task = proc_task(proc); |
7063 | if (task != NULL && |
7064 | IOTaskHasEntitlement(task, entitlement: "com.apple.developer.networking.multicast" )) { |
7065 | return false; |
7066 | } |
7067 | |
7068 | const uint32_t min_sdk = proc_min_sdk(proc); |
7069 | NECPLOG(LOG_INFO, "Dropping unentitled multicast (SDK 0x%x, min 0x%x)" , sdk, min_sdk); |
7070 | |
7071 | return true; |
7072 | } |
7073 | |
7074 | #define NECP_KERNEL_ADDRESS_TYPE_CONDITIONS (NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_LOCAL_EMPTY | NECP_KERNEL_CONDITION_REMOTE_EMPTY | NECP_KERNEL_CONDITION_LOCAL_NETWORKS | NECP_KERNEL_CONDITION_SCHEME_PORT) |
7075 | static void |
7076 | necp_application_fillout_info_locked(task_t task, uuid_t application_uuid, uuid_t real_application_uuid, uuid_t responsible_application_uuid, char *account, char *domain, char *url, pid_t pid, int32_t pid_version, uid_t uid, uid_t real_uid, u_int16_t protocol, u_int32_t bound_interface_index, u_int32_t traffic_class, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr, u_int16_t local_port, u_int16_t remote_port, bool has_client, bool has_system_signed_result, proc_t real_proc, proc_t proc, proc_t responsible_proc, u_int32_t drop_order, u_int32_t client_flags, u_int16_t scheme_port, struct necp_socket_info *info, bool is_loopback, bool is_delegated) |
7077 | { |
7078 | memset(s: info, c: 0, n: sizeof(struct necp_socket_info)); |
7079 | |
7080 | info->pid = pid; |
7081 | info->pid_version = pid_version; |
7082 | info->uid = uid; |
7083 | info->real_uid = real_uid; |
7084 | info->protocol = protocol; |
7085 | info->bound_interface_index = bound_interface_index; |
7086 | info->traffic_class = traffic_class; |
7087 | info->has_client = has_client; |
7088 | info->has_system_signed_result = has_system_signed_result; |
7089 | info->drop_order = drop_order; |
7090 | info->client_flags = client_flags; |
7091 | info->is_loopback = is_loopback; |
7092 | info->is_delegated = is_delegated; |
7093 | |
7094 | if ((necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) && |
7095 | info->bound_interface_index != IFSCOPE_NONE) { |
7096 | ifnet_head_lock_shared(); |
7097 | ifnet_t interface = ifindex2ifnet[info->bound_interface_index]; |
7098 | if (interface != NULL) { |
7099 | info->bound_interface_flags = interface->if_flags; |
7100 | info->bound_interface_eflags = interface->if_eflags; |
7101 | info->bound_interface_xflags = interface->if_xflags; |
7102 | } |
7103 | ifnet_head_done(); |
7104 | } |
7105 | |
7106 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID && !uuid_is_null(uu: application_uuid)) { |
7107 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(uuid: application_uuid); |
7108 | if (existing_mapping) { |
7109 | info->application_id = existing_mapping->id; |
7110 | } |
7111 | } |
7112 | |
7113 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID && !uuid_is_null(uu: real_application_uuid)) { |
7114 | if (uuid_compare(uu1: application_uuid, uu2: real_application_uuid) == 0) { |
7115 | info->real_application_id = info->application_id; |
7116 | } else { |
7117 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(uuid: real_application_uuid); |
7118 | if (existing_mapping) { |
7119 | info->real_application_id = existing_mapping->id; |
7120 | } |
7121 | } |
7122 | } |
7123 | |
7124 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID && !uuid_is_null(uu: responsible_application_uuid)) { |
7125 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(uuid: responsible_application_uuid); |
7126 | if (existing_mapping != NULL) { |
7127 | info->real_application_id = info->application_id; |
7128 | info->application_id = existing_mapping->id; |
7129 | info->used_responsible_pid = true; |
7130 | } |
7131 | } |
7132 | |
7133 | if (info->used_responsible_pid) { |
7134 | proc = responsible_proc; |
7135 | } |
7136 | |
7137 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT && proc != NULL) { |
7138 | info->is_entitled = necp_task_has_match_entitlement(task); |
7139 | if (!info->is_entitled) { |
7140 | // Task does not have entitlement, check the parent task |
7141 | necp_get_parent_is_entitled(task, info); |
7142 | } |
7143 | } |
7144 | |
7145 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_PLATFORM_BINARY && proc != NULL) { |
7146 | info->is_platform_binary = necp_is_platform_binary(proc) ? true : false; |
7147 | } |
7148 | |
7149 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY && real_proc != NULL) { |
7150 | info->real_is_platform_binary = (necp_is_platform_binary(proc: real_proc) ? true : false); |
7151 | } |
7152 | |
7153 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && account != NULL) { |
7154 | struct necp_string_id_mapping *existing_mapping = necp_lookup_string_to_id_locked(list: &necp_account_id_list, string: account); |
7155 | if (existing_mapping) { |
7156 | info->account_id = existing_mapping->id; |
7157 | } |
7158 | } |
7159 | |
7160 | if ((necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN) || |
7161 | (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN) || |
7162 | (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER)) { |
7163 | info->domain = domain; |
7164 | } |
7165 | |
7166 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_URL) { |
7167 | info->url = url; |
7168 | } |
7169 | |
7170 | if ((necp_data_tracing_level && necp_data_tracing_port) || |
7171 | necp_restrict_multicast || |
7172 | (necp_kernel_application_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS)) { |
7173 | if (local_addr && local_addr->sa.sa_len > 0) { |
7174 | memcpy(dst: &info->local_addr, src: local_addr, n: local_addr->sa.sa_len); |
7175 | if (local_port != 0) { |
7176 | info->local_addr.sin6.sin6_port = local_port; |
7177 | } |
7178 | } else { |
7179 | if (remote_addr && remote_addr->sa.sa_len > 0) { |
7180 | info->local_addr.sa.sa_family = remote_addr->sa.sa_family; |
7181 | info->local_addr.sa.sa_len = remote_addr->sa.sa_len; |
7182 | } else { |
7183 | info->local_addr.sin6.sin6_family = AF_INET6; |
7184 | info->local_addr.sin6.sin6_len = sizeof(struct sockaddr_in6); |
7185 | } |
7186 | if (local_port != 0) { |
7187 | info->local_addr.sin6.sin6_port = local_port; |
7188 | } |
7189 | } |
7190 | if (remote_addr && remote_addr->sa.sa_len > 0) { |
7191 | memcpy(dst: &info->remote_addr, src: remote_addr, n: remote_addr->sa.sa_len); |
7192 | if (remote_port != 0) { |
7193 | info->remote_addr.sin6.sin6_port = remote_port; |
7194 | } |
7195 | } else if (remote_port != 0) { |
7196 | info->remote_addr.sin6.sin6_len = sizeof(struct sockaddr_in6); |
7197 | info->remote_addr.sin6.sin6_family = AF_INET6; |
7198 | info->remote_addr.sin6.sin6_port = remote_port; |
7199 | } |
7200 | } |
7201 | |
7202 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
7203 | info->scheme_port = scheme_port; |
7204 | } |
7205 | } |
7206 | |
7207 | static void |
7208 | necp_send_application_interface_denied_event(pid_t pid, uuid_t proc_uuid, u_int32_t if_functional_type) |
7209 | { |
7210 | struct kev_netpolicy_ifdenied ev_ifdenied; |
7211 | |
7212 | bzero(s: &ev_ifdenied, n: sizeof(ev_ifdenied)); |
7213 | |
7214 | ev_ifdenied.ev_data.epid = pid; |
7215 | uuid_copy(dst: ev_ifdenied.ev_data.euuid, src: proc_uuid); |
7216 | ev_ifdenied.ev_if_functional_type = if_functional_type; |
7217 | |
7218 | netpolicy_post_msg(KEV_NETPOLICY_IFDENIED, &ev_ifdenied.ev_data, sizeof(ev_ifdenied)); |
7219 | } |
7220 | |
7221 | static void |
7222 | necp_send_network_denied_event(pid_t pid, uuid_t proc_uuid, u_int32_t network_type) |
7223 | { |
7224 | struct kev_netpolicy_netdenied ev_netdenied = {}; |
7225 | |
7226 | bzero(s: &ev_netdenied, n: sizeof(ev_netdenied)); |
7227 | |
7228 | ev_netdenied.ev_data.epid = pid; |
7229 | uuid_copy(dst: ev_netdenied.ev_data.euuid, src: proc_uuid); |
7230 | ev_netdenied.ev_network_type = network_type; |
7231 | |
7232 | netpolicy_post_msg(KEV_NETPOLICY_NETDENIED, &ev_netdenied.ev_data, sizeof(ev_netdenied)); |
7233 | } |
7234 | |
7235 | extern char *proc_name_address(void *p); |
7236 | |
7237 | #define NECP_VERIFY_DELEGATION_ENTITLEMENT(_p, _c, _d) \ |
7238 | if (!has_checked_delegation_entitlement) { \ |
7239 | has_delegation_entitlement = (priv_check_cred(_c, PRIV_NET_PRIVILEGED_SOCKET_DELEGATE, 0) == 0); \ |
7240 | has_checked_delegation_entitlement = TRUE; \ |
7241 | } \ |
7242 | if (!has_delegation_entitlement) { \ |
7243 | NECPLOG(LOG_ERR, "%s(%d) does not hold the necessary entitlement to delegate network traffic for other processes by %s", \ |
7244 | proc_name_address(_p), proc_pid(_p), _d); \ |
7245 | break; \ |
7246 | } |
7247 | |
7248 | int |
7249 | necp_application_find_policy_match_internal(proc_t proc, |
7250 | u_int8_t *parameters, |
7251 | u_int32_t parameters_size, |
7252 | struct necp_aggregate_result *returned_result, |
7253 | u_int32_t *flags, |
7254 | u_int32_t *reason, |
7255 | u_int required_interface_index, |
7256 | const union necp_sockaddr_union *override_local_addr, |
7257 | const union necp_sockaddr_union *override_remote_addr, |
7258 | struct necp_client_endpoint *returned_v4_gateway, |
7259 | struct necp_client_endpoint *returned_v6_gateway, |
7260 | struct rtentry **returned_route, bool ignore_address, |
7261 | bool has_client, |
7262 | uuid_t *returned_override_euuid) |
7263 | { |
7264 | int error = 0; |
7265 | size_t offset = 0; |
7266 | |
7267 | struct necp_kernel_socket_policy *matched_policy = NULL; |
7268 | struct necp_socket_info info = {}; |
7269 | necp_kernel_policy_filter filter_control_unit = 0; |
7270 | necp_kernel_policy_result service_action = 0; |
7271 | necp_kernel_policy_service service = { 0, 0 }; |
7272 | |
7273 | u_int16_t protocol = 0; |
7274 | u_int32_t bound_interface_index = required_interface_index; |
7275 | u_int32_t traffic_class = 0; |
7276 | u_int32_t client_flags = 0; |
7277 | u_int16_t scheme_port = 0; |
7278 | union necp_sockaddr_union local_addr; |
7279 | union necp_sockaddr_union remote_addr; |
7280 | bool no_remote_addr = FALSE; |
7281 | u_int8_t remote_family = 0; |
7282 | bool no_local_addr = FALSE; |
7283 | u_int16_t local_port = 0; |
7284 | u_int16_t remote_port = 0; |
7285 | u_int32_t remote_endpoint_type = 0; |
7286 | bool remote_address_is_empty = false; |
7287 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
7288 | bool is_delegated = false; |
7289 | |
7290 | if (override_local_addr) { |
7291 | memcpy(dst: &local_addr, src: override_local_addr, n: sizeof(local_addr)); |
7292 | } else { |
7293 | memset(s: &local_addr, c: 0, n: sizeof(local_addr)); |
7294 | } |
7295 | if (override_remote_addr) { |
7296 | memcpy(dst: &remote_addr, src: override_remote_addr, n: sizeof(remote_addr)); |
7297 | } else { |
7298 | memset(s: &remote_addr, c: 0, n: sizeof(remote_addr)); |
7299 | } |
7300 | |
7301 | // Initialize UID, PID, and UUIDs to the current process |
7302 | uid_t uid = 0; |
7303 | uid_t real_uid = 0; |
7304 | kauth_cred_t cred = kauth_cred_proc_ref(procp: proc); |
7305 | if (cred != NULL) { |
7306 | uid = kauth_cred_getuid(cred: cred); |
7307 | real_uid = uid; |
7308 | } |
7309 | task_t task = proc_task(proc); |
7310 | pid_t pid = proc_pid(proc); |
7311 | int32_t pid_version = proc_pidversion(proc); |
7312 | uuid_t application_uuid; |
7313 | uuid_clear(uu: application_uuid); |
7314 | uuid_t real_application_uuid; |
7315 | uuid_clear(uu: real_application_uuid); |
7316 | proc_getexecutableuuid(proc, real_application_uuid, sizeof(real_application_uuid)); |
7317 | uuid_copy(dst: application_uuid, src: real_application_uuid); |
7318 | uuid_t responsible_application_uuid; |
7319 | uuid_clear(uu: responsible_application_uuid); |
7320 | |
7321 | char *domain = NULL; |
7322 | char *url = NULL; |
7323 | char *account = NULL; |
7324 | |
7325 | #define NECP_MAX_REQUIRED_AGENTS 16 |
7326 | u_int32_t num_required_agent_types = 0; |
7327 | struct necp_client_parameter_netagent_type required_agent_types[NECP_MAX_REQUIRED_AGENTS]; |
7328 | memset(s: &required_agent_types, c: 0, n: sizeof(required_agent_types)); |
7329 | |
7330 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
7331 | u_int32_t netagent_use_flags[NECP_MAX_NETAGENTS]; |
7332 | memset(s: &netagent_ids, c: 0, n: sizeof(netagent_ids)); |
7333 | memset(s: &netagent_use_flags, c: 0, n: sizeof(netagent_use_flags)); |
7334 | int netagent_cursor; |
7335 | |
7336 | bool has_checked_delegation_entitlement = false; |
7337 | bool has_delegation_entitlement = false; |
7338 | bool has_system_signed_result = false; |
7339 | |
7340 | proc_t responsible_proc = PROC_NULL; |
7341 | proc_t effective_proc = proc; |
7342 | bool release_eproc = false; |
7343 | necp_socket_bypass_type_t bypass_type = NECP_BYPASS_TYPE_NONE; |
7344 | |
7345 | u_int32_t flow_divert_aggregate_unit = 0; |
7346 | |
7347 | if (returned_result == NULL) { |
7348 | if (cred != NULL) { |
7349 | kauth_cred_unref(&cred); |
7350 | } |
7351 | return EINVAL; |
7352 | } |
7353 | |
7354 | if (returned_v4_gateway != NULL) { |
7355 | memset(s: returned_v4_gateway, c: 0, n: sizeof(struct necp_client_endpoint)); |
7356 | } |
7357 | |
7358 | if (returned_v6_gateway != NULL) { |
7359 | memset(s: returned_v6_gateway, c: 0, n: sizeof(struct necp_client_endpoint)); |
7360 | } |
7361 | |
7362 | if (returned_override_euuid != NULL) { |
7363 | uuid_clear(uu: *returned_override_euuid); |
7364 | } |
7365 | |
7366 | memset(s: returned_result, c: 0, n: sizeof(struct necp_aggregate_result)); |
7367 | |
7368 | u_int32_t drop_order = necp_process_drop_order(cred); |
7369 | |
7370 | necp_kernel_policy_result drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
7371 | |
7372 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
7373 | if (necp_kernel_application_policies_count == 0 && necp_drop_management_order == 0) { |
7374 | if (necp_drop_all_order > 0 || drop_order > 0) { |
7375 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
7376 | lck_rw_done(lck: &necp_kernel_policy_lock); |
7377 | if (cred != NULL) { |
7378 | kauth_cred_unref(&cred); |
7379 | } |
7380 | return 0; |
7381 | } |
7382 | } |
7383 | lck_rw_done(lck: &necp_kernel_policy_lock); |
7384 | |
7385 | while ((offset + sizeof(u_int8_t) + sizeof(u_int32_t)) <= parameters_size) { |
7386 | u_int8_t type = necp_buffer_get_tlv_type(buffer: parameters, tlv_offset: offset); |
7387 | u_int32_t length = necp_buffer_get_tlv_length(buffer: parameters, tlv_offset: offset); |
7388 | |
7389 | if (length > (parameters_size - (offset + sizeof(u_int8_t) + sizeof(u_int32_t)))) { |
7390 | // If the length is larger than what can fit in the remaining parameters size, bail |
7391 | NECPLOG(LOG_ERR, "Invalid TLV length (%u)" , length); |
7392 | break; |
7393 | } |
7394 | |
7395 | if (length > 0) { |
7396 | u_int8_t *value = necp_buffer_get_tlv_value(buffer: parameters, tlv_offset: offset, NULL); |
7397 | if (value != NULL) { |
7398 | switch (type) { |
7399 | case NECP_CLIENT_PARAMETER_APPLICATION: { |
7400 | if (length >= sizeof(uuid_t)) { |
7401 | if (uuid_compare(uu1: application_uuid, uu2: value) == 0) { |
7402 | // No delegation |
7403 | break; |
7404 | } |
7405 | |
7406 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, cred, "euuid" ); |
7407 | |
7408 | is_delegated = true; |
7409 | uuid_copy(dst: application_uuid, src: value); |
7410 | } |
7411 | break; |
7412 | } |
7413 | case NECP_CLIENT_PARAMETER_REAL_APPLICATION: { |
7414 | if (length >= sizeof(uuid_t)) { |
7415 | if (uuid_compare(uu1: real_application_uuid, uu2: value) == 0) { |
7416 | // No delegation |
7417 | break; |
7418 | } |
7419 | |
7420 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, cred, "uuid" ); |
7421 | |
7422 | is_delegated = true; |
7423 | uuid_copy(dst: real_application_uuid, src: value); |
7424 | } |
7425 | break; |
7426 | } |
7427 | case NECP_CLIENT_PARAMETER_PID: { |
7428 | if (length >= sizeof(pid_t)) { |
7429 | if (memcmp(s1: &pid, s2: value, n: sizeof(pid_t)) == 0) { |
7430 | // No delegation |
7431 | break; |
7432 | } |
7433 | |
7434 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, cred, "pid" ); |
7435 | |
7436 | is_delegated = true; |
7437 | memcpy(dst: &pid, src: value, n: sizeof(pid_t)); |
7438 | } |
7439 | break; |
7440 | } |
7441 | case NECP_CLIENT_PARAMETER_UID: { |
7442 | if (length >= sizeof(uid_t)) { |
7443 | if (memcmp(s1: &uid, s2: value, n: sizeof(uid_t)) == 0) { |
7444 | // No delegation |
7445 | break; |
7446 | } |
7447 | |
7448 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, cred, "uid" ); |
7449 | |
7450 | is_delegated = true; |
7451 | memcpy(dst: &uid, src: value, n: sizeof(uid_t)); |
7452 | } |
7453 | break; |
7454 | } |
7455 | case NECP_CLIENT_PARAMETER_DOMAIN: { |
7456 | domain = (char *)value; |
7457 | domain[length - 1] = 0; |
7458 | break; |
7459 | } |
7460 | case NECP_CLIENT_PARAMETER_URL: { |
7461 | url = (char *)value; |
7462 | url[length - 1] = 0; |
7463 | break; |
7464 | } |
7465 | case NECP_CLIENT_PARAMETER_ACCOUNT: { |
7466 | account = (char *)value; |
7467 | account[length - 1] = 0; |
7468 | break; |
7469 | } |
7470 | case NECP_CLIENT_PARAMETER_TRAFFIC_CLASS: { |
7471 | if (length >= sizeof(u_int32_t)) { |
7472 | memcpy(dst: &traffic_class, src: value, n: sizeof(u_int32_t)); |
7473 | } |
7474 | break; |
7475 | } |
7476 | case NECP_CLIENT_PARAMETER_IP_PROTOCOL: { |
7477 | if (length >= sizeof(u_int16_t)) { |
7478 | memcpy(dst: &protocol, src: value, n: sizeof(u_int16_t)); |
7479 | } else if (length >= sizeof(u_int8_t)) { |
7480 | memcpy(dst: &protocol, src: value, n: sizeof(u_int8_t)); |
7481 | } |
7482 | break; |
7483 | } |
7484 | case NECP_CLIENT_PARAMETER_BOUND_INTERFACE: { |
7485 | if (length <= IFXNAMSIZ && length > 0) { |
7486 | ifnet_t bound_interface = NULL; |
7487 | char interface_name[IFXNAMSIZ]; |
7488 | memcpy(dst: interface_name, src: value, n: length); |
7489 | interface_name[length - 1] = 0; // Make sure the string is NULL terminated |
7490 | if (ifnet_find_by_name(ifname: interface_name, interface: &bound_interface) == 0) { |
7491 | bound_interface_index = bound_interface->if_index; |
7492 | ifnet_release(interface: bound_interface); |
7493 | } |
7494 | } |
7495 | break; |
7496 | } |
7497 | case NECP_CLIENT_PARAMETER_LOCAL_ADDRESS: { |
7498 | if (ignore_address || override_local_addr) { |
7499 | break; |
7500 | } |
7501 | |
7502 | if (length >= sizeof(struct necp_policy_condition_addr)) { |
7503 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value; |
7504 | if (necp_address_is_valid(&address_struct->address.sa)) { |
7505 | memcpy(dst: &local_addr, src: &address_struct->address, n: sizeof(address_struct->address)); |
7506 | } |
7507 | } |
7508 | break; |
7509 | } |
7510 | case NECP_CLIENT_PARAMETER_REMOTE_ADDRESS: { |
7511 | if (ignore_address || override_remote_addr) { |
7512 | break; |
7513 | } |
7514 | |
7515 | if (length >= sizeof(struct necp_policy_condition_addr)) { |
7516 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value; |
7517 | if (necp_address_is_valid(&address_struct->address.sa)) { |
7518 | memcpy(dst: &remote_addr, src: &address_struct->address, n: sizeof(address_struct->address)); |
7519 | } |
7520 | } |
7521 | break; |
7522 | } |
7523 | case NECP_CLIENT_PARAMETER_LOCAL_ENDPOINT: { |
7524 | if (ignore_address || override_local_addr) { |
7525 | break; |
7526 | } |
7527 | |
7528 | if (length >= sizeof(struct necp_client_endpoint)) { |
7529 | struct necp_client_endpoint *endpoint = (struct necp_client_endpoint *)(void *)value; |
7530 | if (endpoint->u.endpoint.endpoint_family == AF_UNSPEC && |
7531 | endpoint->u.endpoint.endpoint_port != 0) { |
7532 | // Save port |
7533 | local_port = endpoint->u.endpoint.endpoint_port; |
7534 | } |
7535 | } |
7536 | break; |
7537 | } |
7538 | case NECP_CLIENT_PARAMETER_REMOTE_ENDPOINT: { |
7539 | if (ignore_address || override_remote_addr) { |
7540 | break; |
7541 | } |
7542 | |
7543 | if (length >= sizeof(struct necp_client_endpoint)) { |
7544 | struct necp_client_endpoint *endpoint = (struct necp_client_endpoint *)(void *)value; |
7545 | if (endpoint->u.endpoint.endpoint_family == AF_UNSPEC) { |
7546 | remote_endpoint_type = endpoint->u.endpoint.endpoint_type; |
7547 | if (endpoint->u.endpoint.endpoint_port != 0) { |
7548 | // Save port |
7549 | remote_port = endpoint->u.endpoint.endpoint_port; |
7550 | } |
7551 | } else if (necp_addr_is_empty(addr: &endpoint->u.sa)) { |
7552 | remote_address_is_empty = true; |
7553 | } |
7554 | } |
7555 | break; |
7556 | } |
7557 | case NECP_CLIENT_PARAMETER_FLAGS: { |
7558 | if (length >= sizeof(client_flags)) { |
7559 | memcpy(dst: &client_flags, src: value, n: sizeof(client_flags)); |
7560 | } |
7561 | break; |
7562 | } |
7563 | case NECP_CLIENT_PARAMETER_REQUIRE_AGENT_TYPE: |
7564 | case NECP_CLIENT_PARAMETER_PREFER_AGENT_TYPE: { |
7565 | if (num_required_agent_types >= NECP_MAX_REQUIRED_AGENTS) { |
7566 | break; |
7567 | } |
7568 | if (length >= sizeof(struct necp_client_parameter_netagent_type)) { |
7569 | memcpy(dst: &required_agent_types[num_required_agent_types], src: value, n: sizeof(struct necp_client_parameter_netagent_type)); |
7570 | num_required_agent_types++; |
7571 | } |
7572 | break; |
7573 | } |
7574 | case NECP_CLIENT_PARAMETER_SCHEME_PORT: { |
7575 | if (length >= sizeof(scheme_port)) { |
7576 | memcpy(dst: &scheme_port, src: value, n: sizeof(scheme_port)); |
7577 | } |
7578 | break; |
7579 | } |
7580 | case NECP_CLIENT_PARAMETER_RESOLVER_TAG: { |
7581 | has_system_signed_result = true; |
7582 | struct necp_client_validatable *validatable = (struct necp_client_validatable *)value; |
7583 | if (length >= sizeof(struct necp_client_validatable)) { |
7584 | // Check for system-signed sign_type values |
7585 | if (validatable->signable.sign_type == NECP_CLIENT_SIGN_TYPE_SYSTEM_RESOLVER_ANSWER || |
7586 | validatable->signable.sign_type == NECP_CLIENT_SIGN_TYPE_SYSTEM_BROWSE_RESULT || |
7587 | validatable->signable.sign_type == NECP_CLIENT_SIGN_TYPE_SYSTEM_SERVICE_RESOLVER_ANSWER) { |
7588 | has_system_signed_result = true; |
7589 | } |
7590 | } |
7591 | break; |
7592 | } |
7593 | default: { |
7594 | break; |
7595 | } |
7596 | } |
7597 | } |
7598 | } |
7599 | |
7600 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
7601 | } |
7602 | |
7603 | // Check for loopback exception |
7604 | if (necp_pass_loopback > 0 && necp_is_loopback(SA(&local_addr.sa), SA(&remote_addr.sa), NULL, NULL, bound_interface_index)) { |
7605 | bypass_type = NECP_BYPASS_TYPE_LOOPBACK; |
7606 | } else if (bound_interface_index != IFSCOPE_NONE) { |
7607 | // Check for inter-process exception |
7608 | struct sockaddr *dst = SA(&remote_addr.sa); |
7609 | if (dst->sa_family == AF_INET6) { |
7610 | struct in6_addr *addrv6 = &(((struct sockaddr_in6 *)(void *)dst)->sin6_addr); |
7611 | if (NECP_IS_INTCOPROC_ADDRESS(addrv6)) { |
7612 | ifnet_head_lock_shared(); |
7613 | ifnet_t bound_interface = ifindex2ifnet[bound_interface_index]; |
7614 | if (bound_interface != NULL && IFNET_IS_INTCOPROC(bound_interface)) { |
7615 | bypass_type = NECP_BYPASS_TYPE_INTCOPROC; |
7616 | } |
7617 | ifnet_head_done(); |
7618 | } |
7619 | } |
7620 | } |
7621 | |
7622 | if (bypass_type == NECP_BYPASS_TYPE_INTCOPROC || (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && necp_pass_loopback == NECP_LOOPBACK_PASS_ALL)) { |
7623 | returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7624 | returned_result->skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7625 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_PASS; |
7626 | if (bypass_type == NECP_BYPASS_TYPE_LOOPBACK) { |
7627 | returned_result->routed_interface_index = lo_ifp->if_index; |
7628 | *flags |= (NECP_CLIENT_RESULT_FLAG_IS_LOCAL | NECP_CLIENT_RESULT_FLAG_IS_DIRECT); |
7629 | } else { |
7630 | returned_result->routed_interface_index = bound_interface_index; |
7631 | } |
7632 | if (cred != NULL) { |
7633 | kauth_cred_unref(&cred); |
7634 | } |
7635 | return 0; |
7636 | } |
7637 | |
7638 | if (drop_order != 0) { |
7639 | if (remote_endpoint_type == NECP_CLIENT_ENDPOINT_TYPE_APPLICATION_SERVICE || |
7640 | client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER || |
7641 | ((client_flags & NECP_CLIENT_PARAMETER_FLAG_INBOUND) && remote_address_is_empty)) { |
7642 | // Allow listeners, inbound connections without remote addresses, and |
7643 | // application service connections to bypass the unentitled drop order, |
7644 | // to allow them to connect to application services (not directly over |
7645 | // physical networking interfaces) |
7646 | drop_order = 0; |
7647 | } |
7648 | } |
7649 | |
7650 | if (proc_pid(effective_proc) != pid) { |
7651 | proc_t found_proc = proc_find(pid); |
7652 | if (found_proc != PROC_NULL) { |
7653 | effective_proc = found_proc; |
7654 | pid_version = proc_pidversion(effective_proc); |
7655 | release_eproc = true; |
7656 | } |
7657 | } |
7658 | #if defined(XNU_TARGET_OS_OSX) |
7659 | if (effective_proc->p_responsible_pid > 0 && effective_proc->p_responsible_pid != pid) { |
7660 | proc_getresponsibleuuid(effective_proc, responsible_application_uuid, sizeof(responsible_application_uuid)); |
7661 | responsible_proc = proc_find(pid: effective_proc->p_responsible_pid); |
7662 | } |
7663 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
7664 | |
7665 | // Lock |
7666 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
7667 | |
7668 | u_int32_t route_rule_id_array[MAX_AGGREGATE_ROUTE_RULES]; |
7669 | size_t route_rule_id_array_count = 0; |
7670 | necp_application_fillout_info_locked(task, application_uuid, real_application_uuid, responsible_application_uuid, account, domain, url, pid, pid_version, uid, real_uid, protocol, bound_interface_index, traffic_class, local_addr: &local_addr, remote_addr: &remote_addr, local_port, remote_port, has_client, has_system_signed_result, real_proc: proc, proc: effective_proc, responsible_proc, drop_order, client_flags, scheme_port, info: &info, is_loopback: (bypass_type == NECP_BYPASS_TYPE_LOOPBACK), is_delegated); |
7671 | |
7672 | int debug = NECP_ENABLE_DATA_TRACE((&info.local_addr), (&info.remote_addr), info.protocol, info.pid, info.bound_interface_index); |
7673 | NECP_DATA_TRACE_LOG_APP_LEVEL(debug, "APPLICATION" , "START" , 0, 0); |
7674 | |
7675 | necp_kernel_policy_id skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
7676 | matched_policy = necp_socket_find_policy_match_with_info_locked(policy_search_array: necp_kernel_socket_policies_app_layer_map, info: &info, return_filter: &filter_control_unit, return_route_rule_id_array: route_rule_id_array, return_route_rule_id_array_count: &route_rule_id_array_count, MAX_AGGREGATE_ROUTE_RULES, return_service_action: &service_action, return_service: &service, return_netagent_array: netagent_ids, return_netagent_use_flags_array: netagent_use_flags, NECP_MAX_NETAGENTS, required_agent_types, num_required_agent_types, proc: info.used_responsible_pid ? responsible_proc : effective_proc, pf_tag: 0, skip_policy_id: &skip_policy_id, NULL, return_drop_dest_policy_result: &drop_dest_policy_result, return_drop_all_bypass: &drop_all_bypass, return_flow_divert_aggregate_unit: &flow_divert_aggregate_unit, NULL, debug); |
7677 | |
7678 | // Check for loopback exception again after the policy match |
7679 | if (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && |
7680 | necp_pass_loopback == NECP_LOOPBACK_PASS_WITH_FILTER && |
7681 | (matched_policy == NULL || matched_policy->result != NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT)) { |
7682 | if (filter_control_unit == NECP_FILTER_UNIT_NO_FILTER) { |
7683 | returned_result->filter_control_unit = 0; |
7684 | } else { |
7685 | returned_result->filter_control_unit = filter_control_unit; |
7686 | } |
7687 | |
7688 | if (flow_divert_aggregate_unit > 0) { |
7689 | returned_result->flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
7690 | } |
7691 | |
7692 | returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7693 | returned_result->skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7694 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_PASS; |
7695 | returned_result->routed_interface_index = lo_ifp->if_index; |
7696 | *flags |= (NECP_CLIENT_RESULT_FLAG_IS_LOCAL | NECP_CLIENT_RESULT_FLAG_IS_DIRECT); |
7697 | error = 0; |
7698 | goto done; |
7699 | } |
7700 | |
7701 | if (matched_policy) { |
7702 | returned_result->policy_id = matched_policy->id; |
7703 | returned_result->skip_policy_id = skip_policy_id; |
7704 | returned_result->routing_result = matched_policy->result; |
7705 | memcpy(dst: &returned_result->routing_result_parameter, src: &matched_policy->result_parameter, n: sizeof(returned_result->routing_result_parameter)); |
7706 | if (returned_override_euuid != NULL && info.used_responsible_pid && !(matched_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID)) { |
7707 | uuid_copy(dst: *returned_override_euuid, src: responsible_application_uuid); |
7708 | } |
7709 | } else { |
7710 | bool drop_all = false; |
7711 | if (necp_drop_all_order > 0 || info.drop_order > 0 || drop_dest_policy_result == NECP_KERNEL_POLICY_RESULT_DROP) { |
7712 | // Mark socket as a drop if drop_all is set |
7713 | drop_all = true; |
7714 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
7715 | drop_all_bypass = necp_check_drop_all_bypass_result(proc); |
7716 | } |
7717 | } |
7718 | if (drop_all && drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
7719 | returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7720 | returned_result->skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7721 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
7722 | NECP_DATA_TRACE_LOG_APP_LEVEL(debug, "APPLICATION" , "RESULT - DROP <NO MATCH>" , 0, 0); |
7723 | } else { |
7724 | returned_result->policy_id = 0; |
7725 | returned_result->skip_policy_id = 0; |
7726 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_NONE; |
7727 | } |
7728 | } |
7729 | if (necp_check_missing_client_drop(proc, info: &info) || |
7730 | necp_check_restricted_multicast_drop(proc, info: &info, false)) { |
7731 | // Mark as drop |
7732 | returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7733 | returned_result->skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7734 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
7735 | NECP_DATA_TRACE_LOG_APP_LEVEL(debug, "APPLICATION" , "RESULT - DROP <NO CLIENT / MULTICAST>" , 0, 0); |
7736 | } |
7737 | if (filter_control_unit == NECP_FILTER_UNIT_NO_FILTER) { |
7738 | returned_result->filter_control_unit = 0; |
7739 | } else { |
7740 | returned_result->filter_control_unit = filter_control_unit; |
7741 | } |
7742 | |
7743 | if (flow_divert_aggregate_unit > 0) { |
7744 | returned_result->flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
7745 | } |
7746 | |
7747 | returned_result->service_action = service_action; |
7748 | |
7749 | // Fetch service registration |
7750 | if (service.identifier != 0) { |
7751 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: service.identifier); |
7752 | if (mapping != NULL) { |
7753 | struct necp_service_registration *service_registration = NULL; |
7754 | uuid_copy(dst: returned_result->service_uuid, src: mapping->uuid); |
7755 | returned_result->service_data = service.data; |
7756 | if (service.identifier == NECP_NULL_SERVICE_ID) { |
7757 | // NULL service is always 'registered' |
7758 | returned_result->service_flags |= NECP_SERVICE_FLAGS_REGISTERED; |
7759 | } else { |
7760 | LIST_FOREACH(service_registration, &necp_registered_service_list, kernel_chain) { |
7761 | if (service.identifier == service_registration->service_id) { |
7762 | returned_result->service_flags |= NECP_SERVICE_FLAGS_REGISTERED; |
7763 | break; |
7764 | } |
7765 | } |
7766 | } |
7767 | } |
7768 | } |
7769 | |
7770 | // Handle netagents |
7771 | size_t netagent_i = 0; |
7772 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
7773 | struct necp_uuid_id_mapping *mapping = NULL; |
7774 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
7775 | if (netagent_id == 0) { |
7776 | continue; |
7777 | } |
7778 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: netagent_id); |
7779 | if (mapping != NULL) { |
7780 | uuid_copy(dst: returned_result->netagents[netagent_i], src: mapping->uuid); |
7781 | returned_result->netagent_use_flags[netagent_i] = netagent_use_flags[netagent_cursor]; |
7782 | netagent_i++; |
7783 | } |
7784 | |
7785 | // If the flags say to remove, clear the local copy |
7786 | if (netagent_use_flags[netagent_cursor] & NECP_AGENT_USE_FLAG_REMOVE) { |
7787 | netagent_ids[netagent_cursor] = 0; |
7788 | } |
7789 | } |
7790 | |
7791 | // Do routing evaluation |
7792 | u_int output_bound_interface = bound_interface_index; |
7793 | if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED) { |
7794 | output_bound_interface = returned_result->routing_result_parameter.scoped_interface_index; |
7795 | } else if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL) { |
7796 | output_bound_interface = returned_result->routing_result_parameter.tunnel_interface_index; |
7797 | } else if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT) { |
7798 | output_bound_interface = necp_get_primary_direct_interface_index(); |
7799 | if (output_bound_interface == IFSCOPE_NONE) { |
7800 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
7801 | } else { |
7802 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED; |
7803 | returned_result->routing_result_parameter.scoped_interface_index = output_bound_interface; |
7804 | } |
7805 | } |
7806 | |
7807 | if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_DROP && |
7808 | returned_result->routing_result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_LOCAL_NETWORK) { |
7809 | if (!(matched_policy->result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_SUPPRESS_ALERTS)) { |
7810 | // Trigger the event that we dropped due to a local network policy |
7811 | necp_send_network_denied_event(pid, proc_uuid: application_uuid, NETPOLICY_NETWORKTYPE_LOCAL); |
7812 | } |
7813 | if (reason != NULL) { |
7814 | *reason = NECP_CLIENT_RESULT_REASON_LOCAL_NETWORK_PROHIBITED; |
7815 | } |
7816 | } |
7817 | |
7818 | if (local_addr.sa.sa_len == 0 || |
7819 | (local_addr.sa.sa_family == AF_INET && local_addr.sin.sin_addr.s_addr == 0) || |
7820 | (local_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&local_addr.sin6.sin6_addr))) { |
7821 | no_local_addr = TRUE; |
7822 | } |
7823 | |
7824 | if (remote_addr.sa.sa_len == 0 || |
7825 | (remote_addr.sa.sa_family == AF_INET && remote_addr.sin.sin_addr.s_addr == 0) || |
7826 | (remote_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&remote_addr.sin6.sin6_addr))) { |
7827 | no_remote_addr = TRUE; |
7828 | remote_family = remote_addr.sa.sa_family; |
7829 | } |
7830 | |
7831 | returned_result->routed_interface_index = 0; |
7832 | struct rtentry *rt = NULL; |
7833 | if (!no_local_addr && (client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) != 0) { |
7834 | // Treat the output bound interface as the routed interface for local address |
7835 | // validation later. |
7836 | returned_result->routed_interface_index = output_bound_interface; |
7837 | } else { |
7838 | if (no_remote_addr) { |
7839 | memset(s: &remote_addr, c: 0, n: sizeof(remote_addr)); |
7840 | if (remote_family == AF_INET6) { |
7841 | // Reset address to :: |
7842 | remote_addr.sa.sa_family = AF_INET6; |
7843 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in6); |
7844 | } else { |
7845 | // Reset address to 0.0.0.0 |
7846 | remote_addr.sa.sa_family = AF_INET; |
7847 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in); |
7848 | } |
7849 | } |
7850 | |
7851 | rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, |
7852 | output_bound_interface); |
7853 | |
7854 | if (remote_addr.sa.sa_family == AF_INET && rt != NULL && |
7855 | IS_INTF_CLAT46(rt->rt_ifp)) { |
7856 | rtfree(rt); |
7857 | rt = NULL; |
7858 | returned_result->routed_interface_index = 0; |
7859 | } |
7860 | |
7861 | if (no_remote_addr && remote_family == AF_UNSPEC && |
7862 | (rt == NULL || rt->rt_ifp == NULL)) { |
7863 | // Route lookup for default IPv4 failed, try IPv6 |
7864 | |
7865 | // Cleanup old route if necessary |
7866 | if (rt != NULL) { |
7867 | rtfree(rt); |
7868 | rt = NULL; |
7869 | } |
7870 | |
7871 | // Reset address to :: |
7872 | memset(s: &remote_addr, c: 0, n: sizeof(remote_addr)); |
7873 | remote_addr.sa.sa_family = AF_INET6; |
7874 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in6); |
7875 | |
7876 | // Get route |
7877 | rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, |
7878 | output_bound_interface); |
7879 | } |
7880 | |
7881 | if (rt != NULL && |
7882 | rt->rt_ifp != NULL) { |
7883 | returned_result->routed_interface_index = rt->rt_ifp->if_index; |
7884 | /* |
7885 | * For local addresses, we allow the interface scope to be |
7886 | * either the loopback interface or the interface hosting the |
7887 | * local address. |
7888 | */ |
7889 | if (bound_interface_index != IFSCOPE_NONE && |
7890 | rt->rt_ifa != NULL && rt->rt_ifa->ifa_ifp && |
7891 | (output_bound_interface == lo_ifp->if_index || |
7892 | rt->rt_ifp->if_index == lo_ifp->if_index || |
7893 | rt->rt_ifa->ifa_ifp->if_index == bound_interface_index)) { |
7894 | struct sockaddr_storage dst; |
7895 | unsigned int ifscope = bound_interface_index; |
7896 | |
7897 | /* |
7898 | * Transform dst into the internal routing table form |
7899 | */ |
7900 | (void) sa_copy((struct sockaddr *)&remote_addr, |
7901 | &dst, &ifscope); |
7902 | |
7903 | if ((rt->rt_ifp->if_index == lo_ifp->if_index) || |
7904 | rt_ifa_is_dst((struct sockaddr *)&dst, rt->rt_ifa)) { |
7905 | returned_result->routed_interface_index = |
7906 | bound_interface_index; |
7907 | } |
7908 | } |
7909 | } |
7910 | } |
7911 | |
7912 | if (returned_result->routed_interface_index != 0 && |
7913 | returned_result->routed_interface_index != lo_ifp->if_index && // Loopback can accept any local address |
7914 | !no_local_addr) { |
7915 | // Transform local_addr into the ifaddr form |
7916 | // IPv6 Scope IDs are always embedded in the ifaddr list |
7917 | struct sockaddr_storage local_address_sanitized; |
7918 | u_int ifscope = IFSCOPE_NONE; |
7919 | (void)sa_copy(SA(&local_addr.sa), &local_address_sanitized, &ifscope); |
7920 | SIN(&local_address_sanitized)->sin_port = 0; |
7921 | if (local_address_sanitized.ss_family == AF_INET6) { |
7922 | if (in6_embedded_scope || !IN6_IS_SCOPE_EMBED(&SIN6(&local_address_sanitized)->sin6_addr)) { |
7923 | SIN6(&local_address_sanitized)->sin6_scope_id = 0; |
7924 | } |
7925 | } |
7926 | |
7927 | // Validate local address on routed interface |
7928 | struct ifaddr *ifa = ifa_ifwithaddr_scoped((struct sockaddr *)&local_address_sanitized, returned_result->routed_interface_index); |
7929 | if (ifa == NULL) { |
7930 | // Interface address not found, reject route |
7931 | returned_result->routed_interface_index = 0; |
7932 | if (rt != NULL) { |
7933 | rtfree(rt); |
7934 | rt = NULL; |
7935 | } |
7936 | } else { |
7937 | ifaddr_release(ifaddr: ifa); |
7938 | ifa = NULL; |
7939 | } |
7940 | } |
7941 | |
7942 | if (flags != NULL) { |
7943 | #if SKYWALK && defined(XNU_TARGET_OS_OSX) |
7944 | enum net_filter_event_subsystems filters = net_filter_event_get_state(); |
7945 | |
7946 | if (filters & (NET_FILTER_EVENT_SOCKET | NET_FILTER_EVENT_INTERFACE | NET_FILTER_EVENT_IP)) { |
7947 | *flags |= NECP_CLIENT_RESULT_FLAG_KEXT_FILTER_PRESENT; |
7948 | } |
7949 | if (filters & NET_FILTER_EVENT_PF_PRIVATE_PROXY) { |
7950 | *flags |= NECP_CLIENT_RESULT_FLAG_PF_RULES_PRESENT; |
7951 | } |
7952 | if (filters & NET_FILTER_EVENT_ALF) { |
7953 | *flags |= NECP_CLIENT_RESULT_FLAG_ALF_PRESENT; |
7954 | } |
7955 | if (filters & NET_FILTER_EVENT_PARENTAL_CONTROLS) { |
7956 | *flags |= NECP_CLIENT_RESULT_FLAG_PARENTAL_CONTROLS_PRESENT; |
7957 | } |
7958 | #endif /* SKYWALK && defined(XNU_TARGET_OS_OSX) */ |
7959 | if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) == 0) { |
7960 | // Check for local/direct |
7961 | bool is_local = FALSE; |
7962 | if (rt != NULL && (rt->rt_flags & RTF_LOCAL)) { |
7963 | is_local = TRUE; |
7964 | } else if (returned_result->routed_interface_index != 0 && |
7965 | !no_remote_addr) { |
7966 | // Clean up the address before comparison with interface addresses |
7967 | |
7968 | // Transform remote_addr into the ifaddr form |
7969 | // IPv6 Scope IDs are always embedded in the ifaddr list |
7970 | struct sockaddr_storage remote_address_sanitized; |
7971 | u_int ifscope = IFSCOPE_NONE; |
7972 | (void)sa_copy(SA(&remote_addr.sa), &remote_address_sanitized, &ifscope); |
7973 | SIN(&remote_address_sanitized)->sin_port = 0; |
7974 | if (remote_address_sanitized.ss_family == AF_INET6) { |
7975 | if (in6_embedded_scope || !IN6_IS_SCOPE_EMBED(&SIN6(&remote_address_sanitized)->sin6_addr)) { |
7976 | SIN6(&remote_address_sanitized)->sin6_scope_id = 0; |
7977 | } |
7978 | } |
7979 | |
7980 | // Check if remote address is an interface address |
7981 | struct ifaddr *ifa = ifa_ifwithaddr((struct sockaddr *)&remote_address_sanitized); |
7982 | if (ifa != NULL && ifa->ifa_ifp != NULL) { |
7983 | u_int if_index_for_remote_addr = ifa->ifa_ifp->if_index; |
7984 | if (if_index_for_remote_addr == returned_result->routed_interface_index || |
7985 | if_index_for_remote_addr == lo_ifp->if_index) { |
7986 | is_local = TRUE; |
7987 | } |
7988 | } |
7989 | if (ifa != NULL) { |
7990 | ifaddr_release(ifaddr: ifa); |
7991 | ifa = NULL; |
7992 | } |
7993 | } |
7994 | |
7995 | if (is_local) { |
7996 | *flags |= (NECP_CLIENT_RESULT_FLAG_IS_LOCAL | NECP_CLIENT_RESULT_FLAG_IS_DIRECT); |
7997 | } else if (rt != NULL) { |
7998 | if (rt->rt_flags & RTF_GLOBAL) { |
7999 | *flags |= NECP_CLIENT_RESULT_FLAG_IS_GLOBAL_INTERNET; |
8000 | } else if (!(rt->rt_flags & RTF_GATEWAY) && |
8001 | (rt->rt_ifa && rt->rt_ifa->ifa_ifp && !(rt->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) { |
8002 | // Route is directly accessible |
8003 | *flags |= NECP_CLIENT_RESULT_FLAG_IS_DIRECT; |
8004 | } |
8005 | } |
8006 | |
8007 | if (rt != NULL && |
8008 | rt->rt_ifp != NULL) { |
8009 | // Check probe status |
8010 | if (rt->rt_ifp->if_eflags & IFEF_PROBE_CONNECTIVITY) { |
8011 | *flags |= NECP_CLIENT_RESULT_FLAG_PROBE_CONNECTIVITY; |
8012 | } |
8013 | |
8014 | if (rt->rt_ifp->if_type == IFT_CELLULAR) { |
8015 | struct if_cellular_status_v1 *ifsr; |
8016 | |
8017 | ifnet_lock_shared(ifp: rt->rt_ifp); |
8018 | lck_rw_lock_exclusive(lck: &rt->rt_ifp->if_link_status_lock); |
8019 | |
8020 | if (rt->rt_ifp->if_link_status != NULL) { |
8021 | ifsr = &rt->rt_ifp->if_link_status->ifsr_u.ifsr_cell.if_cell_u.if_status_v1; |
8022 | |
8023 | if (ifsr->valid_bitmask & IF_CELL_UL_MSS_RECOMMENDED_VALID) { |
8024 | if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_NONE) { |
8025 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_NONE; |
8026 | } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_MEDIUM) { |
8027 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_MEDIUM; |
8028 | } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_LOW) { |
8029 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_LOW; |
8030 | } |
8031 | } |
8032 | } |
8033 | lck_rw_done(lck: &rt->rt_ifp->if_link_status_lock); |
8034 | ifnet_lock_done(ifp: rt->rt_ifp); |
8035 | } |
8036 | |
8037 | // Check link quality |
8038 | if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_DISCRETIONARY) && |
8039 | (rt->rt_ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
8040 | rt->rt_ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
8041 | *flags |= NECP_CLIENT_RESULT_FLAG_LINK_QUALITY_ABORT; |
8042 | } |
8043 | |
8044 | // Check QoS marking (fastlane) |
8045 | for (size_t route_rule_index = 0; route_rule_index < route_rule_id_array_count; route_rule_index++) { |
8046 | if (necp_update_qos_marking(ifp: rt->rt_ifp, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, route_rule_id: route_rule_id_array[route_rule_index])) { |
8047 | *flags |= NECP_CLIENT_RESULT_FLAG_ALLOW_QOS_MARKING; |
8048 | // If the route can use QoS markings, stop iterating route rules |
8049 | break; |
8050 | } |
8051 | } |
8052 | |
8053 | if (IFNET_IS_LOW_POWER(rt->rt_ifp)) { |
8054 | *flags |= NECP_CLIENT_RESULT_FLAG_INTERFACE_LOW_POWER; |
8055 | } |
8056 | |
8057 | if (traffic_class == SO_TC_BK_SYS) { |
8058 | // Block BK_SYS traffic if interface is throttled |
8059 | u_int32_t throttle_level = 0; |
8060 | if (ifnet_get_throttle(rt->rt_ifp, &throttle_level) == 0) { |
8061 | if (throttle_level == IFNET_THROTTLE_OPPORTUNISTIC) { |
8062 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
8063 | memset(s: &returned_result->routing_result_parameter, c: 0, n: sizeof(returned_result->routing_result_parameter)); |
8064 | } |
8065 | } |
8066 | } |
8067 | } |
8068 | } |
8069 | |
8070 | u_int interface_to_check = returned_result->routed_interface_index; |
8071 | if (interface_to_check == 0) { |
8072 | interface_to_check = output_bound_interface; |
8073 | } |
8074 | union necp_sockaddr_union default_address; |
8075 | struct rtentry *v4Route = NULL; |
8076 | struct rtentry *v6Route = NULL; |
8077 | |
8078 | memset(s: &default_address, c: 0, n: sizeof(default_address)); |
8079 | |
8080 | // Reset address to 0.0.0.0 |
8081 | default_address.sa.sa_family = AF_INET; |
8082 | default_address.sa.sa_len = sizeof(struct sockaddr_in); |
8083 | v4Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0, |
8084 | returned_result->routed_interface_index); |
8085 | |
8086 | // Reset address to :: |
8087 | default_address.sa.sa_family = AF_INET6; |
8088 | default_address.sa.sa_len = sizeof(struct sockaddr_in6); |
8089 | v6Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0, |
8090 | returned_result->routed_interface_index); |
8091 | |
8092 | if (v4Route != NULL) { |
8093 | if (v4Route->rt_ifp != NULL && !IS_INTF_CLAT46(v4Route->rt_ifp)) { |
8094 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV4; |
8095 | } |
8096 | if (returned_v4_gateway != NULL && |
8097 | v4Route->rt_gateway != NULL && |
8098 | v4Route->rt_gateway->sa_len == sizeof(returned_v4_gateway->u.sin)) { |
8099 | memcpy(dst: &returned_v4_gateway->u.sin, src: v4Route->rt_gateway, n: sizeof(returned_v4_gateway->u.sin)); |
8100 | memset(s: &returned_v4_gateway->u.sin.sin_zero, c: 0, n: sizeof(returned_v4_gateway->u.sin.sin_zero)); |
8101 | } |
8102 | rtfree(v4Route); |
8103 | v4Route = NULL; |
8104 | } |
8105 | |
8106 | if (v6Route != NULL) { |
8107 | if (v6Route->rt_ifp != NULL) { |
8108 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV6; |
8109 | |
8110 | if (ifnet_get_nat64prefix(v6Route->rt_ifp, returned_result->nat64_prefixes) == 0) { |
8111 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_NAT64; |
8112 | } |
8113 | } |
8114 | if (returned_v6_gateway != NULL && |
8115 | v6Route->rt_gateway != NULL && |
8116 | v6Route->rt_gateway->sa_len == sizeof(returned_v6_gateway->u.sin6)) { |
8117 | memcpy(dst: &returned_v6_gateway->u.sin6, src: v6Route->rt_gateway, n: sizeof(returned_v6_gateway->u.sin6)); |
8118 | } |
8119 | rtfree(v6Route); |
8120 | v6Route = NULL; |
8121 | } |
8122 | } |
8123 | |
8124 | // Take two passes through the rule list: first for rules that don't match based on agents, |
8125 | // second for rules that match based on agents. Since rules can modify the agent list itself, |
8126 | // this makes the logic more deterministic. This allows a non-agent matching rule to remove |
8127 | // an agent before it is used for matching later. |
8128 | size_t route_rule_index = 0; |
8129 | bool second_pass = false; |
8130 | while (route_rule_index < route_rule_id_array_count) { |
8131 | bool rule_matches_agents = necp_route_rule_matches_agents(route_rule_id: route_rule_id_array[route_rule_index]); |
8132 | if (rule_matches_agents != second_pass) { |
8133 | // Process rules that match based on agents only in the second pass |
8134 | route_rule_index++; |
8135 | if (route_rule_index == route_rule_id_array_count && !second_pass) { |
8136 | route_rule_index = 0; |
8137 | second_pass = true; |
8138 | } |
8139 | continue; |
8140 | } |
8141 | |
8142 | u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN; |
8143 | bool route_is_allowed = necp_route_is_allowed(route: rt, NULL, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, route_rule_id: route_rule_id_array[route_rule_index], interface_type_denied: &interface_type_denied); |
8144 | if (!route_is_allowed) { |
8145 | // If the route is blocked, treat the lookup as a drop |
8146 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
8147 | memset(s: &returned_result->routing_result_parameter, c: 0, n: sizeof(returned_result->routing_result_parameter)); |
8148 | |
8149 | if (interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) { |
8150 | if (reason != NULL) { |
8151 | if (interface_type_denied == IFRTYPE_FUNCTIONAL_CELLULAR) { |
8152 | *reason = NECP_CLIENT_RESULT_REASON_CELLULAR_DENIED; |
8153 | } else if (interface_type_denied == IFRTYPE_FUNCTIONAL_WIFI_INFRA) { |
8154 | *reason = NECP_CLIENT_RESULT_REASON_WIFI_DENIED; |
8155 | } |
8156 | } |
8157 | necp_send_application_interface_denied_event(pid, proc_uuid: application_uuid, if_functional_type: interface_type_denied); |
8158 | } |
8159 | // If the route gets denied, stop matching rules |
8160 | break; |
8161 | } |
8162 | |
8163 | // Check if there is a route rule that adds flow divert, if we don't already have a terminal policy result |
8164 | if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_NONE) { |
8165 | u_int32_t flow_divert_control_unit = necp_route_get_flow_divert(route: rt, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, |
8166 | route_rule_id: route_rule_id_array[route_rule_index], flow_divert_aggregate_unit: &flow_divert_aggregate_unit); |
8167 | if (flow_divert_control_unit != 0) { |
8168 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT; |
8169 | returned_result->routing_result_parameter.flow_divert_control_unit = flow_divert_control_unit; |
8170 | } |
8171 | if (flow_divert_aggregate_unit != 0) { |
8172 | returned_result->flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
8173 | } |
8174 | } |
8175 | |
8176 | // Check if there is a route rule that adds or removes an agent |
8177 | bool remove = false; |
8178 | u_int32_t netagent_id = necp_route_get_netagent(route: rt, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, route_rule_id: route_rule_id_array[route_rule_index], remove: &remove); |
8179 | if (netagent_id != 0) { |
8180 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: netagent_id); |
8181 | if (mapping != NULL) { |
8182 | bool agent_already_present = false; |
8183 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
8184 | if (uuid_compare(uu1: returned_result->netagents[netagent_cursor], uu2: mapping->uuid) == 0) { |
8185 | // Found the agent already present |
8186 | agent_already_present = true; |
8187 | if (remove) { |
8188 | // Mark as remove if necessary |
8189 | returned_result->netagent_use_flags[netagent_cursor] = NECP_AGENT_USE_FLAG_REMOVE; |
8190 | } |
8191 | } else if (uuid_is_null(uu: returned_result->netagents[netagent_cursor])) { |
8192 | // Found open slot |
8193 | if (!agent_already_present) { |
8194 | uuid_copy(dst: returned_result->netagents[netagent_cursor], src: mapping->uuid); |
8195 | if (remove) { |
8196 | returned_result->netagent_use_flags[netagent_cursor] = NECP_AGENT_USE_FLAG_REMOVE; |
8197 | } else { |
8198 | returned_result->netagent_use_flags[netagent_cursor] = 0; |
8199 | } |
8200 | } |
8201 | break; |
8202 | } |
8203 | } |
8204 | } |
8205 | |
8206 | // Update the local netagent_ids array for future evaluations |
8207 | if (remove) { |
8208 | // Check if the agent ID is in the array, and remove it |
8209 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
8210 | if (netagent_id == netagent_ids[netagent_cursor]) { |
8211 | netagent_ids[netagent_cursor] = 0; |
8212 | } |
8213 | } |
8214 | } else { |
8215 | // Check if the agent ID is not yet in the array, and add it |
8216 | bool found = false; |
8217 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
8218 | if (netagent_id == netagent_ids[netagent_cursor]) { |
8219 | found = true; |
8220 | break; |
8221 | } |
8222 | } |
8223 | if (!found) { |
8224 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
8225 | if (netagent_ids[netagent_cursor] == 0) { |
8226 | // Empty slot, add the agent |
8227 | netagent_ids[netagent_cursor] = netagent_id; |
8228 | break; |
8229 | } |
8230 | } |
8231 | } |
8232 | } |
8233 | } |
8234 | |
8235 | route_rule_index++; |
8236 | if (route_rule_index == route_rule_id_array_count && !second_pass) { |
8237 | route_rule_index = 0; |
8238 | second_pass = true; |
8239 | } |
8240 | } |
8241 | |
8242 | if (rt != NULL && rt->rt_ifp != NULL) { |
8243 | const bool is_listener = ((client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) != 0); |
8244 | const bool is_browser = ((client_flags & NECP_CLIENT_PARAMETER_FLAG_BROWSE) != 0); |
8245 | const bool expensive_prohibited = ((client_flags & NECP_CLIENT_PARAMETER_FLAG_PROHIBIT_EXPENSIVE) && |
8246 | IFNET_IS_EXPENSIVE(rt->rt_ifp)); |
8247 | const bool constrained_prohibited = ((client_flags & NECP_CLIENT_PARAMETER_FLAG_PROHIBIT_CONSTRAINED) && |
8248 | IFNET_IS_CONSTRAINED(rt->rt_ifp)); |
8249 | const bool interface_type_blocked = !necp_route_is_interface_type_allowed(route: rt, NULL, proc, NULL); |
8250 | if (!is_listener && !is_browser) { |
8251 | if (reason != NULL) { |
8252 | if (expensive_prohibited) { |
8253 | *reason = NECP_CLIENT_RESULT_REASON_EXPENSIVE_PROHIBITED; |
8254 | } else if (constrained_prohibited) { |
8255 | *reason = NECP_CLIENT_RESULT_REASON_CONSTRAINED_PROHIBITED; |
8256 | } |
8257 | } |
8258 | if (expensive_prohibited || constrained_prohibited || interface_type_blocked) { |
8259 | // If a property of the interface was not allowed, treat it as a drop |
8260 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
8261 | memset(s: &returned_result->routing_result_parameter, c: 0, n: sizeof(returned_result->routing_result_parameter)); |
8262 | } |
8263 | } |
8264 | } |
8265 | |
8266 | if (rt != NULL) { |
8267 | if (returned_route != NULL) { |
8268 | *returned_route = rt; |
8269 | } else { |
8270 | rtfree(rt); |
8271 | } |
8272 | rt = NULL; |
8273 | } |
8274 | |
8275 | done: |
8276 | // Unlock |
8277 | lck_rw_done(lck: &necp_kernel_policy_lock); |
8278 | |
8279 | if (release_eproc && effective_proc != PROC_NULL) { |
8280 | proc_rele(p: effective_proc); |
8281 | } |
8282 | #if defined(XNU_TARGET_OS_OSX) |
8283 | if (responsible_proc != PROC_NULL) { |
8284 | proc_rele(p: responsible_proc); |
8285 | } |
8286 | #endif |
8287 | |
8288 | if (cred != NULL) { |
8289 | kauth_cred_unref(&cred); |
8290 | } |
8291 | |
8292 | return error; |
8293 | } |
8294 | |
8295 | static bool |
8296 | necp_is_route_local(union necp_sockaddr_union *remote_addr) |
8297 | { |
8298 | struct rtentry *rt = NULL; |
8299 | bool is_local = FALSE; |
8300 | |
8301 | if (remote_addr == NULL) { |
8302 | return NULL; |
8303 | } |
8304 | |
8305 | if (remote_addr->sa.sa_len == 0 || |
8306 | (remote_addr->sa.sa_family == AF_INET && remote_addr->sin.sin_addr.s_addr == 0) || |
8307 | (remote_addr->sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&remote_addr->sin6.sin6_addr))) { |
8308 | return FALSE; |
8309 | } |
8310 | |
8311 | // Lookup route regardless of the scoped interface to check if |
8312 | // remote address is in a local network. |
8313 | rt = rtalloc1_scoped((struct sockaddr *)remote_addr, 0, 0, 0); |
8314 | |
8315 | if (rt == NULL) { |
8316 | goto done; |
8317 | } |
8318 | if (remote_addr->sa.sa_family == AF_INET && IS_INTF_CLAT46(rt->rt_ifp)) { |
8319 | goto free_rt; |
8320 | } |
8321 | is_local = IS_NECP_DEST_IN_LOCAL_NETWORKS(rt); |
8322 | |
8323 | free_rt: |
8324 | rtfree(rt); |
8325 | |
8326 | done: |
8327 | return is_local; |
8328 | } |
8329 | |
8330 | static bool |
8331 | necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_app_id app_id, necp_app_id real_app_id, uint8_t is_entitled, u_int32_t account_id, struct substring domain, u_int8_t domain_dot_count, const char *url, pid_t pid, int32_t pid_version, uid_t uid, uid_t real_uid, u_int32_t bound_interface_index, u_int32_t traffic_class, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote, struct necp_client_parameter_netagent_type *required_agent_types, u_int32_t num_required_agent_types, bool has_client, uint32_t client_flags, int is_platform_binary, bool has_signed_result, proc_t proc, u_int16_t pf_tag, u_int16_t scheme_port, struct rtentry *rt, bool is_loopback, int debug, bool real_is_platform_binary, u_int32_t bound_interface_flags, u_int32_t bound_interface_eflags, u_int32_t bound_interface_xflags, struct necp_socket_info *info, bool is_delegated) |
8332 | { |
8333 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
8334 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
8335 | u_int32_t cond_bound_interface_index = kernel_policy->cond_bound_interface ? kernel_policy->cond_bound_interface->if_index : 0; |
8336 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE, |
8337 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE" , cond_bound_interface_index, bound_interface_index); |
8338 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
8339 | if (bound_interface_index == cond_bound_interface_index) { |
8340 | // No match, matches forbidden interface |
8341 | return FALSE; |
8342 | } |
8343 | } else { |
8344 | if (bound_interface_index != cond_bound_interface_index) { |
8345 | // No match, does not match required interface |
8346 | return FALSE; |
8347 | } |
8348 | } |
8349 | } |
8350 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
8351 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
8352 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - flags" , kernel_policy->cond_bound_interface_flags, bound_interface_flags); |
8353 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
8354 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - eflags" , kernel_policy->cond_bound_interface_eflags, bound_interface_eflags); |
8355 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
8356 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - xflags" , kernel_policy->cond_bound_interface_xflags, bound_interface_xflags); |
8357 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
8358 | if ((kernel_policy->cond_bound_interface_flags && (bound_interface_flags & kernel_policy->cond_bound_interface_flags)) || |
8359 | (kernel_policy->cond_bound_interface_eflags && (bound_interface_eflags & kernel_policy->cond_bound_interface_eflags)) || |
8360 | (kernel_policy->cond_bound_interface_xflags && (bound_interface_xflags & kernel_policy->cond_bound_interface_xflags))) { |
8361 | // No match, matches some forbidden interface flags |
8362 | return FALSE; |
8363 | } |
8364 | } else { |
8365 | if ((kernel_policy->cond_bound_interface_flags && !(bound_interface_flags & kernel_policy->cond_bound_interface_flags)) || |
8366 | (kernel_policy->cond_bound_interface_eflags && !(bound_interface_eflags & kernel_policy->cond_bound_interface_eflags)) || |
8367 | (kernel_policy->cond_bound_interface_xflags && !(bound_interface_xflags & kernel_policy->cond_bound_interface_xflags))) { |
8368 | // No match, does not match some required interface xflags |
8369 | return FALSE; |
8370 | } |
8371 | } |
8372 | } |
8373 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) && |
8374 | !(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS)) { |
8375 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "Requiring no bound interface" , 0, bound_interface_index); |
8376 | if (bound_interface_index != 0) { |
8377 | // No match, requires a non-bound packet |
8378 | return FALSE; |
8379 | } |
8380 | } |
8381 | } |
8382 | |
8383 | if (kernel_policy->condition_mask == 0) { |
8384 | return TRUE; |
8385 | } |
8386 | |
8387 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
8388 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID, |
8389 | "NECP_KERNEL_CONDITION_APP_ID" , kernel_policy->cond_app_id, app_id); |
8390 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
8391 | if (app_id == kernel_policy->cond_app_id) { |
8392 | // No match, matches forbidden application |
8393 | return FALSE; |
8394 | } |
8395 | } else { |
8396 | if (app_id != kernel_policy->cond_app_id) { |
8397 | // No match, does not match required application |
8398 | return FALSE; |
8399 | } |
8400 | } |
8401 | |
8402 | // Check signing identifier only after APP ID matched |
8403 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER || |
8404 | kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER) { |
8405 | u_int8_t matched = necp_boolean_state_false; |
8406 | const char *signing_id = cs_identity_get(proc ? proc : current_proc()); |
8407 | NECP_DATA_TRACE_LOG_CONDITION_STR(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER, |
8408 | "NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER" , |
8409 | kernel_policy->cond_signing_identifier ? kernel_policy->cond_signing_identifier : "<n/a>" , |
8410 | signing_id ? signing_id : "<n/a>" ); |
8411 | if (signing_id != NULL) { |
8412 | size_t signing_id_size = strlen(s: signing_id) + 1; |
8413 | if (memcmp(s1: signing_id, s2: kernel_policy->cond_signing_identifier, n: signing_id_size) == 0) { |
8414 | matched = necp_boolean_state_true; |
8415 | } |
8416 | } |
8417 | |
8418 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SIGNING_IDENTIFIER) { |
8419 | if (matched == necp_boolean_state_true) { |
8420 | return FALSE; |
8421 | } |
8422 | } else { |
8423 | if (matched != necp_boolean_state_true) { |
8424 | return FALSE; |
8425 | } |
8426 | } |
8427 | } |
8428 | } |
8429 | |
8430 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
8431 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REAL_APP_ID, |
8432 | "NECP_KERNEL_CONDITION_REAL_APP_ID" , |
8433 | kernel_policy->cond_real_app_id, real_app_id); |
8434 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
8435 | if (real_app_id == kernel_policy->cond_real_app_id) { |
8436 | // No match, matches forbidden application |
8437 | return FALSE; |
8438 | } |
8439 | } else { |
8440 | if (real_app_id != kernel_policy->cond_real_app_id) { |
8441 | // No match, does not match required application |
8442 | return FALSE; |
8443 | } |
8444 | } |
8445 | } |
8446 | |
8447 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_HAS_CLIENT) { |
8448 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_HAS_CLIENT" , 0, has_client); |
8449 | if (!has_client) { |
8450 | return FALSE; |
8451 | } |
8452 | } |
8453 | |
8454 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
8455 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_ENTITLEMENT" , 0, is_entitled); |
8456 | if (!is_entitled) { |
8457 | // Process is missing entitlement |
8458 | return FALSE; |
8459 | } |
8460 | } |
8461 | |
8462 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PLATFORM_BINARY) { |
8463 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_PLATFORM_BINARY" , 0, is_platform_binary); |
8464 | if (is_platform_binary == 0) { |
8465 | // Process is not platform binary |
8466 | return FALSE; |
8467 | } |
8468 | } |
8469 | |
8470 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT) { |
8471 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT" , 0, is_platform_binary); |
8472 | if (has_signed_result == 0) { |
8473 | // Client did not have a system-signed result |
8474 | return FALSE; |
8475 | } |
8476 | } |
8477 | |
8478 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SDK_VERSION) { |
8479 | if (proc != NULL) { |
8480 | NECP_DATA_TRACE_LOG_CONDITION3(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_SDK_VERSION" , |
8481 | kernel_policy->cond_sdk_version.platform, |
8482 | kernel_policy->cond_sdk_version.min_version, |
8483 | kernel_policy->cond_sdk_version.version, |
8484 | proc_platform(proc), |
8485 | proc_min_sdk(proc), |
8486 | proc_sdk(proc)); |
8487 | if (kernel_policy->cond_sdk_version.platform != 0) { |
8488 | if (kernel_policy->cond_sdk_version.platform != proc_platform(proc)) { |
8489 | // Process does not match platform |
8490 | return FALSE; |
8491 | } |
8492 | } |
8493 | |
8494 | if (kernel_policy->cond_sdk_version.min_version != 0) { |
8495 | if (kernel_policy->cond_sdk_version.min_version > proc_min_sdk(proc)) { |
8496 | // Process min version is older than required min version |
8497 | return FALSE; |
8498 | } |
8499 | } |
8500 | |
8501 | if (kernel_policy->cond_sdk_version.version != 0) { |
8502 | if (kernel_policy->cond_sdk_version.version > proc_sdk(proc)) { |
8503 | // Process SDK version is older than required version |
8504 | return FALSE; |
8505 | } |
8506 | } |
8507 | } |
8508 | } |
8509 | |
8510 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
8511 | NECP_DATA_TRACE_LOG_CONDITION_STR(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT" , "n/a" , kernel_policy->cond_custom_entitlement); |
8512 | if (kernel_policy->cond_custom_entitlement != NULL) { |
8513 | if (proc == NULL) { |
8514 | // No process found, cannot check entitlement |
8515 | return FALSE; |
8516 | } |
8517 | task_t task = proc_task(proc); |
8518 | if (task == NULL || |
8519 | !IOTaskHasEntitlement(task, entitlement: kernel_policy->cond_custom_entitlement)) { |
8520 | // Process is missing custom entitlement |
8521 | return FALSE; |
8522 | } |
8523 | } |
8524 | } |
8525 | |
8526 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN) { |
8527 | NECP_DATA_TRACE_LOG_CONDITION_STR(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN, |
8528 | "NECP_KERNEL_CONDITION_EXACT_DOMAIN" , kernel_policy->cond_domain, domain.string); |
8529 | // Exact match requires the number of dots to match (no suffix matching allowed) |
8530 | bool domain_matches = (domain_dot_count == kernel_policy->cond_domain_dot_count && |
8531 | necp_hostname_matches_domain(hostname_substring: domain, hostname_dot_count: domain_dot_count, domain: kernel_policy->cond_domain, domain_dot_count: kernel_policy->cond_domain_dot_count)); |
8532 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN) { |
8533 | if (domain_matches) { |
8534 | // No match, matches forbidden domain |
8535 | return FALSE; |
8536 | } |
8537 | } else { |
8538 | if (!domain_matches) { |
8539 | // No match, does not match required domain |
8540 | return FALSE; |
8541 | } |
8542 | } |
8543 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
8544 | NECP_DATA_TRACE_LOG_CONDITION_STR(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DOMAIN, |
8545 | "NECP_KERNEL_CONDITION_DOMAIN" , kernel_policy->cond_domain, domain.string); |
8546 | bool domain_matches = necp_hostname_matches_domain(hostname_substring: domain, hostname_dot_count: domain_dot_count, domain: kernel_policy->cond_domain, domain_dot_count: kernel_policy->cond_domain_dot_count); |
8547 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
8548 | if (domain_matches) { |
8549 | // No match, matches forbidden domain |
8550 | return FALSE; |
8551 | } |
8552 | } else { |
8553 | if (!domain_matches) { |
8554 | // No match, does not match required domain |
8555 | return FALSE; |
8556 | } |
8557 | } |
8558 | } |
8559 | |
8560 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER) { |
8561 | struct necp_domain_filter *filter = necp_lookup_domain_filter(list: &necp_global_domain_filter_list, filter_id: kernel_policy->cond_domain_filter); |
8562 | if (filter != NULL && filter->filter != NULL) { |
8563 | bool domain_matches = net_bloom_filter_contains(filter: filter->filter, buffer: domain.string, length: domain.length); |
8564 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER) { |
8565 | if (domain_matches) { |
8566 | // No match, matches forbidden domain |
8567 | return FALSE; |
8568 | } |
8569 | } else { |
8570 | if (!domain_matches) { |
8571 | // No match, does not match required domain |
8572 | return FALSE; |
8573 | } |
8574 | } |
8575 | } |
8576 | } |
8577 | |
8578 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_URL) { |
8579 | NECP_DATA_TRACE_LOG_CONDITION_STR(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_URL, |
8580 | "NECP_KERNEL_CONDITION_URL" , kernel_policy->cond_url, url); |
8581 | bool url_matches = (url ? strncasecmp(s1: kernel_policy->cond_url, s2: url, n: strlen(s: kernel_policy->cond_url)) == 0 : false); |
8582 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_URL) { |
8583 | if (url_matches) { |
8584 | // No match, matches forbidden url |
8585 | return FALSE; |
8586 | } |
8587 | } else { |
8588 | if (!url_matches) { |
8589 | // No match, does not match required url |
8590 | return FALSE; |
8591 | } |
8592 | } |
8593 | } |
8594 | |
8595 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
8596 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID, |
8597 | "NECP_KERNEL_CONDITION_ACCOUNT_ID" , |
8598 | kernel_policy->cond_account_id, account_id); |
8599 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
8600 | if (account_id == kernel_policy->cond_account_id) { |
8601 | // No match, matches forbidden account |
8602 | return FALSE; |
8603 | } |
8604 | } else { |
8605 | if (account_id != kernel_policy->cond_account_id) { |
8606 | // No match, does not match required account |
8607 | return FALSE; |
8608 | } |
8609 | } |
8610 | } |
8611 | |
8612 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PID) { |
8613 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PID, |
8614 | "NECP_KERNEL_CONDITION_PID" , |
8615 | kernel_policy->cond_pid, pid); |
8616 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PID) { |
8617 | if (pid == kernel_policy->cond_pid) { |
8618 | // No match, matches forbidden pid |
8619 | return FALSE; |
8620 | } |
8621 | if (kernel_policy->cond_pid_version != 0 && pid_version == kernel_policy->cond_pid_version) { |
8622 | return FALSE; |
8623 | } |
8624 | } else { |
8625 | if (pid != kernel_policy->cond_pid) { |
8626 | // No match, does not match required pid |
8627 | return FALSE; |
8628 | } |
8629 | if (kernel_policy->cond_pid_version != 0 && pid_version != kernel_policy->cond_pid_version) { |
8630 | return FALSE; |
8631 | } |
8632 | } |
8633 | } |
8634 | |
8635 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_UID) { |
8636 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_UID, |
8637 | "NECP_KERNEL_CONDITION_UID" , |
8638 | kernel_policy->cond_uid, uid); |
8639 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_UID) { |
8640 | if (uid == kernel_policy->cond_uid) { |
8641 | // No match, matches forbidden uid |
8642 | return FALSE; |
8643 | } |
8644 | } else { |
8645 | if (uid != kernel_policy->cond_uid) { |
8646 | // No match, does not match required uid |
8647 | return FALSE; |
8648 | } |
8649 | } |
8650 | } |
8651 | |
8652 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
8653 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REAL_UID, |
8654 | "NECP_KERNEL_CONDITION_REAL_UID" , |
8655 | kernel_policy->cond_real_uid, real_uid); |
8656 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
8657 | if (real_uid == kernel_policy->cond_real_uid) { |
8658 | // No match, matches forbidden uid |
8659 | return FALSE; |
8660 | } |
8661 | } else { |
8662 | if (real_uid != kernel_policy->cond_real_uid) { |
8663 | // No match, does not match required uid |
8664 | return FALSE; |
8665 | } |
8666 | } |
8667 | } |
8668 | |
8669 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
8670 | NECP_DATA_TRACE_LOG_CONDITION3(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_TRAFFIC_CLASS" , |
8671 | kernel_policy->cond_traffic_class.start_tc, kernel_policy->cond_traffic_class.end_tc, 0, |
8672 | traffic_class, 0, 0); |
8673 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
8674 | if (traffic_class >= kernel_policy->cond_traffic_class.start_tc && |
8675 | traffic_class <= kernel_policy->cond_traffic_class.end_tc) { |
8676 | // No match, matches forbidden traffic class |
8677 | return FALSE; |
8678 | } |
8679 | } else { |
8680 | if (traffic_class < kernel_policy->cond_traffic_class.start_tc || |
8681 | traffic_class > kernel_policy->cond_traffic_class.end_tc) { |
8682 | // No match, does not match required traffic class |
8683 | return FALSE; |
8684 | } |
8685 | } |
8686 | } |
8687 | |
8688 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
8689 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL, |
8690 | "NECP_KERNEL_CONDITION_PROTOCOL" , |
8691 | kernel_policy->cond_protocol, protocol); |
8692 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
8693 | if (protocol == kernel_policy->cond_protocol) { |
8694 | // No match, matches forbidden protocol |
8695 | return FALSE; |
8696 | } |
8697 | } else { |
8698 | if (protocol != kernel_policy->cond_protocol) { |
8699 | // No match, does not match required protocol |
8700 | return FALSE; |
8701 | } |
8702 | } |
8703 | } |
8704 | |
8705 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
8706 | NECP_DATA_TRACE_LOG_CONDITION_STR3(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_AGENT_TYPE" , |
8707 | kernel_policy->cond_agent_type.agent_domain, kernel_policy->cond_agent_type.agent_type, "n/a" , |
8708 | "n/a" , "n/a" , "n/a" ); |
8709 | bool matches_agent_type = FALSE; |
8710 | for (u_int32_t i = 0; i < num_required_agent_types; i++) { |
8711 | struct necp_client_parameter_netagent_type *required_agent_type = &required_agent_types[i]; |
8712 | if ((strlen(s: kernel_policy->cond_agent_type.agent_domain) == 0 || |
8713 | strncmp(s1: required_agent_type->netagent_domain, s2: kernel_policy->cond_agent_type.agent_domain, NETAGENT_DOMAINSIZE) == 0) && |
8714 | (strlen(s: kernel_policy->cond_agent_type.agent_type) == 0 || |
8715 | strncmp(s1: required_agent_type->netagent_type, s2: kernel_policy->cond_agent_type.agent_type, NETAGENT_TYPESIZE) == 0)) { |
8716 | // Found a required agent that matches |
8717 | matches_agent_type = TRUE; |
8718 | break; |
8719 | } |
8720 | } |
8721 | if (!matches_agent_type) { |
8722 | return FALSE; |
8723 | } |
8724 | } |
8725 | |
8726 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) { |
8727 | bool is_local = FALSE; |
8728 | |
8729 | if (rt != NULL) { |
8730 | is_local = IS_NECP_DEST_IN_LOCAL_NETWORKS(rt); |
8731 | } else { |
8732 | is_local = necp_is_route_local(remote_addr: remote); |
8733 | } |
8734 | if (info != NULL) { |
8735 | info->is_local = is_local; |
8736 | } |
8737 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , false, "NECP_KERNEL_CONDITION_LOCAL_NETWORKS" , 0, is_local); |
8738 | if (!is_local) { |
8739 | // Either no route to validate or no match for local networks |
8740 | return FALSE; |
8741 | } |
8742 | } |
8743 | |
8744 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
8745 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
8746 | bool inRange = necp_is_addr_in_range(addr: (struct sockaddr *)local, range_start: (struct sockaddr *)&kernel_policy->cond_local_start, range_end: (struct sockaddr *)&kernel_policy->cond_local_end); |
8747 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END, "local address range" , 0, 0); |
8748 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
8749 | if (inRange) { |
8750 | return FALSE; |
8751 | } |
8752 | } else { |
8753 | if (!inRange) { |
8754 | return FALSE; |
8755 | } |
8756 | } |
8757 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
8758 | bool inSubnet = necp_is_addr_in_subnet(addr: (struct sockaddr *)local, subnet_addr: (struct sockaddr *)&kernel_policy->cond_local_start, subnet_prefix: kernel_policy->cond_local_prefix); |
8759 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX, "local address with prefix" , 0, 0); |
8760 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
8761 | if (inSubnet) { |
8762 | return FALSE; |
8763 | } |
8764 | } else { |
8765 | if (!inSubnet) { |
8766 | return FALSE; |
8767 | } |
8768 | } |
8769 | } |
8770 | } |
8771 | |
8772 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
8773 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
8774 | bool inRange = necp_is_addr_in_range(addr: (struct sockaddr *)remote, range_start: (struct sockaddr *)&kernel_policy->cond_remote_start, range_end: (struct sockaddr *)&kernel_policy->cond_remote_end); |
8775 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END, "remote address range" , 0, 0); |
8776 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
8777 | if (inRange) { |
8778 | return FALSE; |
8779 | } |
8780 | } else { |
8781 | if (!inRange) { |
8782 | return FALSE; |
8783 | } |
8784 | } |
8785 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
8786 | bool inSubnet = necp_is_addr_in_subnet(addr: (struct sockaddr *)remote, subnet_addr: (struct sockaddr *)&kernel_policy->cond_remote_start, subnet_prefix: kernel_policy->cond_remote_prefix); |
8787 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX, "remote address with prefix" , 0, 0); |
8788 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
8789 | if (inSubnet) { |
8790 | return FALSE; |
8791 | } |
8792 | } else { |
8793 | if (!inSubnet) { |
8794 | return FALSE; |
8795 | } |
8796 | } |
8797 | } |
8798 | } |
8799 | |
8800 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
8801 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS, |
8802 | "NECP_KERNEL_CONDITION_CLIENT_FLAGS" , |
8803 | kernel_policy->cond_client_flags, client_flags); |
8804 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
8805 | if ((client_flags & kernel_policy->cond_client_flags) == kernel_policy->cond_client_flags) { |
8806 | // Flags do match, and condition is negative, fail. |
8807 | return FALSE; |
8808 | } |
8809 | } else { |
8810 | if ((client_flags & kernel_policy->cond_client_flags) != kernel_policy->cond_client_flags) { |
8811 | // Flags do not match, fail. |
8812 | return FALSE; |
8813 | } |
8814 | } |
8815 | } |
8816 | |
8817 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY) { |
8818 | bool isEmpty = necp_addr_is_empty(addr: (struct sockaddr *)local); |
8819 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY, |
8820 | "NECP_KERNEL_CONDITION_LOCAL_EMPTY" , |
8821 | 0, isEmpty); |
8822 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_EMPTY) { |
8823 | if (isEmpty) { |
8824 | return FALSE; |
8825 | } |
8826 | } else { |
8827 | if (!isEmpty) { |
8828 | return FALSE; |
8829 | } |
8830 | } |
8831 | } |
8832 | |
8833 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY) { |
8834 | bool isEmpty = necp_addr_is_empty(addr: (struct sockaddr *)remote); |
8835 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY, |
8836 | "NECP_KERNEL_CONDITION_REMOTE_EMPTY" , |
8837 | 0, isEmpty); |
8838 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_EMPTY) { |
8839 | if (isEmpty) { |
8840 | return FALSE; |
8841 | } |
8842 | } else { |
8843 | if (!isEmpty) { |
8844 | return FALSE; |
8845 | } |
8846 | } |
8847 | } |
8848 | |
8849 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
8850 | u_int16_t remote_port = 0; |
8851 | if (((struct sockaddr *)remote)->sa_family == AF_INET || ((struct sockaddr *)remote)->sa_family == AF_INET6) { |
8852 | remote_port = ((struct sockaddr_in *)remote)->sin_port; |
8853 | } |
8854 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SCHEME_PORT, |
8855 | "NECP_KERNEL_CONDITION_SCHEME_PORT" , |
8856 | scheme_port, remote_port); |
8857 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
8858 | if (kernel_policy->cond_scheme_port == scheme_port || |
8859 | kernel_policy->cond_scheme_port == remote_port) { |
8860 | return FALSE; |
8861 | } |
8862 | } else { |
8863 | if (kernel_policy->cond_scheme_port != scheme_port && |
8864 | kernel_policy->cond_scheme_port != remote_port) { |
8865 | return FALSE; |
8866 | } |
8867 | } |
8868 | } |
8869 | |
8870 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
8871 | NECP_DATA_TRACE_LOG_CONDITION(debug, "SOCKET" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS, |
8872 | "NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS" , |
8873 | kernel_policy->cond_packet_filter_tags, |
8874 | pf_tag); |
8875 | bool tags_matched = false; |
8876 | if (kernel_policy->cond_packet_filter_tags & NECP_POLICY_CONDITION_PACKET_FILTER_TAG_STACK_DROP) { |
8877 | if (pf_tag == PF_TAG_ID_STACK_DROP) { |
8878 | tags_matched = true; |
8879 | } |
8880 | } |
8881 | |
8882 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
8883 | if (tags_matched) { |
8884 | return FALSE; |
8885 | } |
8886 | } else { |
8887 | if (!tags_matched) { |
8888 | return FALSE; |
8889 | } |
8890 | } |
8891 | } |
8892 | |
8893 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_IS_LOOPBACK) { |
8894 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_IS_LOOPBACK) { |
8895 | if (is_loopback) { |
8896 | return FALSE; |
8897 | } |
8898 | } else { |
8899 | if (!is_loopback) { |
8900 | return FALSE; |
8901 | } |
8902 | } |
8903 | } |
8904 | |
8905 | if (is_delegated && (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY)) { |
8906 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY) { |
8907 | if (real_is_platform_binary) { |
8908 | return FALSE; |
8909 | } |
8910 | } else { |
8911 | if (!real_is_platform_binary) { |
8912 | return FALSE; |
8913 | } |
8914 | } |
8915 | } |
8916 | |
8917 | return TRUE; |
8918 | } |
8919 | |
8920 | static inline u_int32_t |
8921 | necp_socket_calc_flowhash_locked(struct necp_socket_info *info) |
8922 | { |
8923 | return net_flowhash(info, sizeof(*info), necp_kernel_socket_policies_gencount); |
8924 | } |
8925 | |
8926 | static void |
8927 | necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface, bool override_is_inbound, u_int32_t drop_order, proc_t *socket_proc, struct necp_socket_info *info, bool is_loopback) |
8928 | { |
8929 | struct socket *so = NULL; |
8930 | proc_t sock_proc = NULL; |
8931 | proc_t curr_proc = current_proc(); |
8932 | |
8933 | memset(s: info, c: 0, n: sizeof(struct necp_socket_info)); |
8934 | |
8935 | so = inp->inp_socket; |
8936 | |
8937 | info->drop_order = drop_order; |
8938 | info->is_loopback = is_loopback; |
8939 | info->is_delegated = ((so->so_flags & SOF_DELEGATED) ? true : false); |
8940 | |
8941 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_UID || |
8942 | necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_REAL_UID) { |
8943 | info->uid = kauth_cred_getuid(cred: so->so_cred); |
8944 | info->real_uid = info->uid; |
8945 | } |
8946 | |
8947 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
8948 | info->traffic_class = so->so_traffic_class; |
8949 | } |
8950 | |
8951 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_HAS_CLIENT) { |
8952 | info->has_client = !uuid_is_null(uu: inp->necp_client_uuid); |
8953 | } |
8954 | |
8955 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_CLIENT_FLAGS) { |
8956 | info->client_flags = 0; |
8957 | if (INP_NO_CONSTRAINED(inp)) { |
8958 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_PROHIBIT_CONSTRAINED; |
8959 | } |
8960 | if (INP_NO_EXPENSIVE(inp)) { |
8961 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_PROHIBIT_EXPENSIVE; |
8962 | } |
8963 | if (inp->inp_socket->so_flags1 & SOF1_CELLFALLBACK) { |
8964 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_FALLBACK_TRAFFIC; |
8965 | } |
8966 | if (inp->inp_socket->so_flags1 & SOF1_KNOWN_TRACKER) { |
8967 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_KNOWN_TRACKER; |
8968 | } |
8969 | if (inp->inp_socket->so_flags1 & SOF1_APPROVED_APP_DOMAIN) { |
8970 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_APPROVED_APP_DOMAIN; |
8971 | } |
8972 | if (inp->inp_socket->so_flags1 & SOF1_INBOUND || override_is_inbound) { |
8973 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_INBOUND; |
8974 | } |
8975 | if (inp->inp_socket->so_options & SO_ACCEPTCONN || |
8976 | inp->inp_flags2 & INP2_EXTERNAL_PORT) { |
8977 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_LISTENER; |
8978 | } |
8979 | if (inp->inp_socket->so_options & SO_NOWAKEFROMSLEEP) { |
8980 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_NO_WAKE_FROM_SLEEP; |
8981 | } |
8982 | if (inp->inp_socket->so_options & SO_REUSEPORT) { |
8983 | info->client_flags |= NECP_CLIENT_PARAMETER_FLAG_REUSE_LOCAL; |
8984 | } |
8985 | } |
8986 | |
8987 | if ((necp_data_tracing_level && necp_data_tracing_proto) || |
8988 | necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
8989 | if (inp->inp_ip_p) { |
8990 | info->protocol = inp->inp_ip_p; |
8991 | } else { |
8992 | info->protocol = SOCK_PROTO(so); |
8993 | } |
8994 | } |
8995 | |
8996 | if (inp->inp_flags2 & INP2_WANT_APP_POLICY && necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
8997 | u_int32_t responsible_application_id = 0; |
8998 | |
8999 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(uuid: ((so->so_flags & SOF_DELEGATED) ? so->e_uuid : so->last_uuid)); |
9000 | if (existing_mapping) { |
9001 | info->application_id = existing_mapping->id; |
9002 | } |
9003 | |
9004 | #if defined(XNU_TARGET_OS_OSX) |
9005 | if (so->so_rpid > 0) { |
9006 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid: so->so_ruuid); |
9007 | if (existing_mapping != NULL) { |
9008 | responsible_application_id = existing_mapping->id; |
9009 | } |
9010 | } |
9011 | #endif |
9012 | |
9013 | if (responsible_application_id > 0) { |
9014 | info->real_application_id = info->application_id; |
9015 | info->application_id = responsible_application_id; |
9016 | info->used_responsible_pid = true; |
9017 | } else if (!(so->so_flags & SOF_DELEGATED)) { |
9018 | info->real_application_id = info->application_id; |
9019 | } else if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
9020 | struct necp_uuid_id_mapping *real_existing_mapping = necp_uuid_lookup_app_id_locked(uuid: so->last_uuid); |
9021 | if (real_existing_mapping) { |
9022 | info->real_application_id = real_existing_mapping->id; |
9023 | } |
9024 | } |
9025 | } |
9026 | |
9027 | pid_t socket_pid = |
9028 | #if defined(XNU_TARGET_OS_OSX) |
9029 | info->used_responsible_pid ? so->so_rpid : |
9030 | #endif |
9031 | ((so->so_flags & SOF_DELEGATED) ? so->e_pid : so->last_pid); |
9032 | if (socket_pid && (socket_pid != proc_pid(curr_proc))) { |
9033 | sock_proc = proc_find(pid: socket_pid); |
9034 | if (socket_proc) { |
9035 | *socket_proc = sock_proc; |
9036 | } |
9037 | } |
9038 | |
9039 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
9040 | const task_t task = proc_task(sock_proc != NULL ? sock_proc : curr_proc); |
9041 | info->is_entitled = necp_task_has_match_entitlement(task); |
9042 | if (!info->is_entitled) { |
9043 | // Task does not have entitlement, check the parent task |
9044 | necp_get_parent_is_entitled(task, info); |
9045 | } |
9046 | } |
9047 | |
9048 | if ((necp_data_tracing_level && necp_data_tracing_pid) || |
9049 | (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_PID)) { |
9050 | info->pid = socket_pid; |
9051 | info->pid_version = proc_pidversion(sock_proc != NULL ? sock_proc : curr_proc); |
9052 | } |
9053 | |
9054 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_PLATFORM_BINARY) { |
9055 | info->is_platform_binary = necp_is_platform_binary(proc: sock_proc ? sock_proc : curr_proc) ? true : false; |
9056 | } |
9057 | |
9058 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_DELEGATE_IS_PLATFORM_BINARY) { |
9059 | proc_t real_proc = curr_proc; |
9060 | bool release_real_proc = false; |
9061 | if (so->last_pid != proc_pid(real_proc)) { |
9062 | if (so->last_pid == socket_pid && sock_proc != NULL) { |
9063 | real_proc = sock_proc; |
9064 | } else { |
9065 | proc_t last_proc = proc_find(pid: so->last_pid); |
9066 | if (last_proc != NULL) { |
9067 | real_proc = last_proc; |
9068 | release_real_proc = true; |
9069 | } |
9070 | } |
9071 | } |
9072 | if (real_proc != NULL) { |
9073 | info->real_is_platform_binary = (necp_is_platform_binary(proc: real_proc) ? true : false); |
9074 | if (release_real_proc) { |
9075 | proc_rele(p: real_proc); |
9076 | } |
9077 | } |
9078 | } |
9079 | |
9080 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && inp->inp_necp_attributes.inp_account != NULL) { |
9081 | struct necp_string_id_mapping *existing_mapping = necp_lookup_string_to_id_locked(list: &necp_account_id_list, string: inp->inp_necp_attributes.inp_account); |
9082 | if (existing_mapping) { |
9083 | info->account_id = existing_mapping->id; |
9084 | } |
9085 | } |
9086 | |
9087 | if ((necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN) || |
9088 | (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_EXACT_DOMAIN) || |
9089 | (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN_FILTER)) { |
9090 | info->domain = inp->inp_necp_attributes.inp_domain; |
9091 | } |
9092 | |
9093 | if (override_bound_interface) { |
9094 | info->bound_interface_index = override_bound_interface; |
9095 | } else { |
9096 | if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp) { |
9097 | info->bound_interface_index = inp->inp_boundifp->if_index; |
9098 | } |
9099 | } |
9100 | |
9101 | if ((necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) && |
9102 | info->bound_interface_index != IFSCOPE_NONE) { |
9103 | ifnet_head_lock_shared(); |
9104 | ifnet_t interface = ifindex2ifnet[info->bound_interface_index]; |
9105 | if (interface != NULL) { |
9106 | info->bound_interface_flags = interface->if_flags; |
9107 | info->bound_interface_eflags = interface->if_eflags; |
9108 | info->bound_interface_xflags = interface->if_xflags; |
9109 | } |
9110 | ifnet_head_done(); |
9111 | } |
9112 | |
9113 | bool needs_address_for_signature = ((necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT) && |
9114 | uuid_is_null(uu: inp->necp_client_uuid) && |
9115 | necp_socket_has_resolver_signature(inp)); |
9116 | if ((necp_data_tracing_level && necp_data_tracing_port) || |
9117 | necp_restrict_multicast || |
9118 | needs_address_for_signature || |
9119 | (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS)) { |
9120 | if (override_local_addr != NULL) { |
9121 | if (override_local_addr->sa_family == AF_INET6 && override_local_addr->sa_len <= sizeof(struct sockaddr_in6)) { |
9122 | memcpy(dst: &info->local_addr, src: override_local_addr, n: override_local_addr->sa_len); |
9123 | if (IN6_IS_ADDR_V4MAPPED(&(info->local_addr.sin6.sin6_addr))) { |
9124 | struct sockaddr_in sin; |
9125 | in6_sin6_2_sin(sin: &sin, sin6: &(info->local_addr.sin6)); |
9126 | memset(s: &info->local_addr, c: 0, n: sizeof(union necp_sockaddr_union)); |
9127 | memcpy(dst: &info->local_addr, src: &sin, n: sin.sin_len); |
9128 | } |
9129 | } else if (override_local_addr->sa_family == AF_INET && override_local_addr->sa_len <= sizeof(struct sockaddr_in)) { |
9130 | memcpy(dst: &info->local_addr, src: override_local_addr, n: override_local_addr->sa_len); |
9131 | } |
9132 | } else { |
9133 | if (inp->inp_vflag & INP_IPV6) { |
9134 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_family = AF_INET6; |
9135 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_len = sizeof(struct sockaddr_in6); |
9136 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_port = inp->inp_lport; |
9137 | memcpy(dst: &((struct sockaddr_in6 *)&info->local_addr)->sin6_addr, src: &inp->in6p_laddr, n: sizeof(struct in6_addr)); |
9138 | } else if (inp->inp_vflag & INP_IPV4) { |
9139 | ((struct sockaddr_in *)&info->local_addr)->sin_family = AF_INET; |
9140 | ((struct sockaddr_in *)&info->local_addr)->sin_len = sizeof(struct sockaddr_in); |
9141 | ((struct sockaddr_in *)&info->local_addr)->sin_port = inp->inp_lport; |
9142 | memcpy(dst: &((struct sockaddr_in *)&info->local_addr)->sin_addr, src: &inp->inp_laddr, n: sizeof(struct in_addr)); |
9143 | } |
9144 | } |
9145 | |
9146 | if (override_remote_addr != NULL) { |
9147 | if (override_remote_addr->sa_family == AF_INET6 && override_remote_addr->sa_len <= sizeof(struct sockaddr_in6)) { |
9148 | memcpy(dst: &info->remote_addr, src: override_remote_addr, n: override_remote_addr->sa_len); |
9149 | if (IN6_IS_ADDR_V4MAPPED(&(info->remote_addr.sin6.sin6_addr))) { |
9150 | struct sockaddr_in sin; |
9151 | in6_sin6_2_sin(sin: &sin, sin6: &(info->remote_addr.sin6)); |
9152 | memset(s: &info->remote_addr, c: 0, n: sizeof(union necp_sockaddr_union)); |
9153 | memcpy(dst: &info->remote_addr, src: &sin, n: sin.sin_len); |
9154 | } |
9155 | } else if (override_remote_addr->sa_family == AF_INET && override_remote_addr->sa_len <= sizeof(struct sockaddr_in)) { |
9156 | memcpy(dst: &info->remote_addr, src: override_remote_addr, n: override_remote_addr->sa_len); |
9157 | } |
9158 | } else { |
9159 | if (inp->inp_vflag & INP_IPV6) { |
9160 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_family = AF_INET6; |
9161 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_len = sizeof(struct sockaddr_in6); |
9162 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_port = inp->inp_fport; |
9163 | memcpy(dst: &((struct sockaddr_in6 *)&info->remote_addr)->sin6_addr, src: &inp->in6p_faddr, n: sizeof(struct in6_addr)); |
9164 | } else if (inp->inp_vflag & INP_IPV4) { |
9165 | ((struct sockaddr_in *)&info->remote_addr)->sin_family = AF_INET; |
9166 | ((struct sockaddr_in *)&info->remote_addr)->sin_len = sizeof(struct sockaddr_in); |
9167 | ((struct sockaddr_in *)&info->remote_addr)->sin_port = inp->inp_fport; |
9168 | memcpy(dst: &((struct sockaddr_in *)&info->remote_addr)->sin_addr, src: &inp->inp_faddr, n: sizeof(struct in_addr)); |
9169 | } |
9170 | } |
9171 | // Clear the embedded scope id from v6 addresses |
9172 | if (info->local_addr.sa.sa_family == AF_INET6) { |
9173 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&info->local_addr; |
9174 | if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr) && in6_embedded_scope) { |
9175 | if (sin6->sin6_addr.s6_addr16[1] != 0) { |
9176 | sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); |
9177 | sin6->sin6_addr.s6_addr16[1] = 0; |
9178 | } |
9179 | } |
9180 | } |
9181 | if (info->remote_addr.sa.sa_family == AF_INET6) { |
9182 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&info->remote_addr; |
9183 | if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr) && in6_embedded_scope) { |
9184 | if (sin6->sin6_addr.s6_addr16[1] != 0) { |
9185 | sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); |
9186 | sin6->sin6_addr.s6_addr16[1] = 0; |
9187 | } |
9188 | } |
9189 | } |
9190 | } |
9191 | |
9192 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_SYSTEM_SIGNED_RESULT) { |
9193 | // For checking sockets, only validate that there is an NECP client present. It will have |
9194 | // already checked for the signature. |
9195 | if (!uuid_is_null(uu: inp->necp_client_uuid)) { |
9196 | info->has_system_signed_result = true; |
9197 | } else { |
9198 | info->has_system_signed_result = necp_socket_resolver_signature_matches_address(inp, address: &info->remote_addr); |
9199 | } |
9200 | } |
9201 | } |
9202 | |
9203 | #define IS_NECP_KERNEL_POLICY_IP_RESULT(result) (result == NECP_KERNEL_POLICY_RESULT_PASS || result == NECP_KERNEL_POLICY_RESULT_DROP || result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL || result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES) |
9204 | |
9205 | static inline struct necp_kernel_socket_policy * |
9206 | necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, |
9207 | necp_kernel_policy_filter *return_filter, |
9208 | u_int32_t *return_route_rule_id_array, size_t *return_route_rule_id_array_count, size_t route_rule_id_array_count, |
9209 | necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, |
9210 | u_int32_t *return_netagent_array, u_int32_t *return_netagent_use_flags_array, size_t netagent_array_count, |
9211 | struct necp_client_parameter_netagent_type *required_agent_types, |
9212 | u_int32_t num_required_agent_types, proc_t proc, u_int16_t pf_tag, necp_kernel_policy_id *skip_policy_id, struct rtentry *rt, |
9213 | necp_kernel_policy_result *return_drop_dest_policy_result, necp_drop_all_bypass_check_result_t *return_drop_all_bypass, |
9214 | u_int32_t *return_flow_divert_aggregate_unit, struct socket *so, int debug) |
9215 | { |
9216 | struct necp_kernel_socket_policy *matched_policy = NULL; |
9217 | u_int32_t skip_order = 0; |
9218 | u_int32_t skip_session_order = 0; |
9219 | bool skipped_ip_result = false; |
9220 | size_t route_rule_id_count = 0; |
9221 | int i; |
9222 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
9223 | u_int32_t netagent_use_flags[NECP_MAX_NETAGENTS]; |
9224 | memset(s: &netagent_ids, c: 0, n: sizeof(netagent_ids)); |
9225 | memset(s: &netagent_use_flags, c: 0, n: sizeof(netagent_use_flags)); |
9226 | size_t netagent_cursor = 0; |
9227 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
9228 | if (return_drop_all_bypass != NULL) { |
9229 | *return_drop_all_bypass = drop_all_bypass; |
9230 | } |
9231 | |
9232 | if (netagent_array_count > NECP_MAX_NETAGENTS) { |
9233 | netagent_array_count = NECP_MAX_NETAGENTS; |
9234 | } |
9235 | |
9236 | // Pre-process domain for quick matching |
9237 | struct substring domain_substring = necp_trim_dots_and_stars(string: info->domain, length: info->domain ? strlen(s: info->domain) : 0); |
9238 | u_int8_t domain_dot_count = necp_count_dots(string: domain_substring.string, length: domain_substring.length); |
9239 | |
9240 | if (return_filter != NULL) { |
9241 | *return_filter = 0; |
9242 | } |
9243 | |
9244 | if (return_route_rule_id_array_count != NULL) { |
9245 | *return_route_rule_id_array_count = 0; |
9246 | } |
9247 | |
9248 | if (return_service_action != NULL) { |
9249 | *return_service_action = 0; |
9250 | } |
9251 | |
9252 | if (return_service != NULL) { |
9253 | return_service->identifier = 0; |
9254 | return_service->data = 0; |
9255 | } |
9256 | |
9257 | // Do not subject layer-2 filter to NECP policies, return a PASS policy |
9258 | if (necp_pass_interpose > 0 && info->client_flags & NECP_CLIENT_PARAMETER_FLAG_INTERPOSE) { |
9259 | return &pass_policy; |
9260 | } |
9261 | |
9262 | *return_drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
9263 | |
9264 | if (policy_search_array != NULL) { |
9265 | for (i = 0; policy_search_array[i] != NULL; i++) { |
9266 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "EXAMINING" ); |
9267 | |
9268 | if (necp_drop_all_order != 0 && policy_search_array[i]->session_order >= necp_drop_all_order) { |
9269 | // We've hit a drop all rule |
9270 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
9271 | drop_all_bypass = necp_check_drop_all_bypass_result(proc); |
9272 | if (return_drop_all_bypass != NULL) { |
9273 | *return_drop_all_bypass = drop_all_bypass; |
9274 | } |
9275 | } |
9276 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
9277 | NECP_DATA_TRACE_LOG_SOCKET_RESULT(debug, so, "SOCKET" , "RESULT - DROP - (session order > drop-all order)" ); |
9278 | break; |
9279 | } |
9280 | } |
9281 | if (necp_drop_dest_policy.entry_count != 0 && |
9282 | necp_address_matches_drop_dest_policy(&info->remote_addr, policy_search_array[i]->session_order)) { |
9283 | // We've hit a drop by destination address rule |
9284 | *return_drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_DROP; |
9285 | break; |
9286 | } |
9287 | if (info->drop_order != 0 && policy_search_array[i]->session_order >= info->drop_order) { |
9288 | // We've hit a drop order for this socket |
9289 | break; |
9290 | } |
9291 | if (skip_session_order && policy_search_array[i]->session_order >= skip_session_order) { |
9292 | // Done skipping |
9293 | skip_order = 0; |
9294 | skip_session_order = 0; |
9295 | // If we didn't skip any policy with IP result, no need to save the skip for IP evaluation. |
9296 | if (skip_policy_id && *skip_policy_id != NECP_KERNEL_POLICY_ID_NONE && !skipped_ip_result) { |
9297 | *skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
9298 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "SKIP (cleared saved skip)" ); |
9299 | } |
9300 | } |
9301 | if (skip_order) { |
9302 | if (policy_search_array[i]->order < skip_order) { |
9303 | // Skip this policy |
9304 | // Remember if we skipped an interesting PASS/DROP/IP_TUNNEL/ROUTE_RULES policy. If we |
9305 | // didn't, clear out the return value for skip ID when we are done with each session.' |
9306 | if (IS_NECP_KERNEL_POLICY_IP_RESULT(policy_search_array[i]->result)) { |
9307 | skipped_ip_result = true; |
9308 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "SKIPPING IP RESULT" ); |
9309 | } |
9310 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "SKIP (session order < skip-order)" ); |
9311 | continue; |
9312 | } else { |
9313 | // Done skipping |
9314 | skip_order = 0; |
9315 | skip_session_order = 0; |
9316 | } |
9317 | } else if (skip_session_order) { |
9318 | // Skip this policy |
9319 | // Remember if we skipped an interesting PASS/DROP/IP_TUNNEL/ROUTE_RULES policy. If we |
9320 | // didn't, clear out the return value for skip ID when we are done with each session.' |
9321 | if (IS_NECP_KERNEL_POLICY_IP_RESULT(policy_search_array[i]->result)) { |
9322 | skipped_ip_result = true; |
9323 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "SKIPPING IP RESULT" ); |
9324 | } |
9325 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "SKIP (skip-session-order)" ); |
9326 | continue; |
9327 | } |
9328 | |
9329 | if (necp_socket_check_policy(kernel_policy: policy_search_array[i], app_id: info->application_id, real_app_id: info->real_application_id, is_entitled: info->is_entitled, account_id: info->account_id, domain: domain_substring, domain_dot_count, url: info->url, pid: info->pid, pid_version: info->pid_version, uid: info->uid, real_uid: info->real_uid, bound_interface_index: info->bound_interface_index, traffic_class: info->traffic_class, protocol: info->protocol, local: &info->local_addr, remote: &info->remote_addr, required_agent_types, num_required_agent_types, has_client: info->has_client, client_flags: info->client_flags, is_platform_binary: info->is_platform_binary, has_signed_result: info->has_system_signed_result, proc, pf_tag, scheme_port: info->scheme_port, rt, is_loopback: info->is_loopback, debug, real_is_platform_binary: info->real_is_platform_binary, bound_interface_flags: info->bound_interface_flags, bound_interface_eflags: info->bound_interface_eflags, bound_interface_xflags: info->bound_interface_xflags, info, is_delegated: info->is_delegated)) { |
9330 | if (!debug && necp_data_tracing_session_order) { |
9331 | if ((necp_data_tracing_session_order == policy_search_array[i]->session_order) && |
9332 | (!necp_data_tracing_policy_order || (necp_data_tracing_policy_order == policy_search_array[i]->order))) { |
9333 | NECP_DATA_TRACE_LOG_SOCKET_RESULT(true, so, "SOCKET" , "DEBUG - MATCHED POLICY" ); |
9334 | } |
9335 | } |
9336 | |
9337 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER) { |
9338 | if (return_filter && *return_filter != NECP_FILTER_UNIT_NO_FILTER) { |
9339 | necp_kernel_policy_filter control_unit = policy_search_array[i]->result_parameter.filter_control_unit; |
9340 | if (control_unit == NECP_FILTER_UNIT_NO_FILTER) { |
9341 | *return_filter = control_unit; |
9342 | } else { |
9343 | // Preserve pre-existing connections only if all filters preserve. |
9344 | bool preserve_bit_off = false; |
9345 | if ((*return_filter && !(*return_filter & NECP_MASK_PRESERVE_CONNECTIONS)) || |
9346 | (control_unit && !(control_unit & NECP_MASK_PRESERVE_CONNECTIONS))) { |
9347 | preserve_bit_off = true; |
9348 | } |
9349 | *return_filter |= control_unit; |
9350 | if (preserve_bit_off == true) { |
9351 | *return_filter &= ~NECP_MASK_PRESERVE_CONNECTIONS; |
9352 | } |
9353 | } |
9354 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9355 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Filter %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result_parameter.filter_control_unit); |
9356 | } |
9357 | } |
9358 | continue; |
9359 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES) { |
9360 | if (return_route_rule_id_array && route_rule_id_count < route_rule_id_array_count) { |
9361 | return_route_rule_id_array[route_rule_id_count++] = policy_search_array[i]->result_parameter.route_rule_id; |
9362 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9363 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Route Rule %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result_parameter.route_rule_id); |
9364 | } |
9365 | } |
9366 | continue; |
9367 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT || |
9368 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
9369 | if (netagent_cursor < netagent_array_count) { |
9370 | bool agent_already_present = false; |
9371 | for (size_t netagent_i = 0; netagent_i < netagent_cursor; netagent_i++) { |
9372 | if (netagent_ids[netagent_i] == policy_search_array[i]->result_parameter.netagent_id) { |
9373 | // Already present. Mark the "SCOPED" flag if flags are necessary. |
9374 | agent_already_present = true; |
9375 | if (!(netagent_use_flags[netagent_i] & NECP_AGENT_USE_FLAG_REMOVE) && |
9376 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
9377 | netagent_use_flags[netagent_i] |= NECP_AGENT_USE_FLAG_SCOPE; |
9378 | } |
9379 | } |
9380 | } |
9381 | |
9382 | if (!agent_already_present) { |
9383 | netagent_ids[netagent_cursor] = policy_search_array[i]->result_parameter.netagent_id; |
9384 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
9385 | netagent_use_flags[netagent_cursor] |= NECP_AGENT_USE_FLAG_SCOPE; |
9386 | } |
9387 | netagent_cursor++; |
9388 | } |
9389 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9390 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) %s Netagent %d" , |
9391 | info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, |
9392 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT ? "Use" : "Scope" , |
9393 | policy_search_array[i]->result_parameter.netagent_id); |
9394 | } |
9395 | } |
9396 | continue; |
9397 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_REMOVE_NETAGENT) { |
9398 | bool agent_already_present = false; |
9399 | for (size_t netagent_i = 0; netagent_i < netagent_cursor; netagent_i++) { |
9400 | if (netagent_ids[netagent_i] == policy_search_array[i]->result_parameter.netagent_id) { |
9401 | // Already present. Mark the "REMOVE" flag if flags are supported, or just clear the entry |
9402 | agent_already_present = true; |
9403 | netagent_use_flags[netagent_i] = NECP_AGENT_USE_FLAG_REMOVE; |
9404 | } |
9405 | } |
9406 | if (!agent_already_present && netagent_cursor < netagent_array_count) { |
9407 | // If not present, and flags are supported, add an entry with the "REMOVE" flag |
9408 | netagent_ids[netagent_cursor] = policy_search_array[i]->result_parameter.netagent_id; |
9409 | netagent_use_flags[netagent_cursor] = NECP_AGENT_USE_FLAG_REMOVE; |
9410 | netagent_cursor++; |
9411 | } |
9412 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9413 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Remove Netagent %d" , |
9414 | info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, |
9415 | policy_search_array[i]->result_parameter.netagent_id); |
9416 | } |
9417 | continue; |
9418 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT) { |
9419 | u_int32_t control_unit = policy_search_array[i]->result_parameter.flow_divert_control_unit; |
9420 | if (control_unit & FLOW_DIVERT_IS_TRANSPARENT) { |
9421 | /* For transparent proxies, accumulate the control unit and continue to the next policy */ |
9422 | if (return_flow_divert_aggregate_unit != NULL) { |
9423 | *return_flow_divert_aggregate_unit |= (control_unit & ~FLOW_DIVERT_IS_TRANSPARENT); |
9424 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9425 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) flow divert %u" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, control_unit); |
9426 | } |
9427 | } |
9428 | continue; |
9429 | } |
9430 | } |
9431 | |
9432 | // Matched policy is a skip. Do skip and continue. |
9433 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
9434 | NECP_DATA_TRACE_LOG_POLICY(debug, "SOCKET" , "MATCHED SKIP POLICY" ); |
9435 | skip_order = policy_search_array[i]->result_parameter.skip_policy_order; |
9436 | skip_session_order = policy_search_array[i]->session_order + 1; |
9437 | if (skip_policy_id && *skip_policy_id == NECP_KERNEL_POLICY_ID_NONE) { |
9438 | *skip_policy_id = policy_search_array[i]->id; |
9439 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9440 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: MATCHED SKIP POLICY (Application %d Real Application %d BoundInterface %d Proto %d) set skip_policy_id %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, *skip_policy_id); |
9441 | } |
9442 | } |
9443 | continue; |
9444 | } |
9445 | |
9446 | // Matched an allow unentitled, which clears any drop order |
9447 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_ALLOW_UNENTITLED) { |
9448 | info->drop_order = 0; |
9449 | continue; |
9450 | } |
9451 | |
9452 | // Passed all tests, found a match |
9453 | matched_policy = policy_search_array[i]; |
9454 | NECP_DATA_TRACE_LOG_SOCKET_RESULT(debug, so, "SOCKET" , "RESULT - MATCHED POLICY" ); |
9455 | break; |
9456 | } |
9457 | } |
9458 | } |
9459 | |
9460 | if (return_netagent_array != NULL) { |
9461 | if (return_netagent_use_flags_array != NULL) { |
9462 | memcpy(dst: return_netagent_array, src: &netagent_ids, n: sizeof(u_int32_t) * netagent_array_count); |
9463 | memcpy(dst: return_netagent_use_flags_array, src: &netagent_use_flags, n: sizeof(u_int32_t) * netagent_array_count); |
9464 | } else { |
9465 | for (size_t netagent_i = 0; netagent_i < netagent_array_count; netagent_i++) { |
9466 | if (!(netagent_use_flags[netagent_i] & NECP_AGENT_USE_FLAG_REMOVE)) { |
9467 | return_netagent_array[netagent_i] = netagent_ids[netagent_i]; |
9468 | } else { |
9469 | return_netagent_array[netagent_i] = 0; |
9470 | } |
9471 | } |
9472 | } |
9473 | } |
9474 | |
9475 | if (return_route_rule_id_array_count != NULL) { |
9476 | *return_route_rule_id_array_count = route_rule_id_count; |
9477 | } |
9478 | return matched_policy; |
9479 | } |
9480 | |
9481 | static bool |
9482 | necp_socket_uses_interface(struct inpcb *inp, u_int32_t interface_index) |
9483 | { |
9484 | bool found_match = FALSE; |
9485 | ifaddr_t ifa; |
9486 | union necp_sockaddr_union address_storage; |
9487 | int family = AF_INET; |
9488 | |
9489 | ifnet_head_lock_shared(); |
9490 | ifnet_t interface = ifindex2ifnet[interface_index]; |
9491 | ifnet_head_done(); |
9492 | |
9493 | if (inp == NULL || interface == NULL) { |
9494 | return FALSE; |
9495 | } |
9496 | |
9497 | if (inp->inp_vflag & INP_IPV4) { |
9498 | family = AF_INET; |
9499 | } else if (inp->inp_vflag & INP_IPV6) { |
9500 | family = AF_INET6; |
9501 | } else { |
9502 | return FALSE; |
9503 | } |
9504 | |
9505 | // Match socket address against interface addresses |
9506 | ifnet_lock_shared(ifp: interface); |
9507 | TAILQ_FOREACH(ifa, &interface->if_addrhead, ifa_link) { |
9508 | if (ifaddr_address(ifaddr: ifa, SA(&address_storage.sa), addr_size: sizeof(address_storage)) == 0) { |
9509 | if (address_storage.sa.sa_family != family) { |
9510 | continue; |
9511 | } |
9512 | |
9513 | if (family == AF_INET) { |
9514 | if (memcmp(s1: &address_storage.sin.sin_addr, s2: &inp->inp_laddr, n: sizeof(inp->inp_laddr)) == 0) { |
9515 | found_match = TRUE; |
9516 | break; |
9517 | } |
9518 | } else if (family == AF_INET6) { |
9519 | if (memcmp(s1: &address_storage.sin6.sin6_addr, s2: &inp->in6p_laddr, n: sizeof(inp->in6p_laddr)) == 0) { |
9520 | found_match = TRUE; |
9521 | break; |
9522 | } |
9523 | } |
9524 | } |
9525 | } |
9526 | ifnet_lock_done(ifp: interface); |
9527 | |
9528 | return found_match; |
9529 | } |
9530 | |
9531 | static inline bool |
9532 | necp_socket_is_connected(struct inpcb *inp) |
9533 | { |
9534 | return inp->inp_socket->so_state & (SS_ISCONNECTING | SS_ISCONNECTED | SS_ISDISCONNECTING); |
9535 | } |
9536 | |
9537 | static inline necp_socket_bypass_type_t |
9538 | necp_socket_bypass(struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, struct inpcb *inp) |
9539 | { |
9540 | if (necp_pass_loopback > 0 && necp_is_loopback(local_addr: override_local_addr, remote_addr: override_remote_addr, inp, NULL, IFSCOPE_NONE)) { |
9541 | return NECP_BYPASS_TYPE_LOOPBACK; |
9542 | } else if (necp_is_intcoproc(inp, NULL)) { |
9543 | return NECP_BYPASS_TYPE_INTCOPROC; |
9544 | } |
9545 | |
9546 | return NECP_BYPASS_TYPE_NONE; |
9547 | } |
9548 | |
9549 | static inline void |
9550 | necp_socket_ip_tunnel_tso(struct inpcb *inp) |
9551 | { |
9552 | u_int tunnel_interface_index = inp->inp_policyresult.results.result_parameter.tunnel_interface_index; |
9553 | ifnet_t tunnel_interface = NULL; |
9554 | |
9555 | ifnet_head_lock_shared(); |
9556 | tunnel_interface = ifindex2ifnet[tunnel_interface_index]; |
9557 | ifnet_head_done(); |
9558 | |
9559 | if (tunnel_interface != NULL) { |
9560 | tcp_set_tso(intotcpcb(inp), ifp: tunnel_interface); |
9561 | } |
9562 | } |
9563 | |
9564 | static inline void |
9565 | necp_unscope(struct inpcb *inp) |
9566 | { |
9567 | // If the current policy result is "socket scoped" and the pcb was actually re-scoped as a result, then un-bind the pcb |
9568 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED && (inp->inp_flags2 & INP2_SCOPED_BY_NECP)) { |
9569 | inp->inp_flags &= ~INP_BOUND_IF; |
9570 | inp->inp_boundifp = NULL; |
9571 | } |
9572 | } |
9573 | |
9574 | static inline void |
9575 | necp_clear_tunnel(struct inpcb *inp) |
9576 | { |
9577 | if (inp->inp_boundifp != NULL && inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL) { |
9578 | inp->inp_flags &= ~INP_BOUND_IF; |
9579 | inp->inp_boundifp = NULL; |
9580 | } |
9581 | } |
9582 | |
9583 | static inline bool |
9584 | necp_socket_verify_netagents(u_int32_t *netagent_ids, int debug, struct socket *so) |
9585 | { |
9586 | // Verify netagents |
9587 | for (int netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
9588 | struct necp_uuid_id_mapping *mapping = NULL; |
9589 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
9590 | if (netagent_id == 0) { |
9591 | continue; |
9592 | } |
9593 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: netagent_id); |
9594 | if (mapping != NULL) { |
9595 | u_int32_t agent_flags = 0; |
9596 | agent_flags = netagent_get_flags(uuid: mapping->uuid); |
9597 | if (agent_flags & NETAGENT_FLAG_REGISTERED) { |
9598 | if (agent_flags & NETAGENT_FLAG_ACTIVE) { |
9599 | continue; |
9600 | } else if ((agent_flags & NETAGENT_FLAG_VOLUNTARY) == 0) { |
9601 | if (agent_flags & NETAGENT_FLAG_KERNEL_ACTIVATED) { |
9602 | int trigger_error = 0; |
9603 | trigger_error = netagent_kernel_trigger(uuid: mapping->uuid); |
9604 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9605 | NECPLOG(LOG_ERR, "DATA-TRACE: Socket Policy: <so %llx> Triggering inactive agent (%d), error %d" , (unsigned long long)so, netagent_id, trigger_error); |
9606 | } |
9607 | } |
9608 | return false; |
9609 | } |
9610 | } |
9611 | } |
9612 | } |
9613 | return true; |
9614 | } |
9615 | |
9616 | necp_kernel_policy_id |
9617 | necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface) |
9618 | { |
9619 | struct socket *so = NULL; |
9620 | necp_kernel_policy_filter filter_control_unit = 0; |
9621 | struct necp_kernel_socket_policy *matched_policy = NULL; |
9622 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
9623 | necp_kernel_policy_result service_action = 0; |
9624 | necp_kernel_policy_service service = { 0, 0 }; |
9625 | u_int32_t drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
9626 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
9627 | proc_t socket_proc = NULL; |
9628 | necp_socket_bypass_type_t bypass_type = NECP_BYPASS_TYPE_NONE; |
9629 | |
9630 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
9631 | memset(s: &netagent_ids, c: 0, n: sizeof(netagent_ids)); |
9632 | |
9633 | struct necp_socket_info info = {}; |
9634 | |
9635 | u_int32_t flow_divert_aggregate_unit = 0; |
9636 | |
9637 | if (inp == NULL) { |
9638 | return NECP_KERNEL_POLICY_ID_NONE; |
9639 | } |
9640 | |
9641 | // Ignore invalid addresses |
9642 | if (override_local_addr != NULL && |
9643 | !necp_address_is_valid(override_local_addr)) { |
9644 | override_local_addr = NULL; |
9645 | } |
9646 | if (override_remote_addr != NULL && |
9647 | !necp_address_is_valid(override_remote_addr)) { |
9648 | override_remote_addr = NULL; |
9649 | } |
9650 | |
9651 | so = inp->inp_socket; |
9652 | |
9653 | u_int32_t drop_order = necp_process_drop_order(so->so_cred); |
9654 | |
9655 | // Don't lock. Possible race condition, but we don't want the performance hit. |
9656 | if (necp_drop_management_order == 0 && |
9657 | (necp_kernel_socket_policies_count == 0 || |
9658 | (!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0))) { |
9659 | if (necp_drop_all_order > 0 || drop_order > 0) { |
9660 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9661 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9662 | inp->inp_policyresult.policy_gencount = 0; |
9663 | inp->inp_policyresult.app_id = 0; |
9664 | inp->inp_policyresult.flowhash = 0; |
9665 | inp->inp_policyresult.results.filter_control_unit = 0; |
9666 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
9667 | inp->inp_policyresult.results.route_rule_id = 0; |
9668 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp) != NECP_BYPASS_TYPE_NONE) { |
9669 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS; |
9670 | } else { |
9671 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
9672 | } |
9673 | } |
9674 | return NECP_KERNEL_POLICY_ID_NONE; |
9675 | } |
9676 | |
9677 | // Check for loopback exception |
9678 | bypass_type = necp_socket_bypass(override_local_addr, override_remote_addr, inp); |
9679 | if (bypass_type == NECP_BYPASS_TYPE_INTCOPROC || (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && necp_pass_loopback == NECP_LOOPBACK_PASS_ALL)) { |
9680 | // Mark socket as a pass |
9681 | necp_unscope(inp); |
9682 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9683 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9684 | inp->inp_policyresult.policy_gencount = 0; |
9685 | inp->inp_policyresult.app_id = 0; |
9686 | inp->inp_policyresult.flowhash = 0; |
9687 | inp->inp_policyresult.results.filter_control_unit = 0; |
9688 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
9689 | inp->inp_policyresult.results.route_rule_id = 0; |
9690 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS; |
9691 | return NECP_KERNEL_POLICY_ID_NONE; |
9692 | } |
9693 | |
9694 | // Lock |
9695 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
9696 | necp_socket_fillout_info_locked(inp, override_local_addr, override_remote_addr, override_bound_interface, false, drop_order, socket_proc: &socket_proc, info: &info, is_loopback: (bypass_type == NECP_BYPASS_TYPE_LOOPBACK)); |
9697 | |
9698 | int debug = NECP_ENABLE_DATA_TRACE((&info.local_addr), (&info.remote_addr), info.protocol, info.pid, info.bound_interface_index); |
9699 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "START" , 0, 0); |
9700 | |
9701 | // Check info |
9702 | u_int32_t flowhash = necp_socket_calc_flowhash_locked(info: &info); |
9703 | if (inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE && |
9704 | inp->inp_policyresult.policy_gencount == necp_kernel_socket_policies_gencount && |
9705 | inp->inp_policyresult.flowhash == flowhash) { |
9706 | // If already matched this socket on this generation of table, skip |
9707 | |
9708 | // Unlock |
9709 | lck_rw_done(lck: &necp_kernel_policy_lock); |
9710 | |
9711 | if (socket_proc) { |
9712 | proc_rele(p: socket_proc); |
9713 | } |
9714 | |
9715 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9716 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy - INP UPDATE - RESULT - CACHED <MATCHED>: %p (BoundInterface %d Proto %d) Policy %d Result %d Parameter %d" , |
9717 | inp->inp_socket, info.bound_interface_index, info.protocol, |
9718 | inp->inp_policyresult.policy_id, |
9719 | inp->inp_policyresult.results.result, |
9720 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index); |
9721 | } |
9722 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "RESULT - CACHED <MATCHED>" , inp->inp_policyresult.policy_id, 0); |
9723 | return inp->inp_policyresult.policy_id; |
9724 | } |
9725 | |
9726 | inp->inp_policyresult.app_id = info.application_id; |
9727 | |
9728 | // Match socket to policy |
9729 | necp_kernel_policy_id skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
9730 | u_int32_t route_rule_id_array[MAX_AGGREGATE_ROUTE_RULES] = {}; |
9731 | size_t route_rule_id_array_count = 0; |
9732 | |
9733 | matched_policy = necp_socket_find_policy_match_with_info_locked(policy_search_array: necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], info: &info, return_filter: &filter_control_unit, return_route_rule_id_array: route_rule_id_array, return_route_rule_id_array_count: &route_rule_id_array_count, MAX_AGGREGATE_ROUTE_RULES, return_service_action: &service_action, return_service: &service, return_netagent_array: netagent_ids, NULL, NECP_MAX_NETAGENTS, NULL, num_required_agent_types: 0, proc: socket_proc ? socket_proc : current_proc(), pf_tag: 0, skip_policy_id: &skip_policy_id, rt: inp->inp_route.ro_rt, return_drop_dest_policy_result: &drop_dest_policy_result, return_drop_all_bypass: &drop_all_bypass, return_flow_divert_aggregate_unit: &flow_divert_aggregate_unit, so, debug); |
9734 | |
9735 | // Check for loopback exception again after the policy match |
9736 | if (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && |
9737 | necp_pass_loopback == NECP_LOOPBACK_PASS_WITH_FILTER && |
9738 | (matched_policy == NULL || matched_policy->result != NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT)) { |
9739 | // Mark socket as a pass |
9740 | necp_unscope(inp); |
9741 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9742 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9743 | inp->inp_policyresult.policy_gencount = 0; |
9744 | inp->inp_policyresult.app_id = 0; |
9745 | inp->inp_policyresult.flowhash = 0; |
9746 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
9747 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
9748 | inp->inp_policyresult.results.route_rule_id = 0; |
9749 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS; |
9750 | |
9751 | // Unlock |
9752 | lck_rw_done(lck: &necp_kernel_policy_lock); |
9753 | |
9754 | if (socket_proc) { |
9755 | proc_rele(p: socket_proc); |
9756 | } |
9757 | |
9758 | return NECP_KERNEL_POLICY_ID_NONE; |
9759 | } |
9760 | |
9761 | // Verify netagents |
9762 | if (necp_socket_verify_netagents(netagent_ids, debug, so) == false) { |
9763 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9764 | NECPLOG(LOG_ERR, "DATA-TRACE: Socket Policy: <so %llx> (BoundInterface %d Proto %d) Dropping packet because agent is not active" , (unsigned long long)so, info.bound_interface_index, info.protocol); |
9765 | } |
9766 | |
9767 | // Mark socket as a drop if required agent is not active |
9768 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9769 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9770 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9771 | inp->inp_policyresult.flowhash = flowhash; |
9772 | inp->inp_policyresult.results.filter_control_unit = 0; |
9773 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
9774 | inp->inp_policyresult.results.route_rule_id = 0; |
9775 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
9776 | |
9777 | // Unlock |
9778 | lck_rw_done(lck: &necp_kernel_policy_lock); |
9779 | |
9780 | if (socket_proc) { |
9781 | proc_rele(p: socket_proc); |
9782 | } |
9783 | |
9784 | return NECP_KERNEL_POLICY_ID_NONE; |
9785 | } |
9786 | |
9787 | u_int32_t route_rule_id = 0; |
9788 | if (route_rule_id_array_count == 1) { |
9789 | route_rule_id = route_rule_id_array[0]; |
9790 | } else if (route_rule_id_array_count > 1) { |
9791 | route_rule_id = necp_create_aggregate_route_rule(rule_ids: route_rule_id_array); |
9792 | } |
9793 | |
9794 | bool reset_tcp_tunnel_interface = false; |
9795 | bool send_local_network_denied_event = false; |
9796 | if (matched_policy) { |
9797 | // For PASS policy result, clear previous rescope / tunnel inteface |
9798 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_PASS && |
9799 | (info.client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER || info.is_local)) { |
9800 | necp_unscope(inp); |
9801 | necp_clear_tunnel(inp); |
9802 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9803 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "socket unscoped for PASS result" , inp->inp_policyresult.policy_id, 0); |
9804 | } |
9805 | } |
9806 | matched_policy_id = matched_policy->id; |
9807 | inp->inp_policyresult.policy_id = matched_policy->id; |
9808 | inp->inp_policyresult.skip_policy_id = skip_policy_id; |
9809 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9810 | inp->inp_policyresult.flowhash = flowhash; |
9811 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
9812 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
9813 | inp->inp_policyresult.results.route_rule_id = route_rule_id; |
9814 | inp->inp_policyresult.results.result = matched_policy->result; |
9815 | memcpy(dst: &inp->inp_policyresult.results.result_parameter, src: &matched_policy->result_parameter, n: sizeof(matched_policy->result_parameter)); |
9816 | |
9817 | if (info.used_responsible_pid && (matched_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID)) { |
9818 | inp->inp_policyresult.app_id = info.real_application_id; |
9819 | } |
9820 | |
9821 | if (necp_socket_is_connected(inp) && |
9822 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP || |
9823 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && !necp_socket_uses_interface(inp, interface_index: matched_policy->result_parameter.tunnel_interface_index)))) { |
9824 | NECPLOG(LOG_ERR, "Marking socket in state %d as defunct" , so->so_state); |
9825 | sosetdefunct(current_proc(), so, SHUTDOWN_SOCKET_LEVEL_NECP | SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL, TRUE); |
9826 | } else if (necp_socket_is_connected(inp) && |
9827 | matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && |
9828 | info.protocol == IPPROTO_TCP) { |
9829 | // Reset TCP socket interface based parameters if tunnel policy changes |
9830 | reset_tcp_tunnel_interface = true; |
9831 | } |
9832 | |
9833 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9834 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy: %p (BoundInterface %d Proto %d) Policy %d Skip %d Result %d Parameter %d" , inp->inp_socket, info.bound_interface_index, info.protocol, matched_policy->id, skip_policy_id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index); |
9835 | } |
9836 | |
9837 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP && |
9838 | matched_policy->result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_LOCAL_NETWORK && |
9839 | !(matched_policy->result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_SUPPRESS_ALERTS)) { |
9840 | // Trigger the event that we dropped due to a local network policy |
9841 | send_local_network_denied_event = true; |
9842 | } |
9843 | } else { |
9844 | bool drop_all = false; |
9845 | if (necp_drop_all_order > 0 || info.drop_order > 0 || drop_dest_policy_result == NECP_KERNEL_POLICY_RESULT_DROP) { |
9846 | // Mark socket as a drop if set |
9847 | drop_all = true; |
9848 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
9849 | drop_all_bypass = necp_check_drop_all_bypass_result(proc: socket_proc ? socket_proc : current_proc()); |
9850 | } |
9851 | } |
9852 | |
9853 | // Check if there is a route rule that adds flow divert, if we don't already have a terminal policy result |
9854 | u_int32_t flow_divert_control_unit = necp_route_get_flow_divert(NULL, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, route_rule_id, flow_divert_aggregate_unit: &flow_divert_aggregate_unit); |
9855 | if (flow_divert_control_unit != 0) { |
9856 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9857 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9858 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9859 | inp->inp_policyresult.flowhash = flowhash; |
9860 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
9861 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
9862 | inp->inp_policyresult.results.route_rule_id = route_rule_id; |
9863 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT; |
9864 | inp->inp_policyresult.results.result_parameter.flow_divert_control_unit = flow_divert_control_unit; |
9865 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "FLOW DIVERT <ROUTE RULE>" , 0, 0); |
9866 | } else if (drop_all && drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
9867 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9868 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9869 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9870 | inp->inp_policyresult.flowhash = flowhash; |
9871 | inp->inp_policyresult.results.filter_control_unit = 0; |
9872 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
9873 | inp->inp_policyresult.results.route_rule_id = 0; |
9874 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
9875 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "RESULT - DROP <NO MATCH>" , 0, 0); |
9876 | } else { |
9877 | // Mark non-matching socket so we don't re-check it |
9878 | necp_unscope(inp); |
9879 | if (info.client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER || info.is_local) { |
9880 | necp_clear_tunnel(inp); |
9881 | } |
9882 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
9883 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - INP UPDATE" , "socket unscoped for <NO MATCH>" , inp->inp_policyresult.policy_id, 0); |
9884 | } |
9885 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9886 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9887 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9888 | inp->inp_policyresult.flowhash = flowhash; |
9889 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; // We may have matched a filter, so mark it! |
9890 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
9891 | inp->inp_policyresult.results.route_rule_id = route_rule_id; // We may have matched a route rule, so mark it! |
9892 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_NONE; |
9893 | } |
9894 | } |
9895 | |
9896 | if (necp_check_missing_client_drop(proc: socket_proc ? socket_proc : current_proc(), info: &info) || |
9897 | necp_check_restricted_multicast_drop(proc: socket_proc ? socket_proc : current_proc(), info: &info, false)) { |
9898 | // Mark as drop |
9899 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9900 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
9901 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
9902 | inp->inp_policyresult.flowhash = flowhash; |
9903 | inp->inp_policyresult.results.filter_control_unit = 0; |
9904 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
9905 | inp->inp_policyresult.results.route_rule_id = 0; |
9906 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
9907 | } |
9908 | |
9909 | // Unlock |
9910 | lck_rw_done(lck: &necp_kernel_policy_lock); |
9911 | |
9912 | if (reset_tcp_tunnel_interface) { |
9913 | // Update MSS when not holding the policy lock to avoid recursive locking |
9914 | tcp_mtudisc(inp, 0); |
9915 | |
9916 | // Update TSO flag based on the tunnel interface |
9917 | necp_socket_ip_tunnel_tso(inp); |
9918 | } |
9919 | |
9920 | if (send_local_network_denied_event && inp->inp_policyresult.network_denied_notifies == 0) { |
9921 | inp->inp_policyresult.network_denied_notifies++; |
9922 | necp_send_network_denied_event(pid: ((so->so_flags & SOF_DELEGATED) ? so->e_pid : so->last_pid), |
9923 | proc_uuid: ((so->so_flags & SOF_DELEGATED) ? so->e_uuid : so->last_uuid), |
9924 | NETPOLICY_NETWORKTYPE_LOCAL); |
9925 | } |
9926 | |
9927 | if (socket_proc) { |
9928 | proc_rele(p: socket_proc); |
9929 | } |
9930 | |
9931 | return matched_policy_id; |
9932 | } |
9933 | |
9934 | static bool |
9935 | necp_ip_output_check_policy(struct necp_kernel_ip_output_policy *kernel_policy, necp_kernel_policy_id socket_policy_id, necp_kernel_policy_id socket_skip_policy_id, u_int32_t bound_interface_index, u_int32_t last_interface_index, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote, struct rtentry *rt, u_int16_t pf_tag, int debug) |
9936 | { |
9937 | u_int32_t bound_interface_flags = 0; |
9938 | u_int32_t bound_interface_eflags = 0; |
9939 | u_int32_t bound_interface_xflags = 0; |
9940 | |
9941 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
9942 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
9943 | u_int32_t cond_bound_interface_index = kernel_policy->cond_bound_interface ? kernel_policy->cond_bound_interface->if_index : 0; |
9944 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE, |
9945 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE" , |
9946 | cond_bound_interface_index, bound_interface_index); |
9947 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
9948 | if (bound_interface_index == cond_bound_interface_index) { |
9949 | // No match, matches forbidden interface |
9950 | return FALSE; |
9951 | } |
9952 | } else { |
9953 | if (bound_interface_index != cond_bound_interface_index) { |
9954 | // No match, does not match required interface |
9955 | return FALSE; |
9956 | } |
9957 | } |
9958 | } |
9959 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
9960 | if (bound_interface_index != IFSCOPE_NONE) { |
9961 | ifnet_head_lock_shared(); |
9962 | ifnet_t interface = ifindex2ifnet[bound_interface_index]; |
9963 | if (interface != NULL) { |
9964 | bound_interface_flags = interface->if_flags; |
9965 | bound_interface_eflags = interface->if_eflags; |
9966 | bound_interface_xflags = interface->if_xflags; |
9967 | } |
9968 | ifnet_head_done(); |
9969 | } |
9970 | |
9971 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
9972 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - flags" , kernel_policy->cond_bound_interface_flags, bound_interface_flags); |
9973 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
9974 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - eflags" , kernel_policy->cond_bound_interface_eflags, bound_interface_eflags); |
9975 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS, |
9976 | "NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS - xflags" , kernel_policy->cond_bound_interface_xflags, bound_interface_xflags); |
9977 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS) { |
9978 | if ((kernel_policy->cond_bound_interface_flags && (bound_interface_flags & kernel_policy->cond_bound_interface_flags)) || |
9979 | (kernel_policy->cond_bound_interface_eflags && (bound_interface_eflags & kernel_policy->cond_bound_interface_eflags)) || |
9980 | (kernel_policy->cond_bound_interface_xflags && (bound_interface_xflags & kernel_policy->cond_bound_interface_xflags))) { |
9981 | // No match, matches some forbidden interface flags |
9982 | return FALSE; |
9983 | } |
9984 | } else { |
9985 | if ((kernel_policy->cond_bound_interface_flags && !(bound_interface_flags & kernel_policy->cond_bound_interface_flags)) || |
9986 | (kernel_policy->cond_bound_interface_eflags && !(bound_interface_eflags & kernel_policy->cond_bound_interface_eflags)) || |
9987 | (kernel_policy->cond_bound_interface_xflags && !(bound_interface_xflags & kernel_policy->cond_bound_interface_xflags))) { |
9988 | // No match, does not match some required interface xflags |
9989 | return FALSE; |
9990 | } |
9991 | } |
9992 | } |
9993 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) && |
9994 | !(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE_FLAGS)) { |
9995 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , false, "Requiring no bound interface" , 0, bound_interface_index); |
9996 | if (bound_interface_index != 0) { |
9997 | // No match, requires a non-bound packet |
9998 | return FALSE; |
9999 | } |
10000 | } |
10001 | } |
10002 | |
10003 | if (kernel_policy->condition_mask == 0) { |
10004 | return TRUE; |
10005 | } |
10006 | |
10007 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) { |
10008 | necp_kernel_policy_id matched_policy_id = |
10009 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP ? socket_skip_policy_id : socket_policy_id; |
10010 | NECP_DATA_TRACE_LOG_CONDITION3(debug, "IP" , false, |
10011 | "NECP_KERNEL_CONDITION_POLICY_ID" , |
10012 | kernel_policy->cond_policy_id, 0, 0, |
10013 | matched_policy_id, socket_policy_id, socket_skip_policy_id); |
10014 | if (matched_policy_id != kernel_policy->cond_policy_id) { |
10015 | // No match, does not match required id |
10016 | return FALSE; |
10017 | } |
10018 | } |
10019 | |
10020 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LAST_INTERFACE) { |
10021 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , false, |
10022 | "NECP_KERNEL_CONDITION_LAST_INTERFACE" , |
10023 | kernel_policy->cond_last_interface_index, last_interface_index); |
10024 | if (last_interface_index != kernel_policy->cond_last_interface_index) { |
10025 | return FALSE; |
10026 | } |
10027 | } |
10028 | |
10029 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
10030 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL, |
10031 | "NECP_KERNEL_CONDITION_PROTOCOL" , |
10032 | kernel_policy->cond_protocol, protocol); |
10033 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
10034 | if (protocol == kernel_policy->cond_protocol) { |
10035 | // No match, matches forbidden protocol |
10036 | return FALSE; |
10037 | } |
10038 | } else { |
10039 | if (protocol != kernel_policy->cond_protocol) { |
10040 | // No match, does not match required protocol |
10041 | return FALSE; |
10042 | } |
10043 | } |
10044 | } |
10045 | |
10046 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_NETWORKS) { |
10047 | bool is_local = FALSE; |
10048 | |
10049 | if (rt != NULL) { |
10050 | is_local = IS_NECP_DEST_IN_LOCAL_NETWORKS(rt); |
10051 | } else { |
10052 | is_local = necp_is_route_local(remote_addr: remote); |
10053 | } |
10054 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , false, "NECP_KERNEL_CONDITION_LOCAL_NETWORKS" , 0, is_local); |
10055 | if (!is_local) { |
10056 | // Either no route to validate or no match for local networks |
10057 | return FALSE; |
10058 | } |
10059 | } |
10060 | |
10061 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
10062 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
10063 | bool inRange = necp_is_addr_in_range(addr: (struct sockaddr *)local, range_start: (struct sockaddr *)&kernel_policy->cond_local_start, range_end: (struct sockaddr *)&kernel_policy->cond_local_end); |
10064 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END, "local address range" , 0, 0); |
10065 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
10066 | if (inRange) { |
10067 | return FALSE; |
10068 | } |
10069 | } else { |
10070 | if (!inRange) { |
10071 | return FALSE; |
10072 | } |
10073 | } |
10074 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
10075 | bool inSubnet = necp_is_addr_in_subnet(addr: (struct sockaddr *)local, subnet_addr: (struct sockaddr *)&kernel_policy->cond_local_start, subnet_prefix: kernel_policy->cond_local_prefix); |
10076 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX, "local address with prefix" , 0, 0); |
10077 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
10078 | if (inSubnet) { |
10079 | return FALSE; |
10080 | } |
10081 | } else { |
10082 | if (!inSubnet) { |
10083 | return FALSE; |
10084 | } |
10085 | } |
10086 | } |
10087 | } |
10088 | |
10089 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
10090 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
10091 | bool inRange = necp_is_addr_in_range(addr: (struct sockaddr *)remote, range_start: (struct sockaddr *)&kernel_policy->cond_remote_start, range_end: (struct sockaddr *)&kernel_policy->cond_remote_end); |
10092 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END, "remote address range" , 0, 0); |
10093 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
10094 | if (inRange) { |
10095 | return FALSE; |
10096 | } |
10097 | } else { |
10098 | if (!inRange) { |
10099 | return FALSE; |
10100 | } |
10101 | } |
10102 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
10103 | bool inSubnet = necp_is_addr_in_subnet(addr: (struct sockaddr *)remote, subnet_addr: (struct sockaddr *)&kernel_policy->cond_remote_start, subnet_prefix: kernel_policy->cond_remote_prefix); |
10104 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX, "remote address with prefix" , 0, 0); |
10105 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
10106 | if (inSubnet) { |
10107 | return FALSE; |
10108 | } |
10109 | } else { |
10110 | if (!inSubnet) { |
10111 | return FALSE; |
10112 | } |
10113 | } |
10114 | } |
10115 | } |
10116 | |
10117 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
10118 | u_int16_t remote_port = 0; |
10119 | if (((struct sockaddr *)remote)->sa_family == AF_INET || ((struct sockaddr *)remote)->sa_family == AF_INET6) { |
10120 | remote_port = ((struct sockaddr_in *)remote)->sin_port; |
10121 | } |
10122 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SCHEME_PORT, |
10123 | "NECP_KERNEL_CONDITION_SCHEME_PORT" , |
10124 | 0, remote_port); |
10125 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_SCHEME_PORT) { |
10126 | if (kernel_policy->cond_scheme_port == remote_port) { |
10127 | return FALSE; |
10128 | } |
10129 | } else { |
10130 | if (kernel_policy->cond_scheme_port != remote_port) { |
10131 | return FALSE; |
10132 | } |
10133 | } |
10134 | } |
10135 | |
10136 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
10137 | bool tags_matched = false; |
10138 | NECP_DATA_TRACE_LOG_CONDITION(debug, "IP" , kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS, |
10139 | "NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS" , |
10140 | kernel_policy->cond_packet_filter_tags, pf_tag); |
10141 | if (kernel_policy->cond_packet_filter_tags & NECP_POLICY_CONDITION_PACKET_FILTER_TAG_STACK_DROP) { |
10142 | if ((pf_tag & PF_TAG_ID_STACK_DROP) == PF_TAG_ID_STACK_DROP) { |
10143 | tags_matched = true; |
10144 | } |
10145 | |
10146 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PACKET_FILTER_TAGS) { |
10147 | if (tags_matched) { |
10148 | return FALSE; |
10149 | } |
10150 | } else { |
10151 | if (!tags_matched) { |
10152 | return FALSE; |
10153 | } |
10154 | } |
10155 | } |
10156 | } |
10157 | |
10158 | return TRUE; |
10159 | } |
10160 | |
10161 | static inline struct necp_kernel_ip_output_policy * |
10162 | necp_ip_output_find_policy_match_locked(necp_kernel_policy_id socket_policy_id, necp_kernel_policy_id socket_skip_policy_id, u_int32_t bound_interface_index, u_int32_t last_interface_index, u_int16_t protocol, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr, struct rtentry *rt, u_int16_t pf_tag, u_int32_t *return_route_rule_id, necp_kernel_policy_result *return_drop_dest_policy_result, necp_drop_all_bypass_check_result_t *return_drop_all_bypass, int debug) |
10163 | { |
10164 | u_int32_t skip_order = 0; |
10165 | u_int32_t skip_session_order = 0; |
10166 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
10167 | struct necp_kernel_ip_output_policy **policy_search_array = necp_kernel_ip_output_policies_map[NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(socket_policy_id)]; |
10168 | u_int32_t route_rule_id_array[MAX_AGGREGATE_ROUTE_RULES]; |
10169 | size_t route_rule_id_count = 0; |
10170 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
10171 | if (return_drop_all_bypass != NULL) { |
10172 | *return_drop_all_bypass = drop_all_bypass; |
10173 | } |
10174 | |
10175 | if (return_route_rule_id != NULL) { |
10176 | *return_route_rule_id = 0; |
10177 | } |
10178 | |
10179 | *return_drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
10180 | |
10181 | if (policy_search_array != NULL) { |
10182 | for (int i = 0; policy_search_array[i] != NULL; i++) { |
10183 | NECP_DATA_TRACE_LOG_POLICY(debug, "IP" , "EXAMINING" ); |
10184 | if (necp_drop_all_order != 0 && policy_search_array[i]->session_order >= necp_drop_all_order) { |
10185 | // We've hit a drop all rule |
10186 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
10187 | drop_all_bypass = necp_check_drop_all_bypass_result(NULL); |
10188 | if (return_drop_all_bypass != NULL) { |
10189 | *return_drop_all_bypass = drop_all_bypass; |
10190 | } |
10191 | } |
10192 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
10193 | NECP_DATA_TRACE_LOG_IP_RESULT(debug, "IP" , "RESULT - DROP (session order > drop-all order)" ); |
10194 | break; |
10195 | } |
10196 | } |
10197 | if (necp_drop_dest_policy.entry_count > 0 && |
10198 | necp_address_matches_drop_dest_policy(remote_addr, policy_search_array[i]->session_order)) { |
10199 | // We've hit a drop by destination address rule |
10200 | *return_drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_DROP; |
10201 | NECP_DATA_TRACE_LOG_IP_RESULT(debug, "IP" , "RESULT - DROP (destination address rule)" ); |
10202 | break; |
10203 | } |
10204 | if (skip_session_order && policy_search_array[i]->session_order >= skip_session_order) { |
10205 | // Done skipping |
10206 | skip_order = 0; |
10207 | skip_session_order = 0; |
10208 | } |
10209 | if (skip_order) { |
10210 | if (policy_search_array[i]->order < skip_order) { |
10211 | // Skip this policy |
10212 | NECP_DATA_TRACE_LOG_POLICY(debug, "IP" , "SKIP (session order < skip-order)" ); |
10213 | continue; |
10214 | } else { |
10215 | // Done skipping |
10216 | skip_order = 0; |
10217 | skip_session_order = 0; |
10218 | } |
10219 | } else if (skip_session_order) { |
10220 | // Skip this policy |
10221 | NECP_DATA_TRACE_LOG_POLICY(debug, "IP" , "SKIP (skip-session-order)" ); |
10222 | continue; |
10223 | } |
10224 | |
10225 | if (necp_ip_output_check_policy(kernel_policy: policy_search_array[i], socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, local: local_addr, remote: remote_addr, rt, pf_tag, debug)) { |
10226 | if (!debug && necp_data_tracing_session_order) { |
10227 | if ((necp_data_tracing_session_order == policy_search_array[i]->session_order) && |
10228 | (!necp_data_tracing_policy_order || (necp_data_tracing_policy_order == policy_search_array[i]->order))) { |
10229 | NECP_DATA_TRACE_LOG_IP_RESULT(true, "IP" , "DEBUG - MATCHED POLICY" ); |
10230 | } |
10231 | } |
10232 | |
10233 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES) { |
10234 | if (return_route_rule_id != NULL && route_rule_id_count < MAX_AGGREGATE_ROUTE_RULES) { |
10235 | route_rule_id_array[route_rule_id_count++] = policy_search_array[i]->result_parameter.route_rule_id; |
10236 | } |
10237 | continue; |
10238 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
10239 | skip_order = policy_search_array[i]->result_parameter.skip_policy_order; |
10240 | skip_session_order = policy_search_array[i]->session_order + 1; |
10241 | NECP_DATA_TRACE_LOG_POLICY(debug, "IP" , "MATCHED SKIP POLICY" ); |
10242 | continue; |
10243 | } |
10244 | |
10245 | // Passed all tests, found a match |
10246 | matched_policy = policy_search_array[i]; |
10247 | NECP_DATA_TRACE_LOG_IP_RESULT(debug, "IP" , "RESULT - MATCHED POLICY" ); |
10248 | break; |
10249 | } |
10250 | } |
10251 | } |
10252 | |
10253 | if (route_rule_id_count == 1) { |
10254 | *return_route_rule_id = route_rule_id_array[0]; |
10255 | } else if (route_rule_id_count > 1) { |
10256 | *return_route_rule_id = necp_create_aggregate_route_rule(rule_ids: route_rule_id_array); |
10257 | } |
10258 | |
10259 | return matched_policy; |
10260 | } |
10261 | |
10262 | static inline bool |
10263 | necp_output_bypass(struct mbuf *packet) |
10264 | { |
10265 | if (necp_pass_loopback > 0 && necp_is_loopback(NULL, NULL, NULL, packet, IFSCOPE_NONE)) { |
10266 | return true; |
10267 | } |
10268 | if (necp_pass_keepalives > 0 && necp_get_is_keepalive_from_packet(packet)) { |
10269 | return true; |
10270 | } |
10271 | if (necp_is_intcoproc(NULL, packet)) { |
10272 | return true; |
10273 | } |
10274 | return false; |
10275 | } |
10276 | |
10277 | necp_kernel_policy_id |
10278 | necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_args *ipoa, struct rtentry *rt, |
10279 | necp_kernel_policy_result *result, necp_kernel_policy_result_parameter *result_parameter) |
10280 | { |
10281 | struct ip *ip = NULL; |
10282 | int hlen = sizeof(struct ip); |
10283 | necp_kernel_policy_id socket_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10284 | necp_kernel_policy_id socket_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10285 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10286 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
10287 | u_int16_t protocol = 0; |
10288 | u_int32_t bound_interface_index = 0; |
10289 | u_int32_t last_interface_index = 0; |
10290 | union necp_sockaddr_union local_addr = { }; |
10291 | union necp_sockaddr_union remote_addr = { }; |
10292 | u_int32_t drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
10293 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
10294 | u_int16_t pf_tag = 0; |
10295 | |
10296 | if (result) { |
10297 | *result = 0; |
10298 | } |
10299 | |
10300 | if (result_parameter) { |
10301 | memset(s: result_parameter, c: 0, n: sizeof(*result_parameter)); |
10302 | } |
10303 | |
10304 | if (packet == NULL) { |
10305 | return NECP_KERNEL_POLICY_ID_NONE; |
10306 | } |
10307 | |
10308 | socket_policy_id = necp_get_policy_id_from_packet(packet); |
10309 | socket_skip_policy_id = necp_get_skip_policy_id_from_packet(packet); |
10310 | pf_tag = necp_get_packet_filter_tags_from_packet(packet); |
10311 | |
10312 | // Exit early for an empty list |
10313 | // Don't lock. Possible race condition, but we don't want the performance hit. |
10314 | if (necp_kernel_ip_output_policies_count == 0 || |
10315 | (socket_policy_id == NECP_KERNEL_POLICY_ID_NONE && necp_kernel_ip_output_policies_non_id_count == 0 && necp_drop_dest_policy.entry_count == 0)) { |
10316 | if (necp_drop_all_order > 0) { |
10317 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10318 | if (result) { |
10319 | if (necp_output_bypass(packet)) { |
10320 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
10321 | } else { |
10322 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
10323 | } |
10324 | } |
10325 | } |
10326 | |
10327 | return matched_policy_id; |
10328 | } |
10329 | |
10330 | // Check for loopback exception |
10331 | if (necp_output_bypass(packet)) { |
10332 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10333 | if (result) { |
10334 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
10335 | } |
10336 | return matched_policy_id; |
10337 | } |
10338 | |
10339 | last_interface_index = necp_get_last_interface_index_from_packet(packet); |
10340 | |
10341 | // Process packet to get relevant fields |
10342 | ip = mtod(packet, struct ip *); |
10343 | #ifdef _IP_VHL |
10344 | hlen = _IP_VHL_HL(ip->ip_vhl) << 2; |
10345 | #else |
10346 | hlen = ip->ip_hl << 2; |
10347 | #endif |
10348 | |
10349 | protocol = ip->ip_p; |
10350 | |
10351 | if ((flags & IP_OUTARGS) && (ipoa != NULL) && |
10352 | (ipoa->ipoa_flags & IPOAF_BOUND_IF) && |
10353 | ipoa->ipoa_boundif != IFSCOPE_NONE) { |
10354 | bound_interface_index = ipoa->ipoa_boundif; |
10355 | } |
10356 | |
10357 | local_addr.sin.sin_family = AF_INET; |
10358 | local_addr.sin.sin_len = sizeof(struct sockaddr_in); |
10359 | memcpy(dst: &local_addr.sin.sin_addr, src: &ip->ip_src, n: sizeof(ip->ip_src)); |
10360 | |
10361 | remote_addr.sin.sin_family = AF_INET; |
10362 | remote_addr.sin.sin_len = sizeof(struct sockaddr_in); |
10363 | memcpy(dst: &((struct sockaddr_in *)&remote_addr)->sin_addr, src: &ip->ip_dst, n: sizeof(ip->ip_dst)); |
10364 | |
10365 | switch (protocol) { |
10366 | case IPPROTO_TCP: { |
10367 | struct tcphdr th; |
10368 | if ((int)(hlen + sizeof(th)) <= packet->m_pkthdr.len) { |
10369 | m_copydata(packet, hlen, sizeof(th), (u_int8_t *)&th); |
10370 | ((struct sockaddr_in *)&local_addr)->sin_port = th.th_sport; |
10371 | ((struct sockaddr_in *)&remote_addr)->sin_port = th.th_dport; |
10372 | } |
10373 | break; |
10374 | } |
10375 | case IPPROTO_UDP: { |
10376 | struct udphdr uh; |
10377 | if ((int)(hlen + sizeof(uh)) <= packet->m_pkthdr.len) { |
10378 | m_copydata(packet, hlen, sizeof(uh), (u_int8_t *)&uh); |
10379 | ((struct sockaddr_in *)&local_addr)->sin_port = uh.uh_sport; |
10380 | ((struct sockaddr_in *)&remote_addr)->sin_port = uh.uh_dport; |
10381 | } |
10382 | break; |
10383 | } |
10384 | default: { |
10385 | ((struct sockaddr_in *)&local_addr)->sin_port = 0; |
10386 | ((struct sockaddr_in *)&remote_addr)->sin_port = 0; |
10387 | break; |
10388 | } |
10389 | } |
10390 | |
10391 | // Match packet to policy |
10392 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
10393 | u_int32_t route_rule_id = 0; |
10394 | |
10395 | int debug = NECP_ENABLE_DATA_TRACE((&local_addr), (&remote_addr), protocol, 0, bound_interface_index); |
10396 | NECP_DATA_TRACE_LOG_IP4(debug, "IP4" , "START" ); |
10397 | |
10398 | matched_policy = necp_ip_output_find_policy_match_locked(socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, local_addr: &local_addr, remote_addr: &remote_addr, rt, pf_tag, return_route_rule_id: &route_rule_id, return_drop_dest_policy_result: &drop_dest_policy_result, return_drop_all_bypass: &drop_all_bypass, debug); |
10399 | if (matched_policy) { |
10400 | matched_policy_id = matched_policy->id; |
10401 | if (result) { |
10402 | *result = matched_policy->result; |
10403 | } |
10404 | |
10405 | if (result_parameter) { |
10406 | memcpy(dst: result_parameter, src: &matched_policy->result_parameter, n: sizeof(matched_policy->result_parameter)); |
10407 | } |
10408 | |
10409 | if (route_rule_id != 0 && |
10410 | packet->m_pkthdr.necp_mtag.necp_route_rule_id == 0) { |
10411 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
10412 | } |
10413 | |
10414 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
10415 | NECPLOG(LOG_DEBUG, "DATA-TRACE: IP Output: RESULT - MATCHED (ID %d BoundInterface %d LastInterface %d Proto %d) Policy %d Result %d Parameter %d Route Rule %u" , socket_policy_id, bound_interface_index, last_interface_index, protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index, route_rule_id); |
10416 | } |
10417 | } else { |
10418 | bool drop_all = false; |
10419 | /* |
10420 | * Apply drop-all only to packets which have never matched a primary policy (check |
10421 | * if the packet saved policy id is none or falls within the socket policy id range). |
10422 | */ |
10423 | if (socket_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_IP && |
10424 | (necp_drop_all_order > 0 || drop_dest_policy_result == NECP_KERNEL_POLICY_RESULT_DROP)) { |
10425 | drop_all = true; |
10426 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
10427 | drop_all_bypass = necp_check_drop_all_bypass_result(NULL); |
10428 | } |
10429 | } |
10430 | if (drop_all && drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
10431 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10432 | if (result) { |
10433 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
10434 | NECP_DATA_TRACE_LOG_IP4(debug, "IP4" , "RESULT - DROP <NO MATCH>" ); |
10435 | } |
10436 | } else if (route_rule_id != 0 && |
10437 | packet->m_pkthdr.necp_mtag.necp_route_rule_id == 0) { |
10438 | // If we matched a route rule, mark it |
10439 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
10440 | } |
10441 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
10442 | NECPLOG(LOG_DEBUG, "DATA-TRACE: IP Output: RESULT - NO MATCH (ID %d BoundInterface %d LastInterface %d Proto %d)" , socket_policy_id, bound_interface_index, last_interface_index, protocol); |
10443 | } |
10444 | } |
10445 | |
10446 | lck_rw_done(lck: &necp_kernel_policy_lock); |
10447 | |
10448 | return matched_policy_id; |
10449 | } |
10450 | |
10451 | necp_kernel_policy_id |
10452 | necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out_args *ip6oa, struct rtentry *rt, |
10453 | necp_kernel_policy_result *result, necp_kernel_policy_result_parameter *result_parameter) |
10454 | { |
10455 | struct ip6_hdr *ip6 = NULL; |
10456 | int next = -1; |
10457 | int offset = 0; |
10458 | necp_kernel_policy_id socket_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10459 | necp_kernel_policy_id socket_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10460 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
10461 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
10462 | u_int16_t protocol = 0; |
10463 | u_int32_t bound_interface_index = 0; |
10464 | u_int32_t last_interface_index = 0; |
10465 | union necp_sockaddr_union local_addr = { }; |
10466 | union necp_sockaddr_union remote_addr = { }; |
10467 | u_int32_t drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
10468 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
10469 | u_int16_t pf_tag = 0; |
10470 | |
10471 | if (result) { |
10472 | *result = 0; |
10473 | } |
10474 | |
10475 | if (result_parameter) { |
10476 | memset(s: result_parameter, c: 0, n: sizeof(*result_parameter)); |
10477 | } |
10478 | |
10479 | if (packet == NULL) { |
10480 | return NECP_KERNEL_POLICY_ID_NONE; |
10481 | } |
10482 | |
10483 | socket_policy_id = necp_get_policy_id_from_packet(packet); |
10484 | socket_skip_policy_id = necp_get_skip_policy_id_from_packet(packet); |
10485 | pf_tag = necp_get_packet_filter_tags_from_packet(packet); |
10486 | |
10487 | // Exit early for an empty list |
10488 | // Don't lock. Possible race condition, but we don't want the performance hit. |
10489 | if (necp_drop_management_order == 0 && |
10490 | (necp_kernel_ip_output_policies_count == 0 || |
10491 | (socket_policy_id == NECP_KERNEL_POLICY_ID_NONE && necp_kernel_ip_output_policies_non_id_count == 0 && necp_drop_dest_policy.entry_count == 0))) { |
10492 | if (necp_drop_all_order > 0) { |
10493 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10494 | if (result) { |
10495 | if (necp_output_bypass(packet)) { |
10496 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
10497 | } else { |
10498 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
10499 | } |
10500 | } |
10501 | } |
10502 | |
10503 | return matched_policy_id; |
10504 | } |
10505 | |
10506 | // Check for loopback exception |
10507 | if (necp_output_bypass(packet)) { |
10508 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10509 | if (result) { |
10510 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
10511 | } |
10512 | return matched_policy_id; |
10513 | } |
10514 | |
10515 | last_interface_index = necp_get_last_interface_index_from_packet(packet); |
10516 | |
10517 | // Process packet to get relevant fields |
10518 | ip6 = mtod(packet, struct ip6_hdr *); |
10519 | |
10520 | if ((flags & IPV6_OUTARGS) && (ip6oa != NULL) && |
10521 | (ip6oa->ip6oa_flags & IP6OAF_BOUND_IF) && |
10522 | ip6oa->ip6oa_boundif != IFSCOPE_NONE) { |
10523 | bound_interface_index = ip6oa->ip6oa_boundif; |
10524 | } |
10525 | |
10526 | ((struct sockaddr_in6 *)&local_addr)->sin6_family = AF_INET6; |
10527 | ((struct sockaddr_in6 *)&local_addr)->sin6_len = sizeof(struct sockaddr_in6); |
10528 | memcpy(dst: &((struct sockaddr_in6 *)&local_addr)->sin6_addr, src: &ip6->ip6_src, n: sizeof(ip6->ip6_src)); |
10529 | |
10530 | ((struct sockaddr_in6 *)&remote_addr)->sin6_family = AF_INET6; |
10531 | ((struct sockaddr_in6 *)&remote_addr)->sin6_len = sizeof(struct sockaddr_in6); |
10532 | memcpy(dst: &((struct sockaddr_in6 *)&remote_addr)->sin6_addr, src: &ip6->ip6_dst, n: sizeof(ip6->ip6_dst)); |
10533 | |
10534 | offset = ip6_lasthdr(packet, 0, IPPROTO_IPV6, &next); |
10535 | if (offset >= 0 && packet->m_pkthdr.len >= offset) { |
10536 | protocol = next; |
10537 | switch (protocol) { |
10538 | case IPPROTO_TCP: { |
10539 | struct tcphdr th; |
10540 | if ((int)(offset + sizeof(th)) <= packet->m_pkthdr.len) { |
10541 | m_copydata(packet, offset, sizeof(th), (u_int8_t *)&th); |
10542 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = th.th_sport; |
10543 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = th.th_dport; |
10544 | } |
10545 | break; |
10546 | } |
10547 | case IPPROTO_UDP: { |
10548 | struct udphdr uh; |
10549 | if ((int)(offset + sizeof(uh)) <= packet->m_pkthdr.len) { |
10550 | m_copydata(packet, offset, sizeof(uh), (u_int8_t *)&uh); |
10551 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = uh.uh_sport; |
10552 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = uh.uh_dport; |
10553 | } |
10554 | break; |
10555 | } |
10556 | default: { |
10557 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = 0; |
10558 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = 0; |
10559 | break; |
10560 | } |
10561 | } |
10562 | } |
10563 | |
10564 | // Match packet to policy |
10565 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
10566 | u_int32_t route_rule_id = 0; |
10567 | |
10568 | int debug = NECP_ENABLE_DATA_TRACE((&local_addr), (&remote_addr), protocol, 0, bound_interface_index); |
10569 | NECP_DATA_TRACE_LOG_IP6(debug, "IP6" , "START" ); |
10570 | |
10571 | matched_policy = necp_ip_output_find_policy_match_locked(socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, local_addr: &local_addr, remote_addr: &remote_addr, rt, pf_tag, return_route_rule_id: &route_rule_id, return_drop_dest_policy_result: &drop_dest_policy_result, return_drop_all_bypass: &drop_all_bypass, debug); |
10572 | if (matched_policy) { |
10573 | matched_policy_id = matched_policy->id; |
10574 | if (result) { |
10575 | *result = matched_policy->result; |
10576 | } |
10577 | |
10578 | if (result_parameter) { |
10579 | memcpy(dst: result_parameter, src: &matched_policy->result_parameter, n: sizeof(matched_policy->result_parameter)); |
10580 | } |
10581 | |
10582 | if (route_rule_id != 0 && |
10583 | packet->m_pkthdr.necp_mtag.necp_route_rule_id == 0) { |
10584 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
10585 | } |
10586 | |
10587 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
10588 | NECPLOG(LOG_DEBUG, "DATA-TRACE: IP6 Output: RESULT - MATCHED (ID %d BoundInterface %d LastInterface %d Proto %d) Policy %d Result %d Parameter %d Route Rule %u" , socket_policy_id, bound_interface_index, last_interface_index, protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index, route_rule_id); |
10589 | } |
10590 | } else { |
10591 | bool drop_all = false; |
10592 | /* |
10593 | * Apply drop-all only to packets which have never matched a primary policy (check |
10594 | * if the packet saved policy id is none or falls within the socket policy id range). |
10595 | */ |
10596 | if (socket_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_IP && |
10597 | (necp_drop_all_order > 0 || drop_dest_policy_result == NECP_KERNEL_POLICY_RESULT_DROP)) { |
10598 | drop_all = true; |
10599 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
10600 | drop_all_bypass = necp_check_drop_all_bypass_result(NULL); |
10601 | } |
10602 | } |
10603 | if (drop_all && drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
10604 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
10605 | if (result) { |
10606 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
10607 | NECP_DATA_TRACE_LOG_IP6(debug, "IP6" , "RESULT - DROP <NO MATCH>" ); |
10608 | } |
10609 | } else if (route_rule_id != 0 && |
10610 | packet->m_pkthdr.necp_mtag.necp_route_rule_id == 0) { |
10611 | // If we matched a route rule, mark it |
10612 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
10613 | } |
10614 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
10615 | NECPLOG(LOG_DEBUG, "DATA-TRACE: IP6 Output: RESULT - NO MATCH (ID %d BoundInterface %d LastInterface %d Proto %d)" , socket_policy_id, bound_interface_index, last_interface_index, protocol); |
10616 | } |
10617 | } |
10618 | |
10619 | lck_rw_done(lck: &necp_kernel_policy_lock); |
10620 | |
10621 | return matched_policy_id; |
10622 | } |
10623 | |
10624 | // Utilities |
10625 | static bool |
10626 | necp_is_addr_in_range(struct sockaddr *addr, struct sockaddr *range_start, struct sockaddr *range_end) |
10627 | { |
10628 | int cmp = 0; |
10629 | |
10630 | if (addr == NULL || range_start == NULL || range_end == NULL) { |
10631 | return FALSE; |
10632 | } |
10633 | |
10634 | /* Must be greater than or equal to start */ |
10635 | cmp = necp_addr_compare(sa1: addr, sa2: range_start, check_port: 1); |
10636 | if (cmp != 0 && cmp != 1) { |
10637 | return FALSE; |
10638 | } |
10639 | |
10640 | /* Must be less than or equal to end */ |
10641 | cmp = necp_addr_compare(sa1: addr, sa2: range_end, check_port: 1); |
10642 | if (cmp != 0 && cmp != -1) { |
10643 | return FALSE; |
10644 | } |
10645 | |
10646 | return TRUE; |
10647 | } |
10648 | |
10649 | static bool |
10650 | necp_is_range_in_range(struct sockaddr *inner_range_start, struct sockaddr *inner_range_end, struct sockaddr *range_start, struct sockaddr *range_end) |
10651 | { |
10652 | int cmp = 0; |
10653 | |
10654 | if (inner_range_start == NULL || inner_range_end == NULL || range_start == NULL || range_end == NULL) { |
10655 | return FALSE; |
10656 | } |
10657 | |
10658 | /* Must be greater than or equal to start */ |
10659 | cmp = necp_addr_compare(sa1: inner_range_start, sa2: range_start, check_port: 1); |
10660 | if (cmp != 0 && cmp != 1) { |
10661 | return FALSE; |
10662 | } |
10663 | |
10664 | /* Must be less than or equal to end */ |
10665 | cmp = necp_addr_compare(sa1: inner_range_end, sa2: range_end, check_port: 1); |
10666 | if (cmp != 0 && cmp != -1) { |
10667 | return FALSE; |
10668 | } |
10669 | |
10670 | return TRUE; |
10671 | } |
10672 | |
10673 | static bool |
10674 | necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subnet_addr, u_int8_t subnet_prefix) |
10675 | { |
10676 | if (addr == NULL || subnet_addr == NULL) { |
10677 | return FALSE; |
10678 | } |
10679 | |
10680 | if (addr->sa_family != subnet_addr->sa_family || addr->sa_len != subnet_addr->sa_len) { |
10681 | return FALSE; |
10682 | } |
10683 | |
10684 | switch (addr->sa_family) { |
10685 | case AF_INET: { |
10686 | if (satosin(subnet_addr)->sin_port != 0 && |
10687 | satosin(addr)->sin_port != satosin(subnet_addr)->sin_port) { |
10688 | return FALSE; |
10689 | } |
10690 | return necp_buffer_compare_with_bit_prefix(p1: (u_int8_t *)&satosin(addr)->sin_addr, p2: (u_int8_t *)&satosin(subnet_addr)->sin_addr, bits: subnet_prefix); |
10691 | } |
10692 | case AF_INET6: { |
10693 | if (satosin6(subnet_addr)->sin6_port != 0 && |
10694 | satosin6(addr)->sin6_port != satosin6(subnet_addr)->sin6_port) { |
10695 | return FALSE; |
10696 | } |
10697 | if (satosin6(addr)->sin6_scope_id && |
10698 | satosin6(subnet_addr)->sin6_scope_id && |
10699 | satosin6(addr)->sin6_scope_id != satosin6(subnet_addr)->sin6_scope_id) { |
10700 | return FALSE; |
10701 | } |
10702 | return necp_buffer_compare_with_bit_prefix(p1: (u_int8_t *)&satosin6(addr)->sin6_addr, p2: (u_int8_t *)&satosin6(subnet_addr)->sin6_addr, bits: subnet_prefix); |
10703 | } |
10704 | default: { |
10705 | return FALSE; |
10706 | } |
10707 | } |
10708 | |
10709 | return FALSE; |
10710 | } |
10711 | |
10712 | /* |
10713 | * Return values: |
10714 | * -1: sa1 < sa2 |
10715 | * 0: sa1 == sa2 |
10716 | * 1: sa1 > sa2 |
10717 | * 2: Not comparable or error |
10718 | */ |
10719 | static int |
10720 | necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port) |
10721 | { |
10722 | int result = 0; |
10723 | int port_result = 0; |
10724 | |
10725 | if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len) { |
10726 | return 2; |
10727 | } |
10728 | |
10729 | if (sa1->sa_len == 0) { |
10730 | return 0; |
10731 | } |
10732 | |
10733 | switch (sa1->sa_family) { |
10734 | case AF_INET: { |
10735 | if (sa1->sa_len != sizeof(struct sockaddr_in)) { |
10736 | return 2; |
10737 | } |
10738 | |
10739 | result = memcmp(s1: &satosin(sa1)->sin_addr.s_addr, s2: &satosin(sa2)->sin_addr.s_addr, n: sizeof(satosin(sa1)->sin_addr.s_addr)); |
10740 | |
10741 | if (check_port) { |
10742 | if (satosin(sa1)->sin_port < satosin(sa2)->sin_port) { |
10743 | port_result = -1; |
10744 | } else if (satosin(sa1)->sin_port > satosin(sa2)->sin_port) { |
10745 | port_result = 1; |
10746 | } |
10747 | |
10748 | if (result == 0) { |
10749 | result = port_result; |
10750 | } else if ((result > 0 && port_result < 0) || (result < 0 && port_result > 0)) { |
10751 | return 2; |
10752 | } |
10753 | } |
10754 | |
10755 | break; |
10756 | } |
10757 | case AF_INET6: { |
10758 | if (sa1->sa_len != sizeof(struct sockaddr_in6)) { |
10759 | return 2; |
10760 | } |
10761 | |
10762 | if (satosin6(sa1)->sin6_scope_id != satosin6(sa2)->sin6_scope_id) { |
10763 | return 2; |
10764 | } |
10765 | |
10766 | result = memcmp(s1: &satosin6(sa1)->sin6_addr.s6_addr[0], s2: &satosin6(sa2)->sin6_addr.s6_addr[0], n: sizeof(struct in6_addr)); |
10767 | |
10768 | if (check_port) { |
10769 | if (satosin6(sa1)->sin6_port < satosin6(sa2)->sin6_port) { |
10770 | port_result = -1; |
10771 | } else if (satosin6(sa1)->sin6_port > satosin6(sa2)->sin6_port) { |
10772 | port_result = 1; |
10773 | } |
10774 | |
10775 | if (result == 0) { |
10776 | result = port_result; |
10777 | } else if ((result > 0 && port_result < 0) || (result < 0 && port_result > 0)) { |
10778 | return 2; |
10779 | } |
10780 | } |
10781 | |
10782 | break; |
10783 | } |
10784 | default: { |
10785 | result = memcmp(s1: sa1, s2: sa2, n: sa1->sa_len); |
10786 | break; |
10787 | } |
10788 | } |
10789 | |
10790 | if (result < 0) { |
10791 | result = (-1); |
10792 | } else if (result > 0) { |
10793 | result = (1); |
10794 | } |
10795 | |
10796 | return result; |
10797 | } |
10798 | |
10799 | static bool |
10800 | necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits) |
10801 | { |
10802 | u_int8_t mask; |
10803 | |
10804 | /* Handle null pointers */ |
10805 | if (p1 == NULL || p2 == NULL) { |
10806 | return p1 == p2; |
10807 | } |
10808 | |
10809 | while (bits >= 8) { |
10810 | if (*p1++ != *p2++) { |
10811 | return FALSE; |
10812 | } |
10813 | bits -= 8; |
10814 | } |
10815 | |
10816 | if (bits > 0) { |
10817 | mask = ~((1 << (8 - bits)) - 1); |
10818 | if ((*p1 & mask) != (*p2 & mask)) { |
10819 | return FALSE; |
10820 | } |
10821 | } |
10822 | return TRUE; |
10823 | } |
10824 | |
10825 | static bool |
10826 | necp_addr_is_empty(struct sockaddr *addr) |
10827 | { |
10828 | if (addr == NULL) { |
10829 | return TRUE; |
10830 | } |
10831 | |
10832 | if (addr->sa_len == 0) { |
10833 | return TRUE; |
10834 | } |
10835 | |
10836 | switch (addr->sa_family) { |
10837 | case AF_INET: { |
10838 | static struct sockaddr_in ipv4_empty_address = { |
10839 | .sin_len = sizeof(struct sockaddr_in), |
10840 | .sin_family = AF_INET, |
10841 | .sin_port = 0, |
10842 | .sin_addr = { .s_addr = 0 }, // 0.0.0.0 |
10843 | .sin_zero = {0}, |
10844 | }; |
10845 | if (necp_addr_compare(sa1: addr, sa2: (struct sockaddr *)&ipv4_empty_address, check_port: 0) == 0) { |
10846 | return TRUE; |
10847 | } else { |
10848 | return FALSE; |
10849 | } |
10850 | } |
10851 | case AF_INET6: { |
10852 | static struct sockaddr_in6 ipv6_empty_address = { |
10853 | .sin6_len = sizeof(struct sockaddr_in6), |
10854 | .sin6_family = AF_INET6, |
10855 | .sin6_port = 0, |
10856 | .sin6_flowinfo = 0, |
10857 | .sin6_addr = IN6ADDR_ANY_INIT, // :: |
10858 | .sin6_scope_id = 0, |
10859 | }; |
10860 | if (necp_addr_compare(sa1: addr, sa2: (struct sockaddr *)&ipv6_empty_address, check_port: 0) == 0) { |
10861 | return TRUE; |
10862 | } else { |
10863 | return FALSE; |
10864 | } |
10865 | } |
10866 | default: |
10867 | return FALSE; |
10868 | } |
10869 | |
10870 | return FALSE; |
10871 | } |
10872 | |
10873 | static bool |
10874 | necp_update_qos_marking(struct ifnet *ifp, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id) |
10875 | { |
10876 | bool qos_marking = FALSE; |
10877 | int exception_index = 0; |
10878 | struct necp_route_rule *route_rule = NULL; |
10879 | |
10880 | route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id); |
10881 | if (route_rule == NULL) { |
10882 | qos_marking = FALSE; |
10883 | goto done; |
10884 | } |
10885 | |
10886 | if (route_rule->match_netagent_id != 0) { |
10887 | if (netagent_array == NULL || netagent_array_count == 0) { |
10888 | // No agents, ignore rule |
10889 | goto done; |
10890 | } |
10891 | bool found_match = FALSE; |
10892 | for (size_t agent_index = 0; agent_index < netagent_array_count; agent_index++) { |
10893 | if (route_rule->match_netagent_id == netagent_array[agent_index]) { |
10894 | found_match = TRUE; |
10895 | break; |
10896 | } |
10897 | } |
10898 | if (!found_match) { |
10899 | // Agents don't match, ignore rule |
10900 | goto done; |
10901 | } |
10902 | } |
10903 | |
10904 | qos_marking = (route_rule->default_action == NECP_ROUTE_RULE_QOS_MARKING) ? TRUE : FALSE; |
10905 | |
10906 | if (ifp == NULL) { |
10907 | goto done; |
10908 | } |
10909 | |
10910 | for (exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
10911 | if (route_rule->exception_if_indices[exception_index] == 0) { |
10912 | break; |
10913 | } |
10914 | if (route_rule->exception_if_actions[exception_index] != NECP_ROUTE_RULE_QOS_MARKING) { |
10915 | continue; |
10916 | } |
10917 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index) { |
10918 | qos_marking = TRUE; |
10919 | if (necp_debug > 2) { |
10920 | NECPLOG(LOG_DEBUG, "QoS Marking : Interface match %d for Rule %d Allowed %d" , |
10921 | route_rule->exception_if_indices[exception_index], route_rule_id, qos_marking); |
10922 | } |
10923 | goto done; |
10924 | } |
10925 | } |
10926 | |
10927 | if ((route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_CELLULAR(ifp)) || |
10928 | (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIFI(ifp)) || |
10929 | (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIRED(ifp)) || |
10930 | (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_EXPENSIVE(ifp)) || |
10931 | (route_rule->constrained_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_CONSTRAINED(ifp)) || |
10932 | (route_rule->companion_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_COMPANION_LINK(ifp))) { |
10933 | qos_marking = TRUE; |
10934 | if (necp_debug > 2) { |
10935 | NECPLOG(LOG_DEBUG, "QoS Marking: C:%d WF:%d W:%d E:%d Cn:%d Cmpn:%d for Rule %d Allowed %d" , |
10936 | route_rule->cellular_action, route_rule->wifi_action, route_rule->wired_action, |
10937 | route_rule->expensive_action, route_rule->constrained_action, route_rule->companion_action, route_rule_id, qos_marking); |
10938 | } |
10939 | goto done; |
10940 | } |
10941 | done: |
10942 | if (necp_debug > 1) { |
10943 | NECPLOG(LOG_DEBUG, "QoS Marking: Rule %d ifp %s Allowed %d" , |
10944 | route_rule_id, ifp ? ifp->if_xname : "" , qos_marking); |
10945 | } |
10946 | return qos_marking; |
10947 | } |
10948 | |
10949 | bool |
10950 | necp_lookup_current_qos_marking(int32_t *qos_marking_gencount, struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id, bool old_qos_marking) |
10951 | { |
10952 | bool new_qos_marking = old_qos_marking; |
10953 | struct ifnet *ifp = interface; |
10954 | |
10955 | if (net_qos_policy_restricted == 0) { |
10956 | return new_qos_marking; |
10957 | } |
10958 | |
10959 | /* |
10960 | * This is racy but we do not need the performance hit of taking necp_kernel_policy_lock |
10961 | */ |
10962 | if (*qos_marking_gencount == necp_kernel_socket_policies_gencount) { |
10963 | return new_qos_marking; |
10964 | } |
10965 | |
10966 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
10967 | |
10968 | if (ifp == NULL && route != NULL) { |
10969 | ifp = route->rt_ifp; |
10970 | } |
10971 | /* |
10972 | * By default, until we have a interface, do not mark and reevaluate the Qos marking policy |
10973 | */ |
10974 | if (ifp == NULL || route_rule_id == 0) { |
10975 | new_qos_marking = FALSE; |
10976 | goto done; |
10977 | } |
10978 | |
10979 | if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) { |
10980 | struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id); |
10981 | if (aggregate_route_rule != NULL) { |
10982 | int index = 0; |
10983 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
10984 | u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index]; |
10985 | if (sub_route_rule_id == 0) { |
10986 | break; |
10987 | } |
10988 | new_qos_marking = necp_update_qos_marking(ifp, NULL, netagent_array_count: 0, route_rule_id: sub_route_rule_id); |
10989 | if (new_qos_marking == TRUE) { |
10990 | break; |
10991 | } |
10992 | } |
10993 | } |
10994 | } else { |
10995 | new_qos_marking = necp_update_qos_marking(ifp, NULL, netagent_array_count: 0, route_rule_id); |
10996 | } |
10997 | /* |
10998 | * Now that we have an interface we remember the gencount |
10999 | */ |
11000 | *qos_marking_gencount = necp_kernel_socket_policies_gencount; |
11001 | |
11002 | done: |
11003 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11004 | return new_qos_marking; |
11005 | } |
11006 | |
11007 | void |
11008 | necp_socket_update_qos_marking(struct inpcb *inp, struct rtentry *route, u_int32_t route_rule_id) |
11009 | { |
11010 | bool qos_marking = inp->inp_socket->so_flags1 & SOF1_QOSMARKING_ALLOWED ? TRUE : FALSE; |
11011 | |
11012 | if (net_qos_policy_restricted == 0) { |
11013 | return; |
11014 | } |
11015 | if (inp->inp_socket == NULL) { |
11016 | return; |
11017 | } |
11018 | if ((inp->inp_socket->so_flags1 & SOF1_QOSMARKING_POLICY_OVERRIDE)) { |
11019 | return; |
11020 | } |
11021 | |
11022 | qos_marking = necp_lookup_current_qos_marking(qos_marking_gencount: &(inp->inp_policyresult.results.qos_marking_gencount), route, NULL, route_rule_id, old_qos_marking: qos_marking); |
11023 | |
11024 | if (qos_marking == TRUE) { |
11025 | inp->inp_socket->so_flags1 |= SOF1_QOSMARKING_ALLOWED; |
11026 | } else { |
11027 | inp->inp_socket->so_flags1 &= ~SOF1_QOSMARKING_ALLOWED; |
11028 | } |
11029 | } |
11030 | |
11031 | static bool |
11032 | necp_route_is_lqm_abort(struct ifnet *ifp, struct ifnet *delegated_ifp) |
11033 | { |
11034 | if (ifp != NULL && |
11035 | (ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
11036 | ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
11037 | return true; |
11038 | } |
11039 | if (delegated_ifp != NULL && |
11040 | (delegated_ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
11041 | delegated_ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
11042 | return true; |
11043 | } |
11044 | return false; |
11045 | } |
11046 | |
11047 | static bool |
11048 | necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t *netagent_array, size_t netagent_array_count, |
11049 | u_int32_t route_rule_id, u_int32_t *interface_type_denied) |
11050 | { |
11051 | bool default_is_allowed = TRUE; |
11052 | u_int8_t type_aggregate_action = NECP_ROUTE_RULE_NONE; |
11053 | int exception_index = 0; |
11054 | struct ifnet *delegated_ifp = NULL; |
11055 | struct necp_route_rule *route_rule = NULL; |
11056 | |
11057 | route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id); |
11058 | if (route_rule == NULL) { |
11059 | return TRUE; |
11060 | } |
11061 | |
11062 | if (route_rule->match_netagent_id != 0) { |
11063 | if (netagent_array == NULL || netagent_array_count == 0) { |
11064 | // No agents, ignore rule |
11065 | return TRUE; |
11066 | } |
11067 | bool found_match = FALSE; |
11068 | for (size_t agent_index = 0; agent_index < netagent_array_count; agent_index++) { |
11069 | if (route_rule->match_netagent_id == netagent_array[agent_index]) { |
11070 | found_match = TRUE; |
11071 | break; |
11072 | } |
11073 | } |
11074 | if (!found_match) { |
11075 | // Agents don't match, ignore rule |
11076 | return TRUE; |
11077 | } |
11078 | } |
11079 | |
11080 | default_is_allowed = IS_NECP_ROUTE_RULE_DENY(route_rule->default_action) ? FALSE : TRUE; |
11081 | if (ifp == NULL && route != NULL) { |
11082 | ifp = route->rt_ifp; |
11083 | } |
11084 | if (ifp == NULL) { |
11085 | if (necp_debug > 1 && !default_is_allowed) { |
11086 | NECPLOG(LOG_DEBUG, "Route Allowed: No interface for route, using default for Rule %d Allowed %d" , route_rule_id, default_is_allowed); |
11087 | } |
11088 | return default_is_allowed; |
11089 | } |
11090 | |
11091 | delegated_ifp = ifp->if_delegated.ifp; |
11092 | for (exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
11093 | if (route_rule->exception_if_indices[exception_index] == 0) { |
11094 | break; |
11095 | } |
11096 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index || |
11097 | (delegated_ifp != NULL && route_rule->exception_if_indices[exception_index] == delegated_ifp->if_index)) { |
11098 | if (route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11099 | const bool lqm_abort = necp_route_is_lqm_abort(ifp, delegated_ifp); |
11100 | if (necp_debug > 1 && lqm_abort) { |
11101 | NECPLOG(LOG_DEBUG, "Route Allowed: Interface match %d for Rule %d Deny LQM Abort" , |
11102 | route_rule->exception_if_indices[exception_index], route_rule_id); |
11103 | } |
11104 | return false; |
11105 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->exception_if_actions[exception_index])) { |
11106 | if (necp_debug > 1) { |
11107 | NECPLOG(LOG_DEBUG, "Route Allowed: Interface match %d for Rule %d Allowed %d" , route_rule->exception_if_indices[exception_index], route_rule_id, (IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[exception_index]) ? FALSE : TRUE)); |
11108 | } |
11109 | if (IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[exception_index]) && route_rule->effective_type != 0 && interface_type_denied != NULL) { |
11110 | *interface_type_denied = route_rule->effective_type; |
11111 | } |
11112 | return IS_NECP_ROUTE_RULE_DENY(route_rule->exception_if_actions[exception_index]) ? FALSE : TRUE; |
11113 | } |
11114 | } |
11115 | } |
11116 | |
11117 | if (IFNET_IS_CELLULAR(ifp)) { |
11118 | if (route_rule->cellular_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11119 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11120 | if (interface_type_denied != NULL) { |
11121 | *interface_type_denied = IFRTYPE_FUNCTIONAL_CELLULAR; |
11122 | if (route_rule->effective_type != 0) { |
11123 | *interface_type_denied = route_rule->effective_type; |
11124 | } |
11125 | } |
11126 | // Mark aggregate action as deny |
11127 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11128 | } |
11129 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->cellular_action)) { |
11130 | if (interface_type_denied != NULL) { |
11131 | *interface_type_denied = IFRTYPE_FUNCTIONAL_CELLULAR; |
11132 | if (route_rule->effective_type != 0) { |
11133 | *interface_type_denied = route_rule->effective_type; |
11134 | } |
11135 | } |
11136 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11137 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11138 | IS_NECP_ROUTE_RULE_DENY(route_rule->cellular_action))) { |
11139 | // Deny wins if there is a conflict |
11140 | type_aggregate_action = route_rule->cellular_action; |
11141 | } |
11142 | } |
11143 | } |
11144 | |
11145 | if (IFNET_IS_WIFI(ifp)) { |
11146 | if (route_rule->wifi_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11147 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11148 | if (interface_type_denied != NULL) { |
11149 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIFI_INFRA; |
11150 | if (route_rule->effective_type != 0) { |
11151 | *interface_type_denied = route_rule->effective_type; |
11152 | } |
11153 | } |
11154 | // Mark aggregate action as deny |
11155 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11156 | } |
11157 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wifi_action)) { |
11158 | if (interface_type_denied != NULL) { |
11159 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIFI_INFRA; |
11160 | if (route_rule->effective_type != 0) { |
11161 | *interface_type_denied = route_rule->effective_type; |
11162 | } |
11163 | } |
11164 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11165 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11166 | IS_NECP_ROUTE_RULE_DENY(route_rule->wifi_action))) { |
11167 | // Deny wins if there is a conflict |
11168 | type_aggregate_action = route_rule->wifi_action; |
11169 | } |
11170 | } |
11171 | } |
11172 | |
11173 | if (IFNET_IS_COMPANION_LINK(ifp) || |
11174 | (ifp->if_delegated.ifp != NULL && IFNET_IS_COMPANION_LINK(ifp->if_delegated.ifp))) { |
11175 | if (route_rule->companion_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11176 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11177 | if (interface_type_denied != NULL) { |
11178 | *interface_type_denied = IFRTYPE_FUNCTIONAL_COMPANIONLINK; |
11179 | if (route_rule->effective_type != 0) { |
11180 | *interface_type_denied = route_rule->effective_type; |
11181 | } |
11182 | } |
11183 | // Mark aggregate action as deny |
11184 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11185 | } |
11186 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->companion_action)) { |
11187 | if (interface_type_denied != NULL) { |
11188 | *interface_type_denied = IFRTYPE_FUNCTIONAL_COMPANIONLINK; |
11189 | if (route_rule->effective_type != 0) { |
11190 | *interface_type_denied = route_rule->effective_type; |
11191 | } |
11192 | } |
11193 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11194 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11195 | IS_NECP_ROUTE_RULE_DENY(route_rule->companion_action))) { |
11196 | // Deny wins if there is a conflict |
11197 | type_aggregate_action = route_rule->companion_action; |
11198 | } |
11199 | } |
11200 | } |
11201 | |
11202 | if (IFNET_IS_WIRED(ifp)) { |
11203 | if (route_rule->wired_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11204 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11205 | if (interface_type_denied != NULL) { |
11206 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIRED; |
11207 | if (route_rule->effective_type != 0) { |
11208 | *interface_type_denied = route_rule->effective_type; |
11209 | } |
11210 | } |
11211 | // Mark aggregate action as deny |
11212 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11213 | } |
11214 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wired_action)) { |
11215 | if (interface_type_denied != NULL) { |
11216 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIRED; |
11217 | if (route_rule->effective_type != 0) { |
11218 | *interface_type_denied = route_rule->effective_type; |
11219 | } |
11220 | } |
11221 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11222 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11223 | IS_NECP_ROUTE_RULE_DENY(route_rule->wired_action))) { |
11224 | // Deny wins if there is a conflict |
11225 | type_aggregate_action = route_rule->wired_action; |
11226 | } |
11227 | } |
11228 | } |
11229 | |
11230 | if (IFNET_IS_EXPENSIVE(ifp)) { |
11231 | if (route_rule->expensive_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11232 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11233 | // Mark aggregate action as deny |
11234 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11235 | } |
11236 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->expensive_action)) { |
11237 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11238 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11239 | IS_NECP_ROUTE_RULE_DENY(route_rule->expensive_action))) { |
11240 | // Deny wins if there is a conflict |
11241 | type_aggregate_action = route_rule->expensive_action; |
11242 | } |
11243 | } |
11244 | } |
11245 | |
11246 | if (IFNET_IS_CONSTRAINED(ifp)) { |
11247 | if (route_rule->constrained_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
11248 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
11249 | // Mark aggregate action as deny |
11250 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
11251 | } |
11252 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->constrained_action)) { |
11253 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
11254 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
11255 | IS_NECP_ROUTE_RULE_DENY(route_rule->constrained_action))) { |
11256 | // Deny wins if there is a conflict |
11257 | type_aggregate_action = route_rule->constrained_action; |
11258 | } |
11259 | } |
11260 | } |
11261 | |
11262 | if (type_aggregate_action != NECP_ROUTE_RULE_NONE) { |
11263 | if (necp_debug > 1) { |
11264 | NECPLOG(LOG_DEBUG, "Route Allowed: C:%d WF:%d W:%d E:%d Cmpn:%d for Rule %d Allowed %d" , route_rule->cellular_action, route_rule->wifi_action, route_rule->wired_action, route_rule->expensive_action, route_rule->companion_action, route_rule_id, (IS_NECP_ROUTE_RULE_DENY(type_aggregate_action) ? FALSE : TRUE)); |
11265 | } |
11266 | return IS_NECP_ROUTE_RULE_DENY(type_aggregate_action) ? FALSE : TRUE; |
11267 | } |
11268 | |
11269 | if (necp_debug > 1 && !default_is_allowed) { |
11270 | NECPLOG(LOG_DEBUG, "Route Allowed: Using default for Rule %d Allowed %d" , route_rule_id, default_is_allowed); |
11271 | } |
11272 | return default_is_allowed; |
11273 | } |
11274 | |
11275 | static bool |
11276 | necp_proc_is_allowed_on_management_interface(proc_t proc) |
11277 | { |
11278 | bool allowed = false; |
11279 | const task_t task = proc_task(proc); |
11280 | |
11281 | if (task != NULL) { |
11282 | if (IOTaskHasEntitlement(task, INTCOPROC_RESTRICTED_ENTITLEMENT) == true |
11283 | || IOTaskHasEntitlement(task, MANAGEMENT_DATA_ENTITLEMENT) == true |
11284 | #if DEBUG || DEVELOPMENT |
11285 | || IOTaskHasEntitlement(task, INTCOPROC_RESTRICTED_ENTITLEMENT_DEVELOPMENT) == true |
11286 | || IOTaskHasEntitlement(task, MANAGEMENT_DATA_ENTITLEMENT_DEVELOPMENT) == true |
11287 | #endif /* DEBUG || DEVELOPMENT */ |
11288 | ) { |
11289 | allowed = true; |
11290 | } |
11291 | } |
11292 | return allowed; |
11293 | } |
11294 | |
11295 | __attribute__((noinline)) |
11296 | static void |
11297 | necp_log_interface_not_allowed(struct ifnet *ifp, proc_t proc, struct inpcb *inp) |
11298 | { |
11299 | char buf[128]; |
11300 | |
11301 | if (inp != NULL) { |
11302 | inp_snprintf_tuple(inp, buf, sizeof(buf)); |
11303 | } else { |
11304 | *buf = 0; |
11305 | } |
11306 | os_log(OS_LOG_DEFAULT, |
11307 | "necp_route_is_interface_type_allowed %s:%d %s not allowed on management interface %s" , |
11308 | proc != NULL ? proc_best_name(proc) : proc_best_name(current_proc()), |
11309 | proc != NULL ? proc_getpid(proc) : proc_selfpid(), |
11310 | inp != NULL ? buf : "" , |
11311 | ifp->if_xname); |
11312 | } |
11313 | |
11314 | static bool |
11315 | necp_route_is_interface_type_allowed(struct rtentry *route, struct ifnet *ifp, proc_t proc, struct inpcb *inp) |
11316 | { |
11317 | if (if_management_interface_check_needed == false) { |
11318 | return true; |
11319 | } |
11320 | if (necp_drop_management_order == 0) { |
11321 | return true; |
11322 | } |
11323 | if (ifp == NULL && route != NULL) { |
11324 | ifp = route->rt_ifp; |
11325 | } |
11326 | if (ifp == NULL) { |
11327 | return true; |
11328 | } |
11329 | |
11330 | if (IFNET_IS_MANAGEMENT(ifp)) { |
11331 | bool allowed = true; |
11332 | |
11333 | if (inp != NULL) { |
11334 | /* |
11335 | * The entitlement check is already performed for socket flows |
11336 | */ |
11337 | allowed = INP_MANAGEMENT_ALLOWED(inp); |
11338 | } else if (proc != NULL) { |
11339 | allowed = necp_proc_is_allowed_on_management_interface(proc); |
11340 | } |
11341 | if (__improbable(if_management_verbose > 1 && allowed == false)) { |
11342 | necp_log_interface_not_allowed(ifp, proc, inp); |
11343 | } |
11344 | return allowed; |
11345 | } |
11346 | return true; |
11347 | } |
11348 | |
11349 | static bool |
11350 | necp_route_is_allowed(struct rtentry *route, struct ifnet *interface, u_int32_t *netagent_array, size_t netagent_array_count, |
11351 | u_int32_t route_rule_id, u_int32_t *interface_type_denied) |
11352 | { |
11353 | if ((route == NULL && interface == NULL && netagent_array == NULL) || route_rule_id == 0) { |
11354 | if (necp_debug > 1) { |
11355 | NECPLOG(LOG_DEBUG, "Route Allowed: no route or interface, Rule %d Allowed %d" , route_rule_id, TRUE); |
11356 | } |
11357 | return TRUE; |
11358 | } |
11359 | |
11360 | if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) { |
11361 | struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id); |
11362 | if (aggregate_route_rule != NULL) { |
11363 | int index = 0; |
11364 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
11365 | u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index]; |
11366 | if (sub_route_rule_id == 0) { |
11367 | break; |
11368 | } |
11369 | if (!necp_route_is_allowed_inner(route, ifp: interface, netagent_array, netagent_array_count, route_rule_id: sub_route_rule_id, interface_type_denied)) { |
11370 | return FALSE; |
11371 | } |
11372 | } |
11373 | } |
11374 | } else { |
11375 | return necp_route_is_allowed_inner(route, ifp: interface, netagent_array, netagent_array_count, route_rule_id, interface_type_denied); |
11376 | } |
11377 | |
11378 | return TRUE; |
11379 | } |
11380 | |
11381 | static bool |
11382 | necp_route_rule_matches_agents(u_int32_t route_rule_id) |
11383 | { |
11384 | struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id); |
11385 | if (route_rule == NULL) { |
11386 | return false; |
11387 | } |
11388 | |
11389 | return route_rule->match_netagent_id != 0; |
11390 | } |
11391 | |
11392 | static uint32_t |
11393 | necp_route_get_netagent(struct rtentry *route, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id, bool *remove) |
11394 | { |
11395 | if (remove == NULL) { |
11396 | return 0; |
11397 | } |
11398 | |
11399 | struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id); |
11400 | if (route_rule == NULL) { |
11401 | return 0; |
11402 | } |
11403 | |
11404 | // No netagent, skip |
11405 | if (route_rule->netagent_id == 0) { |
11406 | return 0; |
11407 | } |
11408 | |
11409 | if (route_rule->match_netagent_id != 0) { |
11410 | if (netagent_array == NULL || netagent_array_count == 0) { |
11411 | // No agents, ignore rule |
11412 | return 0; |
11413 | } |
11414 | bool found_match = FALSE; |
11415 | for (size_t agent_index = 0; agent_index < netagent_array_count; agent_index++) { |
11416 | if (route_rule->match_netagent_id == netagent_array[agent_index]) { |
11417 | found_match = TRUE; |
11418 | break; |
11419 | } |
11420 | } |
11421 | if (!found_match) { |
11422 | // Agents don't match, ignore rule |
11423 | return 0; |
11424 | } |
11425 | } |
11426 | |
11427 | struct ifnet *ifp = route != NULL ? route->rt_ifp : NULL; |
11428 | if (ifp == NULL) { |
11429 | // No interface, apply the default action |
11430 | if (route_rule->default_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11431 | route_rule->default_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11432 | *remove = (route_rule->default_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11433 | return route_rule->netagent_id; |
11434 | } |
11435 | return 0; |
11436 | } |
11437 | |
11438 | for (int exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
11439 | if (route_rule->exception_if_indices[exception_index] == 0) { |
11440 | break; |
11441 | } |
11442 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index && |
11443 | route_rule->exception_if_actions[exception_index] != NECP_ROUTE_RULE_NONE) { |
11444 | if (route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_USE_NETAGENT || |
11445 | route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11446 | *remove = (route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11447 | return route_rule->netagent_id; |
11448 | } |
11449 | return 0; |
11450 | } |
11451 | } |
11452 | |
11453 | if (ifp->if_type == IFT_CELLULAR && |
11454 | route_rule->cellular_action != NECP_ROUTE_RULE_NONE) { |
11455 | if (route_rule->cellular_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11456 | route_rule->cellular_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11457 | *remove = (route_rule->cellular_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11458 | return route_rule->netagent_id; |
11459 | } |
11460 | return 0; |
11461 | } |
11462 | |
11463 | if (ifp->if_family == IFNET_FAMILY_ETHERNET && ifp->if_subfamily == IFNET_SUBFAMILY_WIFI && |
11464 | route_rule->wifi_action != NECP_ROUTE_RULE_NONE) { |
11465 | if ((route_rule->wifi_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11466 | route_rule->wifi_action == NECP_ROUTE_RULE_REMOVE_NETAGENT)) { |
11467 | *remove = (route_rule->wifi_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11468 | return route_rule->netagent_id; |
11469 | } |
11470 | return 0; |
11471 | } |
11472 | |
11473 | if (IFNET_IS_COMPANION_LINK(ifp) && |
11474 | route_rule->companion_action != NECP_ROUTE_RULE_NONE) { |
11475 | if ((route_rule->companion_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11476 | route_rule->companion_action == NECP_ROUTE_RULE_REMOVE_NETAGENT)) { |
11477 | *remove = (route_rule->companion_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11478 | return route_rule->netagent_id; |
11479 | } |
11480 | return 0; |
11481 | } |
11482 | |
11483 | if ((ifp->if_family == IFNET_FAMILY_ETHERNET || ifp->if_family == IFNET_FAMILY_FIREWIRE) && |
11484 | route_rule->wired_action != NECP_ROUTE_RULE_NONE) { |
11485 | if ((route_rule->wired_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11486 | route_rule->wired_action == NECP_ROUTE_RULE_REMOVE_NETAGENT)) { |
11487 | *remove = (route_rule->wired_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11488 | return route_rule->netagent_id; |
11489 | } |
11490 | return 0; |
11491 | } |
11492 | |
11493 | if (ifp->if_eflags & IFEF_EXPENSIVE && |
11494 | route_rule->expensive_action != NECP_ROUTE_RULE_NONE) { |
11495 | if (route_rule->expensive_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11496 | route_rule->expensive_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11497 | *remove = (route_rule->expensive_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11498 | return route_rule->netagent_id; |
11499 | } |
11500 | return 0; |
11501 | } |
11502 | |
11503 | if (ifp->if_xflags & IFXF_CONSTRAINED && |
11504 | route_rule->constrained_action != NECP_ROUTE_RULE_NONE) { |
11505 | if (route_rule->constrained_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11506 | route_rule->constrained_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11507 | *remove = (route_rule->constrained_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11508 | return route_rule->netagent_id; |
11509 | } |
11510 | return 0; |
11511 | } |
11512 | |
11513 | // No more specific case matched, apply the default action |
11514 | if (route_rule->default_action == NECP_ROUTE_RULE_USE_NETAGENT || |
11515 | route_rule->default_action == NECP_ROUTE_RULE_REMOVE_NETAGENT) { |
11516 | *remove = (route_rule->default_action == NECP_ROUTE_RULE_REMOVE_NETAGENT); |
11517 | return route_rule->netagent_id; |
11518 | } |
11519 | |
11520 | return 0; |
11521 | } |
11522 | |
11523 | static uint32_t |
11524 | necp_route_get_flow_divert_inner(struct rtentry *route, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id) |
11525 | { |
11526 | struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(list: &necp_route_rules, route_rule_id); |
11527 | if (route_rule == NULL) { |
11528 | return 0; |
11529 | } |
11530 | |
11531 | // No control unit, skip |
11532 | if (route_rule->control_unit == 0) { |
11533 | return 0; |
11534 | } |
11535 | |
11536 | if (route_rule->match_netagent_id != 0) { |
11537 | if (netagent_array == NULL || netagent_array_count == 0) { |
11538 | // No agents, ignore rule |
11539 | return 0; |
11540 | } |
11541 | bool found_match = FALSE; |
11542 | for (size_t agent_index = 0; agent_index < netagent_array_count; agent_index++) { |
11543 | if (route_rule->match_netagent_id == netagent_array[agent_index]) { |
11544 | found_match = TRUE; |
11545 | break; |
11546 | } |
11547 | } |
11548 | if (!found_match) { |
11549 | // Agents don't match, ignore rule |
11550 | return 0; |
11551 | } |
11552 | } |
11553 | |
11554 | struct ifnet *ifp = route != NULL ? route->rt_ifp : NULL; |
11555 | if (ifp == NULL) { |
11556 | // No interface, apply the default action |
11557 | if (route_rule->default_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11558 | return route_rule->control_unit; |
11559 | } |
11560 | return 0; |
11561 | } |
11562 | |
11563 | for (int exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
11564 | if (route_rule->exception_if_indices[exception_index] == 0) { |
11565 | break; |
11566 | } |
11567 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index && |
11568 | route_rule->exception_if_actions[exception_index] != NECP_ROUTE_RULE_NONE) { |
11569 | if (route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11570 | return route_rule->control_unit; |
11571 | } |
11572 | return 0; |
11573 | } |
11574 | } |
11575 | |
11576 | if (ifp->if_type == IFT_CELLULAR && |
11577 | route_rule->cellular_action != NECP_ROUTE_RULE_NONE) { |
11578 | if (route_rule->cellular_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11579 | return route_rule->control_unit; |
11580 | } |
11581 | return 0; |
11582 | } |
11583 | |
11584 | if (ifp->if_family == IFNET_FAMILY_ETHERNET && ifp->if_subfamily == IFNET_SUBFAMILY_WIFI && |
11585 | route_rule->wifi_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11586 | if (route_rule->wifi_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11587 | return route_rule->control_unit; |
11588 | } |
11589 | return 0; |
11590 | } |
11591 | |
11592 | if (IFNET_IS_COMPANION_LINK(ifp) && |
11593 | route_rule->companion_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11594 | if (route_rule->companion_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11595 | return route_rule->control_unit; |
11596 | } |
11597 | return 0; |
11598 | } |
11599 | |
11600 | if ((ifp->if_family == IFNET_FAMILY_ETHERNET || ifp->if_family == IFNET_FAMILY_FIREWIRE) && |
11601 | route_rule->wired_action != NECP_ROUTE_RULE_NONE) { |
11602 | if (route_rule->wired_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11603 | return route_rule->control_unit; |
11604 | } |
11605 | return 0; |
11606 | } |
11607 | |
11608 | if (ifp->if_eflags & IFEF_EXPENSIVE && |
11609 | route_rule->expensive_action != NECP_ROUTE_RULE_NONE) { |
11610 | if (route_rule->expensive_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11611 | return route_rule->control_unit; |
11612 | } |
11613 | return 0; |
11614 | } |
11615 | |
11616 | if (ifp->if_xflags & IFXF_CONSTRAINED && |
11617 | route_rule->constrained_action != NECP_ROUTE_RULE_NONE) { |
11618 | if (route_rule->constrained_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11619 | return route_rule->control_unit; |
11620 | } |
11621 | return 0; |
11622 | } |
11623 | |
11624 | // No more specific case matched, apply the default action |
11625 | if (route_rule->default_action == NECP_ROUTE_RULE_DIVERT_SOCKET) { |
11626 | return route_rule->control_unit; |
11627 | } |
11628 | return 0; |
11629 | } |
11630 | |
11631 | static uint32_t |
11632 | necp_route_get_flow_divert(struct rtentry *route, u_int32_t *netagent_array, size_t netagent_array_count, u_int32_t route_rule_id, |
11633 | u_int32_t *flow_divert_aggregate_unit) |
11634 | { |
11635 | if ((route == NULL && netagent_array == NULL) || route_rule_id == 0 || flow_divert_aggregate_unit == NULL) { |
11636 | return 0; |
11637 | } |
11638 | |
11639 | if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) { |
11640 | struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id); |
11641 | if (aggregate_route_rule != NULL) { |
11642 | int index = 0; |
11643 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
11644 | u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index]; |
11645 | if (sub_route_rule_id == 0) { |
11646 | break; |
11647 | } |
11648 | uint32_t control_unit = necp_route_get_flow_divert_inner(route, netagent_array, netagent_array_count, route_rule_id: sub_route_rule_id); |
11649 | if (control_unit & FLOW_DIVERT_IS_TRANSPARENT) { |
11650 | // For transparent proxies, accumulate the control unit and continue to the next route rule |
11651 | *flow_divert_aggregate_unit |= (control_unit & ~FLOW_DIVERT_IS_TRANSPARENT); |
11652 | continue; |
11653 | } |
11654 | |
11655 | if (control_unit != 0) { |
11656 | return control_unit; |
11657 | } |
11658 | } |
11659 | } |
11660 | } else { |
11661 | uint32_t control_unit = necp_route_get_flow_divert_inner(route, netagent_array, netagent_array_count, route_rule_id); |
11662 | if (control_unit & FLOW_DIVERT_IS_TRANSPARENT) { |
11663 | // For transparent proxies, accumulate the control unit and let the caller continue |
11664 | *flow_divert_aggregate_unit |= (control_unit & ~FLOW_DIVERT_IS_TRANSPARENT); |
11665 | return 0; |
11666 | } |
11667 | return control_unit; |
11668 | } |
11669 | |
11670 | return 0; |
11671 | } |
11672 | |
11673 | bool |
11674 | necp_packet_is_allowed_over_interface(struct mbuf *packet, struct ifnet *interface) |
11675 | { |
11676 | bool is_allowed = true; |
11677 | u_int32_t route_rule_id = necp_get_route_rule_id_from_packet(packet); |
11678 | if (route_rule_id != 0 && |
11679 | interface != NULL) { |
11680 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
11681 | is_allowed = necp_route_is_allowed(NULL, interface, NULL, netagent_array_count: 0, route_rule_id: necp_get_route_rule_id_from_packet(packet), NULL); |
11682 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11683 | } |
11684 | return is_allowed; |
11685 | } |
11686 | |
11687 | static bool |
11688 | necp_netagents_allow_traffic(u_int32_t *netagent_ids, size_t netagent_id_count) |
11689 | { |
11690 | size_t netagent_cursor; |
11691 | for (netagent_cursor = 0; netagent_cursor < netagent_id_count; netagent_cursor++) { |
11692 | struct necp_uuid_id_mapping *mapping = NULL; |
11693 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
11694 | if (netagent_id == 0) { |
11695 | continue; |
11696 | } |
11697 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(local_id: netagent_id); |
11698 | if (mapping != NULL) { |
11699 | u_int32_t agent_flags = 0; |
11700 | agent_flags = netagent_get_flags(uuid: mapping->uuid); |
11701 | if (agent_flags & NETAGENT_FLAG_REGISTERED) { |
11702 | if (agent_flags & NETAGENT_FLAG_ACTIVE) { |
11703 | continue; |
11704 | } else if ((agent_flags & NETAGENT_FLAG_VOLUNTARY) == 0) { |
11705 | return FALSE; |
11706 | } |
11707 | } |
11708 | } |
11709 | } |
11710 | return TRUE; |
11711 | } |
11712 | |
11713 | static bool |
11714 | necp_packet_filter_tags_receive(u_int16_t pf_tag, u_int32_t pass_flags) |
11715 | { |
11716 | bool allowed_to_receive = TRUE; |
11717 | |
11718 | if (pf_tag == PF_TAG_ID_STACK_DROP && |
11719 | (pass_flags & NECP_KERNEL_POLICY_PASS_PF_TAG) != NECP_KERNEL_POLICY_PASS_PF_TAG) { |
11720 | allowed_to_receive = FALSE; |
11721 | } |
11722 | |
11723 | return allowed_to_receive; |
11724 | } |
11725 | |
11726 | static bool |
11727 | necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, ifnet_t input_interface, u_int16_t pf_tag, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id, u_int32_t *return_pass_flags) |
11728 | { |
11729 | u_int32_t verifyifindex = input_interface ? input_interface->if_index : 0; |
11730 | bool allowed_to_receive = TRUE; |
11731 | struct necp_socket_info info = {}; |
11732 | u_int32_t flowhash = 0; |
11733 | necp_kernel_policy_result service_action = 0; |
11734 | necp_kernel_policy_service service = { 0, 0 }; |
11735 | u_int32_t route_rule_id = 0; |
11736 | struct rtentry *route = NULL; |
11737 | u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN; |
11738 | necp_kernel_policy_result drop_dest_policy_result = NECP_KERNEL_POLICY_RESULT_NONE; |
11739 | necp_drop_all_bypass_check_result_t drop_all_bypass = NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE; |
11740 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
11741 | proc_t socket_proc = NULL; |
11742 | necp_kernel_policy_filter filter_control_unit = 0; |
11743 | u_int32_t pass_flags = 0; |
11744 | u_int32_t flow_divert_aggregate_unit = 0; |
11745 | necp_socket_bypass_type_t bypass_type = NECP_BYPASS_TYPE_NONE; |
11746 | |
11747 | memset(s: &netagent_ids, c: 0, n: sizeof(netagent_ids)); |
11748 | |
11749 | if (return_policy_id) { |
11750 | *return_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
11751 | } |
11752 | if (return_skip_policy_id) { |
11753 | *return_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
11754 | } |
11755 | if (return_route_rule_id) { |
11756 | *return_route_rule_id = 0; |
11757 | } |
11758 | if (return_pass_flags) { |
11759 | *return_pass_flags = 0; |
11760 | } |
11761 | |
11762 | if (inp == NULL) { |
11763 | goto done; |
11764 | } |
11765 | |
11766 | route = inp->inp_route.ro_rt; |
11767 | |
11768 | struct socket *so = inp->inp_socket; |
11769 | |
11770 | u_int32_t drop_order = necp_process_drop_order(so->so_cred); |
11771 | |
11772 | // Don't lock. Possible race condition, but we don't want the performance hit. |
11773 | if (necp_drop_management_order == 0 && |
11774 | (necp_kernel_socket_policies_count == 0 || |
11775 | (!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0))) { |
11776 | if (necp_drop_all_order > 0 || drop_order > 0) { |
11777 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp) != NECP_BYPASS_TYPE_NONE) { |
11778 | allowed_to_receive = TRUE; |
11779 | } else { |
11780 | allowed_to_receive = FALSE; |
11781 | } |
11782 | } |
11783 | goto done; |
11784 | } |
11785 | |
11786 | // If this socket is connected, or we are not taking addresses into account, try to reuse last result |
11787 | if ((necp_socket_is_connected(inp) || (override_local_addr == NULL && override_remote_addr == NULL)) && inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
11788 | bool policies_have_changed = FALSE; |
11789 | bool route_allowed = necp_route_is_interface_type_allowed(route, ifp: input_interface, NULL, inp); |
11790 | if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount) { |
11791 | policies_have_changed = TRUE; |
11792 | } else { |
11793 | if (inp->inp_policyresult.results.route_rule_id != 0) { |
11794 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
11795 | if (!necp_route_is_allowed(route, interface: input_interface, NULL, netagent_array_count: 0, route_rule_id: inp->inp_policyresult.results.route_rule_id, interface_type_denied: &interface_type_denied)) { |
11796 | route_allowed = FALSE; |
11797 | } |
11798 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11799 | } |
11800 | } |
11801 | |
11802 | if (!policies_have_changed) { |
11803 | if (!route_allowed || |
11804 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_DROP || |
11805 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
11806 | (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && input_interface && |
11807 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index != verifyifindex)) { |
11808 | allowed_to_receive = FALSE; |
11809 | } else { |
11810 | if (return_policy_id) { |
11811 | *return_policy_id = inp->inp_policyresult.policy_id; |
11812 | } |
11813 | if (return_skip_policy_id) { |
11814 | *return_skip_policy_id = inp->inp_policyresult.skip_policy_id; |
11815 | } |
11816 | if (return_route_rule_id) { |
11817 | *return_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
11818 | } |
11819 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_PASS) { |
11820 | pass_flags = inp->inp_policyresult.results.result_parameter.pass_flags; |
11821 | } |
11822 | } |
11823 | goto done; |
11824 | } |
11825 | } |
11826 | |
11827 | // Check for loopback exception |
11828 | bypass_type = necp_socket_bypass(override_local_addr, override_remote_addr, inp); |
11829 | if (bypass_type == NECP_BYPASS_TYPE_INTCOPROC || (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && necp_pass_loopback == NECP_LOOPBACK_PASS_ALL)) { |
11830 | allowed_to_receive = TRUE; |
11831 | goto done; |
11832 | } |
11833 | |
11834 | // Actually calculate policy result |
11835 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
11836 | necp_socket_fillout_info_locked(inp, override_local_addr, override_remote_addr, override_bound_interface: 0, override_is_inbound: input_interface != NULL ? true : false, drop_order, socket_proc: &socket_proc, info: &info, is_loopback: (bypass_type == NECP_BYPASS_TYPE_LOOPBACK)); |
11837 | |
11838 | int debug = NECP_ENABLE_DATA_TRACE((&info.local_addr), (&info.remote_addr), info.protocol, info.pid, info.bound_interface_index); |
11839 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - DATA PATH" , "START" , 0, 0); |
11840 | |
11841 | flowhash = necp_socket_calc_flowhash_locked(info: &info); |
11842 | if (inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE && |
11843 | inp->inp_policyresult.policy_gencount == necp_kernel_socket_policies_gencount && |
11844 | inp->inp_policyresult.flowhash == flowhash) { |
11845 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_DROP || |
11846 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
11847 | (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && input_interface && |
11848 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index != verifyifindex) || |
11849 | !necp_route_is_interface_type_allowed(route, ifp: input_interface, NULL, inp) || |
11850 | (inp->inp_policyresult.results.route_rule_id != 0 && |
11851 | !necp_route_is_allowed(route, interface: input_interface, NULL, netagent_array_count: 0, route_rule_id: inp->inp_policyresult.results.route_rule_id, interface_type_denied: &interface_type_denied))) { |
11852 | allowed_to_receive = FALSE; |
11853 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - DATA PATH" , "RESULT - CACHED <DROP>" , 0, 0); |
11854 | } else { |
11855 | if (return_policy_id) { |
11856 | *return_policy_id = inp->inp_policyresult.policy_id; |
11857 | } |
11858 | if (return_route_rule_id) { |
11859 | *return_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
11860 | } |
11861 | if (return_skip_policy_id) { |
11862 | *return_skip_policy_id = inp->inp_policyresult.skip_policy_id; |
11863 | } |
11864 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_PASS) { |
11865 | pass_flags = inp->inp_policyresult.results.result_parameter.pass_flags; |
11866 | } |
11867 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
11868 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy - Send/Recv - RESULT - CACHED <MATCHED>: %p (BoundInterface %d Proto %d) Policy %d Skip %d Result %d Parameter %d" , |
11869 | inp->inp_socket, info.bound_interface_index, info.protocol, |
11870 | inp->inp_policyresult.policy_id, |
11871 | inp->inp_policyresult.skip_policy_id, |
11872 | inp->inp_policyresult.results.result, |
11873 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index); |
11874 | } |
11875 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - DATA PATH" , "RESULT - CACHED <MATCHED>" , |
11876 | return_policy_id ? *return_policy_id : 0, return_skip_policy_id ? *return_skip_policy_id : 0); |
11877 | } |
11878 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11879 | goto done; |
11880 | } |
11881 | |
11882 | u_int32_t route_rule_id_array[MAX_AGGREGATE_ROUTE_RULES]; |
11883 | size_t route_rule_id_array_count = 0; |
11884 | struct necp_kernel_socket_policy *matched_policy = necp_socket_find_policy_match_with_info_locked(policy_search_array: necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], info: &info, return_filter: &filter_control_unit, return_route_rule_id_array: route_rule_id_array, return_route_rule_id_array_count: &route_rule_id_array_count, MAX_AGGREGATE_ROUTE_RULES, return_service_action: &service_action, return_service: &service, return_netagent_array: netagent_ids, NULL, NECP_MAX_NETAGENTS, NULL, num_required_agent_types: 0, proc: socket_proc ? socket_proc : current_proc(), pf_tag, skip_policy_id: return_skip_policy_id, rt: inp->inp_route.ro_rt, return_drop_dest_policy_result: &drop_dest_policy_result, return_drop_all_bypass: &drop_all_bypass, return_flow_divert_aggregate_unit: &flow_divert_aggregate_unit, so, debug); |
11885 | |
11886 | // Check for loopback exception again after the policy match |
11887 | if (bypass_type == NECP_BYPASS_TYPE_LOOPBACK && |
11888 | necp_pass_loopback == NECP_LOOPBACK_PASS_WITH_FILTER && |
11889 | (matched_policy == NULL || matched_policy->result != NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT)) { |
11890 | // If policies haven't changed since last evaluation, do not update filter result in order to |
11891 | // preserve the very first filter result for the socket. Otherwise, update the filter result to |
11892 | // allow content filter to detect and drop pre-existing flows. |
11893 | if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount && |
11894 | inp->inp_policyresult.results.filter_control_unit != filter_control_unit) { |
11895 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
11896 | } |
11897 | if (inp->inp_policyresult.results.flow_divert_aggregate_unit != flow_divert_aggregate_unit) { |
11898 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
11899 | } |
11900 | allowed_to_receive = TRUE; |
11901 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11902 | goto done; |
11903 | } |
11904 | |
11905 | if (info.protocol != IPPROTO_UDP) { |
11906 | goto skip_agent_check; |
11907 | } |
11908 | |
11909 | // Verify netagents |
11910 | if (necp_socket_verify_netagents(netagent_ids, debug, so) == false) { |
11911 | if (necp_debug > 1 || NECP_DATA_TRACE_POLICY_ON(debug)) { |
11912 | NECPLOG(LOG_ERR, "DATA-TRACE: Socket Policy: <so %llx> (BoundInterface %d Proto %d) Dropping packet because agent is not active" , (unsigned long long)so, info.bound_interface_index, info.protocol); |
11913 | } |
11914 | |
11915 | // Mark socket as a drop if required agent is not active |
11916 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
11917 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
11918 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
11919 | inp->inp_policyresult.flowhash = flowhash; |
11920 | inp->inp_policyresult.results.filter_control_unit = 0; |
11921 | inp->inp_policyresult.results.flow_divert_aggregate_unit = 0; |
11922 | inp->inp_policyresult.results.route_rule_id = 0; |
11923 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
11924 | |
11925 | // Unlock |
11926 | allowed_to_receive = FALSE; |
11927 | lck_rw_done(lck: &necp_kernel_policy_lock); |
11928 | goto done; |
11929 | } |
11930 | |
11931 | skip_agent_check: |
11932 | |
11933 | if (route_rule_id_array_count == 1) { |
11934 | route_rule_id = route_rule_id_array[0]; |
11935 | } else if (route_rule_id_array_count > 1) { |
11936 | route_rule_id = necp_create_aggregate_route_rule(rule_ids: route_rule_id_array); |
11937 | } |
11938 | |
11939 | bool send_local_network_denied_event = false; |
11940 | if (matched_policy != NULL) { |
11941 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP && |
11942 | matched_policy->result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_LOCAL_NETWORK && |
11943 | !(matched_policy->result_parameter.drop_flags & NECP_KERNEL_POLICY_DROP_FLAG_SUPPRESS_ALERTS)) { |
11944 | // Trigger the event that we dropped due to a local network policy |
11945 | send_local_network_denied_event = true; |
11946 | } |
11947 | |
11948 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP || |
11949 | matched_policy->result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
11950 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && input_interface && |
11951 | matched_policy->result_parameter.tunnel_interface_index != verifyifindex) || |
11952 | !necp_route_is_interface_type_allowed(route, ifp: input_interface, NULL, inp) || |
11953 | (route_rule_id != 0 && |
11954 | !necp_route_is_allowed(route, interface: input_interface, netagent_array: netagent_ids, NECP_MAX_NETAGENTS, route_rule_id, interface_type_denied: &interface_type_denied)) || |
11955 | !necp_netagents_allow_traffic(netagent_ids, NECP_MAX_NETAGENTS)) { |
11956 | allowed_to_receive = FALSE; |
11957 | } else { |
11958 | if (return_policy_id) { |
11959 | *return_policy_id = matched_policy->id; |
11960 | } |
11961 | if (return_route_rule_id) { |
11962 | *return_route_rule_id = route_rule_id; |
11963 | } |
11964 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_PASS) { |
11965 | pass_flags = matched_policy->result_parameter.pass_flags; |
11966 | } |
11967 | // If policies haven't changed since last evaluation, do not update filter result in order to |
11968 | // preserve the very first filter result for the socket. Otherwise, update the filter result to |
11969 | // allow content filter to detect and drop pre-existing flows. |
11970 | if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount && |
11971 | inp->inp_policyresult.results.filter_control_unit != filter_control_unit) { |
11972 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
11973 | } |
11974 | if (inp->inp_policyresult.results.flow_divert_aggregate_unit != flow_divert_aggregate_unit) { |
11975 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
11976 | } |
11977 | } |
11978 | |
11979 | if ((necp_debug > 1 && matched_policy->id != inp->inp_policyresult.policy_id) || NECP_DATA_TRACE_POLICY_ON(debug)) { |
11980 | NECPLOG(LOG_DEBUG, "DATA-TRACE: Socket Policy - Send/Recv: %p (BoundInterface %d Proto %d) Policy %d Result %d Parameter %d Allowed %d <filter_control_unit %d flow_divert_aggregate_unit %d>" , |
11981 | inp->inp_socket, info.bound_interface_index, info.protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index, allowed_to_receive, filter_control_unit, flow_divert_aggregate_unit); |
11982 | } |
11983 | } else { |
11984 | bool drop_all = false; |
11985 | if (necp_drop_all_order > 0 || info.drop_order > 0 || drop_dest_policy_result == NECP_KERNEL_POLICY_RESULT_DROP) { |
11986 | drop_all = true; |
11987 | if (drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_NONE) { |
11988 | drop_all_bypass = necp_check_drop_all_bypass_result(proc: socket_proc ? socket_proc : current_proc()); |
11989 | } |
11990 | } |
11991 | if (drop_all && drop_all_bypass == NECP_DROP_ALL_BYPASS_CHECK_RESULT_FALSE) { |
11992 | allowed_to_receive = FALSE; |
11993 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - DATA PATH" , "RESULT - DROP - NO MATCH" , 0, 0); |
11994 | } else { |
11995 | if (return_policy_id) { |
11996 | *return_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
11997 | } |
11998 | if (return_route_rule_id) { |
11999 | *return_route_rule_id = route_rule_id; |
12000 | } |
12001 | |
12002 | // If policies haven't changed since last evaluation, do not update filter result in order to |
12003 | // preserve the very first filter result for the socket. Otherwise, update the filter result to |
12004 | // allow content filter to detect and drop pre-existing flows. |
12005 | if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount && |
12006 | inp->inp_policyresult.results.filter_control_unit != filter_control_unit) { |
12007 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
12008 | } |
12009 | if (inp->inp_policyresult.results.flow_divert_aggregate_unit != flow_divert_aggregate_unit) { |
12010 | inp->inp_policyresult.results.flow_divert_aggregate_unit = flow_divert_aggregate_unit; |
12011 | } |
12012 | } |
12013 | } |
12014 | |
12015 | if (necp_check_restricted_multicast_drop(proc: socket_proc ? socket_proc : current_proc(), info: &info, true)) { |
12016 | allowed_to_receive = FALSE; |
12017 | NECP_DATA_TRACE_LOG_SOCKET(debug, so, "SOCKET - DATA PATH" , "RESULT - DROP - MULTICAST" , 0, 0); |
12018 | } |
12019 | |
12020 | lck_rw_done(lck: &necp_kernel_policy_lock); |
12021 | |
12022 | if (send_local_network_denied_event && inp->inp_policyresult.network_denied_notifies == 0) { |
12023 | inp->inp_policyresult.network_denied_notifies++; |
12024 | necp_send_network_denied_event(pid: ((so->so_flags & SOF_DELEGATED) ? so->e_pid : so->last_pid), |
12025 | proc_uuid: ((so->so_flags & SOF_DELEGATED) ? so->e_uuid : so->last_uuid), |
12026 | NETPOLICY_NETWORKTYPE_LOCAL); |
12027 | } |
12028 | |
12029 | done: |
12030 | if (return_pass_flags != NULL) { |
12031 | *return_pass_flags = pass_flags; |
12032 | } |
12033 | |
12034 | if (pf_tag != 0 && allowed_to_receive) { |
12035 | allowed_to_receive = necp_packet_filter_tags_receive(pf_tag, pass_flags); |
12036 | } |
12037 | |
12038 | if (!allowed_to_receive && interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) { |
12039 | soevent(so: inp->inp_socket, hint: (SO_FILT_HINT_LOCKED | SO_FILT_HINT_IFDENIED)); |
12040 | } |
12041 | |
12042 | if (socket_proc) { |
12043 | proc_rele(p: socket_proc); |
12044 | } |
12045 | |
12046 | return allowed_to_receive; |
12047 | } |
12048 | |
12049 | bool |
12050 | necp_socket_is_allowed_to_send_recv_v4(struct inpcb *inp, u_int16_t local_port, u_int16_t remote_port, struct in_addr *local_addr, struct in_addr *remote_addr, ifnet_t input_interface, u_int16_t pf_tag, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id, u_int32_t *return_pass_flags) |
12051 | { |
12052 | struct sockaddr_in local = {}; |
12053 | struct sockaddr_in remote = {}; |
12054 | local.sin_family = remote.sin_family = AF_INET; |
12055 | local.sin_len = remote.sin_len = sizeof(struct sockaddr_in); |
12056 | local.sin_port = local_port; |
12057 | remote.sin_port = remote_port; |
12058 | memcpy(dst: &local.sin_addr, src: local_addr, n: sizeof(local.sin_addr)); |
12059 | memcpy(dst: &remote.sin_addr, src: remote_addr, n: sizeof(remote.sin_addr)); |
12060 | |
12061 | return necp_socket_is_allowed_to_send_recv_internal(inp, override_local_addr: (struct sockaddr *)&local, override_remote_addr: (struct sockaddr *)&remote, input_interface, |
12062 | pf_tag, return_policy_id, return_route_rule_id, return_skip_policy_id, return_pass_flags); |
12063 | } |
12064 | |
12065 | bool |
12066 | necp_socket_is_allowed_to_send_recv_v6(struct inpcb *inp, u_int16_t local_port, u_int16_t remote_port, struct in6_addr *local_addr, struct in6_addr *remote_addr, ifnet_t input_interface, u_int16_t pf_tag, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id, u_int32_t *return_pass_flags) |
12067 | { |
12068 | struct sockaddr_in6 local = {}; |
12069 | struct sockaddr_in6 remote = {}; |
12070 | local.sin6_family = remote.sin6_family = AF_INET6; |
12071 | local.sin6_len = remote.sin6_len = sizeof(struct sockaddr_in6); |
12072 | local.sin6_port = local_port; |
12073 | remote.sin6_port = remote_port; |
12074 | memcpy(dst: &local.sin6_addr, src: local_addr, n: sizeof(local.sin6_addr)); |
12075 | memcpy(dst: &remote.sin6_addr, src: remote_addr, n: sizeof(remote.sin6_addr)); |
12076 | |
12077 | return necp_socket_is_allowed_to_send_recv_internal(inp, override_local_addr: (struct sockaddr *)&local, override_remote_addr: (struct sockaddr *)&remote, input_interface, |
12078 | pf_tag, return_policy_id, return_route_rule_id, return_skip_policy_id, return_pass_flags); |
12079 | } |
12080 | |
12081 | bool |
12082 | necp_socket_is_allowed_to_send_recv(struct inpcb *inp, ifnet_t input_interface, u_int16_t pf_tag, necp_kernel_policy_id *return_policy_id, |
12083 | u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id, u_int32_t *return_pass_flags) |
12084 | { |
12085 | return necp_socket_is_allowed_to_send_recv_internal(inp, NULL, NULL, input_interface, pf_tag, |
12086 | return_policy_id, return_route_rule_id, |
12087 | return_skip_policy_id, return_pass_flags); |
12088 | } |
12089 | |
12090 | int |
12091 | necp_mark_packet_from_socket(struct mbuf *packet, struct inpcb *inp, necp_kernel_policy_id policy_id, u_int32_t route_rule_id, |
12092 | necp_kernel_policy_id skip_policy_id, u_int32_t pass_flags) |
12093 | { |
12094 | if (packet == NULL || inp == NULL || !(packet->m_flags & M_PKTHDR)) { |
12095 | return EINVAL; |
12096 | } |
12097 | |
12098 | // Mark ID for Pass and IP Tunnel |
12099 | if (policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
12100 | packet->m_pkthdr.necp_mtag.necp_policy_id = policy_id; |
12101 | } else if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_PASS || |
12102 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL) { |
12103 | packet->m_pkthdr.necp_mtag.necp_policy_id = inp->inp_policyresult.policy_id; |
12104 | } else { |
12105 | packet->m_pkthdr.necp_mtag.necp_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
12106 | } |
12107 | packet->m_pkthdr.necp_mtag.necp_last_interface_index = 0; |
12108 | if (route_rule_id != 0) { |
12109 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
12110 | } else { |
12111 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
12112 | } |
12113 | packet->m_pkthdr.necp_mtag.necp_app_id = (inp->inp_policyresult.app_id >= UINT16_MAX ? (inp->inp_policyresult.app_id - UINT16_MAX) : inp->inp_policyresult.app_id); |
12114 | |
12115 | if (skip_policy_id != NECP_KERNEL_POLICY_ID_NONE && |
12116 | skip_policy_id != NECP_KERNEL_POLICY_ID_NO_MATCH) { |
12117 | // Only mark the skip policy if it is a valid policy ID |
12118 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = skip_policy_id; |
12119 | } else if (inp->inp_policyresult.results.filter_control_unit == NECP_FILTER_UNIT_NO_FILTER) { |
12120 | // Overload the meaning of "NECP_KERNEL_POLICY_ID_NO_MATCH" |
12121 | // to indicate that NECP_FILTER_UNIT_NO_FILTER was set |
12122 | // See necp_get_skip_policy_id_from_packet() and |
12123 | // necp_packet_should_skip_filters(). |
12124 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
12125 | } else { |
12126 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
12127 | } |
12128 | |
12129 | if (((pass_flags & NECP_KERNEL_POLICY_PASS_PF_TAG) == NECP_KERNEL_POLICY_PASS_PF_TAG) || |
12130 | ((inp->inp_policyresult.results.result_parameter.pass_flags & NECP_KERNEL_POLICY_PASS_PF_TAG) == NECP_KERNEL_POLICY_PASS_PF_TAG)) { |
12131 | m_pftag(packet)->pftag_tag = PF_TAG_ID_SYSTEM_SERVICE; |
12132 | } |
12133 | |
12134 | return 0; |
12135 | } |
12136 | |
12137 | int |
12138 | necp_mark_packet_from_ip(struct mbuf *packet, necp_kernel_policy_id policy_id) |
12139 | { |
12140 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12141 | return EINVAL; |
12142 | } |
12143 | |
12144 | // Mark ID for Pass and IP Tunnel |
12145 | if (policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
12146 | packet->m_pkthdr.necp_mtag.necp_policy_id = policy_id; |
12147 | } else { |
12148 | packet->m_pkthdr.necp_mtag.necp_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
12149 | } |
12150 | |
12151 | return 0; |
12152 | } |
12153 | |
12154 | int |
12155 | necp_mark_packet_from_ip_with_skip(struct mbuf *packet, necp_kernel_policy_id policy_id, necp_kernel_policy_id skip_policy_id) |
12156 | { |
12157 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12158 | return EINVAL; |
12159 | } |
12160 | |
12161 | // Mark ID for Pass and IP Tunnel |
12162 | if (policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
12163 | packet->m_pkthdr.necp_mtag.necp_policy_id = policy_id; |
12164 | } else { |
12165 | packet->m_pkthdr.necp_mtag.necp_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
12166 | } |
12167 | |
12168 | if (skip_policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
12169 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = skip_policy_id; |
12170 | } else { |
12171 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
12172 | } |
12173 | return 0; |
12174 | } |
12175 | |
12176 | int |
12177 | necp_mark_packet_from_interface(struct mbuf *packet, ifnet_t interface) |
12178 | { |
12179 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12180 | return EINVAL; |
12181 | } |
12182 | |
12183 | // Mark ID for Pass and IP Tunnel |
12184 | if (interface != NULL) { |
12185 | packet->m_pkthdr.necp_mtag.necp_last_interface_index = interface->if_index; |
12186 | } |
12187 | |
12188 | return 0; |
12189 | } |
12190 | |
12191 | int |
12192 | necp_mark_packet_as_keepalive(struct mbuf *packet, bool is_keepalive) |
12193 | { |
12194 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12195 | return EINVAL; |
12196 | } |
12197 | |
12198 | if (is_keepalive) { |
12199 | packet->m_pkthdr.pkt_flags |= PKTF_KEEPALIVE; |
12200 | } else { |
12201 | packet->m_pkthdr.pkt_flags &= ~PKTF_KEEPALIVE; |
12202 | } |
12203 | |
12204 | return 0; |
12205 | } |
12206 | |
12207 | necp_kernel_policy_id |
12208 | necp_get_policy_id_from_packet(struct mbuf *packet) |
12209 | { |
12210 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12211 | return NECP_KERNEL_POLICY_ID_NONE; |
12212 | } |
12213 | |
12214 | return packet->m_pkthdr.necp_mtag.necp_policy_id; |
12215 | } |
12216 | |
12217 | necp_kernel_policy_id |
12218 | necp_get_skip_policy_id_from_packet(struct mbuf *packet) |
12219 | { |
12220 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12221 | return NECP_KERNEL_POLICY_ID_NONE; |
12222 | } |
12223 | |
12224 | // Check for overloaded value. See necp_mark_packet_from_socket(). |
12225 | if (packet->m_pkthdr.necp_mtag.necp_skip_policy_id == NECP_KERNEL_POLICY_ID_NO_MATCH) { |
12226 | return NECP_KERNEL_POLICY_ID_NONE; |
12227 | } |
12228 | |
12229 | return packet->m_pkthdr.necp_mtag.necp_skip_policy_id; |
12230 | } |
12231 | |
12232 | u_int16_t |
12233 | necp_get_packet_filter_tags_from_packet(struct mbuf *packet) |
12234 | { |
12235 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12236 | return 0; |
12237 | } |
12238 | |
12239 | return m_pftag(packet)->pftag_tag; |
12240 | } |
12241 | |
12242 | bool |
12243 | necp_packet_should_skip_filters(struct mbuf *packet) |
12244 | { |
12245 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12246 | return false; |
12247 | } |
12248 | |
12249 | // Check for overloaded value. See necp_mark_packet_from_socket(). |
12250 | return packet->m_pkthdr.necp_mtag.necp_skip_policy_id == NECP_KERNEL_POLICY_ID_NO_MATCH; |
12251 | } |
12252 | |
12253 | u_int32_t |
12254 | necp_get_last_interface_index_from_packet(struct mbuf *packet) |
12255 | { |
12256 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12257 | return 0; |
12258 | } |
12259 | |
12260 | return packet->m_pkthdr.necp_mtag.necp_last_interface_index; |
12261 | } |
12262 | |
12263 | u_int32_t |
12264 | necp_get_route_rule_id_from_packet(struct mbuf *packet) |
12265 | { |
12266 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12267 | return 0; |
12268 | } |
12269 | |
12270 | return packet->m_pkthdr.necp_mtag.necp_route_rule_id; |
12271 | } |
12272 | |
12273 | int |
12274 | necp_get_app_uuid_from_packet(struct mbuf *packet, |
12275 | uuid_t app_uuid) |
12276 | { |
12277 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12278 | return EINVAL; |
12279 | } |
12280 | |
12281 | bool found_mapping = FALSE; |
12282 | if (packet->m_pkthdr.necp_mtag.necp_app_id != 0) { |
12283 | lck_rw_lock_shared(lck: &necp_kernel_policy_lock); |
12284 | necp_app_id app_id = (packet->m_pkthdr.necp_mtag.necp_app_id < UINT16_MAX ? (packet->m_pkthdr.necp_mtag.necp_app_id + UINT16_MAX) : packet->m_pkthdr.necp_mtag.necp_app_id); |
12285 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(local_id: app_id); |
12286 | if (entry != NULL) { |
12287 | uuid_copy(dst: app_uuid, src: entry->uuid); |
12288 | found_mapping = true; |
12289 | } |
12290 | lck_rw_done(lck: &necp_kernel_policy_lock); |
12291 | } |
12292 | if (!found_mapping) { |
12293 | uuid_clear(uu: app_uuid); |
12294 | } |
12295 | return 0; |
12296 | } |
12297 | |
12298 | bool |
12299 | necp_get_is_keepalive_from_packet(struct mbuf *packet) |
12300 | { |
12301 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
12302 | return FALSE; |
12303 | } |
12304 | |
12305 | return packet->m_pkthdr.pkt_flags & PKTF_KEEPALIVE; |
12306 | } |
12307 | |
12308 | u_int32_t |
12309 | necp_socket_get_content_filter_control_unit(struct socket *so) |
12310 | { |
12311 | struct inpcb *inp = sotoinpcb(so); |
12312 | |
12313 | if (inp == NULL) { |
12314 | return 0; |
12315 | } |
12316 | return inp->inp_policyresult.results.filter_control_unit; |
12317 | } |
12318 | |
12319 | bool |
12320 | necp_socket_should_use_flow_divert(struct inpcb *inp) |
12321 | { |
12322 | if (inp == NULL) { |
12323 | return FALSE; |
12324 | } |
12325 | |
12326 | return !(inp->inp_socket->so_flags1 & SOF1_FLOW_DIVERT_SKIP) && |
12327 | (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
12328 | (inp->inp_policyresult.results.flow_divert_aggregate_unit != 0)); |
12329 | } |
12330 | |
12331 | u_int32_t |
12332 | necp_socket_get_flow_divert_control_unit(struct inpcb *inp, uint32_t *aggregate_unit) |
12333 | { |
12334 | if (inp == NULL) { |
12335 | return 0; |
12336 | } |
12337 | |
12338 | if (inp->inp_socket->so_flags1 & SOF1_FLOW_DIVERT_SKIP) { |
12339 | return 0; |
12340 | } |
12341 | |
12342 | if (aggregate_unit != NULL && |
12343 | inp->inp_policyresult.results.flow_divert_aggregate_unit != 0) { |
12344 | *aggregate_unit = inp->inp_policyresult.results.flow_divert_aggregate_unit; |
12345 | } |
12346 | |
12347 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT) { |
12348 | return inp->inp_policyresult.results.result_parameter.flow_divert_control_unit; |
12349 | } |
12350 | |
12351 | return 0; |
12352 | } |
12353 | |
12354 | bool |
12355 | necp_socket_should_rescope(struct inpcb *inp) |
12356 | { |
12357 | if (inp == NULL) { |
12358 | return FALSE; |
12359 | } |
12360 | |
12361 | return inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED || |
12362 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT; |
12363 | } |
12364 | |
12365 | u_int |
12366 | necp_socket_get_rescope_if_index(struct inpcb *inp) |
12367 | { |
12368 | if (inp == NULL) { |
12369 | return 0; |
12370 | } |
12371 | |
12372 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED) { |
12373 | return inp->inp_policyresult.results.result_parameter.scoped_interface_index; |
12374 | } else if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT) { |
12375 | return necp_get_primary_direct_interface_index(); |
12376 | } |
12377 | |
12378 | return 0; |
12379 | } |
12380 | |
12381 | u_int32_t |
12382 | necp_socket_get_effective_mtu(struct inpcb *inp, u_int32_t current_mtu) |
12383 | { |
12384 | if (inp == NULL) { |
12385 | return current_mtu; |
12386 | } |
12387 | |
12388 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && |
12389 | (inp->inp_flags & INP_BOUND_IF) && |
12390 | inp->inp_boundifp) { |
12391 | u_int bound_interface_index = inp->inp_boundifp->if_index; |
12392 | u_int tunnel_interface_index = inp->inp_policyresult.results.result_parameter.tunnel_interface_index; |
12393 | |
12394 | // The result is IP Tunnel, and is rescoping from one interface to another. Recalculate MTU. |
12395 | if (bound_interface_index != tunnel_interface_index) { |
12396 | ifnet_t tunnel_interface = NULL; |
12397 | |
12398 | ifnet_head_lock_shared(); |
12399 | tunnel_interface = ifindex2ifnet[tunnel_interface_index]; |
12400 | ifnet_head_done(); |
12401 | |
12402 | if (tunnel_interface != NULL) { |
12403 | u_int32_t direct_tunnel_mtu = tunnel_interface->if_mtu; |
12404 | u_int32_t delegate_tunnel_mtu = (tunnel_interface->if_delegated.ifp != NULL) ? tunnel_interface->if_delegated.ifp->if_mtu : 0; |
12405 | if (delegate_tunnel_mtu != 0 && |
12406 | strncmp(s1: tunnel_interface->if_name, s2: "ipsec" , n: strlen(s: "ipsec" )) == 0) { |
12407 | // For ipsec interfaces, calculate the overhead from the delegate interface |
12408 | u_int32_t tunnel_overhead = (u_int32_t)(esp_hdrsiz(NULL) + sizeof(struct ip6_hdr)); |
12409 | if (delegate_tunnel_mtu > tunnel_overhead) { |
12410 | delegate_tunnel_mtu -= tunnel_overhead; |
12411 | } |
12412 | |
12413 | if (delegate_tunnel_mtu < direct_tunnel_mtu) { |
12414 | // If the (delegate - overhead) < direct, return (delegate - overhead) |
12415 | return delegate_tunnel_mtu; |
12416 | } else { |
12417 | // Otherwise return direct |
12418 | return direct_tunnel_mtu; |
12419 | } |
12420 | } else { |
12421 | // For non-ipsec interfaces, just return the tunnel MTU |
12422 | return direct_tunnel_mtu; |
12423 | } |
12424 | } |
12425 | } |
12426 | } |
12427 | |
12428 | // By default, just return the MTU passed in |
12429 | return current_mtu; |
12430 | } |
12431 | |
12432 | ifnet_t |
12433 | necp_get_ifnet_from_result_parameter(necp_kernel_policy_result_parameter *result_parameter) |
12434 | { |
12435 | if (result_parameter == NULL) { |
12436 | return NULL; |
12437 | } |
12438 | |
12439 | return ifindex2ifnet[result_parameter->tunnel_interface_index]; |
12440 | } |
12441 | |
12442 | bool |
12443 | necp_packet_can_rebind_to_ifnet(struct mbuf *packet, struct ifnet *interface, struct route *new_route, int family) |
12444 | { |
12445 | bool found_match = FALSE; |
12446 | bool can_rebind = FALSE; |
12447 | ifaddr_t ifa; |
12448 | union necp_sockaddr_union address_storage; |
12449 | |
12450 | if (packet == NULL || interface == NULL || new_route == NULL || (family != AF_INET && family != AF_INET6)) { |
12451 | return FALSE; |
12452 | } |
12453 | |
12454 | // Match source address against interface addresses |
12455 | ifnet_lock_shared(ifp: interface); |
12456 | TAILQ_FOREACH(ifa, &interface->if_addrhead, ifa_link) { |
12457 | if (ifaddr_address(ifaddr: ifa, SA(&address_storage.sa), addr_size: sizeof(address_storage)) == 0) { |
12458 | if (address_storage.sa.sa_family != family) { |
12459 | continue; |
12460 | } |
12461 | |
12462 | if (family == AF_INET) { |
12463 | struct ip *ip = mtod(packet, struct ip *); |
12464 | if (memcmp(s1: &address_storage.sin.sin_addr, s2: &ip->ip_src, n: sizeof(ip->ip_src)) == 0) { |
12465 | found_match = TRUE; |
12466 | break; |
12467 | } |
12468 | } else if (family == AF_INET6) { |
12469 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
12470 | if (memcmp(s1: &address_storage.sin6.sin6_addr, s2: &ip6->ip6_src, n: sizeof(ip6->ip6_src)) == 0) { |
12471 | found_match = TRUE; |
12472 | break; |
12473 | } |
12474 | } |
12475 | } |
12476 | } |
12477 | const uint32_t if_idx = interface->if_index; |
12478 | ifnet_lock_done(ifp: interface); |
12479 | |
12480 | // If source address matched, attempt to construct a route to the destination address |
12481 | if (found_match) { |
12482 | ROUTE_RELEASE(new_route); |
12483 | |
12484 | if (family == AF_INET) { |
12485 | struct ip *ip = mtod(packet, struct ip *); |
12486 | struct sockaddr_in *dst4 = (struct sockaddr_in *)(void *)&new_route->ro_dst; |
12487 | dst4->sin_family = AF_INET; |
12488 | dst4->sin_len = sizeof(struct sockaddr_in); |
12489 | dst4->sin_addr = ip->ip_dst; |
12490 | rtalloc_scoped(new_route, if_idx); |
12491 | if (!ROUTE_UNUSABLE(new_route)) { |
12492 | can_rebind = TRUE; |
12493 | } |
12494 | } else if (family == AF_INET6) { |
12495 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
12496 | struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)(void *)&new_route->ro_dst; |
12497 | dst6->sin6_family = AF_INET6; |
12498 | dst6->sin6_len = sizeof(struct sockaddr_in6); |
12499 | dst6->sin6_addr = ip6->ip6_dst; |
12500 | rtalloc_scoped(new_route, if_idx); |
12501 | if (!ROUTE_UNUSABLE(new_route)) { |
12502 | can_rebind = TRUE; |
12503 | } |
12504 | } |
12505 | } |
12506 | |
12507 | return can_rebind; |
12508 | } |
12509 | |
12510 | static bool |
12511 | necp_addr_is_loopback(struct sockaddr *address) |
12512 | { |
12513 | if (address == NULL) { |
12514 | return FALSE; |
12515 | } |
12516 | |
12517 | if (address->sa_family == AF_INET) { |
12518 | return ntohl(((struct sockaddr_in *)(void *)address)->sin_addr.s_addr) == INADDR_LOOPBACK; |
12519 | } else if (address->sa_family == AF_INET6) { |
12520 | if (!IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)(void *)address)->sin6_addr)) { |
12521 | return IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)(void *)address)->sin6_addr); |
12522 | } else { |
12523 | // Match ::ffff:127.0.0.1 loopback address |
12524 | in6_addr_t *in6_addr_ptr = &(((struct sockaddr_in6 *)(void *)address)->sin6_addr); |
12525 | return *(const __uint32_t *)(const void *)(&in6_addr_ptr->s6_addr[12]) == ntohl(INADDR_LOOPBACK); |
12526 | } |
12527 | } |
12528 | |
12529 | return FALSE; |
12530 | } |
12531 | |
12532 | static bool |
12533 | necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet, u_int32_t bound_interface_index) |
12534 | { |
12535 | // Note: This function only checks for the loopback addresses. |
12536 | // In the future, we may want to expand to also allow any traffic |
12537 | // going through the loopback interface, but until then, this |
12538 | // check is cheaper. |
12539 | |
12540 | if (local_addr != NULL && necp_addr_is_loopback(address: local_addr)) { |
12541 | return TRUE; |
12542 | } |
12543 | |
12544 | if (remote_addr != NULL && necp_addr_is_loopback(address: remote_addr)) { |
12545 | return TRUE; |
12546 | } |
12547 | |
12548 | if (inp != NULL) { |
12549 | if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp && (inp->inp_boundifp->if_flags & IFF_LOOPBACK)) { |
12550 | return TRUE; |
12551 | } |
12552 | if (inp->inp_vflag & INP_IPV4) { |
12553 | if (ntohl(inp->inp_laddr.s_addr) == INADDR_LOOPBACK || |
12554 | ntohl(inp->inp_faddr.s_addr) == INADDR_LOOPBACK) { |
12555 | return TRUE; |
12556 | } |
12557 | } else if (inp->inp_vflag & INP_IPV6) { |
12558 | if (IN6_IS_ADDR_LOOPBACK(&inp->in6p_laddr) || |
12559 | IN6_IS_ADDR_LOOPBACK(&inp->in6p_faddr)) { |
12560 | return TRUE; |
12561 | } |
12562 | } |
12563 | } else if (bound_interface_index != IFSCOPE_NONE && lo_ifp->if_index == bound_interface_index) { |
12564 | return TRUE; |
12565 | } |
12566 | |
12567 | if (packet != NULL) { |
12568 | struct ip *ip = mtod(packet, struct ip *); |
12569 | if (ip->ip_v == 4) { |
12570 | if (ntohl(ip->ip_src.s_addr) == INADDR_LOOPBACK) { |
12571 | return TRUE; |
12572 | } |
12573 | if (ntohl(ip->ip_dst.s_addr) == INADDR_LOOPBACK) { |
12574 | return TRUE; |
12575 | } |
12576 | } else if (ip->ip_v == 6) { |
12577 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
12578 | if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src)) { |
12579 | return TRUE; |
12580 | } |
12581 | if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { |
12582 | return TRUE; |
12583 | } |
12584 | } |
12585 | } |
12586 | |
12587 | return FALSE; |
12588 | } |
12589 | |
12590 | static bool |
12591 | necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet) |
12592 | { |
12593 | if (inp != NULL) { |
12594 | if (!(inp->inp_vflag & INP_IPV6)) { |
12595 | return false; |
12596 | } |
12597 | if (INP_INTCOPROC_ALLOWED(inp)) { |
12598 | return true; |
12599 | } |
12600 | if ((inp->inp_flags & INP_BOUND_IF) && |
12601 | IFNET_IS_INTCOPROC(inp->inp_boundifp)) { |
12602 | return true; |
12603 | } |
12604 | return false; |
12605 | } |
12606 | if (packet != NULL) { |
12607 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
12608 | struct in6_addr *addrv6 = &ip6->ip6_dst; |
12609 | if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION && |
12610 | NECP_IS_INTCOPROC_ADDRESS(addrv6)) { |
12611 | return true; |
12612 | } |
12613 | } |
12614 | |
12615 | return false; |
12616 | } |
12617 | |
12618 | static bool |
12619 | necp_address_matches_drop_dest_policy(union necp_sockaddr_union *sau, u_int32_t session_order) |
12620 | { |
12621 | char dest_str[MAX_IPv6_STR_LEN]; |
12622 | |
12623 | if (necp_drop_dest_debug > 0) { |
12624 | if (sau->sa.sa_family == AF_INET) { |
12625 | (void) inet_ntop(AF_INET, &sau->sin.sin_addr, dest_str, sizeof(dest_str)); |
12626 | } else if (sau->sa.sa_family == AF_INET6) { |
12627 | (void) inet_ntop(AF_INET6, &sau->sin6.sin6_addr, dest_str, sizeof(dest_str)); |
12628 | } else { |
12629 | dest_str[0] = 0; |
12630 | } |
12631 | } |
12632 | for (u_int32_t i = 0; i < necp_drop_dest_policy.entry_count; i++) { |
12633 | struct necp_drop_dest_entry *necp_drop_dest_entry = &necp_drop_dest_policy.entries[i]; |
12634 | struct necp_policy_condition_addr *npca = &necp_drop_dest_entry->cond_addr; |
12635 | |
12636 | if (session_order >= necp_drop_dest_entry->order && necp_is_addr_in_subnet(SA(&sau->sa), SA(&npca->address.sa), subnet_prefix: npca->prefix)) { |
12637 | if (necp_drop_dest_debug > 0) { |
12638 | char subnet_str[MAX_IPv6_STR_LEN]; |
12639 | struct proc *p = current_proc(); |
12640 | pid_t pid = proc_pid(p); |
12641 | |
12642 | if (sau->sa.sa_family == AF_INET) { |
12643 | (void) inet_ntop(AF_INET, &npca->address.sin, subnet_str, sizeof(subnet_str)); |
12644 | os_log(OS_LOG_DEFAULT, "%s (process %s:%u) %s matches %s/%u" , __func__, proc_best_name(p), pid, dest_str, subnet_str, npca->prefix); |
12645 | } else if (sau->sa.sa_family == AF_INET6) { |
12646 | (void) inet_ntop(AF_INET6, &npca->address.sin6, subnet_str, sizeof(subnet_str)); |
12647 | os_log(OS_LOG_DEFAULT, "%s (process %s:%u) %s matches %s/%u" , __func__, proc_best_name(p), pid, dest_str, subnet_str, npca->prefix); |
12648 | } |
12649 | } |
12650 | return true; |
12651 | } |
12652 | } |
12653 | if (necp_drop_dest_debug > 1) { |
12654 | struct proc *p = current_proc(); |
12655 | pid_t pid = proc_pid(p); |
12656 | |
12657 | os_log(OS_LOG_DEFAULT, "%s (process %s:%u) %s no match" , __func__, proc_best_name(p), pid, dest_str); |
12658 | } |
12659 | return false; |
12660 | } |
12661 | |
12662 | static int |
12663 | sysctl_handle_necp_drop_dest_level SYSCTL_HANDLER_ARGS |
12664 | { |
12665 | #pragma unused(arg1, arg2, oidp) |
12666 | int changed = 0; |
12667 | int error = 0; |
12668 | struct necp_drop_dest_policy tmp_drop_dest_policy; |
12669 | struct proc *p = current_proc(); |
12670 | pid_t pid = proc_pid(p); |
12671 | |
12672 | if (req->newptr != USER_ADDR_NULL && proc_suser(p: current_proc()) != 0 && |
12673 | priv_check_cred(cred: kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, flags: 0) != 0) { |
12674 | NECPLOG(LOG_ERR, "%s (process %s:%u) not permitted" , __func__, proc_best_name(p), pid); |
12675 | return EPERM; |
12676 | } |
12677 | if (req->newptr != USER_ADDR_NULL && req->newlen != sizeof(struct necp_drop_dest_policy)) { |
12678 | NECPLOG(LOG_ERR, "%s (process %s:%u) bad newlen %lu" , __func__, proc_best_name(p), pid, req->newlen); |
12679 | return EINVAL; |
12680 | } |
12681 | |
12682 | memcpy(dst: &tmp_drop_dest_policy, src: &necp_drop_dest_policy, n: sizeof(struct necp_drop_dest_policy)); |
12683 | error = sysctl_io_opaque(req, pValue: &tmp_drop_dest_policy, valueSize: sizeof(struct necp_drop_dest_policy), changed: &changed); |
12684 | if (error != 0) { |
12685 | NECPLOG(LOG_ERR, "%s (process %s:%u) sysctl_io_opaque() error %d" , __func__, proc_best_name(p), pid, error); |
12686 | return error; |
12687 | } |
12688 | if (changed == 0 || req->newptr == USER_ADDR_NULL) { |
12689 | return error; |
12690 | } |
12691 | |
12692 | // |
12693 | // Validate the passed parameters |
12694 | // |
12695 | if (tmp_drop_dest_policy.entry_count >= MAX_NECP_DROP_DEST_LEVEL_ADDRS) { |
12696 | NECPLOG(LOG_ERR, "%s (process %s:%u) bad entry_count %u" , __func__, proc_best_name(p), pid, tmp_drop_dest_policy.entry_count); |
12697 | return EINVAL; |
12698 | } |
12699 | for (u_int32_t i = 0; i < tmp_drop_dest_policy.entry_count; i++) { |
12700 | struct necp_drop_dest_entry *tmp_drop_dest_entry = &tmp_drop_dest_policy.entries[i]; |
12701 | struct necp_policy_condition_addr *npca = &tmp_drop_dest_entry->cond_addr; |
12702 | |
12703 | switch (tmp_drop_dest_entry->level) { |
12704 | case NECP_SESSION_PRIORITY_UNKNOWN: |
12705 | if (tmp_drop_dest_policy.entry_count != 0) { |
12706 | NECPLOG(LOG_ERR, "%s (process %s:%u) NECP_SESSION_PRIORITY_UNKNOWN bad entry_count %u" , __func__, proc_best_name(p), pid, tmp_drop_dest_policy.entry_count); |
12707 | return EINVAL; |
12708 | } |
12709 | break; |
12710 | case NECP_SESSION_PRIORITY_CONTROL: |
12711 | case NECP_SESSION_PRIORITY_CONTROL_1: |
12712 | case NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL: |
12713 | case NECP_SESSION_PRIORITY_HIGH: |
12714 | case NECP_SESSION_PRIORITY_HIGH_1: |
12715 | case NECP_SESSION_PRIORITY_HIGH_2: |
12716 | case NECP_SESSION_PRIORITY_HIGH_3: |
12717 | case NECP_SESSION_PRIORITY_HIGH_4: |
12718 | case NECP_SESSION_PRIORITY_HIGH_RESTRICTED: |
12719 | case NECP_SESSION_PRIORITY_DEFAULT: |
12720 | case NECP_SESSION_PRIORITY_LOW: |
12721 | if (tmp_drop_dest_policy.entry_count == 0) { |
12722 | NECPLOG(LOG_ERR, "%s (process %s:%u) priority %u entry_count 0" , __func__, proc_best_name(p), pid, tmp_drop_dest_entry->level); |
12723 | return EINVAL; |
12724 | } |
12725 | break; |
12726 | default: { |
12727 | NECPLOG(LOG_ERR, "%s (process %s:%u) bad level %u" , __func__, proc_best_name(p), pid, tmp_drop_dest_entry->level); |
12728 | return EINVAL; |
12729 | } |
12730 | } |
12731 | |
12732 | switch (npca->address.sa.sa_family) { |
12733 | case AF_INET: { |
12734 | if (npca->prefix > 32) { |
12735 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET bad prefix %u" , __func__, proc_best_name(p), pid, npca->prefix); |
12736 | return EINVAL; |
12737 | } |
12738 | if (npca->address.sin.sin_len != sizeof(struct sockaddr_in)) { |
12739 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET bad sin_len %u" , __func__, proc_best_name(p), pid, npca->address.sin.sin_len); |
12740 | return EINVAL; |
12741 | } |
12742 | if (npca->address.sin.sin_port != 0) { |
12743 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET bad sin_port %u, not zero" , __func__, proc_best_name(p), pid, npca->address.sin.sin_port); |
12744 | return EINVAL; |
12745 | } |
12746 | break; |
12747 | } |
12748 | case AF_INET6: { |
12749 | if (npca->prefix > 128) { |
12750 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET6 bad prefix %u" , __func__, proc_best_name(p), pid, npca->prefix); |
12751 | return EINVAL; |
12752 | } |
12753 | if (npca->address.sin6.sin6_len != sizeof(struct sockaddr_in6)) { |
12754 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET6 bad sin6_len %u" , __func__, proc_best_name(p), pid, npca->address.sin6.sin6_len); |
12755 | return EINVAL; |
12756 | } |
12757 | if (npca->address.sin6.sin6_port != 0) { |
12758 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET6 bad sin6_port %u, not zero" , __func__, proc_best_name(p), pid, npca->address.sin6.sin6_port); |
12759 | return EINVAL; |
12760 | } |
12761 | if (npca->address.sin6.sin6_flowinfo != 0) { |
12762 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET6 bad sin6_flowinfo %u, not zero" , __func__, proc_best_name(p), pid, npca->address.sin6.sin6_flowinfo); |
12763 | return EINVAL; |
12764 | } |
12765 | if (npca->address.sin6.sin6_scope_id != 0) { |
12766 | NECPLOG(LOG_ERR, "%s (process %s:%u) AF_INET6 bad sin6_scope_id %u, not zero" , __func__, proc_best_name(p), pid, npca->address.sin6.sin6_scope_id); |
12767 | return EINVAL; |
12768 | } |
12769 | break; |
12770 | } |
12771 | default: { |
12772 | return EINVAL; |
12773 | } |
12774 | } |
12775 | } |
12776 | |
12777 | // |
12778 | // Commit the changed policy |
12779 | // |
12780 | lck_rw_lock_exclusive(lck: &necp_kernel_policy_lock); |
12781 | memset(s: &necp_drop_dest_policy, c: 0, n: sizeof(struct necp_drop_dest_policy)); |
12782 | |
12783 | necp_drop_dest_policy.entry_count = tmp_drop_dest_policy.entry_count; |
12784 | for (u_int32_t i = 0; i < tmp_drop_dest_policy.entry_count; i++) { |
12785 | struct necp_drop_dest_entry *tmp_drop_dest_entry = &tmp_drop_dest_policy.entries[i]; |
12786 | struct necp_drop_dest_entry *necp_drop_dest_entry = &necp_drop_dest_policy.entries[i]; |
12787 | |
12788 | memcpy(dst: necp_drop_dest_entry, src: tmp_drop_dest_entry, n: sizeof(struct necp_drop_dest_entry)); |
12789 | |
12790 | necp_drop_dest_entry->order = necp_get_first_order_for_priority(priority: necp_drop_dest_entry->level); |
12791 | } |
12792 | lck_rw_done(lck: &necp_kernel_policy_lock); |
12793 | |
12794 | return 0; |
12795 | } |
12796 | |
12797 | const char* |
12798 | necp_get_address_string(union necp_sockaddr_union *address, char addr_str[MAX_IPv6_STR_LEN]) |
12799 | { |
12800 | uint16_t fam = address->sa.sa_family; |
12801 | memset(s: addr_str, c: 0, MAX_IPv6_STR_LEN); |
12802 | if (fam == AF_INET) { |
12803 | (void) inet_ntop(AF_INET, &address->sin.sin_addr, addr_str, MAX_IPv6_STR_LEN); |
12804 | } else if (fam == AF_INET6) { |
12805 | (void) inet_ntop(AF_INET6, &address->sin6.sin6_addr, addr_str, MAX_IPv6_STR_LEN); |
12806 | } |
12807 | return addr_str; |
12808 | } |
12809 | |