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 == |
---|