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
147u_int32_t necp_drop_all_order = 0;
148u_int32_t necp_drop_all_level = 0;
149
150u_int32_t necp_pass_loopback = NECP_LOOPBACK_PASS_ALL;
151u_int32_t necp_pass_keepalives = 1; // 0=Off, 1=On
152u_int32_t necp_pass_interpose = 1; // 0=Off, 1=On
153u_int32_t necp_restrict_multicast = 1; // 0=Off, 1=On
154u_int32_t necp_dedup_policies = 0; // 0=Off, 1=On
155
156u_int32_t necp_drop_unentitled_order = 0;
157#ifdef XNU_TARGET_OS_WATCH
158u_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
160u_int32_t necp_drop_unentitled_level = 0;
161#endif // XNU_TARGET_OS_WATCH
162
163u_int32_t necp_drop_management_order = 0;
164u_int32_t necp_drop_management_level = NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL;
165
166u_int32_t necp_debug = 0; // 0=None, 1=Basic, 2=EveryMatch
167
168os_log_t necp_log_handle = NULL;
169os_log_t necp_data_trace_log_handle = NULL;
170
171u_int32_t necp_session_count = 0;
172
173static KALLOC_TYPE_DEFINE(necp_session_policy_zone,
174 struct necp_session_policy, NET_KT_DEFAULT);
175static KALLOC_TYPE_DEFINE(necp_socket_policy_zone,
176 struct necp_kernel_socket_policy, NET_KT_DEFAULT);
177static 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
273typedef 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
282struct 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
288struct 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};
295static LIST_HEAD(necp_domain_filter_list, necp_domain_filter) necp_global_domain_filter_list;
296
297struct 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
323static TAILQ_HEAD(_necp_session_list, necp_session) necp_session_list;
324
325struct 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
358static LCK_GRP_DECLARE(necp_kernel_policy_mtx_grp, NECP_CONTROL_NAME);
359static LCK_ATTR_DECLARE(necp_kernel_policy_mtx_attr, 0, 0);
360static LCK_RW_DECLARE_ATTR(necp_kernel_policy_lock, &necp_kernel_policy_mtx_grp,
361 &necp_kernel_policy_mtx_attr);
362
363static LCK_GRP_DECLARE(necp_route_rule_mtx_grp, "necp_route_rule");
364static LCK_RW_DECLARE(necp_route_rule_lock, &necp_route_rule_mtx_grp);
365
366os_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 */
373static 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
390typedef 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
396static u_int64_t necp_kernel_application_policies_condition_mask;
397static size_t necp_kernel_application_policies_count;
398static u_int64_t necp_kernel_socket_policies_condition_mask;
399static size_t necp_kernel_socket_policies_count;
400static size_t necp_kernel_socket_policies_non_app_count;
401static 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)
404static size_t necp_kernel_socket_policies_map_counts[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS];
405static struct necp_kernel_socket_policy **necp_kernel_socket_policies_map[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS];
406static size_t necp_kernel_socket_policies_app_layer_map_count;
407static 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
416static u_int64_t necp_kernel_ip_output_policies_condition_mask;
417static size_t necp_kernel_ip_output_policies_count;
418static size_t necp_kernel_ip_output_policies_non_id_count;
419static 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)
422static size_t necp_kernel_ip_output_policies_map_counts[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS];
423static struct necp_kernel_ip_output_policy **necp_kernel_ip_output_policies_map[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS];
424static 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
430static struct necp_session *necp_create_session(void);
431static void necp_delete_session(struct necp_session *session);
432
433static 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);
435static int necp_handle_policy_dump_all(user_addr_t out_buffer, size_t out_buffer_length);
436
437#define MAX_RESULT_STRING_LEN 64
438static inline const char * necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter);
439
440static 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);
441static struct necp_session_policy *necp_policy_find(struct necp_session *session, necp_policy_id policy_id);
442static bool necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy);
443static bool necp_policy_mark_all_for_deletion(struct necp_session *session);
444static bool necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy);
445static void necp_policy_apply_all(struct necp_session *session);
446
447static 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);
448static bool necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id);
449static bool necp_kernel_socket_policies_reprocess(void);
450static bool necp_kernel_socket_policies_update_uuid_table(void);
451static 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
453static 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);
454static bool necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id);
455static bool necp_kernel_ip_output_policies_reprocess(void);
456
457static bool necp_is_addr_in_range(struct sockaddr *addr, struct sockaddr *range_start, struct sockaddr *range_end);
458static bool necp_is_range_in_range(struct sockaddr *inner_range_start, struct sockaddr *inner_range_end, struct sockaddr *range_start, struct sockaddr *range_end);
459static bool necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subnet_addr, u_int8_t subnet_prefix);
460static int necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port);
461static bool necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits);
462static bool necp_addr_is_empty(struct sockaddr *addr);
463static bool necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet, u_int32_t bound_interface_index);
464static bool necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet);
465
466struct 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};
473static size_t necp_num_uuid_app_id_mappings;
474static bool necp_uuid_app_id_mappings_dirty;
475#define NECP_UUID_APP_ID_HASH_SIZE 64
476static u_long necp_uuid_app_id_hash_mask;
477static u_long necp_uuid_app_id_hash_num_buckets;
478static 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
480static u_int32_t necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table);
481static bool necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table);
482static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id);
483
484static struct necp_uuid_id_mapping *necp_uuid_lookup_service_id_locked(uuid_t uuid);
485static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id);
486static u_int32_t necp_create_uuid_service_id_mapping(uuid_t uuid);
487static bool necp_remove_uuid_service_id_mapping(uuid_t uuid);
488static bool necp_remove_uuid_service_id_mapping_with_service_id(u_int32_t service_id);
489
490struct 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};
496static LIST_HEAD(necp_string_id_mapping_list, necp_string_id_mapping) necp_account_id_list;
497static u_int32_t necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain);
498static bool necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain);
499static struct necp_string_id_mapping *necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id);
500
501static 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);
502static bool necp_remove_domain_filter(struct necp_domain_filter_list *list, struct necp_domain_filter_list *owner_list, u_int32_t filter_id);
503static struct necp_domain_filter *necp_lookup_domain_filter(struct necp_domain_filter_list *list, u_int32_t filter_id);
504
505static struct necp_kernel_socket_policy *necp_kernel_socket_policy_find(necp_kernel_policy_id policy_id);
506static struct necp_kernel_ip_output_policy *necp_kernel_ip_output_policy_find(necp_kernel_policy_id policy_id);
507
508static LIST_HEAD(_necp_kernel_service_list, necp_service_registration) necp_registered_service_list;
509
510static char *necp_create_trimmed_domain(char *string, size_t length);
511static inline int necp_count_dots(char *string, size_t length);
512
513static char *necp_copy_string(char *string, size_t length);
514static 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
519struct 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};
537static LIST_HEAD(necp_route_rule_list, necp_route_rule) necp_route_rules;
538static 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);
539static bool necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id);
540static bool necp_route_is_interface_type_allowed(struct rtentry *route, struct ifnet *ifp, proc_t proc, struct inpcb *inp);
541static 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);
543static 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);
544static bool necp_route_rule_matches_agents(u_int32_t route_rule_id);
545static 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);
546static struct necp_route_rule *necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id);
547static inline void necp_get_parent_is_entitled(task_t task, struct necp_socket_info *info);
548
549#define MAX_AGGREGATE_ROUTE_RULES 16
550struct 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};
555static LIST_HEAD(necp_aggregate_route_rule_list, necp_aggregate_route_rule) necp_aggregate_route_rules;
556static u_int32_t necp_create_aggregate_route_rule(u_int32_t *rule_ids);
557
558// Sysctl definitions
559static int sysctl_handle_necp_level SYSCTL_HANDLER_ARGS;
560static int sysctl_handle_necp_unentitled_level SYSCTL_HANDLER_ARGS;
561static int sysctl_handle_necp_management_level SYSCTL_HANDLER_ARGS;
562
563SYSCTL_NODE(_net, OID_AUTO, necp, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "NECP");
564SYSCTL_INT(_net_necp, NECPCTL_DEDUP_POLICIES, dedup_policies, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_dedup_policies, 0, "");
565SYSCTL_INT(_net_necp, NECPCTL_RESTRICT_MULTICAST, restrict_multicast, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_restrict_multicast, 0, "");
566SYSCTL_INT(_net_necp, NECPCTL_PASS_LOOPBACK, pass_loopback, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_loopback, 0, "");
567SYSCTL_INT(_net_necp, NECPCTL_PASS_KEEPALIVES, pass_keepalives, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_keepalives, 0, "");
568SYSCTL_INT(_net_necp, NECPCTL_PASS_INTERPOSE, pass_interpose, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_interpose, 0, "");
569SYSCTL_INT(_net_necp, NECPCTL_DEBUG, debug, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_debug, 0, "");
570SYSCTL_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", "");
571SYSCTL_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", "");
572SYSCTL_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", "");
573SYSCTL_LONG(_net_necp, NECPCTL_SOCKET_POLICY_COUNT, socket_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_socket_policies_count, "");
574SYSCTL_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, "");
575SYSCTL_LONG(_net_necp, NECPCTL_IP_POLICY_COUNT, ip_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_ip_output_policies_count, "");
576SYSCTL_INT(_net_necp, NECPCTL_SESSION_COUNT, session_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_session_count, 0, "");
577
578static struct necp_drop_dest_policy necp_drop_dest_policy;
579static int necp_drop_dest_debug = 0; // 0: off, 1: match, >1: every evaluation
580SYSCTL_INT(_net_necp, OID_AUTO, drop_dest_debug, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_dest_debug, 0, "");
581
582static int sysctl_handle_necp_drop_dest_level SYSCTL_HANDLER_ARGS;
583SYSCTL_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
586static 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 */
600static int necp_data_tracing_level = 0;
601SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_level, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_level, 0, "");
602
603static int necp_data_tracing_port = 0;
604SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_port, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_port, 0, "");
605
606static int necp_data_tracing_proto = 0;
607SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_proto, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_proto, 0, "");
608
609static int necp_data_tracing_pid = 0;
610SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_pid, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_pid, 0, "");
611
612static int necp_data_tracing_ifindex = 0;
613SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_ifindex, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_ifindex, 0, "");
614
615static int necp_data_tracing_match_all = 0;
616SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_match_all, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_match_all, 0, "");
617
618static int necp_data_tracing_session_order = 0;
619SYSCTL_INT(_net_necp, OID_AUTO, data_tracing_session_order, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_data_tracing_session_order, 0, "");
620
621static int necp_data_tracing_policy_order = 0;
622SYSCTL_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
662const 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
741const 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
764static u_int32_t
765necp_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
780static inline u_int32_t
781necp_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
790static int
791sysctl_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
799static int
800sysctl_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
809static 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
823static int
824sysctl_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
834static int necp_session_op_close(struct fileglob *, vfs_context_t);
835
836static 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
847static inline int
848necp_is_platform_binary(proc_t proc)
849{
850 return (proc != NULL) ? (csproc_get_platform_binary(proc) && cs_valid(proc)) : 0;
851}
852
853static inline necp_drop_all_bypass_check_result_t
854necp_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
882int
883necp_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;
926done:
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
937static int
938necp_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
954static int
955necp_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
974static int
975necp_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
1015done:
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
1025static int
1026necp_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
1088done:
1089 if (response != NULL) {
1090 kfree_data(response, response_size);
1091 response = NULL;
1092 }
1093 *retval = error;
1094
1095 return error;
1096}
1097
1098static int
1099necp_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);
1124done:
1125 *retval = error;
1126 return error;
1127}
1128
1129static int
1130necp_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
1138static int
1139necp_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
1189done:
1190 if (response != NULL) {
1191 kfree_data(response, response_size);
1192 response = NULL;
1193 }
1194 *retval = error;
1195
1196 return error;
1197}
1198
1199
1200static int
1201necp_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
1209static int
1210necp_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
1253done:
1254 *retval = error;
1255 return error;
1256}
1257
1258static int
1259necp_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
1267static int
1268necp_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
1295done:
1296 *retval = error;
1297 return error;
1298}
1299
1300static int
1301necp_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
1336done:
1337 *retval = error;
1338 return error;
1339}
1340
1341static int
1342necp_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);
1354done:
1355 *retval = error;
1356 return error;
1357}
1358
1359static int
1360necp_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
1418done:
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
1428static int
1429necp_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
1454done:
1455 *retval = error;
1456 return error;
1457}
1458
1459static int
1460necp_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
1481int
1482necp_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
1576done:
1577 NECP_SESSION_UNLOCK(session);
1578 fp_drop(p, fd: uap->necp_fd, fp, locked: 0);
1579 return return_value;
1580}
1581
1582struct necp_resolver_key_state {
1583 const struct ccdigest_info *digest_info;
1584 uint8_t key[CCSHA256_OUTPUT_SIZE];
1585};
1586static struct necp_resolver_key_state s_necp_resolver_key_state;
1587
1588static void
1589necp_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
1595static void
1596necp_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
1614int
1615necp_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
1649bool
1650necp_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
1684struct necp_application_id_key_state {
1685 const struct ccdigest_info *digest_info;
1686 uint8_t key[CCSHA256_OUTPUT_SIZE];
1687};
1688static struct necp_application_id_key_state s_necp_application_id_key_state;
1689
1690static void
1691necp_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
1697static void
1698necp_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
1713int
1714necp_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
1745bool
1746necp_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
1777void
1778necp_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
1829static void
1830necp_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
1847static inline bool
1848necp_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
1865u_int8_t *
1866necp_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
1892u_int8_t *
1893necp_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
1910u_int8_t
1911necp_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
1923u_int32_t
1924necp_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
1936u_int8_t *
1937necp_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
1953int
1954necp_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
2001static int
2002necp_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
2011static int
2012necp_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
2064static int
2065necp_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
2080static struct necp_session *
2081necp_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
2131static void
2132necp_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
2172static inline u_int8_t
2173necp_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
2178static inline u_int32_t
2179necp_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
2184static inline u_int8_t *
2185necp_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
2190static bool
2191necp_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
2200static 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
2214static bool
2215necp_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
2283static inline u_int8_t
2284necp_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
2289static inline u_int8_t
2290necp_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
2295static inline u_int32_t
2296necp_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
2301static inline u_int8_t *
2302necp_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
2307static inline bool
2308necp_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
2313static inline bool
2314necp_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
2319static inline bool
2320necp_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
2325static inline bool
2326necp_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
2332static inline bool
2333necp_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
2349static bool
2350necp_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
2541static bool
2542necp_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
2548static bool
2549necp_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
2599static int
2600necp_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
2623static necp_policy_id
2624necp_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 ==