1 | /* |
2 | * Copyright (c) 2013-2018 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 <libkern/OSMalloc.h> |
35 | #include <sys/kernel.h> |
36 | #include <sys/kern_control.h> |
37 | #include <sys/mbuf.h> |
38 | #include <sys/kpi_mbuf.h> |
39 | #include <sys/proc_uuid_policy.h> |
40 | #include <net/if.h> |
41 | #include <sys/domain.h> |
42 | #include <sys/protosw.h> |
43 | #include <sys/socket.h> |
44 | #include <sys/socketvar.h> |
45 | #include <sys/coalition.h> |
46 | #include <netinet/ip.h> |
47 | #include <netinet/ip6.h> |
48 | #include <netinet/tcp.h> |
49 | #include <netinet/tcp_var.h> |
50 | #include <netinet/tcp_cache.h> |
51 | #include <netinet/udp.h> |
52 | #include <netinet/in_pcb.h> |
53 | #include <netinet/in_tclass.h> |
54 | #include <netinet6/esp.h> |
55 | #include <net/flowhash.h> |
56 | #include <net/if_var.h> |
57 | #include <sys/kauth.h> |
58 | #include <sys/sysctl.h> |
59 | #include <sys/sysproto.h> |
60 | #include <sys/priv.h> |
61 | #include <sys/kern_event.h> |
62 | #include <sys/file_internal.h> |
63 | #include <IOKit/IOBSD.h> |
64 | #include <net/network_agent.h> |
65 | #include <net/necp.h> |
66 | |
67 | /* |
68 | * NECP - Network Extension Control Policy database |
69 | * ------------------------------------------------ |
70 | * The goal of this module is to allow clients connecting via a |
71 | * kernel control socket to create high-level policy sessions, which |
72 | * are ingested into low-level kernel policies that control and tag |
73 | * traffic at the application, socket, and IP layers. |
74 | * |
75 | * ------------------------------------------------ |
76 | * Sessions |
77 | * ------------------------------------------------ |
78 | * Each session owns a list of session policies, each of which can |
79 | * specify any combination of conditions and a single result. Each |
80 | * session also has a priority level (such as High, Default, or Low) |
81 | * which is requested by the client. Based on the requested level, |
82 | * a session order value is assigned to the session, which will be used |
83 | * to sort kernel policies generated by the session. The session client |
84 | * can specify the sub-order for each policy it creates which will be |
85 | * used to further sort the kernel policies. |
86 | * |
87 | * Kernel Control Socket --> 1 necp_session --> list of necp_session_policy structs |
88 | * |
89 | * ------------------------------------------------ |
90 | * Kernel Policies |
91 | * ------------------------------------------------ |
92 | * Whenever a session send the Apply command, its policies are ingested |
93 | * and generate kernel policies. There are two phases of kernel policy |
94 | * ingestion. |
95 | * |
96 | * 1. The session policy is parsed to create kernel policies at the socket |
97 | * and IP layers, when applicable. For example, a policy that requires |
98 | * all traffic from App1 to Pass will generate a socket kernel policy to |
99 | * match App1 and mark packets with ID1, and also an IP policy to match |
100 | * ID1 and let the packet pass. This is handled in necp_apply_policy. The |
101 | * resulting kernel policies are added to the global socket and IP layer |
102 | * policy lists. |
103 | * necp_session_policy --> necp_kernel_socket_policy and necp_kernel_ip_output_policy |
104 | * || || |
105 | * \/ \/ |
106 | * necp_kernel_socket_policies necp_kernel_ip_output_policies |
107 | * |
108 | * 2. Once the global lists of kernel policies have been filled out, each |
109 | * list is traversed to create optimized sub-lists ("Maps") which are used during |
110 | * data-path evaluation. IP policies are sent into necp_kernel_ip_output_policies_map, |
111 | * which hashes incoming packets based on marked socket-layer policies, and removes |
112 | * duplicate or overlapping policies. Socket policies are sent into two maps, |
113 | * necp_kernel_socket_policies_map and necp_kernel_socket_policies_app_layer_map. |
114 | * The app layer map is used for policy checks coming in from user space, and is one |
115 | * list with duplicate and overlapping policies removed. The socket map hashes based |
116 | * on app UUID, and removes duplicate and overlapping policies. |
117 | * necp_kernel_socket_policy --> necp_kernel_socket_policies_app_layer_map |
118 | * |-> necp_kernel_socket_policies_map |
119 | * |
120 | * necp_kernel_ip_output_policies --> necp_kernel_ip_output_policies_map |
121 | * |
122 | * ------------------------------------------------ |
123 | * Drop All Level |
124 | * ------------------------------------------------ |
125 | * The Drop All Level is a sysctl that controls the level at which policies are allowed |
126 | * to override a global drop rule. If the value is 0, no drop rule is applied. If the value |
127 | * is 1, all traffic is dropped. If the value is greater than 1, all kernel policies created |
128 | * by a session with a priority level better than (numerically less than) the |
129 | * Drop All Level will allow matching traffic to not be dropped. The Drop All Level is |
130 | * dynamically interpreted into necp_drop_all_order, which specifies the equivalent assigned |
131 | * session orders to be dropped. |
132 | */ |
133 | |
134 | u_int32_t necp_drop_all_order = 0; |
135 | u_int32_t necp_drop_all_level = 0; |
136 | |
137 | u_int32_t necp_pass_loopback = 1; // 0=Off, 1=On |
138 | u_int32_t necp_pass_keepalives = 1; // 0=Off, 1=On |
139 | |
140 | u_int32_t necp_debug = 0; // 0=None, 1=Basic, 2=EveryMatch |
141 | |
142 | u_int32_t necp_session_count = 0; |
143 | |
144 | #define LIST_INSERT_SORTED_ASCENDING(head, elm, field, sortfield, tmpelm) do { \ |
145 | if (LIST_EMPTY((head)) || (LIST_FIRST(head)->sortfield >= (elm)->sortfield)) { \ |
146 | LIST_INSERT_HEAD((head), elm, field); \ |
147 | } else { \ |
148 | LIST_FOREACH(tmpelm, head, field) { \ |
149 | if (LIST_NEXT(tmpelm, field) == NULL || LIST_NEXT(tmpelm, field)->sortfield >= (elm)->sortfield) { \ |
150 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
151 | break; \ |
152 | } \ |
153 | } \ |
154 | } \ |
155 | } while (0) |
156 | |
157 | #define LIST_INSERT_SORTED_TWICE_ASCENDING(head, elm, field, firstsortfield, secondsortfield, tmpelm) do { \ |
158 | if (LIST_EMPTY((head)) || (LIST_FIRST(head)->firstsortfield > (elm)->firstsortfield) || ((LIST_FIRST(head)->firstsortfield == (elm)->firstsortfield) && (LIST_FIRST(head)->secondsortfield >= (elm)->secondsortfield))) { \ |
159 | LIST_INSERT_HEAD((head), elm, field); \ |
160 | } else { \ |
161 | LIST_FOREACH(tmpelm, head, field) { \ |
162 | 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))) { \ |
163 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
164 | break; \ |
165 | } \ |
166 | } \ |
167 | } \ |
168 | } while (0) |
169 | |
170 | #define LIST_INSERT_SORTED_THRICE_ASCENDING(head, elm, field, firstsortfield, secondsortfield, thirdsortfield, tmpelm) do { \ |
171 | 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))) { \ |
172 | LIST_INSERT_HEAD((head), elm, field); \ |
173 | } else { \ |
174 | LIST_FOREACH(tmpelm, head, field) { \ |
175 | 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))) { \ |
176 | LIST_INSERT_AFTER(tmpelm, elm, field); \ |
177 | break; \ |
178 | } \ |
179 | } \ |
180 | } \ |
181 | } while (0) |
182 | |
183 | #define IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(x) ((x) == NECP_ROUTE_RULE_DENY_INTERFACE || (x) == NECP_ROUTE_RULE_ALLOW_INTERFACE) |
184 | |
185 | #define NECP_KERNEL_CONDITION_ALL_INTERFACES 0x000001 |
186 | #define NECP_KERNEL_CONDITION_BOUND_INTERFACE 0x000002 |
187 | #define NECP_KERNEL_CONDITION_PROTOCOL 0x000004 |
188 | #define NECP_KERNEL_CONDITION_LOCAL_START 0x000008 |
189 | #define NECP_KERNEL_CONDITION_LOCAL_END 0x000010 |
190 | #define NECP_KERNEL_CONDITION_LOCAL_PREFIX 0x000020 |
191 | #define NECP_KERNEL_CONDITION_REMOTE_START 0x000040 |
192 | #define NECP_KERNEL_CONDITION_REMOTE_END 0x000080 |
193 | #define NECP_KERNEL_CONDITION_REMOTE_PREFIX 0x000100 |
194 | #define NECP_KERNEL_CONDITION_APP_ID 0x000200 |
195 | #define NECP_KERNEL_CONDITION_REAL_APP_ID 0x000400 |
196 | #define NECP_KERNEL_CONDITION_DOMAIN 0x000800 |
197 | #define NECP_KERNEL_CONDITION_ACCOUNT_ID 0x001000 |
198 | #define NECP_KERNEL_CONDITION_POLICY_ID 0x002000 |
199 | #define NECP_KERNEL_CONDITION_PID 0x004000 |
200 | #define NECP_KERNEL_CONDITION_UID 0x008000 |
201 | #define NECP_KERNEL_CONDITION_LAST_INTERFACE 0x010000 // Only set from packets looping between interfaces |
202 | #define NECP_KERNEL_CONDITION_TRAFFIC_CLASS 0x020000 |
203 | #define NECP_KERNEL_CONDITION_ENTITLEMENT 0x040000 |
204 | #define NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT 0x080000 |
205 | #define NECP_KERNEL_CONDITION_AGENT_TYPE 0x100000 |
206 | |
207 | #define NECP_MAX_POLICY_RESULT_SIZE 512 |
208 | #define NECP_MAX_ROUTE_RULES_ARRAY_SIZE 1024 |
209 | #define NECP_MAX_CONDITIONS_ARRAY_SIZE 4096 |
210 | #define NECP_MAX_POLICY_LIST_COUNT 1024 |
211 | |
212 | // Cap the policy size at the max result + conditions size, with room for extra TLVs |
213 | #define NECP_MAX_POLICY_SIZE (1024 + NECP_MAX_POLICY_RESULT_SIZE + NECP_MAX_CONDITIONS_ARRAY_SIZE) |
214 | |
215 | struct necp_service_registration { |
216 | LIST_ENTRY(necp_service_registration) session_chain; |
217 | LIST_ENTRY(necp_service_registration) kernel_chain; |
218 | u_int32_t service_id; |
219 | }; |
220 | |
221 | struct necp_session { |
222 | u_int8_t necp_fd_type; |
223 | u_int32_t control_unit; |
224 | u_int32_t session_priority; // Descriptive priority rating |
225 | u_int32_t session_order; |
226 | |
227 | necp_policy_id last_policy_id; |
228 | |
229 | decl_lck_mtx_data(, lock); |
230 | |
231 | bool proc_locked; // Messages must come from proc_uuid |
232 | uuid_t proc_uuid; |
233 | int proc_pid; |
234 | |
235 | bool dirty; |
236 | LIST_HEAD(_policies, necp_session_policy) policies; |
237 | |
238 | LIST_HEAD(_services, necp_service_registration) services; |
239 | |
240 | TAILQ_ENTRY(necp_session) chain; |
241 | }; |
242 | |
243 | #define NECP_SESSION_LOCK(_s) lck_mtx_lock(&_s->lock) |
244 | #define NECP_SESSION_UNLOCK(_s) lck_mtx_unlock(&_s->lock) |
245 | |
246 | static TAILQ_HEAD(_necp_session_list, necp_session) necp_session_list; |
247 | |
248 | struct necp_socket_info { |
249 | pid_t pid; |
250 | uid_t uid; |
251 | union necp_sockaddr_union local_addr; |
252 | union necp_sockaddr_union remote_addr; |
253 | u_int32_t bound_interface_index; |
254 | u_int32_t traffic_class; |
255 | u_int16_t protocol; |
256 | u_int32_t application_id; |
257 | u_int32_t real_application_id; |
258 | u_int32_t account_id; |
259 | char *domain; |
260 | errno_t cred_result; |
261 | }; |
262 | |
263 | static kern_ctl_ref necp_kctlref; |
264 | static u_int32_t necp_family; |
265 | static OSMallocTag necp_malloc_tag; |
266 | static lck_grp_attr_t *necp_kernel_policy_grp_attr = NULL; |
267 | static lck_attr_t *necp_kernel_policy_mtx_attr = NULL; |
268 | static lck_grp_t *necp_kernel_policy_mtx_grp = NULL; |
269 | decl_lck_rw_data(static, necp_kernel_policy_lock); |
270 | |
271 | static lck_grp_attr_t *necp_route_rule_grp_attr = NULL; |
272 | static lck_attr_t *necp_route_rule_mtx_attr = NULL; |
273 | static lck_grp_t *necp_route_rule_mtx_grp = NULL; |
274 | decl_lck_rw_data(static, necp_route_rule_lock); |
275 | |
276 | /* |
277 | * On modification, invalidate cached lookups by bumping the generation count. |
278 | * Other calls will need to take the slowpath of taking |
279 | * the subsystem lock. |
280 | */ |
281 | static volatile int32_t necp_kernel_socket_policies_gencount; |
282 | #define BUMP_KERNEL_SOCKET_POLICIES_GENERATION_COUNT() do { \ |
283 | if (OSIncrementAtomic(&necp_kernel_socket_policies_gencount) == (INT32_MAX - 1)) { \ |
284 | necp_kernel_socket_policies_gencount = 1; \ |
285 | } \ |
286 | } while (0) |
287 | |
288 | static u_int32_t necp_kernel_application_policies_condition_mask; |
289 | static size_t necp_kernel_application_policies_count; |
290 | static u_int32_t necp_kernel_socket_policies_condition_mask; |
291 | static size_t necp_kernel_socket_policies_count; |
292 | static size_t necp_kernel_socket_policies_non_app_count; |
293 | static LIST_HEAD(_necpkernelsocketconnectpolicies, necp_kernel_socket_policy) necp_kernel_socket_policies; |
294 | #define NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS 5 |
295 | #define NECP_SOCKET_MAP_APP_ID_TO_BUCKET(appid) (appid ? (appid%(NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS - 1) + 1) : 0) |
296 | static struct necp_kernel_socket_policy **necp_kernel_socket_policies_map[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
297 | static struct necp_kernel_socket_policy **necp_kernel_socket_policies_app_layer_map; |
298 | /* |
299 | * A note on policy 'maps': these are used for boosting efficiency when matching policies. For each dimension of the map, |
300 | * such as an ID, the 0 bucket is reserved for sockets/packets that do not have this parameter, while the other |
301 | * buckets lead to an array of policy pointers that form the list applicable when the (parameter%(NUM_BUCKETS - 1) + 1) == bucket_index. |
302 | * |
303 | * For example, a packet with policy ID of 7, when there are 4 ID buckets, will map to bucket (7%3 + 1) = 2. |
304 | */ |
305 | |
306 | static u_int32_t necp_kernel_ip_output_policies_condition_mask; |
307 | static size_t necp_kernel_ip_output_policies_count; |
308 | static size_t necp_kernel_ip_output_policies_non_id_count; |
309 | static LIST_HEAD(_necpkernelipoutputpolicies, necp_kernel_ip_output_policy) necp_kernel_ip_output_policies; |
310 | #define NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS 5 |
311 | #define NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(id) (id ? (id%(NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS - 1) + 1) : 0) |
312 | static struct necp_kernel_ip_output_policy **necp_kernel_ip_output_policies_map[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
313 | |
314 | static struct necp_session *necp_create_session(void); |
315 | static void necp_delete_session(struct necp_session *session); |
316 | |
317 | static necp_policy_id necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet, |
318 | u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *error); |
319 | static void necp_handle_policy_get(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
320 | static void necp_handle_policy_delete(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
321 | static void necp_handle_policy_apply_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
322 | static void necp_handle_policy_list_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
323 | static void necp_handle_policy_delete_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
324 | static int necp_handle_policy_dump_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, |
325 | user_addr_t out_buffer, size_t out_buffer_length, int offset); |
326 | static void necp_handle_set_session_priority(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
327 | static void necp_handle_lock_session_to_proc(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
328 | static void necp_handle_register_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
329 | static void necp_handle_unregister_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset); |
330 | |
331 | #define MAX_RESULT_STRING_LEN 64 |
332 | static inline const char * necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
333 | |
334 | static struct necp_session_policy *necp_policy_create(struct necp_session *session, necp_policy_order order, u_int8_t *conditions_array, u_int32_t conditions_array_size, u_int8_t *route_rules_array, u_int32_t route_rules_array_size, u_int8_t *result, u_int32_t result_size); |
335 | static struct necp_session_policy *necp_policy_find(struct necp_session *session, necp_policy_id policy_id); |
336 | static bool necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy); |
337 | static bool necp_policy_mark_all_for_deletion(struct necp_session *session); |
338 | static bool necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy); |
339 | static void necp_policy_apply_all(struct necp_session *session); |
340 | |
341 | static necp_kernel_policy_id necp_kernel_socket_policy_add(necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_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 *domain, pid_t cond_pid, uid_t cond_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, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
342 | static bool necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id); |
343 | static bool necp_kernel_socket_policies_reprocess(void); |
344 | static bool necp_kernel_socket_policies_update_uuid_table(void); |
345 | static inline struct necp_kernel_socket_policy *necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, 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, necp_kernel_policy_id *skip_policy_id); |
346 | |
347 | static necp_kernel_policy_id necp_kernel_ip_output_policy_add(necp_policy_order order, necp_policy_order suborder, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_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, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter); |
348 | static bool necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id); |
349 | static bool necp_kernel_ip_output_policies_reprocess(void); |
350 | |
351 | static bool necp_is_addr_in_range(struct sockaddr *addr, struct sockaddr *range_start, struct sockaddr *range_end); |
352 | static bool necp_is_range_in_range(struct sockaddr *inner_range_start, struct sockaddr *inner_range_end, struct sockaddr *range_start, struct sockaddr *range_end); |
353 | static bool necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subnet_addr, u_int8_t subnet_prefix); |
354 | static int necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port); |
355 | static bool necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits); |
356 | static bool necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet); |
357 | static bool necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet); |
358 | |
359 | struct necp_uuid_id_mapping { |
360 | LIST_ENTRY(necp_uuid_id_mapping) chain; |
361 | uuid_t uuid; |
362 | u_int32_t id; |
363 | u_int32_t refcount; |
364 | u_int32_t table_refcount; // Add to UUID policy table count |
365 | }; |
366 | static size_t necp_num_uuid_app_id_mappings; |
367 | static bool necp_uuid_app_id_mappings_dirty; |
368 | #define NECP_UUID_APP_ID_HASH_SIZE 64 |
369 | static u_long necp_uuid_app_id_hash_mask; |
370 | static u_long necp_uuid_app_id_hash_num_buckets; |
371 | static LIST_HEAD(necp_uuid_id_mapping_head, necp_uuid_id_mapping) *necp_uuid_app_id_hashtbl, necp_uuid_service_id_list; // App map is real hash table, service map is just mapping |
372 | #define APPUUIDHASH(uuid) (&necp_uuid_app_id_hashtbl[uuid[0] & necp_uuid_app_id_hash_mask]) // Assume first byte of UUIDs are evenly distributed |
373 | static u_int32_t necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table); |
374 | static bool necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table); |
375 | static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id); |
376 | |
377 | static struct necp_uuid_id_mapping *necp_uuid_lookup_service_id_locked(uuid_t uuid); |
378 | static struct necp_uuid_id_mapping *necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id); |
379 | static u_int32_t necp_create_uuid_service_id_mapping(uuid_t uuid); |
380 | static bool necp_remove_uuid_service_id_mapping(uuid_t uuid); |
381 | |
382 | struct necp_string_id_mapping { |
383 | LIST_ENTRY(necp_string_id_mapping) chain; |
384 | char *string; |
385 | necp_app_id id; |
386 | u_int32_t refcount; |
387 | }; |
388 | static LIST_HEAD(necp_string_id_mapping_list, necp_string_id_mapping) necp_account_id_list; |
389 | static u_int32_t necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain); |
390 | static bool necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *domain); |
391 | static struct necp_string_id_mapping *necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id); |
392 | |
393 | static struct necp_kernel_socket_policy *necp_kernel_socket_policy_find(necp_kernel_policy_id policy_id); |
394 | static struct necp_kernel_ip_output_policy *necp_kernel_ip_output_policy_find(necp_kernel_policy_id policy_id); |
395 | |
396 | static LIST_HEAD(_necp_kernel_service_list, necp_service_registration) necp_registered_service_list; |
397 | |
398 | static char *necp_create_trimmed_domain(char *string, size_t length); |
399 | static inline int necp_count_dots(char *string, size_t length); |
400 | |
401 | static char *necp_copy_string(char *string, size_t length); |
402 | static bool necp_update_qos_marking(struct ifnet *ifp, u_int32_t route_rule_id); |
403 | |
404 | #define ROUTE_RULE_IS_AGGREGATE(ruleid) (ruleid > UINT16_MAX) |
405 | |
406 | #define MAX_ROUTE_RULE_INTERFACES 10 |
407 | struct necp_route_rule { |
408 | LIST_ENTRY(necp_route_rule) chain; |
409 | u_int32_t id; |
410 | u_int32_t default_action; |
411 | u_int8_t cellular_action; |
412 | u_int8_t wifi_action; |
413 | u_int8_t wired_action; |
414 | u_int8_t expensive_action; |
415 | u_int exception_if_indices[MAX_ROUTE_RULE_INTERFACES]; |
416 | u_int8_t exception_if_actions[MAX_ROUTE_RULE_INTERFACES]; |
417 | u_int32_t refcount; |
418 | }; |
419 | static LIST_HEAD(necp_route_rule_list, necp_route_rule) necp_route_rules; |
420 | static u_int32_t necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_array, u_int32_t route_rules_array_size); |
421 | static bool necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id); |
422 | static bool necp_route_is_allowed(struct rtentry *route, ifnet_t interface, u_int32_t route_rule_id, u_int32_t *interface_type_denied); |
423 | static struct necp_route_rule *necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id); |
424 | static inline void necp_get_parent_cred_result(proc_t proc, struct necp_socket_info *info); |
425 | |
426 | #define MAX_AGGREGATE_ROUTE_RULES 16 |
427 | struct necp_aggregate_route_rule { |
428 | LIST_ENTRY(necp_aggregate_route_rule) chain; |
429 | u_int32_t id; |
430 | u_int32_t rule_ids[MAX_AGGREGATE_ROUTE_RULES]; |
431 | }; |
432 | static LIST_HEAD(necp_aggregate_route_rule_list, necp_aggregate_route_rule) necp_aggregate_route_rules; |
433 | static u_int32_t necp_create_aggregate_route_rule(u_int32_t *rule_ids); |
434 | |
435 | // Sysctl definitions |
436 | static int sysctl_handle_necp_level SYSCTL_HANDLER_ARGS; |
437 | |
438 | SYSCTL_NODE(_net, OID_AUTO, necp, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "NECP" ); |
439 | SYSCTL_INT(_net_necp, NECPCTL_PASS_LOOPBACK, pass_loopback, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_loopback, 0, "" ); |
440 | SYSCTL_INT(_net_necp, NECPCTL_PASS_KEEPALIVES, pass_keepalives, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_pass_keepalives, 0, "" ); |
441 | SYSCTL_INT(_net_necp, NECPCTL_DEBUG, debug, CTLFLAG_LOCKED | CTLFLAG_RW, &necp_debug, 0, "" ); |
442 | SYSCTL_PROC(_net_necp, NECPCTL_DROP_ALL_LEVEL, drop_all_level, CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW, &necp_drop_all_level, 0, &sysctl_handle_necp_level, "IU" , "" ); |
443 | SYSCTL_LONG(_net_necp, NECPCTL_SOCKET_POLICY_COUNT, socket_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_socket_policies_count, "" ); |
444 | SYSCTL_LONG(_net_necp, NECPCTL_SOCKET_NON_APP_POLICY_COUNT, socket_non_app_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_socket_policies_non_app_count, "" ); |
445 | SYSCTL_LONG(_net_necp, NECPCTL_IP_POLICY_COUNT, ip_policy_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_kernel_ip_output_policies_count, "" ); |
446 | SYSCTL_INT(_net_necp, NECPCTL_SESSION_COUNT, session_count, CTLFLAG_LOCKED | CTLFLAG_RD, &necp_session_count, 0, "" ); |
447 | |
448 | // Session order allocation |
449 | static u_int32_t |
450 | necp_allocate_new_session_order(u_int32_t priority, u_int32_t control_unit) |
451 | { |
452 | u_int32_t new_order = 0; |
453 | |
454 | // For now, just allocate 1000 orders for each priority |
455 | if (priority == NECP_SESSION_PRIORITY_UNKNOWN || priority > NECP_SESSION_NUM_PRIORITIES) { |
456 | priority = NECP_SESSION_PRIORITY_DEFAULT; |
457 | } |
458 | |
459 | // Use the control unit to decide the offset into the priority list |
460 | new_order = (control_unit) + ((priority - 1) * 1000); |
461 | |
462 | return (new_order); |
463 | } |
464 | |
465 | static inline u_int32_t |
466 | necp_get_first_order_for_priority(u_int32_t priority) |
467 | { |
468 | return (((priority - 1) * 1000) + 1); |
469 | } |
470 | |
471 | // Sysctl handler |
472 | static int |
473 | sysctl_handle_necp_level SYSCTL_HANDLER_ARGS |
474 | { |
475 | #pragma unused(arg1, arg2) |
476 | int error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); |
477 | if (necp_drop_all_level == 0) { |
478 | necp_drop_all_order = 0; |
479 | } else { |
480 | necp_drop_all_order = necp_get_first_order_for_priority(necp_drop_all_level); |
481 | } |
482 | return (error); |
483 | } |
484 | |
485 | // Session fd |
486 | |
487 | static int noop_read(struct fileproc *, struct uio *, int, vfs_context_t); |
488 | static int noop_write(struct fileproc *, struct uio *, int, vfs_context_t); |
489 | static int noop_ioctl(struct fileproc *, unsigned long, caddr_t, |
490 | vfs_context_t); |
491 | static int noop_select(struct fileproc *, int, void *, vfs_context_t); |
492 | static int necp_session_op_close(struct fileglob *, vfs_context_t); |
493 | static int noop_kqfilter(struct fileproc *, struct knote *, |
494 | struct kevent_internal_s *, vfs_context_t); |
495 | |
496 | static const struct fileops necp_session_fd_ops = { |
497 | .fo_type = DTYPE_NETPOLICY, |
498 | .fo_read = noop_read, |
499 | .fo_write = noop_write, |
500 | .fo_ioctl = noop_ioctl, |
501 | .fo_select = noop_select, |
502 | .fo_close = necp_session_op_close, |
503 | .fo_kqfilter = noop_kqfilter, |
504 | .fo_drain = NULL, |
505 | }; |
506 | |
507 | static int |
508 | noop_read(struct fileproc *fp, struct uio *uio, int flags, vfs_context_t ctx) |
509 | { |
510 | #pragma unused(fp, uio, flags, ctx) |
511 | return (ENXIO); |
512 | } |
513 | |
514 | static int |
515 | noop_write(struct fileproc *fp, struct uio *uio, int flags, |
516 | vfs_context_t ctx) |
517 | { |
518 | #pragma unused(fp, uio, flags, ctx) |
519 | return (ENXIO); |
520 | } |
521 | |
522 | static int |
523 | noop_ioctl(struct fileproc *fp, unsigned long com, caddr_t data, |
524 | vfs_context_t ctx) |
525 | { |
526 | #pragma unused(fp, com, data, ctx) |
527 | return (ENOTTY); |
528 | } |
529 | |
530 | static int |
531 | noop_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx) |
532 | { |
533 | #pragma unused(fp, which, wql, ctx) |
534 | return (ENXIO); |
535 | } |
536 | |
537 | static int |
538 | noop_kqfilter(struct fileproc *fp, struct knote *kn, |
539 | struct kevent_internal_s *kev, vfs_context_t ctx) |
540 | { |
541 | #pragma unused(fp, kn, kev, ctx) |
542 | return (ENXIO); |
543 | } |
544 | |
545 | int |
546 | necp_session_open(struct proc *p, struct necp_session_open_args *uap, int *retval) |
547 | { |
548 | #pragma unused(uap) |
549 | int error = 0; |
550 | struct necp_session *session = NULL; |
551 | struct fileproc *fp = NULL; |
552 | int fd = -1; |
553 | |
554 | uid_t uid = kauth_cred_getuid(proc_ucred(p)); |
555 | if (uid != 0 && priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0) != 0) { |
556 | NECPLOG0(LOG_ERR, "Process does not hold necessary entitlement to open NECP session" ); |
557 | error = EACCES; |
558 | goto done; |
559 | } |
560 | |
561 | error = falloc(p, &fp, &fd, vfs_context_current()); |
562 | if (error != 0) { |
563 | goto done; |
564 | } |
565 | |
566 | session = necp_create_session(); |
567 | if (session == NULL) { |
568 | error = ENOMEM; |
569 | goto done; |
570 | } |
571 | |
572 | fp->f_fglob->fg_flag = 0; |
573 | fp->f_fglob->fg_ops = &necp_session_fd_ops; |
574 | fp->f_fglob->fg_data = session; |
575 | |
576 | proc_fdlock(p); |
577 | FDFLAGS_SET(p, fd, (UF_EXCLOSE | UF_FORKCLOSE)); |
578 | procfdtbl_releasefd(p, fd, NULL); |
579 | fp_drop(p, fd, fp, 1); |
580 | proc_fdunlock(p); |
581 | |
582 | *retval = fd; |
583 | done: |
584 | if (error != 0) { |
585 | if (fp != NULL) { |
586 | fp_free(p, fd, fp); |
587 | fp = NULL; |
588 | } |
589 | } |
590 | |
591 | return (error); |
592 | } |
593 | |
594 | static int |
595 | necp_session_op_close(struct fileglob *fg, vfs_context_t ctx) |
596 | { |
597 | #pragma unused(ctx) |
598 | struct necp_session *session = (struct necp_session *)fg->fg_data; |
599 | fg->fg_data = NULL; |
600 | |
601 | if (session != NULL) { |
602 | necp_policy_mark_all_for_deletion(session); |
603 | necp_policy_apply_all(session); |
604 | necp_delete_session(session); |
605 | return (0); |
606 | } else { |
607 | return (ENOENT); |
608 | } |
609 | } |
610 | |
611 | static int |
612 | necp_session_find_from_fd(int fd, struct necp_session **session) |
613 | { |
614 | proc_t p = current_proc(); |
615 | struct fileproc *fp = NULL; |
616 | int error = 0; |
617 | |
618 | proc_fdlock_spin(p); |
619 | if ((error = fp_lookup(p, fd, &fp, 1)) != 0) { |
620 | goto done; |
621 | } |
622 | if (fp->f_fglob->fg_ops->fo_type != DTYPE_NETPOLICY) { |
623 | fp_drop(p, fd, fp, 1); |
624 | error = ENODEV; |
625 | goto done; |
626 | } |
627 | *session = (struct necp_session *)fp->f_fglob->fg_data; |
628 | |
629 | if ((*session)->necp_fd_type != necp_fd_type_session) { |
630 | // Not a client fd, ignore |
631 | error = EINVAL; |
632 | goto done; |
633 | } |
634 | |
635 | done: |
636 | proc_fdunlock(p); |
637 | return (error); |
638 | } |
639 | |
640 | static int |
641 | necp_session_add_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
642 | { |
643 | int error = 0; |
644 | u_int8_t *tlv_buffer = NULL; |
645 | |
646 | if (uap->in_buffer_length == 0 || uap->in_buffer_length > NECP_MAX_POLICY_SIZE || uap->in_buffer == 0) { |
647 | NECPLOG(LOG_ERR, "necp_session_add_policy invalid input (%zu)" , uap->in_buffer_length); |
648 | error = EINVAL; |
649 | goto done; |
650 | } |
651 | |
652 | if (uap->out_buffer_length < sizeof(necp_policy_id) || uap->out_buffer == 0) { |
653 | NECPLOG(LOG_ERR, "necp_session_add_policy invalid output buffer (%zu)" , uap->out_buffer_length); |
654 | error = EINVAL; |
655 | goto done; |
656 | } |
657 | |
658 | if ((tlv_buffer = _MALLOC(uap->in_buffer_length, M_NECP, M_WAITOK | M_ZERO)) == NULL) { |
659 | error = ENOMEM; |
660 | goto done; |
661 | } |
662 | |
663 | error = copyin(uap->in_buffer, tlv_buffer, uap->in_buffer_length); |
664 | if (error != 0) { |
665 | NECPLOG(LOG_ERR, "necp_session_add_policy tlv copyin error (%d)" , error); |
666 | goto done; |
667 | } |
668 | |
669 | necp_policy_id new_policy_id = necp_handle_policy_add(session, 0, NULL, tlv_buffer, uap->in_buffer_length, 0, &error); |
670 | if (error != 0) { |
671 | NECPLOG(LOG_ERR, "necp_session_add_policy failed to add policy (%d)" , error); |
672 | goto done; |
673 | } |
674 | |
675 | error = copyout(&new_policy_id, uap->out_buffer, sizeof(new_policy_id)); |
676 | if (error != 0) { |
677 | NECPLOG(LOG_ERR, "necp_session_add_policy policy_id copyout error (%d)" , error); |
678 | goto done; |
679 | } |
680 | |
681 | done: |
682 | if (tlv_buffer != NULL) { |
683 | FREE(tlv_buffer, M_NECP); |
684 | tlv_buffer = NULL; |
685 | } |
686 | *retval = error; |
687 | |
688 | return (error); |
689 | } |
690 | |
691 | static int |
692 | necp_session_get_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
693 | { |
694 | int error = 0; |
695 | u_int8_t *response = NULL; |
696 | |
697 | if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) { |
698 | NECPLOG(LOG_ERR, "necp_session_get_policy invalid input (%zu)" , uap->in_buffer_length); |
699 | error = EINVAL; |
700 | goto done; |
701 | } |
702 | |
703 | necp_policy_id policy_id = 0; |
704 | error = copyin(uap->in_buffer, &policy_id, sizeof(policy_id)); |
705 | if (error != 0) { |
706 | NECPLOG(LOG_ERR, "necp_session_get_policy policy_id copyin error (%d)" , error); |
707 | goto done; |
708 | } |
709 | |
710 | struct necp_session_policy *policy = necp_policy_find(session, policy_id); |
711 | if (policy == NULL || policy->pending_deletion) { |
712 | NECPLOG(LOG_ERR, "Failed to find policy with id %d" , policy_id); |
713 | error = ENOENT; |
714 | goto done; |
715 | } |
716 | |
717 | u_int32_t order_tlv_size = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_order); |
718 | u_int32_t result_tlv_size = (policy->result_size ? (sizeof(u_int8_t) + sizeof(u_int32_t) + policy->result_size) : 0); |
719 | u_int32_t response_size = order_tlv_size + result_tlv_size + policy->conditions_size; |
720 | |
721 | if (uap->out_buffer_length < response_size || uap->out_buffer == 0) { |
722 | NECPLOG(LOG_ERR, "necp_session_get_policy buffer not large enough (%u < %u)" , uap->out_buffer_length, response_size); |
723 | error = EINVAL; |
724 | goto done; |
725 | } |
726 | |
727 | if (response_size > NECP_MAX_POLICY_SIZE) { |
728 | NECPLOG(LOG_ERR, "necp_session_get_policy size too large to copy (%u)" , response_size); |
729 | error = EINVAL; |
730 | goto done; |
731 | } |
732 | |
733 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK | M_ZERO); |
734 | if (response == NULL) { |
735 | error = ENOMEM; |
736 | goto done; |
737 | } |
738 | |
739 | u_int8_t *cursor = response; |
740 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, response, response_size); |
741 | if (result_tlv_size) { |
742 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, policy->result_size, &policy->result, response, response_size); |
743 | } |
744 | if (policy->conditions_size) { |
745 | memcpy(((u_int8_t *)(void *)(cursor)), policy->conditions, policy->conditions_size); |
746 | } |
747 | |
748 | error = copyout(response, uap->out_buffer, response_size); |
749 | if (error != 0) { |
750 | NECPLOG(LOG_ERR, "necp_session_get_policy TLV copyout error (%d)" , error); |
751 | goto done; |
752 | } |
753 | |
754 | done: |
755 | if (response != NULL) { |
756 | FREE(response, M_NECP); |
757 | response = NULL; |
758 | } |
759 | *retval = error; |
760 | |
761 | return (error); |
762 | } |
763 | |
764 | static int |
765 | necp_session_delete_policy(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
766 | { |
767 | int error = 0; |
768 | |
769 | if (uap->in_buffer_length < sizeof(necp_policy_id) || uap->in_buffer == 0) { |
770 | NECPLOG(LOG_ERR, "necp_session_delete_policy invalid input (%zu)" , uap->in_buffer_length); |
771 | error = EINVAL; |
772 | goto done; |
773 | } |
774 | |
775 | necp_policy_id delete_policy_id = 0; |
776 | error = copyin(uap->in_buffer, &delete_policy_id, sizeof(delete_policy_id)); |
777 | if (error != 0) { |
778 | NECPLOG(LOG_ERR, "necp_session_delete_policy policy_id copyin error (%d)" , error); |
779 | goto done; |
780 | } |
781 | |
782 | struct necp_session_policy *policy = necp_policy_find(session, delete_policy_id); |
783 | if (policy == NULL || policy->pending_deletion) { |
784 | NECPLOG(LOG_ERR, "necp_session_delete_policy failed to find policy with id %u" , delete_policy_id); |
785 | error = ENOENT; |
786 | goto done; |
787 | } |
788 | |
789 | necp_policy_mark_for_deletion(session, policy); |
790 | done: |
791 | *retval = error; |
792 | return (error); |
793 | } |
794 | |
795 | static int |
796 | necp_session_apply_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
797 | { |
798 | #pragma unused(uap) |
799 | necp_policy_apply_all(session); |
800 | *retval = 0; |
801 | return (0); |
802 | } |
803 | |
804 | static int |
805 | necp_session_list_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
806 | { |
807 | u_int32_t tlv_size = (sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_id)); |
808 | u_int32_t response_size = 0; |
809 | u_int8_t *response = NULL; |
810 | int num_policies = 0; |
811 | int cur_policy_index = 0; |
812 | int error = 0; |
813 | struct necp_session_policy *policy; |
814 | |
815 | LIST_FOREACH(policy, &session->policies, chain) { |
816 | if (!policy->pending_deletion) { |
817 | num_policies++; |
818 | } |
819 | } |
820 | |
821 | if (num_policies > NECP_MAX_POLICY_LIST_COUNT) { |
822 | NECPLOG(LOG_ERR, "necp_session_list_all size too large to copy (%u policies)" , num_policies); |
823 | error = EINVAL; |
824 | goto done; |
825 | } |
826 | |
827 | response_size = num_policies * tlv_size; |
828 | if (uap->out_buffer_length < response_size || uap->out_buffer == 0) { |
829 | NECPLOG(LOG_ERR, "necp_session_list_all buffer not large enough (%u < %u)" , uap->out_buffer_length, response_size); |
830 | error = EINVAL; |
831 | goto done; |
832 | } |
833 | |
834 | // Create a response with one Policy ID TLV for each policy |
835 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK | M_ZERO); |
836 | if (response == NULL) { |
837 | error = ENOMEM; |
838 | goto done; |
839 | } |
840 | |
841 | u_int8_t *cursor = response; |
842 | LIST_FOREACH(policy, &session->policies, chain) { |
843 | if (!policy->pending_deletion && cur_policy_index < num_policies) { |
844 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(u_int32_t), &policy->local_id, response, response_size); |
845 | cur_policy_index++; |
846 | } |
847 | } |
848 | |
849 | error = copyout(response, uap->out_buffer, response_size); |
850 | if (error != 0) { |
851 | NECPLOG(LOG_ERR, "necp_session_list_all TLV copyout error (%d)" , error); |
852 | goto done; |
853 | } |
854 | |
855 | done: |
856 | if (response != NULL) { |
857 | FREE(response, M_NECP); |
858 | response = NULL; |
859 | } |
860 | *retval = error; |
861 | |
862 | return (error); |
863 | } |
864 | |
865 | |
866 | static int |
867 | necp_session_delete_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
868 | { |
869 | #pragma unused(uap) |
870 | necp_policy_mark_all_for_deletion(session); |
871 | *retval = 0; |
872 | return (0); |
873 | } |
874 | |
875 | static int |
876 | necp_session_set_session_priority(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
877 | { |
878 | int error = 0; |
879 | struct necp_session_policy *policy = NULL; |
880 | struct necp_session_policy *temp_policy = NULL; |
881 | |
882 | if (uap->in_buffer_length < sizeof(necp_session_priority) || uap->in_buffer == 0) { |
883 | NECPLOG(LOG_ERR, "necp_session_set_session_priority invalid input (%zu)" , uap->in_buffer_length); |
884 | error = EINVAL; |
885 | goto done; |
886 | } |
887 | |
888 | necp_session_priority requested_session_priority = 0; |
889 | error = copyin(uap->in_buffer, &requested_session_priority, sizeof(requested_session_priority)); |
890 | if (error != 0) { |
891 | NECPLOG(LOG_ERR, "necp_session_set_session_priority priority copyin error (%d)" , error); |
892 | goto done; |
893 | } |
894 | |
895 | // Enforce special session priorities with entitlements |
896 | if (requested_session_priority == NECP_SESSION_PRIORITY_CONTROL || |
897 | requested_session_priority == NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL) { |
898 | errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0); |
899 | if (cred_result != 0) { |
900 | NECPLOG(LOG_ERR, "Session does not hold necessary entitlement to claim priority level %d" , requested_session_priority); |
901 | error = EPERM; |
902 | goto done; |
903 | } |
904 | } |
905 | |
906 | if (session->session_priority != requested_session_priority) { |
907 | session->session_priority = requested_session_priority; |
908 | session->session_order = necp_allocate_new_session_order(session->session_priority, session->control_unit); |
909 | session->dirty = TRUE; |
910 | |
911 | // Mark all policies as needing updates |
912 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
913 | policy->pending_update = TRUE; |
914 | } |
915 | } |
916 | |
917 | done: |
918 | *retval = error; |
919 | return (error); |
920 | } |
921 | |
922 | static int |
923 | necp_session_lock_to_process(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
924 | { |
925 | #pragma unused(uap) |
926 | session->proc_locked = TRUE; |
927 | *retval = 0; |
928 | return (0); |
929 | } |
930 | |
931 | static int |
932 | necp_session_register_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
933 | { |
934 | int error = 0; |
935 | struct necp_service_registration *new_service = NULL; |
936 | |
937 | if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) { |
938 | NECPLOG(LOG_ERR, "necp_session_register_service invalid input (%zu)" , uap->in_buffer_length); |
939 | error = EINVAL; |
940 | goto done; |
941 | } |
942 | |
943 | uuid_t service_uuid; |
944 | error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid)); |
945 | if (error != 0) { |
946 | NECPLOG(LOG_ERR, "necp_session_register_service uuid copyin error (%d)" , error); |
947 | goto done; |
948 | } |
949 | |
950 | MALLOC(new_service, struct necp_service_registration *, sizeof(*new_service), M_NECP, M_WAITOK | M_ZERO); |
951 | if (new_service == NULL) { |
952 | NECPLOG0(LOG_ERR, "Failed to allocate service registration" ); |
953 | error = ENOMEM; |
954 | goto done; |
955 | } |
956 | |
957 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
958 | new_service->service_id = necp_create_uuid_service_id_mapping(service_uuid); |
959 | LIST_INSERT_HEAD(&session->services, new_service, session_chain); |
960 | LIST_INSERT_HEAD(&necp_registered_service_list, new_service, kernel_chain); |
961 | lck_rw_done(&necp_kernel_policy_lock); |
962 | |
963 | done: |
964 | *retval = error; |
965 | return (error); |
966 | } |
967 | |
968 | static int |
969 | necp_session_unregister_service(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
970 | { |
971 | int error = 0; |
972 | struct necp_service_registration *service = NULL; |
973 | struct necp_service_registration *temp_service = NULL; |
974 | struct necp_uuid_id_mapping *mapping = NULL; |
975 | |
976 | if (uap->in_buffer_length < sizeof(uuid_t) || uap->in_buffer == 0) { |
977 | NECPLOG(LOG_ERR, "necp_session_unregister_service invalid input (%zu)" , uap->in_buffer_length); |
978 | error = EINVAL; |
979 | goto done; |
980 | } |
981 | |
982 | uuid_t service_uuid; |
983 | error = copyin(uap->in_buffer, service_uuid, sizeof(service_uuid)); |
984 | if (error != 0) { |
985 | NECPLOG(LOG_ERR, "necp_session_unregister_service uuid copyin error (%d)" , error); |
986 | goto done; |
987 | } |
988 | |
989 | // Remove all matching services for this session |
990 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
991 | mapping = necp_uuid_lookup_service_id_locked(service_uuid); |
992 | if (mapping != NULL) { |
993 | LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) { |
994 | if (service->service_id == mapping->id) { |
995 | LIST_REMOVE(service, session_chain); |
996 | LIST_REMOVE(service, kernel_chain); |
997 | FREE(service, M_NECP); |
998 | } |
999 | } |
1000 | necp_remove_uuid_service_id_mapping(service_uuid); |
1001 | } |
1002 | lck_rw_done(&necp_kernel_policy_lock); |
1003 | |
1004 | done: |
1005 | *retval = error; |
1006 | return (error); |
1007 | } |
1008 | |
1009 | static int |
1010 | necp_session_dump_all(struct necp_session *session, struct necp_session_action_args *uap, int *retval) |
1011 | { |
1012 | int error = 0; |
1013 | |
1014 | if (uap->out_buffer_length == 0 || uap->out_buffer == 0) { |
1015 | NECPLOG(LOG_ERR, "necp_session_dump_all invalid output buffer (%zu)" , uap->out_buffer_length); |
1016 | error = EINVAL; |
1017 | goto done; |
1018 | } |
1019 | |
1020 | error = necp_handle_policy_dump_all(session, 0, NULL, uap->out_buffer, uap->out_buffer_length, 0); |
1021 | done: |
1022 | *retval = error; |
1023 | return (error); |
1024 | } |
1025 | |
1026 | int |
1027 | necp_session_action(struct proc *p, struct necp_session_action_args *uap, int *retval) |
1028 | { |
1029 | #pragma unused(p) |
1030 | int error = 0; |
1031 | int return_value = 0; |
1032 | struct necp_session *session = NULL; |
1033 | error = necp_session_find_from_fd(uap->necp_fd, &session); |
1034 | if (error != 0) { |
1035 | NECPLOG(LOG_ERR, "necp_session_action find fd error (%d)" , error); |
1036 | return (error); |
1037 | } |
1038 | |
1039 | NECP_SESSION_LOCK(session); |
1040 | |
1041 | if (session->proc_locked) { |
1042 | // Verify that the calling process is allowed to do actions |
1043 | uuid_t proc_uuid; |
1044 | proc_getexecutableuuid(current_proc(), proc_uuid, sizeof(proc_uuid)); |
1045 | if (uuid_compare(proc_uuid, session->proc_uuid) != 0) { |
1046 | error = EPERM; |
1047 | goto done; |
1048 | } |
1049 | } else { |
1050 | // If not locked, update the proc_uuid and proc_pid of the session |
1051 | proc_getexecutableuuid(current_proc(), session->proc_uuid, sizeof(session->proc_uuid)); |
1052 | session->proc_pid = proc_pid(current_proc()); |
1053 | } |
1054 | |
1055 | u_int32_t action = uap->action; |
1056 | switch (action) { |
1057 | case NECP_SESSION_ACTION_POLICY_ADD: { |
1058 | return_value = necp_session_add_policy(session, uap, retval); |
1059 | break; |
1060 | } |
1061 | case NECP_SESSION_ACTION_POLICY_GET: { |
1062 | return_value = necp_session_get_policy(session, uap, retval); |
1063 | break; |
1064 | } |
1065 | case NECP_SESSION_ACTION_POLICY_DELETE: { |
1066 | return_value = necp_session_delete_policy(session, uap, retval); |
1067 | break; |
1068 | } |
1069 | case NECP_SESSION_ACTION_POLICY_APPLY_ALL: { |
1070 | return_value = necp_session_apply_all(session, uap, retval); |
1071 | break; |
1072 | } |
1073 | case NECP_SESSION_ACTION_POLICY_LIST_ALL: { |
1074 | return_value = necp_session_list_all(session, uap, retval); |
1075 | break; |
1076 | } |
1077 | case NECP_SESSION_ACTION_POLICY_DELETE_ALL: { |
1078 | return_value = necp_session_delete_all(session, uap, retval); |
1079 | break; |
1080 | } |
1081 | case NECP_SESSION_ACTION_SET_SESSION_PRIORITY: { |
1082 | return_value = necp_session_set_session_priority(session, uap, retval); |
1083 | break; |
1084 | } |
1085 | case NECP_SESSION_ACTION_LOCK_SESSION_TO_PROC: { |
1086 | return_value = necp_session_lock_to_process(session, uap, retval); |
1087 | break; |
1088 | } |
1089 | case NECP_SESSION_ACTION_REGISTER_SERVICE: { |
1090 | return_value = necp_session_register_service(session, uap, retval); |
1091 | break; |
1092 | } |
1093 | case NECP_SESSION_ACTION_UNREGISTER_SERVICE: { |
1094 | return_value = necp_session_unregister_service(session, uap, retval); |
1095 | break; |
1096 | } |
1097 | case NECP_SESSION_ACTION_POLICY_DUMP_ALL: { |
1098 | return_value = necp_session_dump_all(session, uap, retval); |
1099 | break; |
1100 | } |
1101 | default: { |
1102 | NECPLOG(LOG_ERR, "necp_session_action unknown action (%u)" , action); |
1103 | return_value = EINVAL; |
1104 | break; |
1105 | } |
1106 | } |
1107 | |
1108 | done: |
1109 | NECP_SESSION_UNLOCK(session); |
1110 | file_drop(uap->necp_fd); |
1111 | |
1112 | return (return_value); |
1113 | } |
1114 | |
1115 | // Kernel Control functions |
1116 | static errno_t necp_register_control(void); |
1117 | static errno_t necp_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, void **unitinfo); |
1118 | static errno_t necp_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo); |
1119 | static errno_t necp_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t m, int flags); |
1120 | static void necp_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int flags); |
1121 | static errno_t necp_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, void *data, size_t *len); |
1122 | static errno_t necp_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, void *data, size_t len); |
1123 | |
1124 | static bool necp_send_ctl_data(struct necp_session *session, u_int8_t *buffer, size_t buffer_size); |
1125 | |
1126 | errno_t |
1127 | necp_init(void) |
1128 | { |
1129 | errno_t result = 0; |
1130 | |
1131 | result = necp_register_control(); |
1132 | if (result != 0) { |
1133 | goto done; |
1134 | } |
1135 | |
1136 | necp_kernel_policy_grp_attr = lck_grp_attr_alloc_init(); |
1137 | if (necp_kernel_policy_grp_attr == NULL) { |
1138 | NECPLOG0(LOG_ERR, "lck_grp_attr_alloc_init failed" ); |
1139 | result = ENOMEM; |
1140 | goto done; |
1141 | } |
1142 | |
1143 | necp_kernel_policy_mtx_grp = lck_grp_alloc_init(NECP_CONTROL_NAME, necp_kernel_policy_grp_attr); |
1144 | if (necp_kernel_policy_mtx_grp == NULL) { |
1145 | NECPLOG0(LOG_ERR, "lck_grp_alloc_init failed" ); |
1146 | result = ENOMEM; |
1147 | goto done; |
1148 | } |
1149 | |
1150 | necp_kernel_policy_mtx_attr = lck_attr_alloc_init(); |
1151 | if (necp_kernel_policy_mtx_attr == NULL) { |
1152 | NECPLOG0(LOG_ERR, "lck_attr_alloc_init failed" ); |
1153 | result = ENOMEM; |
1154 | goto done; |
1155 | } |
1156 | |
1157 | lck_rw_init(&necp_kernel_policy_lock, necp_kernel_policy_mtx_grp, necp_kernel_policy_mtx_attr); |
1158 | |
1159 | necp_route_rule_grp_attr = lck_grp_attr_alloc_init(); |
1160 | if (necp_route_rule_grp_attr == NULL) { |
1161 | NECPLOG0(LOG_ERR, "lck_grp_attr_alloc_init failed" ); |
1162 | result = ENOMEM; |
1163 | goto done; |
1164 | } |
1165 | |
1166 | necp_route_rule_mtx_grp = lck_grp_alloc_init("necp_route_rule" , necp_route_rule_grp_attr); |
1167 | if (necp_route_rule_mtx_grp == NULL) { |
1168 | NECPLOG0(LOG_ERR, "lck_grp_alloc_init failed" ); |
1169 | result = ENOMEM; |
1170 | goto done; |
1171 | } |
1172 | |
1173 | necp_route_rule_mtx_attr = lck_attr_alloc_init(); |
1174 | if (necp_route_rule_mtx_attr == NULL) { |
1175 | NECPLOG0(LOG_ERR, "lck_attr_alloc_init failed" ); |
1176 | result = ENOMEM; |
1177 | goto done; |
1178 | } |
1179 | |
1180 | lck_rw_init(&necp_route_rule_lock, necp_route_rule_mtx_grp, necp_route_rule_mtx_attr); |
1181 | |
1182 | necp_client_init(); |
1183 | |
1184 | TAILQ_INIT(&necp_session_list); |
1185 | |
1186 | LIST_INIT(&necp_kernel_socket_policies); |
1187 | LIST_INIT(&necp_kernel_ip_output_policies); |
1188 | |
1189 | LIST_INIT(&necp_account_id_list); |
1190 | |
1191 | LIST_INIT(&necp_uuid_service_id_list); |
1192 | |
1193 | LIST_INIT(&necp_registered_service_list); |
1194 | |
1195 | LIST_INIT(&necp_route_rules); |
1196 | LIST_INIT(&necp_aggregate_route_rules); |
1197 | |
1198 | necp_uuid_app_id_hashtbl = hashinit(NECP_UUID_APP_ID_HASH_SIZE, M_NECP, &necp_uuid_app_id_hash_mask); |
1199 | necp_uuid_app_id_hash_num_buckets = necp_uuid_app_id_hash_mask + 1; |
1200 | necp_num_uuid_app_id_mappings = 0; |
1201 | necp_uuid_app_id_mappings_dirty = FALSE; |
1202 | |
1203 | necp_kernel_application_policies_condition_mask = 0; |
1204 | necp_kernel_socket_policies_condition_mask = 0; |
1205 | necp_kernel_ip_output_policies_condition_mask = 0; |
1206 | |
1207 | necp_kernel_application_policies_count = 0; |
1208 | necp_kernel_socket_policies_count = 0; |
1209 | necp_kernel_socket_policies_non_app_count = 0; |
1210 | necp_kernel_ip_output_policies_count = 0; |
1211 | necp_kernel_ip_output_policies_non_id_count = 0; |
1212 | |
1213 | necp_kernel_socket_policies_gencount = 1; |
1214 | |
1215 | memset(&necp_kernel_socket_policies_map, 0, sizeof(necp_kernel_socket_policies_map)); |
1216 | memset(&necp_kernel_ip_output_policies_map, 0, sizeof(necp_kernel_ip_output_policies_map)); |
1217 | necp_kernel_socket_policies_app_layer_map = NULL; |
1218 | |
1219 | done: |
1220 | if (result != 0) { |
1221 | if (necp_kernel_policy_mtx_attr != NULL) { |
1222 | lck_attr_free(necp_kernel_policy_mtx_attr); |
1223 | necp_kernel_policy_mtx_attr = NULL; |
1224 | } |
1225 | if (necp_kernel_policy_mtx_grp != NULL) { |
1226 | lck_grp_free(necp_kernel_policy_mtx_grp); |
1227 | necp_kernel_policy_mtx_grp = NULL; |
1228 | } |
1229 | if (necp_kernel_policy_grp_attr != NULL) { |
1230 | lck_grp_attr_free(necp_kernel_policy_grp_attr); |
1231 | necp_kernel_policy_grp_attr = NULL; |
1232 | } |
1233 | if (necp_route_rule_mtx_attr != NULL) { |
1234 | lck_attr_free(necp_route_rule_mtx_attr); |
1235 | necp_route_rule_mtx_attr = NULL; |
1236 | } |
1237 | if (necp_route_rule_mtx_grp != NULL) { |
1238 | lck_grp_free(necp_route_rule_mtx_grp); |
1239 | necp_route_rule_mtx_grp = NULL; |
1240 | } |
1241 | if (necp_route_rule_grp_attr != NULL) { |
1242 | lck_grp_attr_free(necp_route_rule_grp_attr); |
1243 | necp_route_rule_grp_attr = NULL; |
1244 | } |
1245 | if (necp_kctlref != NULL) { |
1246 | ctl_deregister(necp_kctlref); |
1247 | necp_kctlref = NULL; |
1248 | } |
1249 | } |
1250 | return (result); |
1251 | } |
1252 | |
1253 | static errno_t |
1254 | necp_register_control(void) |
1255 | { |
1256 | struct kern_ctl_reg kern_ctl; |
1257 | errno_t result = 0; |
1258 | |
1259 | // Create a tag to allocate memory |
1260 | necp_malloc_tag = OSMalloc_Tagalloc(NECP_CONTROL_NAME, OSMT_DEFAULT); |
1261 | |
1262 | // Find a unique value for our interface family |
1263 | result = mbuf_tag_id_find(NECP_CONTROL_NAME, &necp_family); |
1264 | if (result != 0) { |
1265 | NECPLOG(LOG_ERR, "mbuf_tag_id_find_internal failed: %d" , result); |
1266 | return (result); |
1267 | } |
1268 | |
1269 | bzero(&kern_ctl, sizeof(kern_ctl)); |
1270 | strlcpy(kern_ctl.ctl_name, NECP_CONTROL_NAME, sizeof(kern_ctl.ctl_name)); |
1271 | kern_ctl.ctl_name[sizeof(kern_ctl.ctl_name) - 1] = 0; |
1272 | kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED; // Require root |
1273 | kern_ctl.ctl_sendsize = 64 * 1024; |
1274 | kern_ctl.ctl_recvsize = 64 * 1024; |
1275 | kern_ctl.ctl_connect = necp_ctl_connect; |
1276 | kern_ctl.ctl_disconnect = necp_ctl_disconnect; |
1277 | kern_ctl.ctl_send = necp_ctl_send; |
1278 | kern_ctl.ctl_rcvd = necp_ctl_rcvd; |
1279 | kern_ctl.ctl_setopt = necp_ctl_setopt; |
1280 | kern_ctl.ctl_getopt = necp_ctl_getopt; |
1281 | |
1282 | result = ctl_register(&kern_ctl, &necp_kctlref); |
1283 | if (result != 0) { |
1284 | NECPLOG(LOG_ERR, "ctl_register failed: %d" , result); |
1285 | return (result); |
1286 | } |
1287 | |
1288 | return (0); |
1289 | } |
1290 | |
1291 | static void |
1292 | necp_post_change_event(struct kev_necp_policies_changed_data *necp_event_data) |
1293 | { |
1294 | struct kev_msg ev_msg; |
1295 | memset(&ev_msg, 0, sizeof(ev_msg)); |
1296 | |
1297 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
1298 | ev_msg.kev_class = KEV_NETWORK_CLASS; |
1299 | ev_msg.kev_subclass = KEV_NECP_SUBCLASS; |
1300 | ev_msg.event_code = KEV_NECP_POLICIES_CHANGED; |
1301 | |
1302 | ev_msg.dv[0].data_ptr = necp_event_data; |
1303 | ev_msg.dv[0].data_length = sizeof(necp_event_data->changed_count); |
1304 | ev_msg.dv[1].data_length = 0; |
1305 | |
1306 | kev_post_msg(&ev_msg); |
1307 | } |
1308 | |
1309 | static errno_t |
1310 | necp_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, void **unitinfo) |
1311 | { |
1312 | #pragma unused(kctlref, sac) |
1313 | *unitinfo = necp_create_session(); |
1314 | if (*unitinfo == NULL) { |
1315 | // Could not allocate session |
1316 | return (ENOBUFS); |
1317 | } |
1318 | |
1319 | return (0); |
1320 | } |
1321 | |
1322 | static errno_t |
1323 | necp_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo) |
1324 | { |
1325 | #pragma unused(kctlref, unit) |
1326 | struct necp_session *session = (struct necp_session *)unitinfo; |
1327 | if (session != NULL) { |
1328 | necp_policy_mark_all_for_deletion(session); |
1329 | necp_policy_apply_all(session); |
1330 | necp_delete_session((struct necp_session *)unitinfo); |
1331 | } |
1332 | |
1333 | return (0); |
1334 | } |
1335 | |
1336 | |
1337 | // Message handling |
1338 | static int |
1339 | necp_packet_find_tlv(mbuf_t packet, int offset, u_int8_t type, int *err, int next) |
1340 | { |
1341 | size_t cursor = offset; |
1342 | int error = 0; |
1343 | u_int32_t curr_length; |
1344 | u_int8_t curr_type; |
1345 | |
1346 | *err = 0; |
1347 | |
1348 | do { |
1349 | if (!next) { |
1350 | error = mbuf_copydata(packet, cursor, sizeof(curr_type), &curr_type); |
1351 | if (error) { |
1352 | *err = ENOENT; |
1353 | return (-1); |
1354 | } |
1355 | } else { |
1356 | next = 0; |
1357 | curr_type = NECP_TLV_NIL; |
1358 | } |
1359 | |
1360 | if (curr_type != type) { |
1361 | cursor += sizeof(curr_type); |
1362 | error = mbuf_copydata(packet, cursor, sizeof(curr_length), &curr_length); |
1363 | if (error) { |
1364 | *err = error; |
1365 | return (-1); |
1366 | } |
1367 | cursor += (sizeof(curr_length) + curr_length); |
1368 | } |
1369 | } while (curr_type != type); |
1370 | |
1371 | return (cursor); |
1372 | } |
1373 | |
1374 | static int |
1375 | necp_packet_get_tlv_at_offset(mbuf_t packet, int tlv_offset, u_int32_t buff_len, void *buff, u_int32_t *value_size) |
1376 | { |
1377 | int error = 0; |
1378 | u_int32_t length; |
1379 | |
1380 | if (tlv_offset < 0) { |
1381 | return (EINVAL); |
1382 | } |
1383 | |
1384 | error = mbuf_copydata(packet, tlv_offset + sizeof(u_int8_t), sizeof(length), &length); |
1385 | if (error) { |
1386 | return (error); |
1387 | } |
1388 | |
1389 | u_int32_t total_len = m_length2(packet, NULL); |
1390 | if (total_len < (tlv_offset + sizeof(u_int8_t) + sizeof(length) + length)) { |
1391 | NECPLOG(LOG_ERR, "Got a bad TLV, length (%u) + offset (%d) < total length (%u)" , |
1392 | length, (tlv_offset + sizeof(u_int8_t) + sizeof(length)), total_len); |
1393 | return (EINVAL); |
1394 | } |
1395 | |
1396 | if (value_size != NULL) { |
1397 | *value_size = length; |
1398 | } |
1399 | |
1400 | if (buff != NULL && buff_len > 0) { |
1401 | u_int32_t to_copy = (length < buff_len) ? length : buff_len; |
1402 | error = mbuf_copydata(packet, tlv_offset + sizeof(u_int8_t) + sizeof(length), to_copy, buff); |
1403 | if (error) { |
1404 | return (error); |
1405 | } |
1406 | } |
1407 | |
1408 | return (0); |
1409 | } |
1410 | |
1411 | static u_int8_t * |
1412 | (u_int8_t *buffer, u_int8_t packet_type, u_int8_t flags, u_int32_t message_id) |
1413 | { |
1414 | ((struct necp_packet_header *)(void *)buffer)->packet_type = packet_type; |
1415 | ((struct necp_packet_header *)(void *)buffer)->flags = flags; |
1416 | ((struct necp_packet_header *)(void *)buffer)->message_id = message_id; |
1417 | return (buffer + sizeof(struct necp_packet_header)); |
1418 | } |
1419 | |
1420 | static inline bool |
1421 | necp_buffer_write_tlv_validate(u_int8_t *cursor, u_int8_t type, u_int32_t length, |
1422 | u_int8_t *buffer, u_int32_t buffer_length) |
1423 | { |
1424 | if (cursor < buffer || (uintptr_t)(cursor - buffer) > buffer_length) { |
1425 | NECPLOG0(LOG_ERR, "Cannot write TLV in buffer (invalid cursor)" ); |
1426 | return (false); |
1427 | } |
1428 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1429 | if (next_tlv <= buffer || // make sure the next TLV start doesn't overflow |
1430 | (uintptr_t)(next_tlv - buffer) > buffer_length) { // make sure the next TLV has enough room in buffer |
1431 | NECPLOG(LOG_ERR, "Cannot write TLV in buffer (TLV length %u, buffer length %u)" , |
1432 | length, buffer_length); |
1433 | return (false); |
1434 | } |
1435 | return (true); |
1436 | } |
1437 | |
1438 | u_int8_t * |
1439 | necp_buffer_write_tlv_if_different(u_int8_t *cursor, u_int8_t type, |
1440 | u_int32_t length, const void *value, bool *updated, |
1441 | u_int8_t *buffer, u_int32_t buffer_length) |
1442 | { |
1443 | if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) { |
1444 | return (NULL); |
1445 | } |
1446 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1447 | if (*updated || *(u_int8_t *)(cursor) != type) { |
1448 | *(u_int8_t *)(cursor) = type; |
1449 | *updated = TRUE; |
1450 | } |
1451 | if (*updated || *(u_int32_t *)(void *)(cursor + sizeof(type)) != length) { |
1452 | *(u_int32_t *)(void *)(cursor + sizeof(type)) = length; |
1453 | *updated = TRUE; |
1454 | } |
1455 | if (length > 0) { |
1456 | if (*updated || memcmp((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length) != 0) { |
1457 | memcpy((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length); |
1458 | *updated = TRUE; |
1459 | } |
1460 | } |
1461 | return (next_tlv); |
1462 | } |
1463 | |
1464 | u_int8_t * |
1465 | necp_buffer_write_tlv(u_int8_t *cursor, u_int8_t type, |
1466 | u_int32_t length, const void *value, |
1467 | u_int8_t *buffer, u_int32_t buffer_length) |
1468 | { |
1469 | if (!necp_buffer_write_tlv_validate(cursor, type, length, buffer, buffer_length)) { |
1470 | return (NULL); |
1471 | } |
1472 | u_int8_t *next_tlv = (u_int8_t *)(cursor + sizeof(type) + sizeof(length) + length); |
1473 | *(u_int8_t *)(cursor) = type; |
1474 | *(u_int32_t *)(void *)(cursor + sizeof(type)) = length; |
1475 | if (length > 0) { |
1476 | memcpy((u_int8_t *)(cursor + sizeof(type) + sizeof(length)), value, length); |
1477 | } |
1478 | |
1479 | return (next_tlv); |
1480 | } |
1481 | |
1482 | u_int8_t |
1483 | necp_buffer_get_tlv_type(u_int8_t *buffer, int tlv_offset) |
1484 | { |
1485 | u_int8_t *type = NULL; |
1486 | |
1487 | if (buffer == NULL) { |
1488 | return (0); |
1489 | } |
1490 | |
1491 | type = (u_int8_t *)((u_int8_t *)buffer + tlv_offset); |
1492 | return (type ? *type : 0); |
1493 | } |
1494 | |
1495 | u_int32_t |
1496 | necp_buffer_get_tlv_length(u_int8_t *buffer, int tlv_offset) |
1497 | { |
1498 | u_int32_t *length = NULL; |
1499 | |
1500 | if (buffer == NULL) { |
1501 | return (0); |
1502 | } |
1503 | |
1504 | length = (u_int32_t *)(void *)((u_int8_t *)buffer + tlv_offset + sizeof(u_int8_t)); |
1505 | return (length ? *length : 0); |
1506 | } |
1507 | |
1508 | u_int8_t * |
1509 | necp_buffer_get_tlv_value(u_int8_t *buffer, int tlv_offset, u_int32_t *value_size) |
1510 | { |
1511 | u_int8_t *value = NULL; |
1512 | u_int32_t length = necp_buffer_get_tlv_length(buffer, tlv_offset); |
1513 | if (length == 0) { |
1514 | return (value); |
1515 | } |
1516 | |
1517 | if (value_size) { |
1518 | *value_size = length; |
1519 | } |
1520 | |
1521 | value = (u_int8_t *)((u_int8_t *)buffer + tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t)); |
1522 | return (value); |
1523 | } |
1524 | |
1525 | int |
1526 | necp_buffer_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int next) |
1527 | { |
1528 | if (offset < 0) { |
1529 | return (-1); |
1530 | } |
1531 | int cursor = offset; |
1532 | int next_cursor; |
1533 | u_int32_t curr_length; |
1534 | u_int8_t curr_type; |
1535 | |
1536 | while (TRUE) { |
1537 | if ((((u_int32_t)cursor) + sizeof(curr_type) + sizeof(curr_length)) > buffer_length) { |
1538 | return (-1); |
1539 | } |
1540 | if (!next) { |
1541 | curr_type = necp_buffer_get_tlv_type(buffer, cursor); |
1542 | } else { |
1543 | next = 0; |
1544 | curr_type = NECP_TLV_NIL; |
1545 | } |
1546 | curr_length = necp_buffer_get_tlv_length(buffer, cursor); |
1547 | if (curr_length > buffer_length - ((u_int32_t)cursor + sizeof(curr_type) + sizeof(curr_length))) { |
1548 | return (-1); |
1549 | } |
1550 | |
1551 | next_cursor = (cursor + sizeof(curr_type) + sizeof(curr_length) + curr_length); |
1552 | if (curr_type == type) { |
1553 | // check if entire TLV fits inside buffer |
1554 | if (((u_int32_t)next_cursor) <= buffer_length) { |
1555 | return (cursor); |
1556 | } else { |
1557 | return (-1); |
1558 | } |
1559 | } |
1560 | cursor = next_cursor; |
1561 | } |
1562 | } |
1563 | |
1564 | static int |
1565 | necp_find_tlv(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length, int offset, u_int8_t type, int *err, int next) |
1566 | { |
1567 | int cursor = -1; |
1568 | if (packet != NULL) { |
1569 | cursor = necp_packet_find_tlv(packet, offset, type, err, next); |
1570 | } else if (buffer != NULL) { |
1571 | cursor = necp_buffer_find_tlv(buffer, buffer_length, offset, type, next); |
1572 | } |
1573 | return (cursor); |
1574 | } |
1575 | |
1576 | static int |
1577 | necp_get_tlv_at_offset(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length, |
1578 | int tlv_offset, u_int32_t out_buffer_length, void *out_buffer, u_int32_t *value_size) |
1579 | { |
1580 | if (packet != NULL) { |
1581 | // Handle mbuf parsing |
1582 | return necp_packet_get_tlv_at_offset(packet, tlv_offset, out_buffer_length, out_buffer, value_size); |
1583 | } |
1584 | |
1585 | if (buffer == NULL) { |
1586 | NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset buffer is NULL" ); |
1587 | return (EINVAL); |
1588 | } |
1589 | |
1590 | // Handle buffer parsing |
1591 | |
1592 | // Validate that buffer has enough room for any TLV |
1593 | if (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) > buffer_length) { |
1594 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV (%u < %u)" , |
1595 | buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t)); |
1596 | return (EINVAL); |
1597 | } |
1598 | |
1599 | // Validate that buffer has enough room for this TLV |
1600 | u_int32_t tlv_length = necp_buffer_get_tlv_length(buffer, tlv_offset); |
1601 | if (tlv_length > buffer_length - (tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t))) { |
1602 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset buffer_length is too small for TLV of length %u (%u < %u)" , |
1603 | tlv_length, buffer_length, tlv_offset + sizeof(u_int8_t) + sizeof(u_int32_t) + tlv_length); |
1604 | return (EINVAL); |
1605 | } |
1606 | |
1607 | if (out_buffer != NULL && out_buffer_length > 0) { |
1608 | // Validate that out buffer is large enough for value |
1609 | if (out_buffer_length < tlv_length) { |
1610 | NECPLOG(LOG_ERR, "necp_get_tlv_at_offset out_buffer_length is too small for TLV value (%u < %u)" , |
1611 | out_buffer_length, tlv_length); |
1612 | return (EINVAL); |
1613 | } |
1614 | |
1615 | // Get value pointer |
1616 | u_int8_t *tlv_value = necp_buffer_get_tlv_value(buffer, tlv_offset, NULL); |
1617 | if (tlv_value == NULL) { |
1618 | NECPLOG0(LOG_ERR, "necp_get_tlv_at_offset tlv_value is NULL" ); |
1619 | return (ENOENT); |
1620 | } |
1621 | |
1622 | // Copy value |
1623 | memcpy(out_buffer, tlv_value, tlv_length); |
1624 | } |
1625 | |
1626 | // Copy out length |
1627 | if (value_size != NULL) { |
1628 | *value_size = tlv_length; |
1629 | } |
1630 | |
1631 | return (0); |
1632 | } |
1633 | |
1634 | static int |
1635 | necp_get_tlv(mbuf_t packet, u_int8_t *buffer, u_int32_t buffer_length, |
1636 | int offset, u_int8_t type, u_int32_t buff_len, void *buff, u_int32_t *value_size) |
1637 | { |
1638 | int error = 0; |
1639 | |
1640 | int tlv_offset = necp_find_tlv(packet, buffer, buffer_length, offset, type, &error, 0); |
1641 | if (tlv_offset < 0) { |
1642 | return (error); |
1643 | } |
1644 | |
1645 | return (necp_get_tlv_at_offset(packet, buffer, buffer_length, tlv_offset, buff_len, buff, value_size)); |
1646 | } |
1647 | |
1648 | static bool |
1649 | necp_send_ctl_data(struct necp_session *session, u_int8_t *buffer, size_t buffer_size) |
1650 | { |
1651 | int error; |
1652 | |
1653 | if (necp_kctlref == NULL || session == NULL || buffer == NULL || buffer_size == 0) { |
1654 | return (FALSE); |
1655 | } |
1656 | |
1657 | error = ctl_enqueuedata(necp_kctlref, session->control_unit, buffer, buffer_size, CTL_DATA_EOR); |
1658 | |
1659 | return (error == 0); |
1660 | } |
1661 | |
1662 | static bool |
1663 | necp_send_success_response(struct necp_session *session, u_int8_t packet_type, u_int32_t message_id) |
1664 | { |
1665 | bool success = TRUE; |
1666 | u_int8_t *response = NULL; |
1667 | u_int8_t *cursor = NULL; |
1668 | size_t response_size = sizeof(struct necp_packet_header) + sizeof(u_int8_t) + sizeof(u_int32_t); |
1669 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK); |
1670 | if (response == NULL) { |
1671 | return (FALSE); |
1672 | } |
1673 | cursor = response; |
1674 | cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id); |
1675 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_NIL, 0, NULL, response, response_size); |
1676 | |
1677 | if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) { |
1678 | NECPLOG0(LOG_ERR, "Failed to send response" ); |
1679 | } |
1680 | |
1681 | FREE(response, M_NECP); |
1682 | return (success); |
1683 | } |
1684 | |
1685 | static bool |
1686 | necp_send_error_response(struct necp_session *session, u_int8_t packet_type, u_int32_t message_id, u_int32_t error) |
1687 | { |
1688 | bool success = TRUE; |
1689 | u_int8_t *response = NULL; |
1690 | u_int8_t *cursor = NULL; |
1691 | size_t response_size = sizeof(struct necp_packet_header) + sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(u_int32_t); |
1692 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK); |
1693 | if (response == NULL) { |
1694 | return (FALSE); |
1695 | } |
1696 | cursor = response; |
1697 | cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id); |
1698 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_ERROR, sizeof(error), &error, response, response_size); |
1699 | |
1700 | if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) { |
1701 | NECPLOG0(LOG_ERR, "Failed to send response" ); |
1702 | } |
1703 | |
1704 | FREE(response, M_NECP); |
1705 | return (success); |
1706 | } |
1707 | |
1708 | static bool |
1709 | necp_send_policy_id_response(struct necp_session *session, u_int8_t packet_type, u_int32_t message_id, necp_policy_id policy_id) |
1710 | { |
1711 | bool success = TRUE; |
1712 | u_int8_t *response = NULL; |
1713 | u_int8_t *cursor = NULL; |
1714 | size_t response_size = sizeof(struct necp_packet_header) + sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(u_int32_t); |
1715 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK); |
1716 | if (response == NULL) { |
1717 | return (FALSE); |
1718 | } |
1719 | cursor = response; |
1720 | cursor = necp_buffer_write_packet_header(cursor, packet_type, NECP_PACKET_FLAGS_RESPONSE, message_id); |
1721 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, response, response_size); |
1722 | |
1723 | if (!(success = necp_send_ctl_data(session, (u_int8_t *)response, response_size))) { |
1724 | NECPLOG0(LOG_ERR, "Failed to send response" ); |
1725 | } |
1726 | |
1727 | FREE(response, M_NECP); |
1728 | return (success); |
1729 | } |
1730 | |
1731 | static errno_t |
1732 | necp_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t packet, int flags) |
1733 | { |
1734 | #pragma unused(kctlref, unit, flags) |
1735 | struct necp_session *session = (struct necp_session *)unitinfo; |
1736 | struct necp_packet_header ; |
1737 | int error = 0; |
1738 | |
1739 | if (session == NULL) { |
1740 | NECPLOG0(LOG_ERR, "Got a NULL session" ); |
1741 | error = EINVAL; |
1742 | goto done; |
1743 | } |
1744 | |
1745 | if (mbuf_pkthdr_len(packet) < sizeof(header)) { |
1746 | NECPLOG(LOG_ERR, "Got a bad packet, length (%lu) < sizeof header (%lu)" , mbuf_pkthdr_len(packet), sizeof(header)); |
1747 | error = EINVAL; |
1748 | goto done; |
1749 | } |
1750 | |
1751 | error = mbuf_copydata(packet, 0, sizeof(header), &header); |
1752 | if (error) { |
1753 | NECPLOG(LOG_ERR, "mbuf_copydata failed for the header: %d" , error); |
1754 | error = ENOBUFS; |
1755 | goto done; |
1756 | } |
1757 | |
1758 | if (session->proc_locked) { |
1759 | // Verify that the calling process is allowed to send messages |
1760 | uuid_t proc_uuid; |
1761 | proc_getexecutableuuid(current_proc(), proc_uuid, sizeof(proc_uuid)); |
1762 | if (uuid_compare(proc_uuid, session->proc_uuid) != 0) { |
1763 | necp_send_error_response(session, header.packet_type, header.message_id, NECP_ERROR_INVALID_PROCESS); |
1764 | goto done; |
1765 | } |
1766 | } else { |
1767 | // If not locked, update the proc_uuid and proc_pid of the session |
1768 | proc_getexecutableuuid(current_proc(), session->proc_uuid, sizeof(session->proc_uuid)); |
1769 | session->proc_pid = proc_pid(current_proc()); |
1770 | } |
1771 | |
1772 | switch (header.packet_type) { |
1773 | case NECP_PACKET_TYPE_POLICY_ADD: { |
1774 | necp_handle_policy_add(session, header.message_id, packet, NULL, 0, sizeof(header), NULL); |
1775 | break; |
1776 | } |
1777 | case NECP_PACKET_TYPE_POLICY_GET: { |
1778 | necp_handle_policy_get(session, header.message_id, packet, sizeof(header)); |
1779 | break; |
1780 | } |
1781 | case NECP_PACKET_TYPE_POLICY_DELETE: { |
1782 | necp_handle_policy_delete(session, header.message_id, packet, sizeof(header)); |
1783 | break; |
1784 | } |
1785 | case NECP_PACKET_TYPE_POLICY_APPLY_ALL: { |
1786 | necp_handle_policy_apply_all(session, header.message_id, packet, sizeof(header)); |
1787 | break; |
1788 | } |
1789 | case NECP_PACKET_TYPE_POLICY_LIST_ALL: { |
1790 | necp_handle_policy_list_all(session, header.message_id, packet, sizeof(header)); |
1791 | break; |
1792 | } |
1793 | case NECP_PACKET_TYPE_POLICY_DELETE_ALL: { |
1794 | necp_handle_policy_delete_all(session, header.message_id, packet, sizeof(header)); |
1795 | break; |
1796 | } |
1797 | case NECP_PACKET_TYPE_POLICY_DUMP_ALL: { |
1798 | necp_handle_policy_dump_all(session, header.message_id, packet, 0, 0, sizeof(header)); |
1799 | break; |
1800 | } |
1801 | case NECP_PACKET_TYPE_SET_SESSION_PRIORITY: { |
1802 | necp_handle_set_session_priority(session, header.message_id, packet, sizeof(header)); |
1803 | break; |
1804 | } |
1805 | case NECP_PACKET_TYPE_LOCK_SESSION_TO_PROC: { |
1806 | necp_handle_lock_session_to_proc(session, header.message_id, packet, sizeof(header)); |
1807 | break; |
1808 | } |
1809 | case NECP_PACKET_TYPE_REGISTER_SERVICE: { |
1810 | necp_handle_register_service(session, header.message_id, packet, sizeof(header)); |
1811 | break; |
1812 | } |
1813 | case NECP_PACKET_TYPE_UNREGISTER_SERVICE: { |
1814 | necp_handle_unregister_service(session, header.message_id, packet, sizeof(header)); |
1815 | break; |
1816 | } |
1817 | default: { |
1818 | NECPLOG(LOG_ERR, "Received unknown message type %d" , header.packet_type); |
1819 | necp_send_error_response(session, header.packet_type, header.message_id, NECP_ERROR_UNKNOWN_PACKET_TYPE); |
1820 | break; |
1821 | } |
1822 | } |
1823 | |
1824 | done: |
1825 | mbuf_freem(packet); |
1826 | return (error); |
1827 | } |
1828 | |
1829 | static void |
1830 | necp_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int flags) |
1831 | { |
1832 | #pragma unused(kctlref, unit, unitinfo, flags) |
1833 | return; |
1834 | } |
1835 | |
1836 | static errno_t |
1837 | necp_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, void *data, size_t *len) |
1838 | { |
1839 | #pragma unused(kctlref, unit, unitinfo, opt, data, len) |
1840 | return (0); |
1841 | } |
1842 | |
1843 | static errno_t |
1844 | necp_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int opt, void *data, size_t len) |
1845 | { |
1846 | #pragma unused(kctlref, unit, unitinfo, opt, data, len) |
1847 | return (0); |
1848 | } |
1849 | |
1850 | // Session Management |
1851 | |
1852 | static struct necp_session * |
1853 | necp_create_session(void) |
1854 | { |
1855 | struct necp_session *new_session = NULL; |
1856 | |
1857 | MALLOC(new_session, struct necp_session *, sizeof(*new_session), M_NECP, M_WAITOK | M_ZERO); |
1858 | if (new_session == NULL) { |
1859 | goto done; |
1860 | } |
1861 | |
1862 | new_session->necp_fd_type = necp_fd_type_session; |
1863 | new_session->session_priority = NECP_SESSION_PRIORITY_UNKNOWN; |
1864 | new_session->dirty = FALSE; |
1865 | LIST_INIT(&new_session->policies); |
1866 | lck_mtx_init(&new_session->lock, necp_kernel_policy_mtx_grp, necp_kernel_policy_mtx_attr); |
1867 | |
1868 | // Take the lock |
1869 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
1870 | |
1871 | // Find the next available control unit |
1872 | u_int32_t control_unit = 1; |
1873 | struct necp_session *next_session = NULL; |
1874 | TAILQ_FOREACH(next_session, &necp_session_list, chain) { |
1875 | if (next_session->control_unit > control_unit) { |
1876 | // Found a gap, grab this control unit |
1877 | break; |
1878 | } |
1879 | |
1880 | // Try the next control unit, loop around |
1881 | control_unit = next_session->control_unit + 1; |
1882 | } |
1883 | |
1884 | new_session->control_unit = control_unit; |
1885 | new_session->session_order = necp_allocate_new_session_order(new_session->session_priority, control_unit); |
1886 | |
1887 | if (next_session != NULL) { |
1888 | TAILQ_INSERT_BEFORE(next_session, new_session, chain); |
1889 | } else { |
1890 | TAILQ_INSERT_TAIL(&necp_session_list, new_session, chain); |
1891 | } |
1892 | |
1893 | necp_session_count++; |
1894 | lck_rw_done(&necp_kernel_policy_lock); |
1895 | |
1896 | if (necp_debug) { |
1897 | NECPLOG(LOG_DEBUG, "Created NECP session, control unit %d" , control_unit); |
1898 | } |
1899 | |
1900 | done: |
1901 | return (new_session); |
1902 | } |
1903 | |
1904 | static void |
1905 | necp_delete_session(struct necp_session *session) |
1906 | { |
1907 | if (session != NULL) { |
1908 | struct necp_service_registration *service = NULL; |
1909 | struct necp_service_registration *temp_service = NULL; |
1910 | LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) { |
1911 | LIST_REMOVE(service, session_chain); |
1912 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
1913 | LIST_REMOVE(service, kernel_chain); |
1914 | lck_rw_done(&necp_kernel_policy_lock); |
1915 | FREE(service, M_NECP); |
1916 | } |
1917 | if (necp_debug) { |
1918 | NECPLOG0(LOG_DEBUG, "Deleted NECP session" ); |
1919 | } |
1920 | |
1921 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
1922 | TAILQ_REMOVE(&necp_session_list, session, chain); |
1923 | necp_session_count--; |
1924 | lck_rw_done(&necp_kernel_policy_lock); |
1925 | |
1926 | lck_mtx_destroy(&session->lock, necp_kernel_policy_mtx_grp); |
1927 | FREE(session, M_NECP); |
1928 | } |
1929 | } |
1930 | |
1931 | // Session Policy Management |
1932 | |
1933 | static inline u_int8_t |
1934 | necp_policy_result_get_type_from_buffer(u_int8_t *buffer, u_int32_t length) |
1935 | { |
1936 | return ((buffer && length >= sizeof(u_int8_t)) ? buffer[0] : 0); |
1937 | } |
1938 | |
1939 | static inline u_int32_t |
1940 | necp_policy_result_get_parameter_length_from_buffer(u_int8_t *buffer, u_int32_t length) |
1941 | { |
1942 | return ((buffer && length > sizeof(u_int8_t)) ? (length - sizeof(u_int8_t)) : 0); |
1943 | } |
1944 | |
1945 | static inline u_int8_t * |
1946 | necp_policy_result_get_parameter_pointer_from_buffer(u_int8_t *buffer, u_int32_t length) |
1947 | { |
1948 | return ((buffer && length > sizeof(u_int8_t)) ? (buffer + sizeof(u_int8_t)) : NULL); |
1949 | } |
1950 | |
1951 | static bool |
1952 | necp_policy_result_requires_route_rules(u_int8_t *buffer, u_int32_t length) |
1953 | { |
1954 | u_int8_t type = necp_policy_result_get_type_from_buffer(buffer, length); |
1955 | if (type == NECP_POLICY_RESULT_ROUTE_RULES) { |
1956 | return (TRUE); |
1957 | } |
1958 | return (FALSE); |
1959 | } |
1960 | |
1961 | static inline bool |
1962 | necp_address_is_valid(struct sockaddr *address) |
1963 | { |
1964 | if (address->sa_family == AF_INET) { |
1965 | return (address->sa_len == sizeof(struct sockaddr_in)); |
1966 | } else if (address->sa_family == AF_INET6) { |
1967 | return (address->sa_len == sizeof(struct sockaddr_in6)); |
1968 | } else { |
1969 | return (FALSE); |
1970 | } |
1971 | } |
1972 | |
1973 | static bool |
1974 | necp_policy_result_is_valid(u_int8_t *buffer, u_int32_t length) |
1975 | { |
1976 | bool validated = FALSE; |
1977 | u_int8_t type = necp_policy_result_get_type_from_buffer(buffer, length); |
1978 | u_int32_t parameter_length = necp_policy_result_get_parameter_length_from_buffer(buffer, length); |
1979 | switch (type) { |
1980 | case NECP_POLICY_RESULT_PASS: |
1981 | case NECP_POLICY_RESULT_DROP: |
1982 | case NECP_POLICY_RESULT_ROUTE_RULES: |
1983 | case NECP_POLICY_RESULT_SCOPED_DIRECT: { |
1984 | validated = TRUE; |
1985 | break; |
1986 | } |
1987 | case NECP_POLICY_RESULT_SKIP: |
1988 | case NECP_POLICY_RESULT_SOCKET_DIVERT: |
1989 | case NECP_POLICY_RESULT_SOCKET_FILTER: { |
1990 | if (parameter_length >= sizeof(u_int32_t)) { |
1991 | validated = TRUE; |
1992 | } |
1993 | break; |
1994 | } |
1995 | case NECP_POLICY_RESULT_IP_TUNNEL: { |
1996 | if (parameter_length > sizeof(u_int32_t)) { |
1997 | validated = TRUE; |
1998 | } |
1999 | break; |
2000 | } |
2001 | case NECP_POLICY_RESULT_SOCKET_SCOPED: { |
2002 | if (parameter_length > 0) { |
2003 | validated = TRUE; |
2004 | } |
2005 | break; |
2006 | } |
2007 | case NECP_POLICY_RESULT_TRIGGER: |
2008 | case NECP_POLICY_RESULT_TRIGGER_IF_NEEDED: |
2009 | case NECP_POLICY_RESULT_TRIGGER_SCOPED: |
2010 | case NECP_POLICY_RESULT_NO_TRIGGER_SCOPED: |
2011 | case NECP_POLICY_RESULT_USE_NETAGENT: |
2012 | case NECP_POLICY_RESULT_NETAGENT_SCOPED:{ |
2013 | if (parameter_length >= sizeof(uuid_t)) { |
2014 | validated = TRUE; |
2015 | } |
2016 | break; |
2017 | } |
2018 | default: { |
2019 | validated = FALSE; |
2020 | break; |
2021 | } |
2022 | } |
2023 | |
2024 | if (necp_debug) { |
2025 | NECPLOG(LOG_DEBUG, "Policy result type %d, valid %d" , type, validated); |
2026 | } |
2027 | |
2028 | return (validated); |
2029 | } |
2030 | |
2031 | static inline u_int8_t |
2032 | necp_policy_condition_get_type_from_buffer(u_int8_t *buffer, u_int32_t length) |
2033 | { |
2034 | return ((buffer && length >= sizeof(u_int8_t)) ? buffer[0] : 0); |
2035 | } |
2036 | |
2037 | static inline u_int8_t |
2038 | necp_policy_condition_get_flags_from_buffer(u_int8_t *buffer, u_int32_t length) |
2039 | { |
2040 | return ((buffer && length >= (2 * sizeof(u_int8_t))) ? buffer[1] : 0); |
2041 | } |
2042 | |
2043 | static inline u_int32_t |
2044 | necp_policy_condition_get_value_length_from_buffer(u_int8_t *buffer, u_int32_t length) |
2045 | { |
2046 | return ((buffer && length >= (2 * sizeof(u_int8_t))) ? (length - (2 * sizeof(u_int8_t))) : 0); |
2047 | } |
2048 | |
2049 | static inline u_int8_t * |
2050 | necp_policy_condition_get_value_pointer_from_buffer(u_int8_t *buffer, u_int32_t length) |
2051 | { |
2052 | return ((buffer && length > (2 * sizeof(u_int8_t))) ? (buffer + (2 * sizeof(u_int8_t))) : NULL); |
2053 | } |
2054 | |
2055 | static inline bool |
2056 | necp_policy_condition_is_default(u_int8_t *buffer, u_int32_t length) |
2057 | { |
2058 | return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_DEFAULT); |
2059 | } |
2060 | |
2061 | static inline bool |
2062 | necp_policy_condition_is_application(u_int8_t *buffer, u_int32_t length) |
2063 | { |
2064 | return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_APPLICATION); |
2065 | } |
2066 | |
2067 | static inline bool |
2068 | necp_policy_condition_is_real_application(u_int8_t *buffer, u_int32_t length) |
2069 | { |
2070 | return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_REAL_APPLICATION); |
2071 | } |
2072 | |
2073 | static inline bool |
2074 | necp_policy_condition_requires_application(u_int8_t *buffer, u_int32_t length) |
2075 | { |
2076 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2077 | return (type == NECP_POLICY_CONDITION_REAL_APPLICATION); |
2078 | } |
2079 | |
2080 | static inline bool |
2081 | necp_policy_condition_requires_real_application(u_int8_t *buffer, u_int32_t length) |
2082 | { |
2083 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2084 | return (type == NECP_POLICY_CONDITION_ENTITLEMENT); |
2085 | } |
2086 | |
2087 | static bool |
2088 | necp_policy_condition_is_valid(u_int8_t *buffer, u_int32_t length, u_int8_t policy_result_type) |
2089 | { |
2090 | bool validated = FALSE; |
2091 | bool result_cannot_have_ip_layer = (policy_result_type == NECP_POLICY_RESULT_SOCKET_DIVERT || |
2092 | policy_result_type == NECP_POLICY_RESULT_SOCKET_FILTER || |
2093 | policy_result_type == NECP_POLICY_RESULT_TRIGGER || |
2094 | policy_result_type == NECP_POLICY_RESULT_TRIGGER_IF_NEEDED || |
2095 | policy_result_type == NECP_POLICY_RESULT_TRIGGER_SCOPED || |
2096 | policy_result_type == NECP_POLICY_RESULT_NO_TRIGGER_SCOPED || |
2097 | policy_result_type == NECP_POLICY_RESULT_SOCKET_SCOPED || |
2098 | policy_result_type == NECP_POLICY_RESULT_ROUTE_RULES || |
2099 | policy_result_type == NECP_POLICY_RESULT_USE_NETAGENT || |
2100 | policy_result_type == NECP_POLICY_RESULT_NETAGENT_SCOPED || |
2101 | policy_result_type == NECP_POLICY_RESULT_SCOPED_DIRECT) ? TRUE : FALSE; |
2102 | u_int32_t condition_length = necp_policy_condition_get_value_length_from_buffer(buffer, length); |
2103 | u_int8_t *condition_value = necp_policy_condition_get_value_pointer_from_buffer(buffer, length); |
2104 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2105 | u_int8_t flags = necp_policy_condition_get_flags_from_buffer(buffer, length); |
2106 | switch (type) { |
2107 | case NECP_POLICY_CONDITION_APPLICATION: |
2108 | case NECP_POLICY_CONDITION_REAL_APPLICATION: { |
2109 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE) && |
2110 | condition_length >= sizeof(uuid_t) && |
2111 | condition_value != NULL && |
2112 | !uuid_is_null(condition_value)) { |
2113 | validated = TRUE; |
2114 | } |
2115 | break; |
2116 | } |
2117 | case NECP_POLICY_CONDITION_DOMAIN: |
2118 | case NECP_POLICY_CONDITION_ACCOUNT: |
2119 | case NECP_POLICY_CONDITION_BOUND_INTERFACE: { |
2120 | if (condition_length > 0) { |
2121 | validated = TRUE; |
2122 | } |
2123 | break; |
2124 | } |
2125 | case NECP_POLICY_CONDITION_TRAFFIC_CLASS: { |
2126 | if (condition_length >= sizeof(struct necp_policy_condition_tc_range)) { |
2127 | validated = TRUE; |
2128 | } |
2129 | break; |
2130 | } |
2131 | case NECP_POLICY_CONDITION_DEFAULT: |
2132 | case NECP_POLICY_CONDITION_ALL_INTERFACES: |
2133 | case NECP_POLICY_CONDITION_ENTITLEMENT: { |
2134 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE)) { |
2135 | validated = TRUE; |
2136 | } |
2137 | break; |
2138 | } |
2139 | case NECP_POLICY_CONDITION_IP_PROTOCOL: { |
2140 | if (condition_length >= sizeof(u_int16_t)) { |
2141 | validated = TRUE; |
2142 | } |
2143 | break; |
2144 | } |
2145 | case NECP_POLICY_CONDITION_PID: { |
2146 | if (condition_length >= sizeof(pid_t) && |
2147 | condition_value != NULL && |
2148 | *((pid_t *)(void *)condition_value) != 0) { |
2149 | validated = TRUE; |
2150 | } |
2151 | break; |
2152 | } |
2153 | case NECP_POLICY_CONDITION_UID: { |
2154 | if (condition_length >= sizeof(uid_t)) { |
2155 | validated = TRUE; |
2156 | } |
2157 | break; |
2158 | } |
2159 | case NECP_POLICY_CONDITION_LOCAL_ADDR: |
2160 | case NECP_POLICY_CONDITION_REMOTE_ADDR: { |
2161 | if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr) && |
2162 | necp_address_is_valid(&((struct necp_policy_condition_addr *)(void *)condition_value)->address.sa)) { |
2163 | validated = TRUE; |
2164 | } |
2165 | break; |
2166 | } |
2167 | case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: |
2168 | case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: { |
2169 | if (!result_cannot_have_ip_layer && condition_length >= sizeof(struct necp_policy_condition_addr_range) && |
2170 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->start_address.sa) && |
2171 | necp_address_is_valid(&((struct necp_policy_condition_addr_range *)(void *)condition_value)->end_address.sa)) { |
2172 | validated = TRUE; |
2173 | } |
2174 | break; |
2175 | } |
2176 | case NECP_POLICY_CONDITION_AGENT_TYPE: { |
2177 | if (!(flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE) && |
2178 | condition_length >= sizeof(struct necp_policy_condition_agent_type)) { |
2179 | validated = TRUE; |
2180 | } |
2181 | break; |
2182 | } |
2183 | default: { |
2184 | validated = FALSE; |
2185 | break; |
2186 | } |
2187 | } |
2188 | |
2189 | if (necp_debug) { |
2190 | NECPLOG(LOG_DEBUG, "Policy condition type %d, valid %d" , type, validated); |
2191 | } |
2192 | |
2193 | return (validated); |
2194 | } |
2195 | |
2196 | static bool |
2197 | necp_policy_route_rule_is_default(u_int8_t *buffer, u_int32_t length) |
2198 | { |
2199 | return (necp_policy_condition_get_value_length_from_buffer(buffer, length) == 0 && |
2200 | necp_policy_condition_get_flags_from_buffer(buffer, length) == 0); |
2201 | } |
2202 | |
2203 | static bool |
2204 | necp_policy_route_rule_is_valid(u_int8_t *buffer, u_int32_t length) |
2205 | { |
2206 | bool validated = FALSE; |
2207 | u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length); |
2208 | switch (type) { |
2209 | case NECP_ROUTE_RULE_ALLOW_INTERFACE: { |
2210 | validated = TRUE; |
2211 | break; |
2212 | } |
2213 | case NECP_ROUTE_RULE_DENY_INTERFACE: { |
2214 | validated = TRUE; |
2215 | break; |
2216 | } |
2217 | case NECP_ROUTE_RULE_QOS_MARKING: { |
2218 | validated = TRUE; |
2219 | break; |
2220 | } |
2221 | case NECP_ROUTE_RULE_DENY_LQM_ABORT: { |
2222 | validated = TRUE; |
2223 | break; |
2224 | } |
2225 | default: { |
2226 | validated = FALSE; |
2227 | break; |
2228 | } |
2229 | } |
2230 | |
2231 | if (necp_debug) { |
2232 | NECPLOG(LOG_DEBUG, "Policy route rule type %d, valid %d" , type, validated); |
2233 | } |
2234 | |
2235 | return (validated); |
2236 | } |
2237 | |
2238 | static int |
2239 | necp_get_posix_error_for_necp_error(int response_error) |
2240 | { |
2241 | switch (response_error) { |
2242 | case NECP_ERROR_UNKNOWN_PACKET_TYPE: |
2243 | case NECP_ERROR_INVALID_TLV: |
2244 | case NECP_ERROR_POLICY_RESULT_INVALID: |
2245 | case NECP_ERROR_POLICY_CONDITIONS_INVALID: |
2246 | case NECP_ERROR_ROUTE_RULES_INVALID: { |
2247 | return (EINVAL); |
2248 | } |
2249 | case NECP_ERROR_POLICY_ID_NOT_FOUND: { |
2250 | return (ENOENT); |
2251 | } |
2252 | case NECP_ERROR_INVALID_PROCESS: { |
2253 | return (EPERM); |
2254 | } |
2255 | case NECP_ERROR_INTERNAL: |
2256 | default: { |
2257 | return (ENOMEM); |
2258 | } |
2259 | } |
2260 | } |
2261 | |
2262 | static void |
2263 | necp_handle_set_session_priority(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2264 | { |
2265 | int error; |
2266 | struct necp_session_policy *policy = NULL; |
2267 | struct necp_session_policy *temp_policy = NULL; |
2268 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2269 | u_int32_t requested_session_priority = NECP_SESSION_PRIORITY_UNKNOWN; |
2270 | |
2271 | // Read policy id |
2272 | error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SESSION_PRIORITY, sizeof(requested_session_priority), &requested_session_priority, NULL); |
2273 | if (error) { |
2274 | NECPLOG(LOG_ERR, "Failed to get session priority: %d" , error); |
2275 | response_error = NECP_ERROR_INVALID_TLV; |
2276 | goto fail; |
2277 | } |
2278 | |
2279 | if (session == NULL) { |
2280 | NECPLOG0(LOG_ERR, "Failed to find session" ); |
2281 | response_error = NECP_ERROR_INTERNAL; |
2282 | goto fail; |
2283 | } |
2284 | |
2285 | // Enforce special session priorities with entitlements |
2286 | if (requested_session_priority == NECP_SESSION_PRIORITY_CONTROL || |
2287 | requested_session_priority == NECP_SESSION_PRIORITY_PRIVILEGED_TUNNEL) { |
2288 | errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0); |
2289 | if (cred_result != 0) { |
2290 | NECPLOG(LOG_ERR, "Session does not hold necessary entitlement to claim priority level %d" , requested_session_priority); |
2291 | goto fail; |
2292 | } |
2293 | } |
2294 | |
2295 | if (session->session_priority != requested_session_priority) { |
2296 | session->session_priority = requested_session_priority; |
2297 | session->session_order = necp_allocate_new_session_order(session->session_priority, session->control_unit); |
2298 | session->dirty = TRUE; |
2299 | |
2300 | // Mark all policies as needing updates |
2301 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
2302 | policy->pending_update = TRUE; |
2303 | } |
2304 | } |
2305 | |
2306 | necp_send_success_response(session, NECP_PACKET_TYPE_SET_SESSION_PRIORITY, message_id); |
2307 | return; |
2308 | |
2309 | fail: |
2310 | necp_send_error_response(session, NECP_PACKET_TYPE_SET_SESSION_PRIORITY, message_id, response_error); |
2311 | } |
2312 | |
2313 | static void |
2314 | necp_handle_lock_session_to_proc(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2315 | { |
2316 | #pragma unused(packet, offset) |
2317 | // proc_uuid already filled out |
2318 | session->proc_locked = TRUE; |
2319 | necp_send_success_response(session, NECP_PACKET_TYPE_LOCK_SESSION_TO_PROC, message_id); |
2320 | } |
2321 | |
2322 | static void |
2323 | necp_handle_register_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2324 | { |
2325 | int error; |
2326 | struct necp_service_registration *new_service = NULL; |
2327 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2328 | uuid_t service_uuid; |
2329 | uuid_clear(service_uuid); |
2330 | |
2331 | if (session == NULL) { |
2332 | NECPLOG0(LOG_ERR, "Failed to find session" ); |
2333 | response_error = NECP_ERROR_INTERNAL; |
2334 | goto fail; |
2335 | } |
2336 | |
2337 | // Enforce entitlements |
2338 | errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0); |
2339 | if (cred_result != 0) { |
2340 | NECPLOG0(LOG_ERR, "Session does not hold necessary entitlement to register service" ); |
2341 | goto fail; |
2342 | } |
2343 | |
2344 | // Read service uuid |
2345 | error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL); |
2346 | if (error) { |
2347 | NECPLOG(LOG_ERR, "Failed to get service UUID: %d" , error); |
2348 | response_error = NECP_ERROR_INVALID_TLV; |
2349 | goto fail; |
2350 | } |
2351 | |
2352 | MALLOC(new_service, struct necp_service_registration *, sizeof(*new_service), M_NECP, M_WAITOK); |
2353 | if (new_service == NULL) { |
2354 | NECPLOG0(LOG_ERR, "Failed to allocate service registration" ); |
2355 | response_error = NECP_ERROR_INTERNAL; |
2356 | goto fail; |
2357 | } |
2358 | |
2359 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
2360 | memset(new_service, 0, sizeof(*new_service)); |
2361 | new_service->service_id = necp_create_uuid_service_id_mapping(service_uuid); |
2362 | LIST_INSERT_HEAD(&session->services, new_service, session_chain); |
2363 | LIST_INSERT_HEAD(&necp_registered_service_list, new_service, kernel_chain); |
2364 | lck_rw_done(&necp_kernel_policy_lock); |
2365 | |
2366 | necp_send_success_response(session, NECP_PACKET_TYPE_REGISTER_SERVICE, message_id); |
2367 | return; |
2368 | fail: |
2369 | necp_send_error_response(session, NECP_PACKET_TYPE_REGISTER_SERVICE, message_id, response_error); |
2370 | } |
2371 | |
2372 | static void |
2373 | necp_handle_unregister_service(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2374 | { |
2375 | int error; |
2376 | struct necp_service_registration *service = NULL; |
2377 | struct necp_service_registration *temp_service = NULL; |
2378 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2379 | struct necp_uuid_id_mapping *mapping = NULL; |
2380 | uuid_t service_uuid; |
2381 | uuid_clear(service_uuid); |
2382 | |
2383 | if (session == NULL) { |
2384 | NECPLOG0(LOG_ERR, "Failed to find session" ); |
2385 | response_error = NECP_ERROR_INTERNAL; |
2386 | goto fail; |
2387 | } |
2388 | |
2389 | // Read service uuid |
2390 | error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_SERVICE_UUID, sizeof(uuid_t), service_uuid, NULL); |
2391 | if (error) { |
2392 | NECPLOG(LOG_ERR, "Failed to get service UUID: %d" , error); |
2393 | response_error = NECP_ERROR_INVALID_TLV; |
2394 | goto fail; |
2395 | } |
2396 | |
2397 | // Mark remove all matching services for this session |
2398 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
2399 | mapping = necp_uuid_lookup_service_id_locked(service_uuid); |
2400 | if (mapping != NULL) { |
2401 | LIST_FOREACH_SAFE(service, &session->services, session_chain, temp_service) { |
2402 | if (service->service_id == mapping->id) { |
2403 | LIST_REMOVE(service, session_chain); |
2404 | LIST_REMOVE(service, kernel_chain); |
2405 | FREE(service, M_NECP); |
2406 | } |
2407 | } |
2408 | necp_remove_uuid_service_id_mapping(service_uuid); |
2409 | } |
2410 | lck_rw_done(&necp_kernel_policy_lock); |
2411 | |
2412 | necp_send_success_response(session, NECP_PACKET_TYPE_UNREGISTER_SERVICE, message_id); |
2413 | return; |
2414 | fail: |
2415 | necp_send_error_response(session, NECP_PACKET_TYPE_UNREGISTER_SERVICE, message_id, response_error); |
2416 | } |
2417 | |
2418 | static necp_policy_id |
2419 | necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_t packet, |
2420 | u_int8_t *tlv_buffer, size_t tlv_buffer_length, int offset, int *return_error) |
2421 | { |
2422 | bool has_default_condition = FALSE; |
2423 | bool has_non_default_condition = FALSE; |
2424 | bool has_application_condition = FALSE; |
2425 | bool has_real_application_condition = FALSE; |
2426 | bool requires_application_condition = FALSE; |
2427 | bool requires_real_application_condition = FALSE; |
2428 | u_int8_t *conditions_array = NULL; |
2429 | u_int32_t conditions_array_size = 0; |
2430 | int conditions_array_cursor; |
2431 | |
2432 | bool has_default_route_rule = FALSE; |
2433 | u_int8_t *route_rules_array = NULL; |
2434 | u_int32_t route_rules_array_size = 0; |
2435 | int route_rules_array_cursor; |
2436 | |
2437 | int cursor; |
2438 | int error = 0; |
2439 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2440 | |
2441 | necp_policy_order order = 0; |
2442 | struct necp_session_policy *policy = NULL; |
2443 | u_int8_t *policy_result = NULL; |
2444 | u_int32_t policy_result_size = 0; |
2445 | |
2446 | // Read policy order |
2447 | error = necp_get_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_ORDER, sizeof(order), &order, NULL); |
2448 | if (error) { |
2449 | NECPLOG(LOG_ERR, "Failed to get policy order: %d" , error); |
2450 | response_error = NECP_ERROR_INVALID_TLV; |
2451 | goto fail; |
2452 | } |
2453 | |
2454 | // Read policy result |
2455 | cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_RESULT, &error, 0); |
2456 | error = necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &policy_result_size); |
2457 | if (error || policy_result_size == 0) { |
2458 | NECPLOG(LOG_ERR, "Failed to get policy result length: %d" , error); |
2459 | response_error = NECP_ERROR_INVALID_TLV; |
2460 | goto fail; |
2461 | } |
2462 | if (policy_result_size > NECP_MAX_POLICY_RESULT_SIZE) { |
2463 | NECPLOG(LOG_ERR, "Policy result length too large: %u" , policy_result_size); |
2464 | response_error = NECP_ERROR_INVALID_TLV; |
2465 | goto fail; |
2466 | } |
2467 | MALLOC(policy_result, u_int8_t *, policy_result_size, M_NECP, M_WAITOK); |
2468 | if (policy_result == NULL) { |
2469 | NECPLOG(LOG_ERR, "Failed to allocate a policy result buffer (size %d)" , policy_result_size); |
2470 | response_error = NECP_ERROR_INTERNAL; |
2471 | goto fail; |
2472 | } |
2473 | error = necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, policy_result_size, policy_result, NULL); |
2474 | if (error) { |
2475 | NECPLOG(LOG_ERR, "Failed to get policy result: %d" , error); |
2476 | response_error = NECP_ERROR_POLICY_RESULT_INVALID; |
2477 | goto fail; |
2478 | } |
2479 | if (!necp_policy_result_is_valid(policy_result, policy_result_size)) { |
2480 | NECPLOG0(LOG_ERR, "Failed to validate policy result" ); |
2481 | response_error = NECP_ERROR_POLICY_RESULT_INVALID; |
2482 | goto fail; |
2483 | } |
2484 | |
2485 | if (necp_policy_result_requires_route_rules(policy_result, policy_result_size)) { |
2486 | // Read route rules conditions |
2487 | for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, &error, 0); |
2488 | cursor >= 0; |
2489 | cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) { |
2490 | u_int32_t route_rule_size = 0; |
2491 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &route_rule_size); |
2492 | if (route_rule_size > 0) { |
2493 | route_rules_array_size += (sizeof(u_int8_t) + sizeof(u_int32_t) + route_rule_size); |
2494 | } |
2495 | } |
2496 | |
2497 | if (route_rules_array_size == 0) { |
2498 | NECPLOG0(LOG_ERR, "Failed to get policy route rules" ); |
2499 | response_error = NECP_ERROR_INVALID_TLV; |
2500 | goto fail; |
2501 | } |
2502 | if (route_rules_array_size > NECP_MAX_ROUTE_RULES_ARRAY_SIZE) { |
2503 | NECPLOG(LOG_ERR, "Route rules length too large: %u" , route_rules_array_size); |
2504 | response_error = NECP_ERROR_INVALID_TLV; |
2505 | goto fail; |
2506 | } |
2507 | MALLOC(route_rules_array, u_int8_t *, route_rules_array_size, M_NECP, M_WAITOK); |
2508 | if (route_rules_array == NULL) { |
2509 | NECPLOG(LOG_ERR, "Failed to allocate a policy route rules array (size %d)" , route_rules_array_size); |
2510 | response_error = NECP_ERROR_INTERNAL; |
2511 | goto fail; |
2512 | } |
2513 | |
2514 | route_rules_array_cursor = 0; |
2515 | for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_ROUTE_RULE, &error, 0); |
2516 | cursor >= 0; |
2517 | cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_ROUTE_RULE, &error, 1)) { |
2518 | u_int8_t route_rule_type = NECP_TLV_ROUTE_RULE; |
2519 | u_int32_t route_rule_size = 0; |
2520 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &route_rule_size); |
2521 | if (route_rule_size > 0 && route_rule_size <= (route_rules_array_size - route_rules_array_cursor)) { |
2522 | // Add type |
2523 | memcpy((route_rules_array + route_rules_array_cursor), &route_rule_type, sizeof(route_rule_type)); |
2524 | route_rules_array_cursor += sizeof(route_rule_type); |
2525 | |
2526 | // Add length |
2527 | memcpy((route_rules_array + route_rules_array_cursor), &route_rule_size, sizeof(route_rule_size)); |
2528 | route_rules_array_cursor += sizeof(route_rule_size); |
2529 | |
2530 | // Add value |
2531 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, route_rule_size, (route_rules_array + route_rules_array_cursor), NULL); |
2532 | |
2533 | if (!necp_policy_route_rule_is_valid((route_rules_array + route_rules_array_cursor), route_rule_size)) { |
2534 | NECPLOG0(LOG_ERR, "Failed to validate policy route rule" ); |
2535 | response_error = NECP_ERROR_ROUTE_RULES_INVALID; |
2536 | goto fail; |
2537 | } |
2538 | |
2539 | if (necp_policy_route_rule_is_default((route_rules_array + route_rules_array_cursor), route_rule_size)) { |
2540 | if (has_default_route_rule) { |
2541 | NECPLOG0(LOG_ERR, "Failed to validate route rule; contained multiple default route rules" ); |
2542 | response_error = NECP_ERROR_ROUTE_RULES_INVALID; |
2543 | goto fail; |
2544 | } |
2545 | has_default_route_rule = TRUE; |
2546 | } |
2547 | |
2548 | route_rules_array_cursor += route_rule_size; |
2549 | } |
2550 | } |
2551 | } |
2552 | |
2553 | // Read policy conditions |
2554 | for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, &error, 0); |
2555 | cursor >= 0; |
2556 | cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) { |
2557 | u_int32_t condition_size = 0; |
2558 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &condition_size); |
2559 | |
2560 | if (condition_size > 0) { |
2561 | conditions_array_size += (sizeof(u_int8_t) + sizeof(u_int32_t) + condition_size); |
2562 | } |
2563 | } |
2564 | |
2565 | if (conditions_array_size == 0) { |
2566 | NECPLOG0(LOG_ERR, "Failed to get policy conditions" ); |
2567 | response_error = NECP_ERROR_INVALID_TLV; |
2568 | goto fail; |
2569 | } |
2570 | if (conditions_array_size > NECP_MAX_CONDITIONS_ARRAY_SIZE) { |
2571 | NECPLOG(LOG_ERR, "Conditions length too large: %u" , conditions_array_size); |
2572 | response_error = NECP_ERROR_INVALID_TLV; |
2573 | goto fail; |
2574 | } |
2575 | MALLOC(conditions_array, u_int8_t *, conditions_array_size, M_NECP, M_WAITOK); |
2576 | if (conditions_array == NULL) { |
2577 | NECPLOG(LOG_ERR, "Failed to allocate a policy conditions array (size %d)" , conditions_array_size); |
2578 | response_error = NECP_ERROR_INTERNAL; |
2579 | goto fail; |
2580 | } |
2581 | |
2582 | conditions_array_cursor = 0; |
2583 | for (cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, offset, NECP_TLV_POLICY_CONDITION, &error, 0); |
2584 | cursor >= 0; |
2585 | cursor = necp_find_tlv(packet, tlv_buffer, tlv_buffer_length, cursor, NECP_TLV_POLICY_CONDITION, &error, 1)) { |
2586 | u_int8_t condition_type = NECP_TLV_POLICY_CONDITION; |
2587 | u_int32_t condition_size = 0; |
2588 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, 0, NULL, &condition_size); |
2589 | if (condition_size > 0 && condition_size <= (conditions_array_size - conditions_array_cursor)) { |
2590 | // Add type |
2591 | memcpy((conditions_array + conditions_array_cursor), &condition_type, sizeof(condition_type)); |
2592 | conditions_array_cursor += sizeof(condition_type); |
2593 | |
2594 | // Add length |
2595 | memcpy((conditions_array + conditions_array_cursor), &condition_size, sizeof(condition_size)); |
2596 | conditions_array_cursor += sizeof(condition_size); |
2597 | |
2598 | // Add value |
2599 | necp_get_tlv_at_offset(packet, tlv_buffer, tlv_buffer_length, cursor, condition_size, (conditions_array + conditions_array_cursor), NULL); |
2600 | if (!necp_policy_condition_is_valid((conditions_array + conditions_array_cursor), condition_size, necp_policy_result_get_type_from_buffer(policy_result, policy_result_size))) { |
2601 | NECPLOG0(LOG_ERR, "Failed to validate policy condition" ); |
2602 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2603 | goto fail; |
2604 | } |
2605 | |
2606 | if (necp_policy_condition_is_default((conditions_array + conditions_array_cursor), condition_size)) { |
2607 | has_default_condition = TRUE; |
2608 | } else { |
2609 | has_non_default_condition = TRUE; |
2610 | } |
2611 | if (has_default_condition && has_non_default_condition) { |
2612 | NECPLOG0(LOG_ERR, "Failed to validate conditions; contained default and non-default conditions" ); |
2613 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2614 | goto fail; |
2615 | } |
2616 | |
2617 | if (necp_policy_condition_is_application((conditions_array + conditions_array_cursor), condition_size)) { |
2618 | has_application_condition = TRUE; |
2619 | } |
2620 | |
2621 | if (necp_policy_condition_is_real_application((conditions_array + conditions_array_cursor), condition_size)) { |
2622 | has_real_application_condition = TRUE; |
2623 | } |
2624 | |
2625 | if (necp_policy_condition_requires_application((conditions_array + conditions_array_cursor), condition_size)) { |
2626 | requires_application_condition = TRUE; |
2627 | } |
2628 | |
2629 | if (necp_policy_condition_requires_real_application((conditions_array + conditions_array_cursor), condition_size)) { |
2630 | requires_real_application_condition = TRUE; |
2631 | } |
2632 | |
2633 | conditions_array_cursor += condition_size; |
2634 | } |
2635 | } |
2636 | |
2637 | if (requires_application_condition && !has_application_condition) { |
2638 | NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain application condition" ); |
2639 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2640 | goto fail; |
2641 | } |
2642 | |
2643 | if (requires_real_application_condition && !has_real_application_condition) { |
2644 | NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain real application condition" ); |
2645 | response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID; |
2646 | goto fail; |
2647 | } |
2648 | |
2649 | if ((policy = necp_policy_create(session, order, conditions_array, conditions_array_size, route_rules_array, route_rules_array_size, policy_result, policy_result_size)) == NULL) { |
2650 | response_error = NECP_ERROR_INTERNAL; |
2651 | goto fail; |
2652 | } |
2653 | |
2654 | if (packet != NULL) { |
2655 | necp_send_policy_id_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, policy->local_id); |
2656 | } |
2657 | return (policy->local_id); |
2658 | |
2659 | fail: |
2660 | if (policy_result != NULL) { |
2661 | FREE(policy_result, M_NECP); |
2662 | } |
2663 | if (conditions_array != NULL) { |
2664 | FREE(conditions_array, M_NECP); |
2665 | } |
2666 | if (route_rules_array != NULL) { |
2667 | FREE(route_rules_array, M_NECP); |
2668 | } |
2669 | |
2670 | if (packet != NULL) { |
2671 | necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_ADD, message_id, response_error); |
2672 | } |
2673 | if (return_error != NULL) { |
2674 | *return_error = necp_get_posix_error_for_necp_error(response_error); |
2675 | } |
2676 | return (0); |
2677 | } |
2678 | |
2679 | static void |
2680 | necp_handle_policy_get(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2681 | { |
2682 | #pragma unused(offset) |
2683 | int error; |
2684 | u_int8_t *response = NULL; |
2685 | u_int8_t *cursor = NULL; |
2686 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2687 | necp_policy_id policy_id = 0; |
2688 | u_int32_t order_tlv_size = 0; |
2689 | u_int32_t result_tlv_size = 0; |
2690 | u_int32_t response_size = 0; |
2691 | |
2692 | struct necp_session_policy *policy = NULL; |
2693 | |
2694 | // Read policy id |
2695 | error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL); |
2696 | if (error) { |
2697 | NECPLOG(LOG_ERR, "Failed to get policy id: %d" , error); |
2698 | response_error = NECP_ERROR_INVALID_TLV; |
2699 | goto fail; |
2700 | } |
2701 | |
2702 | policy = necp_policy_find(session, policy_id); |
2703 | if (policy == NULL || policy->pending_deletion) { |
2704 | NECPLOG(LOG_ERR, "Failed to find policy with id %d" , policy_id); |
2705 | response_error = NECP_ERROR_POLICY_ID_NOT_FOUND; |
2706 | goto fail; |
2707 | } |
2708 | |
2709 | order_tlv_size = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(necp_policy_order); |
2710 | result_tlv_size = (policy->result_size ? (sizeof(u_int8_t) + sizeof(u_int32_t) + policy->result_size) : 0); |
2711 | response_size = sizeof(struct necp_packet_header) + order_tlv_size + result_tlv_size + policy->conditions_size; |
2712 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK); |
2713 | if (response == NULL) { |
2714 | necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_GET, message_id, NECP_ERROR_INTERNAL); |
2715 | return; |
2716 | } |
2717 | |
2718 | cursor = response; |
2719 | cursor = necp_buffer_write_packet_header(cursor, NECP_PACKET_TYPE_POLICY_GET, NECP_PACKET_FLAGS_RESPONSE, message_id); |
2720 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, response, response_size); |
2721 | |
2722 | if (result_tlv_size) { |
2723 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT, policy->result_size, &policy->result, response, response_size); |
2724 | } |
2725 | if (policy->conditions_size) { |
2726 | memcpy(((u_int8_t *)(void *)(cursor)), policy->conditions, policy->conditions_size); |
2727 | } |
2728 | |
2729 | if (!necp_send_ctl_data(session, (u_int8_t *)response, response_size)) { |
2730 | NECPLOG0(LOG_ERR, "Failed to send response" ); |
2731 | } |
2732 | |
2733 | FREE(response, M_NECP); |
2734 | return; |
2735 | |
2736 | fail: |
2737 | necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_GET, message_id, response_error); |
2738 | } |
2739 | |
2740 | static void |
2741 | necp_handle_policy_delete(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2742 | { |
2743 | int error; |
2744 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2745 | necp_policy_id policy_id = 0; |
2746 | |
2747 | struct necp_session_policy *policy = NULL; |
2748 | |
2749 | // Read policy id |
2750 | error = necp_get_tlv(packet, NULL, 0, offset, NECP_TLV_POLICY_ID, sizeof(policy_id), &policy_id, NULL); |
2751 | if (error) { |
2752 | NECPLOG(LOG_ERR, "Failed to get policy id: %d" , error); |
2753 | response_error = NECP_ERROR_INVALID_TLV; |
2754 | goto fail; |
2755 | } |
2756 | |
2757 | policy = necp_policy_find(session, policy_id); |
2758 | if (policy == NULL || policy->pending_deletion) { |
2759 | NECPLOG(LOG_ERR, "Failed to find policy with id %d" , policy_id); |
2760 | response_error = NECP_ERROR_POLICY_ID_NOT_FOUND; |
2761 | goto fail; |
2762 | } |
2763 | |
2764 | necp_policy_mark_for_deletion(session, policy); |
2765 | |
2766 | necp_send_success_response(session, NECP_PACKET_TYPE_POLICY_DELETE, message_id); |
2767 | return; |
2768 | |
2769 | fail: |
2770 | necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_DELETE, message_id, response_error); |
2771 | } |
2772 | |
2773 | static void |
2774 | necp_handle_policy_apply_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2775 | { |
2776 | #pragma unused(packet, offset) |
2777 | necp_policy_apply_all(session); |
2778 | necp_send_success_response(session, NECP_PACKET_TYPE_POLICY_APPLY_ALL, message_id); |
2779 | } |
2780 | |
2781 | static void |
2782 | necp_handle_policy_list_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2783 | { |
2784 | #pragma unused(packet, offset) |
2785 | u_int32_t tlv_size = (sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(u_int32_t)); |
2786 | u_int32_t response_size = 0; |
2787 | u_int8_t *response = NULL; |
2788 | u_int8_t *cursor = NULL; |
2789 | int num_policies = 0; |
2790 | int cur_policy_index = 0; |
2791 | struct necp_session_policy *policy; |
2792 | |
2793 | LIST_FOREACH(policy, &session->policies, chain) { |
2794 | if (!policy->pending_deletion) { |
2795 | num_policies++; |
2796 | } |
2797 | } |
2798 | |
2799 | // Create a response with one Policy ID TLV for each policy |
2800 | response_size = sizeof(struct necp_packet_header) + num_policies * tlv_size; |
2801 | MALLOC(response, u_int8_t *, response_size, M_NECP, M_WAITOK); |
2802 | if (response == NULL) { |
2803 | necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_LIST_ALL, message_id, NECP_ERROR_INTERNAL); |
2804 | return; |
2805 | } |
2806 | |
2807 | cursor = response; |
2808 | cursor = necp_buffer_write_packet_header(cursor, NECP_PACKET_TYPE_POLICY_LIST_ALL, NECP_PACKET_FLAGS_RESPONSE, message_id); |
2809 | |
2810 | LIST_FOREACH(policy, &session->policies, chain) { |
2811 | if (!policy->pending_deletion && cur_policy_index < num_policies) { |
2812 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(u_int32_t), &policy->local_id, response, response_size); |
2813 | cur_policy_index++; |
2814 | } |
2815 | } |
2816 | |
2817 | if (!necp_send_ctl_data(session, (u_int8_t *)response, response_size)) { |
2818 | NECPLOG0(LOG_ERR, "Failed to send response" ); |
2819 | } |
2820 | |
2821 | FREE(response, M_NECP); |
2822 | } |
2823 | |
2824 | static void |
2825 | necp_handle_policy_delete_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, int offset) |
2826 | { |
2827 | #pragma unused(packet, offset) |
2828 | necp_policy_mark_all_for_deletion(session); |
2829 | necp_send_success_response(session, NECP_PACKET_TYPE_POLICY_DELETE_ALL, message_id); |
2830 | } |
2831 | |
2832 | static necp_policy_id |
2833 | necp_policy_get_new_id(struct necp_session *session) |
2834 | { |
2835 | session->last_policy_id++; |
2836 | if (session->last_policy_id < 1) { |
2837 | session->last_policy_id = 1; |
2838 | } |
2839 | |
2840 | necp_policy_id newid = session->last_policy_id; |
2841 | |
2842 | if (newid == 0) { |
2843 | NECPLOG0(LOG_ERR, "Allocate policy id failed.\n" ); |
2844 | return (0); |
2845 | } |
2846 | |
2847 | return (newid); |
2848 | } |
2849 | |
2850 | /* |
2851 | * For the policy dump response this is the structure: |
2852 | * |
2853 | * <NECP_PACKET_HEADER> |
2854 | * { |
2855 | * type : NECP_TLV_POLICY_DUMP |
2856 | * length : ... |
2857 | * value : |
2858 | * { |
2859 | * { |
2860 | * type : NECP_TLV_POLICY_ID |
2861 | * len : ... |
2862 | * value : ... |
2863 | * } |
2864 | * { |
2865 | * type : NECP_TLV_POLICY_ORDER |
2866 | * len : ... |
2867 | * value : ... |
2868 | * } |
2869 | * { |
2870 | * type : NECP_TLV_POLICY_RESULT_STRING |
2871 | * len : ... |
2872 | * value : ... |
2873 | * } |
2874 | * { |
2875 | * type : NECP_TLV_POLICY_OWNER |
2876 | * len : ... |
2877 | * value : ... |
2878 | * } |
2879 | * { |
2880 | * type : NECP_TLV_POLICY_CONDITION |
2881 | * len : ... |
2882 | * value : |
2883 | * { |
2884 | * { |
2885 | * type : NECP_POLICY_CONDITION_ALL_INTERFACES |
2886 | * len : ... |
2887 | * value : ... |
2888 | * } |
2889 | * { |
2890 | * type : NECP_POLICY_CONDITION_BOUND_INTERFACES |
2891 | * len : ... |
2892 | * value : ... |
2893 | * } |
2894 | * ... |
2895 | * } |
2896 | * } |
2897 | * } |
2898 | * } |
2899 | * { |
2900 | * type : NECP_TLV_POLICY_DUMP |
2901 | * length : ... |
2902 | * value : |
2903 | * { |
2904 | * { |
2905 | * type : NECP_TLV_POLICY_ID |
2906 | * len : ... |
2907 | * value : ... |
2908 | * } |
2909 | * { |
2910 | * type : NECP_TLV_POLICY_ORDER |
2911 | * len : ... |
2912 | * value : ... |
2913 | * } |
2914 | * { |
2915 | * type : NECP_TLV_POLICY_RESULT_STRING |
2916 | * len : ... |
2917 | * value : ... |
2918 | * } |
2919 | * { |
2920 | * type : NECP_TLV_POLICY_OWNER |
2921 | * len : ... |
2922 | * value : ... |
2923 | * } |
2924 | * { |
2925 | * type : NECP_TLV_POLICY_CONDITION |
2926 | * len : ... |
2927 | * value : |
2928 | * { |
2929 | * { |
2930 | * type : NECP_POLICY_CONDITION_ALL_INTERFACES |
2931 | * len : ... |
2932 | * value : ... |
2933 | * } |
2934 | * { |
2935 | * type : NECP_POLICY_CONDITION_BOUND_INTERFACES |
2936 | * len : ... |
2937 | * value : ... |
2938 | * } |
2939 | * ... |
2940 | * } |
2941 | * } |
2942 | * } |
2943 | * } |
2944 | * ... |
2945 | */ |
2946 | static int |
2947 | necp_handle_policy_dump_all(struct necp_session *session, u_int32_t message_id, mbuf_t packet, |
2948 | user_addr_t out_buffer, size_t out_buffer_length, int offset) |
2949 | { |
2950 | #pragma unused(offset) |
2951 | struct necp_kernel_socket_policy *policy = NULL; |
2952 | int policy_i; |
2953 | int policy_count = 0; |
2954 | u_int8_t **tlv_buffer_pointers = NULL; |
2955 | u_int32_t *tlv_buffer_lengths = NULL; |
2956 | u_int32_t total_tlv_len = 0; |
2957 | u_int8_t *result_buf = NULL; |
2958 | u_int8_t *result_buf_cursor = result_buf; |
2959 | char result_string[MAX_RESULT_STRING_LEN]; |
2960 | char proc_name_string[MAXCOMLEN + 1]; |
2961 | |
2962 | int error_code = 0; |
2963 | bool error_occured = false; |
2964 | u_int32_t response_error = NECP_ERROR_INTERNAL; |
2965 | |
2966 | #define REPORT_ERROR(error) error_occured = true; \ |
2967 | response_error = error; \ |
2968 | goto done |
2969 | |
2970 | #define UNLOCK_AND_REPORT_ERROR(lock, error) lck_rw_done(lock); \ |
2971 | REPORT_ERROR(error) |
2972 | |
2973 | errno_t cred_result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NECP_POLICIES, 0); |
2974 | if (cred_result != 0) { |
2975 | NECPLOG0(LOG_ERR, "Session does not hold the necessary entitlement to get Network Extension Policy information" ); |
2976 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
2977 | } |
2978 | |
2979 | // LOCK |
2980 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
2981 | |
2982 | if (necp_debug) { |
2983 | NECPLOG0(LOG_DEBUG, "Gathering policies" ); |
2984 | } |
2985 | |
2986 | policy_count = necp_kernel_application_policies_count; |
2987 | |
2988 | MALLOC(tlv_buffer_pointers, u_int8_t **, sizeof(u_int8_t *) * policy_count, M_NECP, M_NOWAIT | M_ZERO); |
2989 | if (tlv_buffer_pointers == NULL) { |
2990 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_pointers (%u bytes)" , sizeof(u_int8_t *) * policy_count); |
2991 | UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL); |
2992 | } |
2993 | |
2994 | MALLOC(tlv_buffer_lengths, u_int32_t *, sizeof(u_int32_t) * policy_count, M_NECP, M_NOWAIT | M_ZERO); |
2995 | if (tlv_buffer_lengths == NULL) { |
2996 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer_lengths (%u bytes)" , sizeof(u_int32_t) * policy_count); |
2997 | UNLOCK_AND_REPORT_ERROR(&necp_kernel_policy_lock, NECP_ERROR_INTERNAL); |
2998 | } |
2999 | |
3000 | for (policy_i = 0; necp_kernel_socket_policies_app_layer_map != NULL && necp_kernel_socket_policies_app_layer_map[policy_i] != NULL; policy_i++) { |
3001 | policy = necp_kernel_socket_policies_app_layer_map[policy_i]; |
3002 | |
3003 | memset(result_string, 0, MAX_RESULT_STRING_LEN); |
3004 | memset(proc_name_string, 0, MAXCOMLEN + 1); |
3005 | |
3006 | necp_get_result_description(result_string, policy->result, policy->result_parameter); |
3007 | proc_name(policy->session_pid, proc_name_string, MAXCOMLEN); |
3008 | |
3009 | u_int16_t proc_name_len = strlen(proc_name_string) + 1; |
3010 | u_int16_t result_string_len = strlen(result_string) + 1; |
3011 | |
3012 | if (necp_debug) { |
3013 | NECPLOG(LOG_DEBUG, "Policy: process: %s, result: %s" , proc_name_string, result_string); |
3014 | } |
3015 | |
3016 | u_int32_t total_allocated_bytes = sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->id) + // NECP_TLV_POLICY_ID |
3017 | sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->order) + // NECP_TLV_POLICY_ORDER |
3018 | sizeof(u_int8_t) + sizeof(u_int32_t) + sizeof(policy->session_order) + // NECP_TLV_POLICY_SESSION_ORDER |
3019 | sizeof(u_int8_t) + sizeof(u_int32_t) + result_string_len + // NECP_TLV_POLICY_RESULT_STRING |
3020 | sizeof(u_int8_t) + sizeof(u_int32_t) + proc_name_len + // NECP_TLV_POLICY_OWNER |
3021 | sizeof(u_int8_t) + sizeof(u_int32_t); // NECP_TLV_POLICY_CONDITION |
3022 | |
3023 | // We now traverse the condition_mask to see how much space we need to allocate |
3024 | u_int32_t condition_mask = policy->condition_mask; |
3025 | u_int8_t num_conditions = 0; |
3026 | struct necp_string_id_mapping *account_id_entry = NULL; |
3027 | char if_name[IFXNAMSIZ]; |
3028 | u_int32_t condition_tlv_length = 0; |
3029 | memset(if_name, 0, sizeof(if_name)); |
3030 | |
3031 | if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) { |
3032 | num_conditions++; |
3033 | } else { |
3034 | if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) { |
3035 | num_conditions++; |
3036 | } |
3037 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
3038 | snprintf(if_name, IFXNAMSIZ, "%s%d" , ifnet_name(policy->cond_bound_interface), ifnet_unit(policy->cond_bound_interface)); |
3039 | condition_tlv_length += strlen(if_name) + 1; |
3040 | num_conditions++; |
3041 | } |
3042 | if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
3043 | condition_tlv_length += sizeof(policy->cond_protocol); |
3044 | num_conditions++; |
3045 | } |
3046 | if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
3047 | condition_tlv_length += sizeof(uuid_t); |
3048 | num_conditions++; |
3049 | } |
3050 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
3051 | condition_tlv_length += sizeof(uuid_t); |
3052 | num_conditions++; |
3053 | } |
3054 | if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
3055 | u_int32_t domain_len = strlen(policy->cond_domain) + 1; |
3056 | condition_tlv_length += domain_len; |
3057 | num_conditions++; |
3058 | } |
3059 | if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
3060 | account_id_entry = necp_lookup_string_with_id_locked(&necp_account_id_list, policy->cond_account_id); |
3061 | u_int32_t account_id_len = 0; |
3062 | if (account_id_entry) { |
3063 | account_id_len = account_id_entry->string ? strlen(account_id_entry->string) + 1 : 0; |
3064 | } |
3065 | condition_tlv_length += account_id_len; |
3066 | num_conditions++; |
3067 | } |
3068 | if (condition_mask & NECP_KERNEL_CONDITION_PID) { |
3069 | condition_tlv_length += sizeof(pid_t); |
3070 | num_conditions++; |
3071 | } |
3072 | if (condition_mask & NECP_KERNEL_CONDITION_UID) { |
3073 | condition_tlv_length += sizeof(uid_t); |
3074 | num_conditions++; |
3075 | } |
3076 | if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
3077 | condition_tlv_length += sizeof(struct necp_policy_condition_tc_range); |
3078 | num_conditions++; |
3079 | } |
3080 | if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
3081 | num_conditions++; |
3082 | } |
3083 | if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
3084 | u_int32_t entitlement_len = strlen(policy->cond_custom_entitlement) + 1; |
3085 | condition_tlv_length += entitlement_len; |
3086 | num_conditions++; |
3087 | } |
3088 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
3089 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
3090 | condition_tlv_length += sizeof(struct necp_policy_condition_addr_range); |
3091 | } else { |
3092 | condition_tlv_length += sizeof(struct necp_policy_condition_addr); |
3093 | } |
3094 | num_conditions++; |
3095 | } |
3096 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
3097 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
3098 | condition_tlv_length += sizeof(struct necp_policy_condition_addr_range); |
3099 | } else { |
3100 | condition_tlv_length += sizeof(struct necp_policy_condition_addr); |
3101 | } |
3102 | num_conditions++; |
3103 | } |
3104 | if (condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
3105 | condition_tlv_length += sizeof(struct necp_policy_condition_agent_type); |
3106 | num_conditions++; |
3107 | } |
3108 | } |
3109 | |
3110 | condition_tlv_length += num_conditions * (sizeof(u_int8_t) + sizeof(u_int32_t)); // These are for the condition TLVs. The space for "value" is already accounted for above. |
3111 | total_allocated_bytes += condition_tlv_length; |
3112 | |
3113 | u_int8_t *tlv_buffer; |
3114 | MALLOC(tlv_buffer, u_int8_t *, total_allocated_bytes, M_NECP, M_NOWAIT | M_ZERO); |
3115 | if (tlv_buffer == NULL) { |
3116 | NECPLOG(LOG_DEBUG, "Failed to allocate tlv_buffer (%u bytes)" , total_allocated_bytes); |
3117 | continue; |
3118 | } |
3119 | |
3120 | u_int8_t *cursor = tlv_buffer; |
3121 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ID, sizeof(policy->id), &policy->id, tlv_buffer, total_allocated_bytes); |
3122 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_ORDER, sizeof(necp_policy_order), &policy->order, tlv_buffer, total_allocated_bytes); |
3123 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_SESSION_ORDER, sizeof(policy->session_order), &policy->session_order, tlv_buffer, total_allocated_bytes); |
3124 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_RESULT_STRING, result_string_len, result_string, tlv_buffer, total_allocated_bytes); |
3125 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_OWNER, proc_name_len, proc_name_string, tlv_buffer, total_allocated_bytes); |
3126 | |
3127 | #define N_QUICK 256 |
3128 | u_int8_t q_cond_buf[N_QUICK]; // Minor optimization |
3129 | |
3130 | u_int8_t *cond_buf; // To be used for condition TLVs |
3131 | if (condition_tlv_length <= N_QUICK) { |
3132 | cond_buf = q_cond_buf; |
3133 | } else { |
3134 | MALLOC(cond_buf, u_int8_t *, condition_tlv_length, M_NECP, M_NOWAIT); |
3135 | if (cond_buf == NULL) { |
3136 | NECPLOG(LOG_DEBUG, "Failed to allocate cond_buffer (%u bytes)" , condition_tlv_length); |
3137 | FREE(tlv_buffer, M_NECP); |
3138 | continue; |
3139 | } |
3140 | } |
3141 | |
3142 | memset(cond_buf, 0, condition_tlv_length); |
3143 | u_int8_t *cond_buf_cursor = cond_buf; |
3144 | if (condition_mask == NECP_POLICY_CONDITION_DEFAULT) { |
3145 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_DEFAULT, 0, "" , cond_buf, condition_tlv_length); |
3146 | } else { |
3147 | if (condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) { |
3148 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ALL_INTERFACES, 0, "" , cond_buf, condition_tlv_length); |
3149 | } |
3150 | if (condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
3151 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_BOUND_INTERFACE, strlen(if_name) + 1, |
3152 | if_name, cond_buf, condition_tlv_length); |
3153 | } |
3154 | if (condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
3155 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_IP_PROTOCOL, sizeof(policy->cond_protocol), &policy->cond_protocol, |
3156 | cond_buf, condition_tlv_length); |
3157 | } |
3158 | if (condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
3159 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(policy->cond_app_id); |
3160 | if (entry != NULL) { |
3161 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_APPLICATION, sizeof(entry->uuid), entry->uuid, |
3162 | cond_buf, condition_tlv_length); |
3163 | } |
3164 | } |
3165 | if (condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
3166 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(policy->cond_real_app_id); |
3167 | if (entry != NULL) { |
3168 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REAL_APPLICATION, sizeof(entry->uuid), entry->uuid, |
3169 | cond_buf, condition_tlv_length); |
3170 | } |
3171 | } |
3172 | if (condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
3173 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_DOMAIN, strlen(policy->cond_domain) + 1, policy->cond_domain, |
3174 | cond_buf, condition_tlv_length); |
3175 | } |
3176 | if (condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
3177 | if (account_id_entry != NULL) { |
3178 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ACCOUNT, strlen(account_id_entry->string) + 1, account_id_entry->string, |
3179 | cond_buf, condition_tlv_length); |
3180 | } |
3181 | } |
3182 | if (condition_mask & NECP_KERNEL_CONDITION_PID) { |
3183 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_PID, sizeof(policy->cond_pid), &policy->cond_pid, |
3184 | cond_buf, condition_tlv_length); |
3185 | } |
3186 | if (condition_mask & NECP_KERNEL_CONDITION_UID) { |
3187 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_UID, sizeof(policy->cond_uid), &policy->cond_uid, |
3188 | cond_buf, condition_tlv_length); |
3189 | } |
3190 | if (condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
3191 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_TRAFFIC_CLASS, sizeof(policy->cond_traffic_class), &policy->cond_traffic_class, |
3192 | cond_buf, condition_tlv_length); |
3193 | } |
3194 | if (condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
3195 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, 0, "" , |
3196 | cond_buf, condition_tlv_length); |
3197 | } |
3198 | if (condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
3199 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_ENTITLEMENT, strlen(policy->cond_custom_entitlement) + 1, policy->cond_custom_entitlement, |
3200 | cond_buf, condition_tlv_length); |
3201 | } |
3202 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
3203 | if (condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
3204 | struct necp_policy_condition_addr_range range; |
3205 | memcpy(&range.start_address, &policy->cond_local_start, sizeof(policy->cond_local_start)); |
3206 | memcpy(&range.end_address, &policy->cond_local_end, sizeof(policy->cond_local_end)); |
3207 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE, sizeof(range), &range, |
3208 | cond_buf, condition_tlv_length); |
3209 | } else { |
3210 | struct necp_policy_condition_addr addr; |
3211 | addr.prefix = policy->cond_local_prefix; |
3212 | memcpy(&addr.address, &policy->cond_local_start, sizeof(policy->cond_local_start)); |
3213 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_LOCAL_ADDR, sizeof(addr), &addr, |
3214 | cond_buf, condition_tlv_length); |
3215 | } |
3216 | } |
3217 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
3218 | if (condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
3219 | struct necp_policy_condition_addr_range range; |
3220 | memcpy(&range.start_address, &policy->cond_remote_start, sizeof(policy->cond_remote_start)); |
3221 | memcpy(&range.end_address, &policy->cond_remote_end, sizeof(policy->cond_remote_end)); |
3222 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE, sizeof(range), &range, |
3223 | cond_buf, condition_tlv_length); |
3224 | } else { |
3225 | struct necp_policy_condition_addr addr; |
3226 | addr.prefix = policy->cond_remote_prefix; |
3227 | memcpy(&addr.address, &policy->cond_remote_start, sizeof(policy->cond_remote_start)); |
3228 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_REMOTE_ADDR, sizeof(addr), &addr, |
3229 | cond_buf, condition_tlv_length); |
3230 | } |
3231 | } |
3232 | if (condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
3233 | cond_buf_cursor = necp_buffer_write_tlv(cond_buf_cursor, NECP_POLICY_CONDITION_AGENT_TYPE, |
3234 | sizeof(policy->cond_agent_type), &policy->cond_agent_type, |
3235 | cond_buf, condition_tlv_length); |
3236 | } |
3237 | } |
3238 | |
3239 | cursor = necp_buffer_write_tlv(cursor, NECP_TLV_POLICY_CONDITION, cond_buf_cursor - cond_buf, cond_buf, tlv_buffer, total_allocated_bytes); |
3240 | if (cond_buf != q_cond_buf) { |
3241 | FREE(cond_buf, M_NECP); |
3242 | } |
3243 | |
3244 | tlv_buffer_pointers[policy_i] = tlv_buffer; |
3245 | tlv_buffer_lengths[policy_i] = (cursor - tlv_buffer); |
3246 | |
3247 | // This is the length of the TLV for NECP_TLV_POLICY_DUMP |
3248 | total_tlv_len += sizeof(u_int8_t) + sizeof(u_int32_t) + (cursor - tlv_buffer); |
3249 | } |
3250 | |
3251 | // UNLOCK |
3252 | lck_rw_done(&necp_kernel_policy_lock); |
3253 | |
3254 | // Send packet |
3255 | if (packet != NULL) { |
3256 | u_int32_t total_result_length = sizeof(struct necp_packet_header) + total_tlv_len; |
3257 | |
3258 | // Allow malloc to wait, since the total buffer may be large and we are not holding any locks |
3259 | MALLOC(result_buf, u_int8_t *, total_result_length, M_NECP, M_WAITOK | M_ZERO); |
3260 | if (result_buf == NULL) { |
3261 | NECPLOG(LOG_DEBUG, "Failed to allocate result_buffer (%u bytes)" , total_result_length); |
3262 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3263 | } |
3264 | |
3265 | result_buf_cursor = result_buf; |
3266 | result_buf_cursor = necp_buffer_write_packet_header(result_buf_cursor, NECP_PACKET_TYPE_POLICY_DUMP_ALL, NECP_PACKET_FLAGS_RESPONSE, message_id); |
3267 | |
3268 | for (int i = 0; i < policy_count; i++) { |
3269 | if (tlv_buffer_pointers[i] != NULL) { |
3270 | result_buf_cursor = necp_buffer_write_tlv(result_buf_cursor, NECP_TLV_POLICY_DUMP, tlv_buffer_lengths[i], tlv_buffer_pointers[i], result_buf, total_result_length); |
3271 | } |
3272 | } |
3273 | |
3274 | if (!necp_send_ctl_data(session, result_buf, result_buf_cursor - result_buf)) { |
3275 | NECPLOG(LOG_ERR, "Failed to send response (%u bytes)" , result_buf_cursor - result_buf); |
3276 | } else { |
3277 | NECPLOG(LOG_ERR, "Sent data worth %u bytes. Total result buffer length was %u bytes" , result_buf_cursor - result_buf, total_result_length); |
3278 | } |
3279 | } |
3280 | |
3281 | // Copy out |
3282 | if (out_buffer != 0) { |
3283 | if (out_buffer_length < total_tlv_len + sizeof(u_int32_t)) { |
3284 | NECPLOG(LOG_DEBUG, "out_buffer_length too small (%u < %u)" , out_buffer_length, total_tlv_len + sizeof(u_int32_t)); |
3285 | REPORT_ERROR(NECP_ERROR_INVALID_TLV); |
3286 | } |
3287 | |
3288 | // Allow malloc to wait, since the total buffer may be large and we are not holding any locks |
3289 | MALLOC(result_buf, u_int8_t *, total_tlv_len + sizeof(u_int32_t), M_NECP, M_WAITOK | M_ZERO); |
3290 | if (result_buf == NULL) { |
3291 | NECPLOG(LOG_DEBUG, "Failed to allocate result_buffer (%u bytes)" , total_tlv_len + sizeof(u_int32_t)); |
3292 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3293 | } |
3294 | |
3295 | // Add four bytes for total length at the start |
3296 | memcpy(result_buf, &total_tlv_len, sizeof(u_int32_t)); |
3297 | |
3298 | // Copy the TLVs |
3299 | result_buf_cursor = result_buf + sizeof(u_int32_t); |
3300 | for (int i = 0; i < policy_count; i++) { |
3301 | if (tlv_buffer_pointers[i] != NULL) { |
3302 | result_buf_cursor = necp_buffer_write_tlv(result_buf_cursor, NECP_TLV_POLICY_DUMP, tlv_buffer_lengths[i], tlv_buffer_pointers[i], |
3303 | result_buf, total_tlv_len + sizeof(u_int32_t)); |
3304 | } |
3305 | } |
3306 | |
3307 | int copy_error = copyout(result_buf, out_buffer, total_tlv_len + sizeof(u_int32_t)); |
3308 | if (copy_error) { |
3309 | NECPLOG(LOG_DEBUG, "Failed to copy out result_buffer (%u bytes)" , total_tlv_len + sizeof(u_int32_t)); |
3310 | REPORT_ERROR(NECP_ERROR_INTERNAL); |
3311 | } |
3312 | } |
3313 | |
3314 | done: |
3315 | |
3316 | if (error_occured) { |
3317 | if (packet != NULL) { |
3318 | if(!necp_send_error_response(session, NECP_PACKET_TYPE_POLICY_DUMP_ALL, message_id, response_error)) { |
3319 | NECPLOG0(LOG_ERR, "Failed to send error response" ); |
3320 | } else { |
3321 | NECPLOG0(LOG_ERR, "Sent error response" ); |
3322 | } |
3323 | } |
3324 | error_code = necp_get_posix_error_for_necp_error(response_error); |
3325 | } |
3326 | |
3327 | if (result_buf != NULL) { |
3328 | FREE(result_buf, M_NECP); |
3329 | } |
3330 | |
3331 | if (tlv_buffer_pointers != NULL) { |
3332 | for (int i = 0; i < policy_count; i++) { |
3333 | if (tlv_buffer_pointers[i] != NULL) { |
3334 | FREE(tlv_buffer_pointers[i], M_NECP); |
3335 | tlv_buffer_pointers[i] = NULL; |
3336 | } |
3337 | } |
3338 | FREE(tlv_buffer_pointers, M_NECP); |
3339 | } |
3340 | |
3341 | if (tlv_buffer_lengths != NULL) { |
3342 | FREE(tlv_buffer_lengths, M_NECP); |
3343 | } |
3344 | #undef N_QUICK |
3345 | #undef RESET_COND_BUF |
3346 | #undef REPORT_ERROR |
3347 | #undef UNLOCK_AND_REPORT_ERROR |
3348 | |
3349 | return (error_code); |
3350 | } |
3351 | |
3352 | static struct necp_session_policy * |
3353 | 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) |
3354 | { |
3355 | struct necp_session_policy *new_policy = NULL; |
3356 | struct necp_session_policy *tmp_policy = NULL; |
3357 | |
3358 | if (session == NULL || conditions_array == NULL || result == NULL || result_size == 0) { |
3359 | goto done; |
3360 | } |
3361 | |
3362 | MALLOC_ZONE(new_policy, struct necp_session_policy *, sizeof(*new_policy), M_NECP_SESSION_POLICY, M_WAITOK); |
3363 | if (new_policy == NULL) { |
3364 | goto done; |
3365 | } |
3366 | |
3367 | memset(new_policy, 0, sizeof(*new_policy)); // M_ZERO is not supported for MALLOC_ZONE |
3368 | new_policy->applied = FALSE; |
3369 | new_policy->pending_deletion = FALSE; |
3370 | new_policy->pending_update = FALSE; |
3371 | new_policy->order = order; |
3372 | new_policy->conditions = conditions_array; |
3373 | new_policy->conditions_size = conditions_array_size; |
3374 | new_policy->route_rules = route_rules_array; |
3375 | new_policy->route_rules_size = route_rules_array_size; |
3376 | new_policy->result = result; |
3377 | new_policy->result_size = result_size; |
3378 | new_policy->local_id = necp_policy_get_new_id(session); |
3379 | |
3380 | LIST_INSERT_SORTED_ASCENDING(&session->policies, new_policy, chain, order, tmp_policy); |
3381 | |
3382 | session->dirty = TRUE; |
3383 | |
3384 | if (necp_debug) { |
3385 | NECPLOG(LOG_DEBUG, "Created NECP policy, order %d" , order); |
3386 | } |
3387 | done: |
3388 | return (new_policy); |
3389 | } |
3390 | |
3391 | static struct necp_session_policy * |
3392 | necp_policy_find(struct necp_session *session, necp_policy_id policy_id) |
3393 | { |
3394 | struct necp_session_policy *policy = NULL; |
3395 | if (policy_id == 0) { |
3396 | return (NULL); |
3397 | } |
3398 | |
3399 | LIST_FOREACH(policy, &session->policies, chain) { |
3400 | if (policy->local_id == policy_id) { |
3401 | return (policy); |
3402 | } |
3403 | } |
3404 | |
3405 | return (NULL); |
3406 | } |
3407 | |
3408 | static inline u_int8_t |
3409 | necp_policy_get_result_type(struct necp_session_policy *policy) |
3410 | { |
3411 | return (policy ? necp_policy_result_get_type_from_buffer(policy->result, policy->result_size) : 0); |
3412 | } |
3413 | |
3414 | static inline u_int32_t |
3415 | necp_policy_get_result_parameter_length(struct necp_session_policy *policy) |
3416 | { |
3417 | return (policy ? necp_policy_result_get_parameter_length_from_buffer(policy->result, policy->result_size) : 0); |
3418 | } |
3419 | |
3420 | static bool |
3421 | necp_policy_get_result_parameter(struct necp_session_policy *policy, u_int8_t *parameter_buffer, u_int32_t parameter_buffer_length) |
3422 | { |
3423 | if (policy) { |
3424 | u_int32_t parameter_length = necp_policy_result_get_parameter_length_from_buffer(policy->result, policy->result_size); |
3425 | if (parameter_buffer_length >= parameter_length) { |
3426 | u_int8_t *parameter = necp_policy_result_get_parameter_pointer_from_buffer(policy->result, policy->result_size); |
3427 | if (parameter && parameter_buffer) { |
3428 | memcpy(parameter_buffer, parameter, parameter_length); |
3429 | return (TRUE); |
3430 | } |
3431 | } |
3432 | } |
3433 | |
3434 | return (FALSE); |
3435 | } |
3436 | |
3437 | static bool |
3438 | necp_policy_mark_for_deletion(struct necp_session *session, struct necp_session_policy *policy) |
3439 | { |
3440 | if (session == NULL || policy == NULL) { |
3441 | return (FALSE); |
3442 | } |
3443 | |
3444 | policy->pending_deletion = TRUE; |
3445 | session->dirty = TRUE; |
3446 | |
3447 | if (necp_debug) { |
3448 | NECPLOG0(LOG_DEBUG, "Marked NECP policy for removal" ); |
3449 | } |
3450 | return (TRUE); |
3451 | } |
3452 | |
3453 | static bool |
3454 | necp_policy_mark_all_for_deletion(struct necp_session *session) |
3455 | { |
3456 | struct necp_session_policy *policy = NULL; |
3457 | struct necp_session_policy *temp_policy = NULL; |
3458 | |
3459 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
3460 | necp_policy_mark_for_deletion(session, policy); |
3461 | } |
3462 | |
3463 | return (TRUE); |
3464 | } |
3465 | |
3466 | static bool |
3467 | necp_policy_delete(struct necp_session *session, struct necp_session_policy *policy) |
3468 | { |
3469 | if (session == NULL || policy == NULL) { |
3470 | return (FALSE); |
3471 | } |
3472 | |
3473 | LIST_REMOVE(policy, chain); |
3474 | |
3475 | if (policy->result) { |
3476 | FREE(policy->result, M_NECP); |
3477 | policy->result = NULL; |
3478 | } |
3479 | |
3480 | if (policy->conditions) { |
3481 | FREE(policy->conditions, M_NECP); |
3482 | policy->conditions = NULL; |
3483 | } |
3484 | |
3485 | if (policy->route_rules) { |
3486 | FREE(policy->route_rules, M_NECP); |
3487 | policy->route_rules = NULL; |
3488 | } |
3489 | |
3490 | FREE_ZONE(policy, sizeof(*policy), M_NECP_SESSION_POLICY); |
3491 | |
3492 | if (necp_debug) { |
3493 | NECPLOG0(LOG_DEBUG, "Removed NECP policy" ); |
3494 | } |
3495 | return (TRUE); |
3496 | } |
3497 | |
3498 | static bool |
3499 | necp_policy_unapply(struct necp_session_policy *policy) |
3500 | { |
3501 | int i = 0; |
3502 | if (policy == NULL) { |
3503 | return (FALSE); |
3504 | } |
3505 | |
3506 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
3507 | |
3508 | // Release local uuid mappings |
3509 | if (!uuid_is_null(policy->applied_app_uuid)) { |
3510 | bool removed_mapping = FALSE; |
3511 | if (necp_remove_uuid_app_id_mapping(policy->applied_app_uuid, &removed_mapping, TRUE) && removed_mapping) { |
3512 | necp_uuid_app_id_mappings_dirty = TRUE; |
3513 | necp_num_uuid_app_id_mappings--; |
3514 | } |
3515 | uuid_clear(policy->applied_app_uuid); |
3516 | } |
3517 | if (!uuid_is_null(policy->applied_real_app_uuid)) { |
3518 | necp_remove_uuid_app_id_mapping(policy->applied_real_app_uuid, NULL, FALSE); |
3519 | uuid_clear(policy->applied_real_app_uuid); |
3520 | } |
3521 | if (!uuid_is_null(policy->applied_result_uuid)) { |
3522 | necp_remove_uuid_service_id_mapping(policy->applied_result_uuid); |
3523 | uuid_clear(policy->applied_result_uuid); |
3524 | } |
3525 | |
3526 | // Release string mappings |
3527 | if (policy->applied_account != NULL) { |
3528 | necp_remove_string_to_id_mapping(&necp_account_id_list, policy->applied_account); |
3529 | FREE(policy->applied_account, M_NECP); |
3530 | policy->applied_account = NULL; |
3531 | } |
3532 | |
3533 | // Release route rule |
3534 | if (policy->applied_route_rules_id != 0) { |
3535 | necp_remove_route_rule(&necp_route_rules, policy->applied_route_rules_id); |
3536 | policy->applied_route_rules_id = 0; |
3537 | } |
3538 | |
3539 | // Remove socket policies |
3540 | for (i = 0; i < MAX_KERNEL_SOCKET_POLICIES; i++) { |
3541 | if (policy->kernel_socket_policies[i] != 0) { |
3542 | necp_kernel_socket_policy_delete(policy->kernel_socket_policies[i]); |
3543 | policy->kernel_socket_policies[i] = 0; |
3544 | } |
3545 | } |
3546 | |
3547 | // Remove IP output policies |
3548 | for (i = 0; i < MAX_KERNEL_IP_OUTPUT_POLICIES; i++) { |
3549 | if (policy->kernel_ip_output_policies[i] != 0) { |
3550 | necp_kernel_ip_output_policy_delete(policy->kernel_ip_output_policies[i]); |
3551 | policy->kernel_ip_output_policies[i] = 0; |
3552 | } |
3553 | } |
3554 | |
3555 | policy->applied = FALSE; |
3556 | |
3557 | return (TRUE); |
3558 | } |
3559 | |
3560 | #define NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION 0 |
3561 | #define NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION 1 |
3562 | #define NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION 2 |
3563 | #define NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS 3 |
3564 | struct necp_policy_result_ip_tunnel { |
3565 | u_int32_t secondary_result; |
3566 | char interface_name[IFXNAMSIZ]; |
3567 | } __attribute__((__packed__)); |
3568 | |
3569 | struct necp_policy_result_service { |
3570 | uuid_t identifier; |
3571 | u_int32_t data; |
3572 | } __attribute__((__packed__)); |
3573 | |
3574 | static bool |
3575 | necp_policy_apply(struct necp_session *session, struct necp_session_policy *policy) |
3576 | { |
3577 | bool socket_only_conditions = FALSE; |
3578 | bool socket_ip_conditions = FALSE; |
3579 | |
3580 | bool socket_layer_non_id_conditions = FALSE; |
3581 | bool ip_output_layer_non_id_conditions = FALSE; |
3582 | bool ip_output_layer_non_id_only = FALSE; |
3583 | bool ip_output_layer_id_condition = FALSE; |
3584 | bool ip_output_layer_tunnel_condition_from_id = FALSE; |
3585 | bool ip_output_layer_tunnel_condition_from_non_id = FALSE; |
3586 | necp_kernel_policy_id cond_ip_output_layer_id = NECP_KERNEL_POLICY_ID_NONE; |
3587 | |
3588 | u_int32_t master_condition_mask = 0; |
3589 | u_int32_t master_condition_negated_mask = 0; |
3590 | ifnet_t cond_bound_interface = NULL; |
3591 | u_int32_t cond_account_id = 0; |
3592 | char *cond_domain = NULL; |
3593 | char *cond_custom_entitlement = NULL; |
3594 | pid_t cond_pid = 0; |
3595 | uid_t cond_uid = 0; |
3596 | necp_app_id cond_app_id = 0; |
3597 | necp_app_id cond_real_app_id = 0; |
3598 | struct necp_policy_condition_tc_range cond_traffic_class; |
3599 | cond_traffic_class.start_tc = 0; |
3600 | cond_traffic_class.end_tc = 0; |
3601 | u_int16_t cond_protocol = 0; |
3602 | union necp_sockaddr_union cond_local_start; |
3603 | union necp_sockaddr_union cond_local_end; |
3604 | u_int8_t cond_local_prefix = 0; |
3605 | union necp_sockaddr_union cond_remote_start; |
3606 | union necp_sockaddr_union cond_remote_end; |
3607 | u_int8_t cond_remote_prefix = 0; |
3608 | u_int32_t offset = 0; |
3609 | u_int8_t ultimate_result = 0; |
3610 | u_int32_t secondary_result = 0; |
3611 | struct necp_policy_condition_agent_type cond_agent_type = {}; |
3612 | necp_kernel_policy_result_parameter secondary_result_parameter; |
3613 | memset(&secondary_result_parameter, 0, sizeof(secondary_result_parameter)); |
3614 | u_int32_t cond_last_interface_index = 0; |
3615 | necp_kernel_policy_result_parameter ultimate_result_parameter; |
3616 | memset(&ultimate_result_parameter, 0, sizeof(ultimate_result_parameter)); |
3617 | |
3618 | if (policy == NULL) { |
3619 | return (FALSE); |
3620 | } |
3621 | |
3622 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
3623 | |
3624 | // Process conditions |
3625 | while (offset < policy->conditions_size) { |
3626 | u_int32_t length = 0; |
3627 | u_int8_t *value = necp_buffer_get_tlv_value(policy->conditions, offset, &length); |
3628 | |
3629 | u_int8_t condition_type = necp_policy_condition_get_type_from_buffer(value, length); |
3630 | u_int8_t condition_flags = necp_policy_condition_get_flags_from_buffer(value, length); |
3631 | bool condition_is_negative = condition_flags & NECP_POLICY_CONDITION_FLAGS_NEGATIVE; |
3632 | u_int32_t condition_length = necp_policy_condition_get_value_length_from_buffer(value, length); |
3633 | u_int8_t *condition_value = necp_policy_condition_get_value_pointer_from_buffer(value, length); |
3634 | switch (condition_type) { |
3635 | case NECP_POLICY_CONDITION_DEFAULT: { |
3636 | socket_ip_conditions = TRUE; |
3637 | break; |
3638 | } |
3639 | case NECP_POLICY_CONDITION_ALL_INTERFACES: { |
3640 | master_condition_mask |= NECP_KERNEL_CONDITION_ALL_INTERFACES; |
3641 | socket_ip_conditions = TRUE; |
3642 | break; |
3643 | } |
3644 | case NECP_POLICY_CONDITION_ENTITLEMENT: { |
3645 | if (condition_length > 0) { |
3646 | if (cond_custom_entitlement == NULL) { |
3647 | cond_custom_entitlement = necp_copy_string((char *)condition_value, condition_length); |
3648 | if (cond_custom_entitlement != NULL) { |
3649 | master_condition_mask |= NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT; |
3650 | socket_only_conditions = TRUE; |
3651 | } |
3652 | } |
3653 | } else { |
3654 | master_condition_mask |= NECP_KERNEL_CONDITION_ENTITLEMENT; |
3655 | socket_only_conditions = TRUE; |
3656 | } |
3657 | break; |
3658 | } |
3659 | case NECP_POLICY_CONDITION_DOMAIN: { |
3660 | // Make sure there is only one such rule |
3661 | if (condition_length > 0 && cond_domain == NULL) { |
3662 | cond_domain = necp_create_trimmed_domain((char *)condition_value, condition_length); |
3663 | if (cond_domain != NULL) { |
3664 | master_condition_mask |= NECP_KERNEL_CONDITION_DOMAIN; |
3665 | if (condition_is_negative) { |
3666 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_DOMAIN; |
3667 | } |
3668 | socket_only_conditions = TRUE; |
3669 | } |
3670 | } |
3671 | break; |
3672 | } |
3673 | case NECP_POLICY_CONDITION_ACCOUNT: { |
3674 | // Make sure there is only one such rule |
3675 | if (condition_length > 0 && cond_account_id == 0 && policy->applied_account == NULL) { |
3676 | char *string = NULL; |
3677 | MALLOC(string, char *, condition_length + 1, M_NECP, M_WAITOK); |
3678 | if (string != NULL) { |
3679 | memcpy(string, condition_value, condition_length); |
3680 | string[condition_length] = 0; |
3681 | cond_account_id = necp_create_string_to_id_mapping(&necp_account_id_list, string); |
3682 | if (cond_account_id != 0) { |
3683 | policy->applied_account = string; // Save the string in parent policy |
3684 | master_condition_mask |= NECP_KERNEL_CONDITION_ACCOUNT_ID; |
3685 | if (condition_is_negative) { |
3686 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_ACCOUNT_ID; |
3687 | } |
3688 | socket_only_conditions = TRUE; |
3689 | } else { |
3690 | FREE(string, M_NECP); |
3691 | } |
3692 | } |
3693 | } |
3694 | break; |
3695 | } |
3696 | case NECP_POLICY_CONDITION_APPLICATION: { |
3697 | // Make sure there is only one such rule, because we save the uuid in the policy |
3698 | if (condition_length >= sizeof(uuid_t) && cond_app_id == 0) { |
3699 | bool allocated_mapping = FALSE; |
3700 | uuid_t application_uuid; |
3701 | memcpy(application_uuid, condition_value, sizeof(uuid_t)); |
3702 | cond_app_id = necp_create_uuid_app_id_mapping(application_uuid, &allocated_mapping, TRUE); |
3703 | if (cond_app_id != 0) { |
3704 | if (allocated_mapping) { |
3705 | necp_uuid_app_id_mappings_dirty = TRUE; |
3706 | necp_num_uuid_app_id_mappings++; |
3707 | } |
3708 | uuid_copy(policy->applied_app_uuid, application_uuid); |
3709 | master_condition_mask |= NECP_KERNEL_CONDITION_APP_ID; |
3710 | if (condition_is_negative) { |
3711 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_APP_ID; |
3712 | } |
3713 | socket_only_conditions = TRUE; |
3714 | } |
3715 | } |
3716 | break; |
3717 | } |
3718 | case NECP_POLICY_CONDITION_REAL_APPLICATION: { |
3719 | // Make sure there is only one such rule, because we save the uuid in the policy |
3720 | if (condition_length >= sizeof(uuid_t) && cond_real_app_id == 0) { |
3721 | uuid_t real_application_uuid; |
3722 | memcpy(real_application_uuid, condition_value, sizeof(uuid_t)); |
3723 | cond_real_app_id = necp_create_uuid_app_id_mapping(real_application_uuid, NULL, FALSE); |
3724 | if (cond_real_app_id != 0) { |
3725 | uuid_copy(policy->applied_real_app_uuid, real_application_uuid); |
3726 | master_condition_mask |= NECP_KERNEL_CONDITION_REAL_APP_ID; |
3727 | if (condition_is_negative) { |
3728 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REAL_APP_ID; |
3729 | } |
3730 | socket_only_conditions = TRUE; |
3731 | } |
3732 | } |
3733 | break; |
3734 | } |
3735 | case NECP_POLICY_CONDITION_PID: { |
3736 | if (condition_length >= sizeof(pid_t)) { |
3737 | master_condition_mask |= NECP_KERNEL_CONDITION_PID; |
3738 | if (condition_is_negative) { |
3739 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_PID; |
3740 | } |
3741 | memcpy(&cond_pid, condition_value, sizeof(cond_pid)); |
3742 | socket_only_conditions = TRUE; |
3743 | } |
3744 | break; |
3745 | } |
3746 | case NECP_POLICY_CONDITION_UID: { |
3747 | if (condition_length >= sizeof(uid_t)) { |
3748 | master_condition_mask |= NECP_KERNEL_CONDITION_UID; |
3749 | if (condition_is_negative) { |
3750 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_UID; |
3751 | } |
3752 | memcpy(&cond_uid, condition_value, sizeof(cond_uid)); |
3753 | socket_only_conditions = TRUE; |
3754 | } |
3755 | break; |
3756 | } |
3757 | case NECP_POLICY_CONDITION_TRAFFIC_CLASS: { |
3758 | if (condition_length >= sizeof(struct necp_policy_condition_tc_range)) { |
3759 | master_condition_mask |= NECP_KERNEL_CONDITION_TRAFFIC_CLASS; |
3760 | if (condition_is_negative) { |
3761 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_TRAFFIC_CLASS; |
3762 | } |
3763 | memcpy(&cond_traffic_class, condition_value, sizeof(cond_traffic_class)); |
3764 | socket_only_conditions = TRUE; |
3765 | } |
3766 | break; |
3767 | } |
3768 | case NECP_POLICY_CONDITION_BOUND_INTERFACE: { |
3769 | if (condition_length <= IFXNAMSIZ && condition_length > 0) { |
3770 | char interface_name[IFXNAMSIZ]; |
3771 | memcpy(interface_name, condition_value, condition_length); |
3772 | interface_name[condition_length - 1] = 0; // Make sure the string is NULL terminated |
3773 | if (ifnet_find_by_name(interface_name, &cond_bound_interface) == 0) { |
3774 | master_condition_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
3775 | if (condition_is_negative) { |
3776 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
3777 | } |
3778 | } |
3779 | socket_ip_conditions = TRUE; |
3780 | } |
3781 | break; |
3782 | } |
3783 | case NECP_POLICY_CONDITION_IP_PROTOCOL: { |
3784 | if (condition_length >= sizeof(u_int16_t)) { |
3785 | master_condition_mask |= NECP_KERNEL_CONDITION_PROTOCOL; |
3786 | if (condition_is_negative) { |
3787 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_PROTOCOL; |
3788 | } |
3789 | memcpy(&cond_protocol, condition_value, sizeof(cond_protocol)); |
3790 | socket_ip_conditions = TRUE; |
3791 | } |
3792 | break; |
3793 | } |
3794 | case NECP_POLICY_CONDITION_LOCAL_ADDR: { |
3795 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value; |
3796 | if (!necp_address_is_valid(&address_struct->address.sa)) { |
3797 | break; |
3798 | } |
3799 | |
3800 | cond_local_prefix = address_struct->prefix; |
3801 | memcpy(&cond_local_start, &address_struct->address, sizeof(address_struct->address)); |
3802 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
3803 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
3804 | if (condition_is_negative) { |
3805 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
3806 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
3807 | } |
3808 | socket_ip_conditions = TRUE; |
3809 | break; |
3810 | } |
3811 | case NECP_POLICY_CONDITION_REMOTE_ADDR: { |
3812 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value; |
3813 | if (!necp_address_is_valid(&address_struct->address.sa)) { |
3814 | break; |
3815 | } |
3816 | |
3817 | cond_remote_prefix = address_struct->prefix; |
3818 | memcpy(&cond_remote_start, &address_struct->address, sizeof(address_struct->address)); |
3819 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
3820 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
3821 | if (condition_is_negative) { |
3822 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
3823 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
3824 | } |
3825 | socket_ip_conditions = TRUE; |
3826 | break; |
3827 | } |
3828 | case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: { |
3829 | struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value; |
3830 | if (!necp_address_is_valid(&address_struct->start_address.sa) || |
3831 | !necp_address_is_valid(&address_struct->end_address.sa)) { |
3832 | break; |
3833 | } |
3834 | |
3835 | memcpy(&cond_local_start, &address_struct->start_address, sizeof(address_struct->start_address)); |
3836 | memcpy(&cond_local_end, &address_struct->end_address, sizeof(address_struct->end_address)); |
3837 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
3838 | master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_END; |
3839 | if (condition_is_negative) { |
3840 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_START; |
3841 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_LOCAL_END; |
3842 | } |
3843 | socket_ip_conditions = TRUE; |
3844 | break; |
3845 | } |
3846 | case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: { |
3847 | struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value; |
3848 | if (!necp_address_is_valid(&address_struct->start_address.sa) || |
3849 | !necp_address_is_valid(&address_struct->end_address.sa)) { |
3850 | break; |
3851 | } |
3852 | |
3853 | memcpy(&cond_remote_start, &address_struct->start_address, sizeof(address_struct->start_address)); |
3854 | memcpy(&cond_remote_end, &address_struct->end_address, sizeof(address_struct->end_address)); |
3855 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
3856 | master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_END; |
3857 | if (condition_is_negative) { |
3858 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_START; |
3859 | master_condition_negated_mask |= NECP_KERNEL_CONDITION_REMOTE_END; |
3860 | } |
3861 | socket_ip_conditions = TRUE; |
3862 | break; |
3863 | } |
3864 | case NECP_POLICY_CONDITION_AGENT_TYPE: { |
3865 | if (condition_length >= sizeof(cond_agent_type)) { |
3866 | master_condition_mask |= NECP_KERNEL_CONDITION_AGENT_TYPE; |
3867 | memcpy(&cond_agent_type, condition_value, sizeof(cond_agent_type)); |
3868 | socket_only_conditions = TRUE; |
3869 | } |
3870 | break; |
3871 | } |
3872 | default: { |
3873 | break; |
3874 | } |
3875 | } |
3876 | |
3877 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
3878 | } |
3879 | |
3880 | // Process result |
3881 | ultimate_result = necp_policy_get_result_type(policy); |
3882 | switch (ultimate_result) { |
3883 | case NECP_POLICY_RESULT_PASS: { |
3884 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
3885 | socket_layer_non_id_conditions = TRUE; |
3886 | ip_output_layer_id_condition = TRUE; |
3887 | } else if (socket_ip_conditions) { |
3888 | socket_layer_non_id_conditions = TRUE; |
3889 | ip_output_layer_id_condition = TRUE; |
3890 | ip_output_layer_non_id_conditions = TRUE; |
3891 | } |
3892 | break; |
3893 | } |
3894 | case NECP_POLICY_RESULT_DROP: { |
3895 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
3896 | socket_layer_non_id_conditions = TRUE; |
3897 | } else if (socket_ip_conditions) { |
3898 | socket_layer_non_id_conditions = TRUE; |
3899 | ip_output_layer_non_id_conditions = TRUE; |
3900 | ip_output_layer_non_id_only = TRUE; // Only apply drop to packets that didn't go through socket layer |
3901 | } |
3902 | break; |
3903 | } |
3904 | case NECP_POLICY_RESULT_SKIP: { |
3905 | u_int32_t skip_policy_order = 0; |
3906 | if (necp_policy_get_result_parameter(policy, (u_int8_t *)&skip_policy_order, sizeof(skip_policy_order))) { |
3907 | ultimate_result_parameter.skip_policy_order = skip_policy_order; |
3908 | } |
3909 | |
3910 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
3911 | socket_layer_non_id_conditions = TRUE; |
3912 | ip_output_layer_id_condition = TRUE; |
3913 | } else if (socket_ip_conditions) { |
3914 | socket_layer_non_id_conditions = TRUE; |
3915 | ip_output_layer_non_id_conditions = TRUE; |
3916 | } |
3917 | break; |
3918 | } |
3919 | case NECP_POLICY_RESULT_SOCKET_DIVERT: |
3920 | case NECP_POLICY_RESULT_SOCKET_FILTER: { |
3921 | u_int32_t control_unit = 0; |
3922 | if (necp_policy_get_result_parameter(policy, (u_int8_t *)&control_unit, sizeof(control_unit))) { |
3923 | ultimate_result_parameter.flow_divert_control_unit = control_unit; |
3924 | } |
3925 | socket_layer_non_id_conditions = TRUE; |
3926 | break; |
3927 | } |
3928 | case NECP_POLICY_RESULT_IP_TUNNEL: { |
3929 | struct necp_policy_result_ip_tunnel tunnel_parameters; |
3930 | u_int32_t tunnel_parameters_length = necp_policy_get_result_parameter_length(policy); |
3931 | if (tunnel_parameters_length > sizeof(u_int32_t) && |
3932 | tunnel_parameters_length <= sizeof(struct necp_policy_result_ip_tunnel) && |
3933 | necp_policy_get_result_parameter(policy, (u_int8_t *)&tunnel_parameters, sizeof(tunnel_parameters))) { |
3934 | ifnet_t tunnel_interface = NULL; |
3935 | tunnel_parameters.interface_name[tunnel_parameters_length - sizeof(u_int32_t) - 1] = 0; // Make sure the string is NULL terminated |
3936 | if (ifnet_find_by_name(tunnel_parameters.interface_name, &tunnel_interface) == 0) { |
3937 | ultimate_result_parameter.tunnel_interface_index = tunnel_interface->if_index; |
3938 | ifnet_release(tunnel_interface); |
3939 | } |
3940 | |
3941 | secondary_result = tunnel_parameters.secondary_result; |
3942 | if (secondary_result) { |
3943 | cond_last_interface_index = ultimate_result_parameter.tunnel_interface_index; |
3944 | } |
3945 | } |
3946 | |
3947 | if (socket_only_conditions) { // socket_ip_conditions can be TRUE or FALSE |
3948 | socket_layer_non_id_conditions = TRUE; |
3949 | ip_output_layer_id_condition = TRUE; |
3950 | if (secondary_result) { |
3951 | ip_output_layer_tunnel_condition_from_id = TRUE; |
3952 | } |
3953 | } else if (socket_ip_conditions) { |
3954 | socket_layer_non_id_conditions = TRUE; |
3955 | ip_output_layer_id_condition = TRUE; |
3956 | ip_output_layer_non_id_conditions = TRUE; |
3957 | if (secondary_result) { |
3958 | ip_output_layer_tunnel_condition_from_id = TRUE; |
3959 | ip_output_layer_tunnel_condition_from_non_id = TRUE; |
3960 | } |
3961 | } |
3962 | break; |
3963 | } |
3964 | case NECP_POLICY_RESULT_TRIGGER: |
3965 | case NECP_POLICY_RESULT_TRIGGER_IF_NEEDED: |
3966 | case NECP_POLICY_RESULT_TRIGGER_SCOPED: |
3967 | case NECP_POLICY_RESULT_NO_TRIGGER_SCOPED: { |
3968 | struct necp_policy_result_service service_parameters; |
3969 | u_int32_t service_result_length = necp_policy_get_result_parameter_length(policy); |
3970 | bool = FALSE; |
3971 | if (service_result_length >= (sizeof(service_parameters))) { |
3972 | has_extra_service_data = TRUE; |
3973 | } |
3974 | if (necp_policy_get_result_parameter(policy, (u_int8_t *)&service_parameters, sizeof(service_parameters))) { |
3975 | ultimate_result_parameter.service.identifier = necp_create_uuid_service_id_mapping(service_parameters.identifier); |
3976 | if (ultimate_result_parameter.service.identifier != 0) { |
3977 | uuid_copy(policy->applied_result_uuid, service_parameters.identifier); |
3978 | socket_layer_non_id_conditions = TRUE; |
3979 | if (has_extra_service_data) { |
3980 | ultimate_result_parameter.service.data = service_parameters.data; |
3981 | } else { |
3982 | ultimate_result_parameter.service.data = 0; |
3983 | } |
3984 | } |
3985 | } |
3986 | break; |
3987 | } |
3988 | case NECP_POLICY_RESULT_USE_NETAGENT: |
3989 | case NECP_POLICY_RESULT_NETAGENT_SCOPED: { |
3990 | uuid_t netagent_uuid; |
3991 | if (necp_policy_get_result_parameter(policy, (u_int8_t *)&netagent_uuid, sizeof(netagent_uuid))) { |
3992 | ultimate_result_parameter.netagent_id = necp_create_uuid_service_id_mapping(netagent_uuid); |
3993 | if (ultimate_result_parameter.netagent_id != 0) { |
3994 | uuid_copy(policy->applied_result_uuid, netagent_uuid); |
3995 | socket_layer_non_id_conditions = TRUE; |
3996 | } |
3997 | } |
3998 | break; |
3999 | } |
4000 | case NECP_POLICY_RESULT_SOCKET_SCOPED: { |
4001 | u_int32_t interface_name_length = necp_policy_get_result_parameter_length(policy); |
4002 | if (interface_name_length <= IFXNAMSIZ && interface_name_length > 0) { |
4003 | char interface_name[IFXNAMSIZ]; |
4004 | ifnet_t scope_interface = NULL; |
4005 | necp_policy_get_result_parameter(policy, (u_int8_t *)interface_name, interface_name_length); |
4006 | interface_name[interface_name_length - 1] = 0; // Make sure the string is NULL terminated |
4007 | if (ifnet_find_by_name(interface_name, &scope_interface) == 0) { |
4008 | ultimate_result_parameter.scoped_interface_index = scope_interface->if_index; |
4009 | socket_layer_non_id_conditions = TRUE; |
4010 | ifnet_release(scope_interface); |
4011 | } |
4012 | } |
4013 | break; |
4014 | } |
4015 | case NECP_POLICY_RESULT_SCOPED_DIRECT: { |
4016 | socket_layer_non_id_conditions = TRUE; |
4017 | break; |
4018 | } |
4019 | case NECP_POLICY_RESULT_ROUTE_RULES: { |
4020 | if (policy->route_rules != NULL && policy->route_rules_size > 0) { |
4021 | u_int32_t route_rule_id = necp_create_route_rule(&necp_route_rules, policy->route_rules, policy->route_rules_size); |
4022 | if (route_rule_id > 0) { |
4023 | policy->applied_route_rules_id = route_rule_id; |
4024 | ultimate_result_parameter.route_rule_id = route_rule_id; |
4025 | socket_layer_non_id_conditions = TRUE; |
4026 | } |
4027 | } |
4028 | break; |
4029 | } |
4030 | default: { |
4031 | break; |
4032 | } |
4033 | } |
4034 | |
4035 | if (socket_layer_non_id_conditions) { |
4036 | necp_kernel_policy_id policy_id = necp_kernel_socket_policy_add(policy->order, session->session_order, session->proc_pid, master_condition_mask, master_condition_negated_mask, cond_app_id, cond_real_app_id, cond_custom_entitlement, cond_account_id, cond_domain, cond_pid, cond_uid, cond_bound_interface, cond_traffic_class, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, &cond_agent_type, ultimate_result, ultimate_result_parameter); |
4037 | |
4038 | if (policy_id == 0) { |
4039 | NECPLOG0(LOG_DEBUG, "Error applying socket kernel policy" ); |
4040 | goto fail; |
4041 | } |
4042 | |
4043 | cond_ip_output_layer_id = policy_id; |
4044 | policy->kernel_socket_policies[0] = policy_id; |
4045 | } |
4046 | |
4047 | if (ip_output_layer_non_id_conditions) { |
4048 | u_int32_t condition_mask = master_condition_mask; |
4049 | if (ip_output_layer_non_id_only) { |
4050 | condition_mask |= NECP_KERNEL_CONDITION_POLICY_ID; |
4051 | } |
4052 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS, session->session_order, session->proc_pid, condition_mask, master_condition_negated_mask, NECP_KERNEL_POLICY_ID_NONE, cond_bound_interface, 0, cond_protocol, &cond_local_start, &cond_local_end, cond_local_prefix, &cond_remote_start, &cond_remote_end, cond_remote_prefix, ultimate_result, ultimate_result_parameter); |
4053 | |
4054 | if (policy_id == 0) { |
4055 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4056 | goto fail; |
4057 | } |
4058 | |
4059 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS] = policy_id; |
4060 | } |
4061 | |
4062 | if (ip_output_layer_id_condition) { |
4063 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->order, NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION, session->session_order, session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_ALL_INTERFACES, 0, cond_ip_output_layer_id, NULL, 0, 0, NULL, NULL, 0, NULL, NULL, 0, ultimate_result, ultimate_result_parameter); |
4064 | |
4065 | if (policy_id == 0) { |
4066 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4067 | goto fail; |
4068 | } |
4069 | |
4070 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION] = policy_id; |
4071 | } |
4072 | |
4073 | // Extra policies for IP Output tunnels for when packets loop back |
4074 | if (ip_output_layer_tunnel_condition_from_id) { |
4075 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->order, NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION, session->session_order, session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE | NECP_KERNEL_CONDITION_ALL_INTERFACES, 0, policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_CONDITIONS], NULL, cond_last_interface_index, 0, NULL, NULL, 0, NULL, NULL, 0, secondary_result, secondary_result_parameter); |
4076 | |
4077 | if (policy_id == 0) { |
4078 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4079 | goto fail; |
4080 | } |
4081 | |
4082 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION] = policy_id; |
4083 | } |
4084 | |
4085 | if (ip_output_layer_tunnel_condition_from_id) { |
4086 | necp_kernel_policy_id policy_id = necp_kernel_ip_output_policy_add(policy->order, NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION, session->session_order, session->proc_pid, NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE | NECP_KERNEL_CONDITION_ALL_INTERFACES, 0, policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION], NULL, cond_last_interface_index, 0, NULL, NULL, 0, NULL, NULL, 0, secondary_result, secondary_result_parameter); |
4087 | |
4088 | if (policy_id == 0) { |
4089 | NECPLOG0(LOG_DEBUG, "Error applying IP output kernel policy" ); |
4090 | goto fail; |
4091 | } |
4092 | |
4093 | policy->kernel_ip_output_policies[NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION] = policy_id; |
4094 | } |
4095 | |
4096 | policy->applied = TRUE; |
4097 | policy->pending_update = FALSE; |
4098 | return (TRUE); |
4099 | |
4100 | fail: |
4101 | return (FALSE); |
4102 | } |
4103 | |
4104 | static void |
4105 | necp_policy_apply_all(struct necp_session *session) |
4106 | { |
4107 | struct necp_session_policy *policy = NULL; |
4108 | struct necp_session_policy *temp_policy = NULL; |
4109 | struct kev_necp_policies_changed_data kev_data; |
4110 | kev_data.changed_count = 0; |
4111 | |
4112 | lck_rw_lock_exclusive(&necp_kernel_policy_lock); |
4113 | |
4114 | // Remove exisiting applied policies |
4115 | if (session->dirty) { |
4116 | LIST_FOREACH_SAFE(policy, &session->policies, chain, temp_policy) { |
4117 | if (policy->pending_deletion) { |
4118 | if (policy->applied) { |
4119 | necp_policy_unapply(policy); |
4120 | } |
4121 | // Delete the policy |
4122 | necp_policy_delete(session, policy); |
4123 | } else if (!policy->applied) { |
4124 | necp_policy_apply(session, policy); |
4125 | } else if (policy->pending_update) { |
4126 | // Must have been applied, but needs an update. Remove and re-add. |
4127 | necp_policy_unapply(policy); |
4128 | necp_policy_apply(session, policy); |
4129 | } |
4130 | } |
4131 | |
4132 | necp_kernel_socket_policies_update_uuid_table(); |
4133 | necp_kernel_socket_policies_reprocess(); |
4134 | necp_kernel_ip_output_policies_reprocess(); |
4135 | |
4136 | // Clear dirty bit flags |
4137 | session->dirty = FALSE; |
4138 | } |
4139 | |
4140 | lck_rw_done(&necp_kernel_policy_lock); |
4141 | |
4142 | necp_update_all_clients(); |
4143 | necp_post_change_event(&kev_data); |
4144 | |
4145 | if (necp_debug) { |
4146 | NECPLOG0(LOG_DEBUG, "Applied NECP policies" ); |
4147 | } |
4148 | } |
4149 | |
4150 | // Kernel Policy Management |
4151 | // --------------------- |
4152 | // Kernel policies are derived from session policies |
4153 | static necp_kernel_policy_id |
4154 | necp_kernel_policy_get_new_id(bool socket_level) |
4155 | { |
4156 | static necp_kernel_policy_id necp_last_kernel_socket_policy_id = 0; |
4157 | static necp_kernel_policy_id necp_last_kernel_ip_policy_id = 0; |
4158 | |
4159 | necp_kernel_policy_id newid = NECP_KERNEL_POLICY_ID_NONE; |
4160 | |
4161 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4162 | |
4163 | if (socket_level) { |
4164 | bool wrapped = FALSE; |
4165 | do { |
4166 | necp_last_kernel_socket_policy_id++; |
4167 | if (necp_last_kernel_socket_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET || |
4168 | necp_last_kernel_socket_policy_id >= NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) { |
4169 | if (wrapped) { |
4170 | // Already wrapped, give up |
4171 | NECPLOG0(LOG_ERR, "Failed to find a free socket kernel policy ID.\n" ); |
4172 | return (NECP_KERNEL_POLICY_ID_NONE); |
4173 | } |
4174 | necp_last_kernel_socket_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_SOCKET; |
4175 | wrapped = TRUE; |
4176 | } |
4177 | newid = necp_last_kernel_socket_policy_id; |
4178 | } while (necp_kernel_socket_policy_find(newid) != NULL); // If already used, keep trying |
4179 | } else { |
4180 | bool wrapped = FALSE; |
4181 | do { |
4182 | necp_last_kernel_ip_policy_id++; |
4183 | if (necp_last_kernel_ip_policy_id < NECP_KERNEL_POLICY_ID_FIRST_VALID_IP) { |
4184 | if (wrapped) { |
4185 | // Already wrapped, give up |
4186 | NECPLOG0(LOG_ERR, "Failed to find a free IP kernel policy ID.\n" ); |
4187 | return (NECP_KERNEL_POLICY_ID_NONE); |
4188 | } |
4189 | necp_last_kernel_ip_policy_id = NECP_KERNEL_POLICY_ID_FIRST_VALID_IP; |
4190 | wrapped = TRUE; |
4191 | } |
4192 | newid = necp_last_kernel_ip_policy_id; |
4193 | } while (necp_kernel_ip_output_policy_find(newid) != NULL); // If already used, keep trying |
4194 | } |
4195 | |
4196 | if (newid == NECP_KERNEL_POLICY_ID_NONE) { |
4197 | NECPLOG0(LOG_ERR, "Allocate kernel policy id failed.\n" ); |
4198 | return (NECP_KERNEL_POLICY_ID_NONE); |
4199 | } |
4200 | |
4201 | return (newid); |
4202 | } |
4203 | |
4204 | #define NECP_KERNEL_VALID_SOCKET_CONDITIONS (NECP_KERNEL_CONDITION_APP_ID | NECP_KERNEL_CONDITION_REAL_APP_ID | NECP_KERNEL_CONDITION_DOMAIN | NECP_KERNEL_CONDITION_ACCOUNT_ID | NECP_KERNEL_CONDITION_PID | NECP_KERNEL_CONDITION_UID | NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_TRAFFIC_CLASS | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_ENTITLEMENT | NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT | NECP_KERNEL_CONDITION_AGENT_TYPE) |
4205 | static necp_kernel_policy_id |
4206 | necp_kernel_socket_policy_add(necp_policy_order order, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_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, pid_t cond_pid, uid_t cond_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, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
4207 | { |
4208 | struct necp_kernel_socket_policy *new_kernel_policy = NULL; |
4209 | struct necp_kernel_socket_policy *tmp_kernel_policy = NULL; |
4210 | |
4211 | MALLOC_ZONE(new_kernel_policy, struct necp_kernel_socket_policy *, sizeof(*new_kernel_policy), M_NECP_SOCKET_POLICY, M_WAITOK); |
4212 | if (new_kernel_policy == NULL) { |
4213 | goto done; |
4214 | } |
4215 | |
4216 | memset(new_kernel_policy, 0, sizeof(*new_kernel_policy)); // M_ZERO is not supported for MALLOC_ZONE |
4217 | new_kernel_policy->id = necp_kernel_policy_get_new_id(true); |
4218 | new_kernel_policy->order = order; |
4219 | new_kernel_policy->session_order = session_order; |
4220 | new_kernel_policy->session_pid = session_pid; |
4221 | |
4222 | // Sanitize condition mask |
4223 | new_kernel_policy->condition_mask = (condition_mask & NECP_KERNEL_VALID_SOCKET_CONDITIONS); |
4224 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE)) { |
4225 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
4226 | } |
4227 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) && !(new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID)) { |
4228 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REAL_APP_ID; |
4229 | } |
4230 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) && !(new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID)) { |
4231 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_ENTITLEMENT; |
4232 | } |
4233 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX)) { |
4234 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
4235 | } |
4236 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX)) { |
4237 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
4238 | } |
4239 | new_kernel_policy->condition_negated_mask = condition_negated_mask & new_kernel_policy->condition_mask; |
4240 | |
4241 | // Set condition values |
4242 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
4243 | new_kernel_policy->cond_app_id = cond_app_id; |
4244 | } |
4245 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
4246 | new_kernel_policy->cond_real_app_id = cond_real_app_id; |
4247 | } |
4248 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
4249 | new_kernel_policy->cond_custom_entitlement = cond_custom_entitlement; |
4250 | new_kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_unknown; |
4251 | } |
4252 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
4253 | new_kernel_policy->cond_account_id = cond_account_id; |
4254 | } |
4255 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
4256 | new_kernel_policy->cond_domain = cond_domain; |
4257 | new_kernel_policy->cond_domain_dot_count = necp_count_dots(cond_domain, strlen(cond_domain)); |
4258 | } |
4259 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PID) { |
4260 | new_kernel_policy->cond_pid = cond_pid; |
4261 | } |
4262 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_UID) { |
4263 | new_kernel_policy->cond_uid = cond_uid; |
4264 | } |
4265 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
4266 | if (cond_bound_interface) { |
4267 | ifnet_reference(cond_bound_interface); |
4268 | } |
4269 | new_kernel_policy->cond_bound_interface = cond_bound_interface; |
4270 | } |
4271 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
4272 | new_kernel_policy->cond_traffic_class = cond_traffic_class; |
4273 | } |
4274 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
4275 | new_kernel_policy->cond_protocol = cond_protocol; |
4276 | } |
4277 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
4278 | memcpy(&new_kernel_policy->cond_local_start, cond_local_start, cond_local_start->sa.sa_len); |
4279 | } |
4280 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
4281 | memcpy(&new_kernel_policy->cond_local_end, cond_local_end, cond_local_end->sa.sa_len); |
4282 | } |
4283 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
4284 | new_kernel_policy->cond_local_prefix = cond_local_prefix; |
4285 | } |
4286 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
4287 | memcpy(&new_kernel_policy->cond_remote_start, cond_remote_start, cond_remote_start->sa.sa_len); |
4288 | } |
4289 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
4290 | memcpy(&new_kernel_policy->cond_remote_end, cond_remote_end, cond_remote_end->sa.sa_len); |
4291 | } |
4292 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
4293 | new_kernel_policy->cond_remote_prefix = cond_remote_prefix; |
4294 | } |
4295 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
4296 | memcpy(&new_kernel_policy->cond_agent_type, cond_agent_type, sizeof(*cond_agent_type)); |
4297 | } |
4298 | |
4299 | new_kernel_policy->result = result; |
4300 | memcpy(&new_kernel_policy->result_parameter, &result_parameter, sizeof(result_parameter)); |
4301 | |
4302 | if (necp_debug) { |
4303 | NECPLOG(LOG_DEBUG, "Added kernel policy: socket, id=%d, mask=%x\n" , new_kernel_policy->id, new_kernel_policy->condition_mask); |
4304 | } |
4305 | LIST_INSERT_SORTED_TWICE_ASCENDING(&necp_kernel_socket_policies, new_kernel_policy, chain, session_order, order, tmp_kernel_policy); |
4306 | done: |
4307 | return (new_kernel_policy ? new_kernel_policy->id : 0); |
4308 | } |
4309 | |
4310 | static struct necp_kernel_socket_policy * |
4311 | necp_kernel_socket_policy_find(necp_kernel_policy_id policy_id) |
4312 | { |
4313 | struct necp_kernel_socket_policy *kernel_policy = NULL; |
4314 | struct necp_kernel_socket_policy *tmp_kernel_policy = NULL; |
4315 | |
4316 | if (policy_id == 0) { |
4317 | return (NULL); |
4318 | } |
4319 | |
4320 | LIST_FOREACH_SAFE(kernel_policy, &necp_kernel_socket_policies, chain, tmp_kernel_policy) { |
4321 | if (kernel_policy->id == policy_id) { |
4322 | return (kernel_policy); |
4323 | } |
4324 | } |
4325 | |
4326 | return (NULL); |
4327 | } |
4328 | |
4329 | static bool |
4330 | necp_kernel_socket_policy_delete(necp_kernel_policy_id policy_id) |
4331 | { |
4332 | struct necp_kernel_socket_policy *policy = NULL; |
4333 | |
4334 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4335 | |
4336 | policy = necp_kernel_socket_policy_find(policy_id); |
4337 | if (policy) { |
4338 | LIST_REMOVE(policy, chain); |
4339 | |
4340 | if (policy->cond_bound_interface) { |
4341 | ifnet_release(policy->cond_bound_interface); |
4342 | policy->cond_bound_interface = NULL; |
4343 | } |
4344 | |
4345 | if (policy->cond_domain) { |
4346 | FREE(policy->cond_domain, M_NECP); |
4347 | policy->cond_domain = NULL; |
4348 | } |
4349 | |
4350 | if (policy->cond_custom_entitlement) { |
4351 | FREE(policy->cond_custom_entitlement, M_NECP); |
4352 | policy->cond_custom_entitlement = NULL; |
4353 | } |
4354 | |
4355 | FREE_ZONE(policy, sizeof(*policy), M_NECP_SOCKET_POLICY); |
4356 | return (TRUE); |
4357 | } |
4358 | |
4359 | return (FALSE); |
4360 | } |
4361 | |
4362 | static inline const char * |
4363 | necp_get_result_description(char *result_string, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
4364 | { |
4365 | uuid_string_t uuid_string; |
4366 | switch (result) { |
4367 | case NECP_KERNEL_POLICY_RESULT_NONE: { |
4368 | snprintf(result_string, MAX_RESULT_STRING_LEN, "None" ); |
4369 | break; |
4370 | } |
4371 | case NECP_KERNEL_POLICY_RESULT_PASS: { |
4372 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Pass" ); |
4373 | break; |
4374 | } |
4375 | case NECP_KERNEL_POLICY_RESULT_SKIP: { |
4376 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Skip (%u)" , result_parameter.skip_policy_order); |
4377 | break; |
4378 | } |
4379 | case NECP_KERNEL_POLICY_RESULT_DROP: { |
4380 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Drop" ); |
4381 | break; |
4382 | } |
4383 | case NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT: { |
4384 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketDivert (%d)" , result_parameter.flow_divert_control_unit); |
4385 | break; |
4386 | } |
4387 | case NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER: { |
4388 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketFilter (%d)" , result_parameter.filter_control_unit); |
4389 | break; |
4390 | } |
4391 | case NECP_KERNEL_POLICY_RESULT_IP_TUNNEL: { |
4392 | ifnet_t interface = ifindex2ifnet[result_parameter.tunnel_interface_index]; |
4393 | snprintf(result_string, MAX_RESULT_STRING_LEN, "IPTunnel (%s%d)" , ifnet_name(interface), ifnet_unit(interface)); |
4394 | break; |
4395 | } |
4396 | case NECP_KERNEL_POLICY_RESULT_IP_FILTER: { |
4397 | snprintf(result_string, MAX_RESULT_STRING_LEN, "IPFilter" ); |
4398 | break; |
4399 | } |
4400 | case NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED: { |
4401 | ifnet_t interface = ifindex2ifnet[result_parameter.scoped_interface_index]; |
4402 | snprintf(result_string, MAX_RESULT_STRING_LEN, "SocketScoped (%s%d)" , ifnet_name(interface), ifnet_unit(interface)); |
4403 | break; |
4404 | } |
4405 | case NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT: { |
4406 | snprintf(result_string, MAX_RESULT_STRING_LEN, "ScopedDirect" ); |
4407 | break; |
4408 | } |
4409 | case NECP_KERNEL_POLICY_RESULT_ROUTE_RULES: { |
4410 | int index = 0; |
4411 | char interface_names[IFXNAMSIZ][MAX_ROUTE_RULE_INTERFACES]; |
4412 | struct necp_route_rule *route_rule = necp_lookup_route_rule_locked(&necp_route_rules, result_parameter.route_rule_id); |
4413 | if (route_rule != NULL) { |
4414 | for (index = 0; index < MAX_ROUTE_RULE_INTERFACES; index++) { |
4415 | if (route_rule->exception_if_indices[index] != 0) { |
4416 | ifnet_t interface = ifindex2ifnet[route_rule->exception_if_indices[index]]; |
4417 | snprintf(interface_names[index], IFXNAMSIZ, "%s%d" , ifnet_name(interface), ifnet_unit(interface)); |
4418 | } else { |
4419 | memset(interface_names[index], 0, IFXNAMSIZ); |
4420 | } |
4421 | } |
4422 | switch (route_rule->default_action) { |
4423 | case NECP_ROUTE_RULE_DENY_INTERFACE: |
4424 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Only %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4425 | (route_rule->cellular_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Cell " : "" , |
4426 | (route_rule->wifi_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "WiFi " : "" , |
4427 | (route_rule->wired_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Wired " : "" , |
4428 | (route_rule->expensive_action == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? "Exp " : "" , |
4429 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[0] : "" , |
4430 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4431 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[1] : "" , |
4432 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4433 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[2] : "" , |
4434 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4435 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[3] : "" , |
4436 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4437 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[4] : "" , |
4438 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4439 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[5] : "" , |
4440 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4441 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[6] : "" , |
4442 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4443 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[7] : "" , |
4444 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4445 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[8] : "" , |
4446 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? " " : "" , |
4447 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_ALLOW_INTERFACE) ? interface_names[9] : "" ); |
4448 | break; |
4449 | case NECP_ROUTE_RULE_ALLOW_INTERFACE: |
4450 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4451 | (route_rule->cellular_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!Cell " : "" , |
4452 | (route_rule->wifi_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!WiFi " : "" , |
4453 | (route_rule->wired_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!Wired " : "" , |
4454 | (route_rule->expensive_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!Exp " : "" , |
4455 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4456 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[0] : "" , |
4457 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4458 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[1] : "" , |
4459 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4460 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[2] : "" , |
4461 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4462 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[3] : "" , |
4463 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4464 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[4] : "" , |
4465 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4466 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[5] : "" , |
4467 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4468 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[6] : "" , |
4469 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4470 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[7] : "" , |
4471 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4472 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[8] : "" , |
4473 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_DENY_INTERFACE) ? "!" : "" , |
4474 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_DENY_INTERFACE) ? interface_names[9] : "" ); |
4475 | break; |
4476 | case NECP_ROUTE_RULE_QOS_MARKING: |
4477 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (QoSMarking %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)" , |
4478 | (route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Cell " : "" , |
4479 | (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING) ? "WiFi " : "" , |
4480 | (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Wired " : "" , |
4481 | (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING) ? "Exp " : "" , |
4482 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[0] : "" , |
4483 | (route_rule->exception_if_actions[0] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4484 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[1] : "" , |
4485 | (route_rule->exception_if_actions[1] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4486 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[2] : "" , |
4487 | (route_rule->exception_if_actions[2] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4488 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[3] : "" , |
4489 | (route_rule->exception_if_actions[3] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4490 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[4] : "" , |
4491 | (route_rule->exception_if_actions[4] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4492 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[5] : "" , |
4493 | (route_rule->exception_if_actions[5] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4494 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[6] : "" , |
4495 | (route_rule->exception_if_actions[6] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4496 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[7] : "" , |
4497 | (route_rule->exception_if_actions[7] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4498 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[8] : "" , |
4499 | (route_rule->exception_if_actions[8] == NECP_ROUTE_RULE_QOS_MARKING) ? " " : "" , |
4500 | (route_rule->exception_if_actions[9] == NECP_ROUTE_RULE_QOS_MARKING) ? interface_names[9] : "" ); |
4501 | break; |
4502 | default: |
4503 | snprintf(result_string, MAX_RESULT_STRING_LEN, "RouteRules (Unknown)" ); |
4504 | break; |
4505 | } |
4506 | } |
4507 | break; |
4508 | } |
4509 | case NECP_KERNEL_POLICY_RESULT_USE_NETAGENT: { |
4510 | bool found_mapping = FALSE; |
4511 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.netagent_id); |
4512 | if (mapping != NULL) { |
4513 | uuid_unparse(mapping->uuid, uuid_string); |
4514 | found_mapping = TRUE; |
4515 | } |
4516 | snprintf(result_string, MAX_RESULT_STRING_LEN, "UseNetAgent (%s)" , found_mapping ? uuid_string : "Unknown" ); |
4517 | break; |
4518 | } |
4519 | case NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED: { |
4520 | bool found_mapping = FALSE; |
4521 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.netagent_id); |
4522 | if (mapping != NULL) { |
4523 | uuid_unparse(mapping->uuid, uuid_string); |
4524 | found_mapping = TRUE; |
4525 | } |
4526 | snprintf(result_string, MAX_RESULT_STRING_LEN, "NetAgentScoped (%s)" , found_mapping ? uuid_string : "Unknown" ); |
4527 | break; |
4528 | } |
4529 | case NECP_POLICY_RESULT_TRIGGER: { |
4530 | bool found_mapping = FALSE; |
4531 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.service.identifier); |
4532 | if (mapping != NULL) { |
4533 | uuid_unparse(mapping->uuid, uuid_string); |
4534 | found_mapping = TRUE; |
4535 | } |
4536 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Trigger (%s.%d)" , found_mapping ? uuid_string : "Unknown" , result_parameter.service.data); |
4537 | break; |
4538 | } |
4539 | case NECP_POLICY_RESULT_TRIGGER_IF_NEEDED: { |
4540 | bool found_mapping = FALSE; |
4541 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.service.identifier); |
4542 | if (mapping != NULL) { |
4543 | uuid_unparse(mapping->uuid, uuid_string); |
4544 | found_mapping = TRUE; |
4545 | } |
4546 | snprintf(result_string, MAX_RESULT_STRING_LEN, "TriggerIfNeeded (%s.%d)" , found_mapping ? uuid_string : "Unknown" , result_parameter.service.data); |
4547 | break; |
4548 | } |
4549 | case NECP_POLICY_RESULT_TRIGGER_SCOPED: { |
4550 | bool found_mapping = FALSE; |
4551 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.service.identifier); |
4552 | if (mapping != NULL) { |
4553 | uuid_unparse(mapping->uuid, uuid_string); |
4554 | found_mapping = TRUE; |
4555 | } |
4556 | snprintf(result_string, MAX_RESULT_STRING_LEN, "TriggerScoped (%s.%d)" , found_mapping ? uuid_string : "Unknown" , result_parameter.service.data); |
4557 | break; |
4558 | } |
4559 | case NECP_POLICY_RESULT_NO_TRIGGER_SCOPED: { |
4560 | bool found_mapping = FALSE; |
4561 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(result_parameter.service.identifier); |
4562 | if (mapping != NULL) { |
4563 | uuid_unparse(mapping->uuid, uuid_string); |
4564 | found_mapping = TRUE; |
4565 | } |
4566 | snprintf(result_string, MAX_RESULT_STRING_LEN, "NoTriggerScoped (%s.%d)" , found_mapping ? uuid_string : "Unknown" , result_parameter.service.data); |
4567 | break; |
4568 | } |
4569 | default: { |
4570 | snprintf(result_string, MAX_RESULT_STRING_LEN, "Unknown %d (%d)" , result, result_parameter.tunnel_interface_index); |
4571 | break; |
4572 | } |
4573 | } |
4574 | return (result_string); |
4575 | } |
4576 | |
4577 | static void |
4578 | necp_kernel_socket_policies_dump_all(void) |
4579 | { |
4580 | if (necp_debug) { |
4581 | struct necp_kernel_socket_policy *policy = NULL; |
4582 | int policy_i; |
4583 | int app_i; |
4584 | char result_string[MAX_RESULT_STRING_LEN]; |
4585 | char proc_name_string[MAXCOMLEN + 1]; |
4586 | memset(result_string, 0, MAX_RESULT_STRING_LEN); |
4587 | memset(proc_name_string, 0, MAXCOMLEN + 1); |
4588 | |
4589 | NECPLOG0(LOG_DEBUG, "NECP Application Policies:\n" ); |
4590 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4591 | for (policy_i = 0; necp_kernel_socket_policies_app_layer_map != NULL && necp_kernel_socket_policies_app_layer_map[policy_i] != NULL; policy_i++) { |
4592 | policy = necp_kernel_socket_policies_app_layer_map[policy_i]; |
4593 | proc_name(policy->session_pid, proc_name_string, MAXCOMLEN); |
4594 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d\tMask: %5x\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
4595 | } |
4596 | if (necp_kernel_socket_policies_app_layer_map[0] != NULL) { |
4597 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4598 | } |
4599 | |
4600 | NECPLOG0(LOG_DEBUG, "NECP Socket Policies:\n" ); |
4601 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4602 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4603 | NECPLOG(LOG_DEBUG, "\tApp Bucket: %d\n" , app_i); |
4604 | for (policy_i = 0; necp_kernel_socket_policies_map[app_i] != NULL && (necp_kernel_socket_policies_map[app_i])[policy_i] != NULL; policy_i++) { |
4605 | policy = (necp_kernel_socket_policies_map[app_i])[policy_i]; |
4606 | proc_name(policy->session_pid, proc_name_string, MAXCOMLEN); |
4607 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d\tMask: %5x\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
4608 | } |
4609 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
4610 | } |
4611 | } |
4612 | } |
4613 | |
4614 | static inline bool |
4615 | necp_kernel_socket_result_is_trigger_service_type(struct necp_kernel_socket_policy *kernel_policy) |
4616 | { |
4617 | return (kernel_policy->result >= NECP_KERNEL_POLICY_RESULT_TRIGGER && kernel_policy->result <= NECP_KERNEL_POLICY_RESULT_NO_TRIGGER_SCOPED); |
4618 | } |
4619 | |
4620 | static inline bool |
4621 | necp_kernel_socket_policy_results_overlap(struct necp_kernel_socket_policy *upper_policy, struct necp_kernel_socket_policy *lower_policy) |
4622 | { |
4623 | if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_DROP) { |
4624 | // Drop always cancels out lower policies |
4625 | return (TRUE); |
4626 | } else if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER || |
4627 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES || |
4628 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT || |
4629 | upper_policy->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
4630 | // Filters and route rules never cancel out lower policies |
4631 | return (FALSE); |
4632 | } else if (necp_kernel_socket_result_is_trigger_service_type(upper_policy)) { |
4633 | // Trigger/Scoping policies can overlap one another, but not other results |
4634 | return (necp_kernel_socket_result_is_trigger_service_type(lower_policy)); |
4635 | } else if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
4636 | if (upper_policy->session_order != lower_policy->session_order) { |
4637 | // A skip cannot override a policy of a different session |
4638 | return (FALSE); |
4639 | } else { |
4640 | if (upper_policy->result_parameter.skip_policy_order == 0 || |
4641 | lower_policy->order >= upper_policy->result_parameter.skip_policy_order) { |
4642 | // This policy is beyond the skip |
4643 | return (FALSE); |
4644 | } else { |
4645 | // This policy is inside the skip |
4646 | return (TRUE); |
4647 | } |
4648 | } |
4649 | } |
4650 | |
4651 | // A hard pass, flow divert, tunnel, or scope will currently block out lower policies |
4652 | return (TRUE); |
4653 | } |
4654 | |
4655 | static bool |
4656 | necp_kernel_socket_policy_is_unnecessary(struct necp_kernel_socket_policy *policy, struct necp_kernel_socket_policy **policy_array, int valid_indices) |
4657 | { |
4658 | bool can_skip = FALSE; |
4659 | u_int32_t highest_skip_session_order = 0; |
4660 | u_int32_t highest_skip_order = 0; |
4661 | int i; |
4662 | for (i = 0; i < valid_indices; i++) { |
4663 | struct necp_kernel_socket_policy *compared_policy = policy_array[i]; |
4664 | |
4665 | // For policies in a skip window, we can't mark conflicting policies as unnecessary |
4666 | if (can_skip) { |
4667 | if (highest_skip_session_order != compared_policy->session_order || |
4668 | (highest_skip_order != 0 && compared_policy->order >= highest_skip_order)) { |
4669 | // If we've moved on to the next session, or passed the skip window |
4670 | highest_skip_session_order = 0; |
4671 | highest_skip_order = 0; |
4672 | can_skip = FALSE; |
4673 | } else { |
4674 | // If this policy is also a skip, in can increase the skip window |
4675 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
4676 | if (compared_policy->result_parameter.skip_policy_order > highest_skip_order) { |
4677 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
4678 | } |
4679 | } |
4680 | continue; |
4681 | } |
4682 | } |
4683 | |
4684 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
4685 | // This policy is a skip. Set the skip window accordingly |
4686 | can_skip = TRUE; |
4687 | highest_skip_session_order = compared_policy->session_order; |
4688 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
4689 | } |
4690 | |
4691 | // The result of the compared policy must be able to block out this policy result |
4692 | if (!necp_kernel_socket_policy_results_overlap(compared_policy, policy)) { |
4693 | continue; |
4694 | } |
4695 | |
4696 | // If new policy matches All Interfaces, compared policy must also |
4697 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
4698 | continue; |
4699 | } |
4700 | |
4701 | // Default makes lower policies unecessary always |
4702 | if (compared_policy->condition_mask == 0) { |
4703 | return (TRUE); |
4704 | } |
4705 | |
4706 | // Compared must be more general than policy, and include only conditions within policy |
4707 | if ((policy->condition_mask & compared_policy->condition_mask) != compared_policy->condition_mask) { |
4708 | continue; |
4709 | } |
4710 | |
4711 | // Negative conditions must match for the overlapping conditions |
4712 | if ((policy->condition_negated_mask & compared_policy->condition_mask) != (compared_policy->condition_negated_mask & compared_policy->condition_mask)) { |
4713 | continue; |
4714 | } |
4715 | |
4716 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN && |
4717 | strcmp(compared_policy->cond_domain, policy->cond_domain) != 0) { |
4718 | continue; |
4719 | } |
4720 | |
4721 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT && |
4722 | strcmp(compared_policy->cond_custom_entitlement, policy->cond_custom_entitlement) != 0) { |
4723 | continue; |
4724 | } |
4725 | |
4726 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && |
4727 | compared_policy->cond_account_id != policy->cond_account_id) { |
4728 | continue; |
4729 | } |
4730 | |
4731 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID && |
4732 | compared_policy->cond_policy_id != policy->cond_policy_id) { |
4733 | continue; |
4734 | } |
4735 | |
4736 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID && |
4737 | compared_policy->cond_app_id != policy->cond_app_id) { |
4738 | continue; |
4739 | } |
4740 | |
4741 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID && |
4742 | compared_policy->cond_real_app_id != policy->cond_real_app_id) { |
4743 | continue; |
4744 | } |
4745 | |
4746 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PID && |
4747 | compared_policy->cond_pid != policy->cond_pid) { |
4748 | continue; |
4749 | } |
4750 | |
4751 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_UID && |
4752 | compared_policy->cond_uid != policy->cond_uid) { |
4753 | continue; |
4754 | } |
4755 | |
4756 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE && |
4757 | compared_policy->cond_bound_interface != policy->cond_bound_interface) { |
4758 | continue; |
4759 | } |
4760 | |
4761 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL && |
4762 | compared_policy->cond_protocol != policy->cond_protocol) { |
4763 | continue; |
4764 | } |
4765 | |
4766 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS && |
4767 | !(compared_policy->cond_traffic_class.start_tc <= policy->cond_traffic_class.start_tc && |
4768 | compared_policy->cond_traffic_class.end_tc >= policy->cond_traffic_class.end_tc)) { |
4769 | continue; |
4770 | } |
4771 | |
4772 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
4773 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
4774 | if (!necp_is_range_in_range((struct sockaddr *)&policy->cond_local_start, (struct sockaddr *)&policy->cond_local_end, (struct sockaddr *)&compared_policy->cond_local_start, (struct sockaddr *)&compared_policy->cond_local_end)) { |
4775 | continue; |
4776 | } |
4777 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
4778 | if (compared_policy->cond_local_prefix > policy->cond_local_prefix || |
4779 | !necp_is_addr_in_subnet((struct sockaddr *)&policy->cond_local_start, (struct sockaddr *)&compared_policy->cond_local_start, compared_policy->cond_local_prefix)) { |
4780 | continue; |
4781 | } |
4782 | } |
4783 | } |
4784 | |
4785 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
4786 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
4787 | if (!necp_is_range_in_range((struct sockaddr *)&policy->cond_remote_start, (struct sockaddr *)&policy->cond_remote_end, (struct sockaddr *)&compared_policy->cond_remote_start, (struct sockaddr *)&compared_policy->cond_remote_end)) { |
4788 | continue; |
4789 | } |
4790 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
4791 | if (compared_policy->cond_remote_prefix > policy->cond_remote_prefix || |
4792 | !necp_is_addr_in_subnet((struct sockaddr *)&policy->cond_remote_start, (struct sockaddr *)&compared_policy->cond_remote_start, compared_policy->cond_remote_prefix)) { |
4793 | continue; |
4794 | } |
4795 | } |
4796 | } |
4797 | |
4798 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE && |
4799 | memcmp(&compared_policy->cond_agent_type, &policy->cond_agent_type, sizeof(policy->cond_agent_type)) == 0) { |
4800 | continue; |
4801 | } |
4802 | |
4803 | return (TRUE); |
4804 | } |
4805 | |
4806 | return (FALSE); |
4807 | } |
4808 | |
4809 | static bool |
4810 | necp_kernel_socket_policies_reprocess(void) |
4811 | { |
4812 | int app_i; |
4813 | int bucket_allocation_counts[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
4814 | int bucket_current_free_index[NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS]; |
4815 | int app_layer_allocation_count = 0; |
4816 | int app_layer_current_free_index = 0; |
4817 | struct necp_kernel_socket_policy *kernel_policy = NULL; |
4818 | |
4819 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4820 | |
4821 | // Reset mask to 0 |
4822 | necp_kernel_application_policies_condition_mask = 0; |
4823 | necp_kernel_socket_policies_condition_mask = 0; |
4824 | necp_kernel_application_policies_count = 0; |
4825 | necp_kernel_socket_policies_count = 0; |
4826 | necp_kernel_socket_policies_non_app_count = 0; |
4827 | |
4828 | // Reset all maps to NULL |
4829 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4830 | if (necp_kernel_socket_policies_map[app_i] != NULL) { |
4831 | FREE(necp_kernel_socket_policies_map[app_i], M_NECP); |
4832 | necp_kernel_socket_policies_map[app_i] = NULL; |
4833 | } |
4834 | |
4835 | // Init counts |
4836 | bucket_allocation_counts[app_i] = 0; |
4837 | } |
4838 | if (necp_kernel_socket_policies_app_layer_map != NULL) { |
4839 | FREE(necp_kernel_socket_policies_app_layer_map, M_NECP); |
4840 | necp_kernel_socket_policies_app_layer_map = NULL; |
4841 | } |
4842 | |
4843 | // Create masks and counts |
4844 | LIST_FOREACH(kernel_policy, &necp_kernel_socket_policies, chain) { |
4845 | // App layer mask/count |
4846 | necp_kernel_application_policies_condition_mask |= kernel_policy->condition_mask; |
4847 | necp_kernel_application_policies_count++; |
4848 | app_layer_allocation_count++; |
4849 | |
4850 | if ((kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE)) { |
4851 | // Agent type conditions only apply to app layer |
4852 | continue; |
4853 | } |
4854 | |
4855 | // Update socket layer bucket mask/counts |
4856 | necp_kernel_socket_policies_condition_mask |= kernel_policy->condition_mask; |
4857 | necp_kernel_socket_policies_count++; |
4858 | |
4859 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) || |
4860 | kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
4861 | necp_kernel_socket_policies_non_app_count++; |
4862 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4863 | bucket_allocation_counts[app_i]++; |
4864 | } |
4865 | } else { |
4866 | bucket_allocation_counts[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(kernel_policy->cond_app_id)]++; |
4867 | } |
4868 | } |
4869 | |
4870 | // Allocate maps |
4871 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4872 | if (bucket_allocation_counts[app_i] > 0) { |
4873 | // Allocate a NULL-terminated array of policy pointers for each bucket |
4874 | MALLOC(necp_kernel_socket_policies_map[app_i], struct necp_kernel_socket_policy **, sizeof(struct necp_kernel_socket_policy *) * (bucket_allocation_counts[app_i] + 1), M_NECP, M_WAITOK); |
4875 | if (necp_kernel_socket_policies_map[app_i] == NULL) { |
4876 | goto fail; |
4877 | } |
4878 | |
4879 | // Initialize the first entry to NULL |
4880 | (necp_kernel_socket_policies_map[app_i])[0] = NULL; |
4881 | } |
4882 | bucket_current_free_index[app_i] = 0; |
4883 | } |
4884 | MALLOC(necp_kernel_socket_policies_app_layer_map, struct necp_kernel_socket_policy **, sizeof(struct necp_kernel_socket_policy *) * (app_layer_allocation_count + 1), M_NECP, M_WAITOK); |
4885 | if (necp_kernel_socket_policies_app_layer_map == NULL) { |
4886 | goto fail; |
4887 | } |
4888 | necp_kernel_socket_policies_app_layer_map[0] = NULL; |
4889 | |
4890 | // Fill out maps |
4891 | LIST_FOREACH(kernel_policy, &necp_kernel_socket_policies, chain) { |
4892 | // Add app layer policies |
4893 | if (!necp_kernel_socket_policy_is_unnecessary(kernel_policy, necp_kernel_socket_policies_app_layer_map, app_layer_current_free_index)) { |
4894 | necp_kernel_socket_policies_app_layer_map[app_layer_current_free_index] = kernel_policy; |
4895 | app_layer_current_free_index++; |
4896 | necp_kernel_socket_policies_app_layer_map[app_layer_current_free_index] = NULL; |
4897 | } |
4898 | |
4899 | if ((kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE)) { |
4900 | // Agent type conditions only apply to app layer |
4901 | continue; |
4902 | } |
4903 | |
4904 | // Add socket policies |
4905 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) || |
4906 | kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
4907 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4908 | if (!necp_kernel_socket_policy_is_unnecessary(kernel_policy, necp_kernel_socket_policies_map[app_i], bucket_current_free_index[app_i])) { |
4909 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = kernel_policy; |
4910 | bucket_current_free_index[app_i]++; |
4911 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = NULL; |
4912 | } |
4913 | } |
4914 | } else { |
4915 | app_i = NECP_SOCKET_MAP_APP_ID_TO_BUCKET(kernel_policy->cond_app_id); |
4916 | if (!necp_kernel_socket_policy_is_unnecessary(kernel_policy, necp_kernel_socket_policies_map[app_i], bucket_current_free_index[app_i])) { |
4917 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = kernel_policy; |
4918 | bucket_current_free_index[app_i]++; |
4919 | (necp_kernel_socket_policies_map[app_i])[(bucket_current_free_index[app_i])] = NULL; |
4920 | } |
4921 | } |
4922 | } |
4923 | necp_kernel_socket_policies_dump_all(); |
4924 | BUMP_KERNEL_SOCKET_POLICIES_GENERATION_COUNT(); |
4925 | return (TRUE); |
4926 | |
4927 | fail: |
4928 | // Free memory, reset masks to 0 |
4929 | necp_kernel_application_policies_condition_mask = 0; |
4930 | necp_kernel_socket_policies_condition_mask = 0; |
4931 | necp_kernel_application_policies_count = 0; |
4932 | necp_kernel_socket_policies_count = 0; |
4933 | necp_kernel_socket_policies_non_app_count = 0; |
4934 | for (app_i = 0; app_i < NECP_KERNEL_SOCKET_POLICIES_MAP_NUM_APP_ID_BUCKETS; app_i++) { |
4935 | if (necp_kernel_socket_policies_map[app_i] != NULL) { |
4936 | FREE(necp_kernel_socket_policies_map[app_i], M_NECP); |
4937 | necp_kernel_socket_policies_map[app_i] = NULL; |
4938 | } |
4939 | } |
4940 | if (necp_kernel_socket_policies_app_layer_map != NULL) { |
4941 | FREE(necp_kernel_socket_policies_app_layer_map, M_NECP); |
4942 | necp_kernel_socket_policies_app_layer_map = NULL; |
4943 | } |
4944 | return (FALSE); |
4945 | } |
4946 | |
4947 | static u_int32_t |
4948 | necp_get_new_string_id(void) |
4949 | { |
4950 | static u_int32_t necp_last_string_id = 0; |
4951 | |
4952 | u_int32_t newid = 0; |
4953 | |
4954 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
4955 | |
4956 | bool wrapped = FALSE; |
4957 | do { |
4958 | necp_last_string_id++; |
4959 | if (necp_last_string_id < 1) { |
4960 | if (wrapped) { |
4961 | // Already wrapped, give up |
4962 | NECPLOG0(LOG_ERR, "Failed to find a free app UUID.\n" ); |
4963 | return (0); |
4964 | } |
4965 | necp_last_string_id = 1; |
4966 | wrapped = TRUE; |
4967 | } |
4968 | newid = necp_last_string_id; |
4969 | } while (necp_lookup_string_with_id_locked(&necp_account_id_list, newid) != NULL); // If already used, keep trying |
4970 | |
4971 | if (newid == 0) { |
4972 | NECPLOG0(LOG_ERR, "Allocate string id failed.\n" ); |
4973 | return (0); |
4974 | } |
4975 | |
4976 | return (newid); |
4977 | } |
4978 | |
4979 | static struct necp_string_id_mapping * |
4980 | necp_lookup_string_to_id_locked(struct necp_string_id_mapping_list *list, char *string) |
4981 | { |
4982 | struct necp_string_id_mapping *searchentry = NULL; |
4983 | struct necp_string_id_mapping *foundentry = NULL; |
4984 | |
4985 | LIST_FOREACH(searchentry, list, chain) { |
4986 | if (strcmp(searchentry->string, string) == 0) { |
4987 | foundentry = searchentry; |
4988 | break; |
4989 | } |
4990 | } |
4991 | |
4992 | return (foundentry); |
4993 | } |
4994 | |
4995 | static struct necp_string_id_mapping * |
4996 | necp_lookup_string_with_id_locked(struct necp_string_id_mapping_list *list, u_int32_t local_id) |
4997 | { |
4998 | struct necp_string_id_mapping *searchentry = NULL; |
4999 | struct necp_string_id_mapping *foundentry = NULL; |
5000 | |
5001 | LIST_FOREACH(searchentry, list, chain) { |
5002 | if (searchentry->id == local_id) { |
5003 | foundentry = searchentry; |
5004 | break; |
5005 | } |
5006 | } |
5007 | |
5008 | return (foundentry); |
5009 | } |
5010 | |
5011 | static u_int32_t |
5012 | necp_create_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *string) |
5013 | { |
5014 | u_int32_t string_id = 0; |
5015 | struct necp_string_id_mapping *existing_mapping = NULL; |
5016 | |
5017 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5018 | |
5019 | existing_mapping = necp_lookup_string_to_id_locked(list, string); |
5020 | if (existing_mapping != NULL) { |
5021 | string_id = existing_mapping->id; |
5022 | existing_mapping->refcount++; |
5023 | } else { |
5024 | struct necp_string_id_mapping *new_mapping = NULL; |
5025 | MALLOC(new_mapping, struct necp_string_id_mapping *, sizeof(struct necp_string_id_mapping), M_NECP, M_WAITOK); |
5026 | if (new_mapping != NULL) { |
5027 | memset(new_mapping, 0, sizeof(struct necp_string_id_mapping)); |
5028 | |
5029 | size_t length = strlen(string) + 1; |
5030 | MALLOC(new_mapping->string, char *, length, M_NECP, M_WAITOK); |
5031 | if (new_mapping->string != NULL) { |
5032 | memcpy(new_mapping->string, string, length); |
5033 | new_mapping->id = necp_get_new_string_id(); |
5034 | new_mapping->refcount = 1; |
5035 | LIST_INSERT_HEAD(list, new_mapping, chain); |
5036 | string_id = new_mapping->id; |
5037 | } else { |
5038 | FREE(new_mapping, M_NECP); |
5039 | new_mapping = NULL; |
5040 | } |
5041 | } |
5042 | } |
5043 | return (string_id); |
5044 | } |
5045 | |
5046 | static bool |
5047 | necp_remove_string_to_id_mapping(struct necp_string_id_mapping_list *list, char *string) |
5048 | { |
5049 | struct necp_string_id_mapping *existing_mapping = NULL; |
5050 | |
5051 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5052 | |
5053 | existing_mapping = necp_lookup_string_to_id_locked(list, string); |
5054 | if (existing_mapping != NULL) { |
5055 | if (--existing_mapping->refcount == 0) { |
5056 | LIST_REMOVE(existing_mapping, chain); |
5057 | FREE(existing_mapping->string, M_NECP); |
5058 | FREE(existing_mapping, M_NECP); |
5059 | } |
5060 | return (TRUE); |
5061 | } |
5062 | |
5063 | return (FALSE); |
5064 | } |
5065 | |
5066 | #define NECP_FIRST_VALID_ROUTE_RULE_ID 1 |
5067 | #define NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID UINT16_MAX |
5068 | static u_int32_t |
5069 | necp_get_new_route_rule_id(bool aggregate) |
5070 | { |
5071 | static u_int32_t necp_last_route_rule_id = 0; |
5072 | static u_int32_t necp_last_aggregate_route_rule_id = 0; |
5073 | |
5074 | u_int32_t newid = 0; |
5075 | |
5076 | if (!aggregate) { |
5077 | // Main necp_kernel_policy_lock protects non-aggregate rule IDs |
5078 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5079 | |
5080 | bool wrapped = FALSE; |
5081 | do { |
5082 | necp_last_route_rule_id++; |
5083 | if (necp_last_route_rule_id < NECP_FIRST_VALID_ROUTE_RULE_ID || |
5084 | necp_last_route_rule_id >= NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID) { |
5085 | if (wrapped) { |
5086 | // Already wrapped, give up |
5087 | NECPLOG0(LOG_ERR, "Failed to find a free route rule id.\n" ); |
5088 | return (0); |
5089 | } |
5090 | necp_last_route_rule_id = NECP_FIRST_VALID_ROUTE_RULE_ID; |
5091 | wrapped = TRUE; |
5092 | } |
5093 | newid = necp_last_route_rule_id; |
5094 | } while (necp_lookup_route_rule_locked(&necp_route_rules, newid) != NULL); // If already used, keep trying |
5095 | } else { |
5096 | // necp_route_rule_lock protects aggregate rule IDs |
5097 | LCK_RW_ASSERT(&necp_route_rule_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5098 | |
5099 | bool wrapped = FALSE; |
5100 | do { |
5101 | necp_last_aggregate_route_rule_id++; |
5102 | if (necp_last_aggregate_route_rule_id < NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID) { |
5103 | if (wrapped) { |
5104 | // Already wrapped, give up |
5105 | NECPLOG0(LOG_ERR, "Failed to find a free aggregate route rule id.\n" ); |
5106 | return (0); |
5107 | } |
5108 | necp_last_aggregate_route_rule_id = NECP_FIRST_VALID_AGGREGATE_ROUTE_RULE_ID; |
5109 | wrapped = TRUE; |
5110 | } |
5111 | newid = necp_last_aggregate_route_rule_id; |
5112 | } while (necp_lookup_route_rule_locked(&necp_route_rules, newid) != NULL); // If already used, keep trying |
5113 | } |
5114 | |
5115 | if (newid == 0) { |
5116 | NECPLOG0(LOG_ERR, "Allocate route rule ID failed.\n" ); |
5117 | return (0); |
5118 | } |
5119 | |
5120 | return (newid); |
5121 | } |
5122 | |
5123 | static struct necp_route_rule * |
5124 | necp_lookup_route_rule_locked(struct necp_route_rule_list *list, u_int32_t route_rule_id) |
5125 | { |
5126 | struct necp_route_rule *searchentry = NULL; |
5127 | struct necp_route_rule *foundentry = NULL; |
5128 | |
5129 | LIST_FOREACH(searchentry, list, chain) { |
5130 | if (searchentry->id == route_rule_id) { |
5131 | foundentry = searchentry; |
5132 | break; |
5133 | } |
5134 | } |
5135 | |
5136 | return (foundentry); |
5137 | } |
5138 | |
5139 | static struct necp_route_rule * |
5140 | necp_lookup_route_rule_by_contents_locked(struct necp_route_rule_list *list, u_int32_t default_action, u_int8_t cellular_action, u_int8_t wifi_action, u_int8_t wired_action, u_int8_t expensive_action, u_int32_t *if_indices, u_int8_t *if_actions) |
5141 | { |
5142 | struct necp_route_rule *searchentry = NULL; |
5143 | struct necp_route_rule *foundentry = NULL; |
5144 | |
5145 | LIST_FOREACH(searchentry, list, chain) { |
5146 | if (searchentry->default_action == default_action && |
5147 | searchentry->cellular_action == cellular_action && |
5148 | searchentry->wifi_action == wifi_action && |
5149 | searchentry->wired_action == wired_action && |
5150 | searchentry->expensive_action == expensive_action) { |
5151 | bool match_failed = FALSE; |
5152 | size_t index_a = 0; |
5153 | size_t index_b = 0; |
5154 | size_t count_a = 0; |
5155 | size_t count_b = 0; |
5156 | for (index_a = 0; index_a < MAX_ROUTE_RULE_INTERFACES; index_a++) { |
5157 | bool found_index = FALSE; |
5158 | if (searchentry->exception_if_indices[index_a] == 0) { |
5159 | break; |
5160 | } |
5161 | count_a++; |
5162 | for (index_b = 0; index_b < MAX_ROUTE_RULE_INTERFACES; index_b++) { |
5163 | if (if_indices[index_b] == 0) { |
5164 | break; |
5165 | } |
5166 | if (index_b >= count_b) { |
5167 | count_b = index_b + 1; |
5168 | } |
5169 | if (searchentry->exception_if_indices[index_a] == if_indices[index_b] && |
5170 | searchentry->exception_if_actions[index_a] == if_actions[index_b]) { |
5171 | found_index = TRUE; |
5172 | break; |
5173 | } |
5174 | } |
5175 | if (!found_index) { |
5176 | match_failed = TRUE; |
5177 | break; |
5178 | } |
5179 | } |
5180 | if (!match_failed && count_a == count_b) { |
5181 | foundentry = searchentry; |
5182 | break; |
5183 | } |
5184 | } |
5185 | } |
5186 | |
5187 | return (foundentry); |
5188 | } |
5189 | |
5190 | static u_int32_t |
5191 | necp_create_route_rule(struct necp_route_rule_list *list, u_int8_t *route_rules_array, u_int32_t route_rules_array_size) |
5192 | { |
5193 | size_t offset = 0; |
5194 | u_int32_t route_rule_id = 0; |
5195 | struct necp_route_rule *existing_rule = NULL; |
5196 | u_int32_t default_action = NECP_ROUTE_RULE_ALLOW_INTERFACE; |
5197 | u_int8_t cellular_action = NECP_ROUTE_RULE_NONE; |
5198 | u_int8_t wifi_action = NECP_ROUTE_RULE_NONE; |
5199 | u_int8_t wired_action = NECP_ROUTE_RULE_NONE; |
5200 | u_int8_t expensive_action = NECP_ROUTE_RULE_NONE; |
5201 | u_int32_t if_indices[MAX_ROUTE_RULE_INTERFACES]; |
5202 | size_t num_valid_indices = 0; |
5203 | memset(&if_indices, 0, sizeof(if_indices)); |
5204 | u_int8_t if_actions[MAX_ROUTE_RULE_INTERFACES]; |
5205 | memset(&if_actions, 0, sizeof(if_actions)); |
5206 | |
5207 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5208 | |
5209 | if (route_rules_array == NULL || route_rules_array_size == 0) { |
5210 | return (0); |
5211 | } |
5212 | |
5213 | // Process rules |
5214 | while (offset < route_rules_array_size) { |
5215 | ifnet_t rule_interface = NULL; |
5216 | char interface_name[IFXNAMSIZ]; |
5217 | u_int32_t length = 0; |
5218 | u_int8_t *value = necp_buffer_get_tlv_value(route_rules_array, offset, &length); |
5219 | |
5220 | u_int8_t rule_type = necp_policy_condition_get_type_from_buffer(value, length); |
5221 | u_int8_t rule_flags = necp_policy_condition_get_flags_from_buffer(value, length); |
5222 | u_int32_t rule_length = necp_policy_condition_get_value_length_from_buffer(value, length); |
5223 | u_int8_t *rule_value = necp_policy_condition_get_value_pointer_from_buffer(value, length); |
5224 | |
5225 | if (rule_type == NECP_ROUTE_RULE_NONE) { |
5226 | // Don't allow an explicit rule to be None action |
5227 | continue; |
5228 | } |
5229 | |
5230 | if (rule_length == 0) { |
5231 | if (rule_flags & NECP_ROUTE_RULE_FLAG_CELLULAR) { |
5232 | cellular_action = rule_type; |
5233 | } |
5234 | if (rule_flags & NECP_ROUTE_RULE_FLAG_WIFI) { |
5235 | wifi_action = rule_type; |
5236 | } |
5237 | if (rule_flags & NECP_ROUTE_RULE_FLAG_WIRED) { |
5238 | wired_action = rule_type; |
5239 | } |
5240 | if (rule_flags & NECP_ROUTE_RULE_FLAG_EXPENSIVE) { |
5241 | expensive_action = rule_type; |
5242 | } |
5243 | if (rule_flags == 0) { |
5244 | default_action = rule_type; |
5245 | } |
5246 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
5247 | continue; |
5248 | } |
5249 | |
5250 | if (num_valid_indices >= MAX_ROUTE_RULE_INTERFACES) { |
5251 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
5252 | continue; |
5253 | } |
5254 | |
5255 | if (rule_length <= IFXNAMSIZ) { |
5256 | memcpy(interface_name, rule_value, rule_length); |
5257 | interface_name[rule_length - 1] = 0; // Make sure the string is NULL terminated |
5258 | if (ifnet_find_by_name(interface_name, &rule_interface) == 0) { |
5259 | if_actions[num_valid_indices] = rule_type; |
5260 | if_indices[num_valid_indices++] = rule_interface->if_index; |
5261 | ifnet_release(rule_interface); |
5262 | } |
5263 | } |
5264 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
5265 | } |
5266 | |
5267 | existing_rule = necp_lookup_route_rule_by_contents_locked(list, default_action, cellular_action, wifi_action, wired_action, expensive_action, if_indices, if_actions); |
5268 | if (existing_rule != NULL) { |
5269 | route_rule_id = existing_rule->id; |
5270 | existing_rule->refcount++; |
5271 | } else { |
5272 | struct necp_route_rule *new_rule = NULL; |
5273 | MALLOC(new_rule, struct necp_route_rule *, sizeof(struct necp_route_rule), M_NECP, M_WAITOK); |
5274 | if (new_rule != NULL) { |
5275 | memset(new_rule, 0, sizeof(struct necp_route_rule)); |
5276 | route_rule_id = new_rule->id = necp_get_new_route_rule_id(false); |
5277 | new_rule->default_action = default_action; |
5278 | new_rule->cellular_action = cellular_action; |
5279 | new_rule->wifi_action = wifi_action; |
5280 | new_rule->wired_action = wired_action; |
5281 | new_rule->expensive_action = expensive_action; |
5282 | memcpy(&new_rule->exception_if_indices, &if_indices, sizeof(if_indices)); |
5283 | memcpy(&new_rule->exception_if_actions, &if_actions, sizeof(if_actions)); |
5284 | new_rule->refcount = 1; |
5285 | LIST_INSERT_HEAD(list, new_rule, chain); |
5286 | } |
5287 | } |
5288 | return (route_rule_id); |
5289 | } |
5290 | |
5291 | static void |
5292 | necp_remove_aggregate_route_rule_for_id(u_int32_t rule_id) |
5293 | { |
5294 | if (rule_id) { |
5295 | lck_rw_lock_exclusive(&necp_route_rule_lock); |
5296 | |
5297 | struct necp_aggregate_route_rule *existing_rule = NULL; |
5298 | struct necp_aggregate_route_rule *tmp_rule = NULL; |
5299 | |
5300 | LIST_FOREACH_SAFE(existing_rule, &necp_aggregate_route_rules, chain, tmp_rule) { |
5301 | int index = 0; |
5302 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
5303 | u_int32_t route_rule_id = existing_rule->rule_ids[index]; |
5304 | if (route_rule_id == rule_id) { |
5305 | LIST_REMOVE(existing_rule, chain); |
5306 | FREE(existing_rule, M_NECP); |
5307 | break; |
5308 | } |
5309 | } |
5310 | } |
5311 | |
5312 | lck_rw_done(&necp_route_rule_lock); |
5313 | } |
5314 | } |
5315 | |
5316 | static bool |
5317 | necp_remove_route_rule(struct necp_route_rule_list *list, u_int32_t route_rule_id) |
5318 | { |
5319 | struct necp_route_rule *existing_rule = NULL; |
5320 | |
5321 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5322 | |
5323 | existing_rule = necp_lookup_route_rule_locked(list, route_rule_id); |
5324 | if (existing_rule != NULL) { |
5325 | if (--existing_rule->refcount == 0) { |
5326 | necp_remove_aggregate_route_rule_for_id(existing_rule->id); |
5327 | LIST_REMOVE(existing_rule, chain); |
5328 | FREE(existing_rule, M_NECP); |
5329 | } |
5330 | return (TRUE); |
5331 | } |
5332 | |
5333 | return (FALSE); |
5334 | } |
5335 | |
5336 | static struct necp_aggregate_route_rule * |
5337 | necp_lookup_aggregate_route_rule_locked(u_int32_t route_rule_id) |
5338 | { |
5339 | struct necp_aggregate_route_rule *searchentry = NULL; |
5340 | struct necp_aggregate_route_rule *foundentry = NULL; |
5341 | |
5342 | lck_rw_lock_shared(&necp_route_rule_lock); |
5343 | |
5344 | LIST_FOREACH(searchentry, &necp_aggregate_route_rules, chain) { |
5345 | if (searchentry->id == route_rule_id) { |
5346 | foundentry = searchentry; |
5347 | break; |
5348 | } |
5349 | } |
5350 | |
5351 | lck_rw_done(&necp_route_rule_lock); |
5352 | |
5353 | return (foundentry); |
5354 | } |
5355 | |
5356 | static u_int32_t |
5357 | necp_create_aggregate_route_rule(u_int32_t *rule_ids) |
5358 | { |
5359 | u_int32_t aggregate_route_rule_id = 0; |
5360 | struct necp_aggregate_route_rule *new_rule = NULL; |
5361 | struct necp_aggregate_route_rule *existing_rule = NULL; |
5362 | |
5363 | LIST_FOREACH(existing_rule, &necp_aggregate_route_rules, chain) { |
5364 | if (memcmp(existing_rule->rule_ids, rule_ids, (sizeof(u_int32_t) * MAX_AGGREGATE_ROUTE_RULES)) == 0) { |
5365 | return (existing_rule->id); |
5366 | } |
5367 | } |
5368 | |
5369 | lck_rw_lock_exclusive(&necp_route_rule_lock); |
5370 | |
5371 | LIST_FOREACH(existing_rule, &necp_aggregate_route_rules, chain) { |
5372 | // Re-check, in case something else created the rule while we are waiting to lock |
5373 | if (memcmp(existing_rule->rule_ids, rule_ids, (sizeof(u_int32_t) * MAX_AGGREGATE_ROUTE_RULES)) == 0) { |
5374 | lck_rw_done(&necp_route_rule_lock); |
5375 | return (existing_rule->id); |
5376 | } |
5377 | } |
5378 | |
5379 | MALLOC(new_rule, struct necp_aggregate_route_rule *, sizeof(struct necp_aggregate_route_rule), M_NECP, M_WAITOK); |
5380 | if (new_rule != NULL) { |
5381 | memset(new_rule, 0, sizeof(struct necp_aggregate_route_rule)); |
5382 | aggregate_route_rule_id = new_rule->id = necp_get_new_route_rule_id(true); |
5383 | new_rule->id = aggregate_route_rule_id; |
5384 | memcpy(new_rule->rule_ids, rule_ids, (sizeof(u_int32_t) * MAX_AGGREGATE_ROUTE_RULES)); |
5385 | LIST_INSERT_HEAD(&necp_aggregate_route_rules, new_rule, chain); |
5386 | } |
5387 | lck_rw_done(&necp_route_rule_lock); |
5388 | |
5389 | return (aggregate_route_rule_id); |
5390 | } |
5391 | |
5392 | #define NECP_NULL_SERVICE_ID 1 |
5393 | #define NECP_FIRST_VALID_SERVICE_ID 2 |
5394 | #define NECP_FIRST_VALID_APP_ID UINT16_MAX |
5395 | static u_int32_t |
5396 | necp_get_new_uuid_id(bool service) |
5397 | { |
5398 | static u_int32_t necp_last_service_uuid_id = 0; |
5399 | static u_int32_t necp_last_app_uuid_id = 0; |
5400 | |
5401 | u_int32_t newid = 0; |
5402 | |
5403 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5404 | |
5405 | if (service) { |
5406 | bool wrapped = FALSE; |
5407 | do { |
5408 | necp_last_service_uuid_id++; |
5409 | if (necp_last_service_uuid_id < NECP_FIRST_VALID_SERVICE_ID || |
5410 | necp_last_service_uuid_id >= NECP_FIRST_VALID_APP_ID) { |
5411 | if (wrapped) { |
5412 | // Already wrapped, give up |
5413 | NECPLOG0(LOG_ERR, "Failed to find a free service UUID.\n" ); |
5414 | return (NECP_NULL_SERVICE_ID); |
5415 | } |
5416 | necp_last_service_uuid_id = NECP_FIRST_VALID_SERVICE_ID; |
5417 | wrapped = TRUE; |
5418 | } |
5419 | newid = necp_last_service_uuid_id; |
5420 | } while (necp_uuid_lookup_uuid_with_service_id_locked(newid) != NULL); // If already used, keep trying |
5421 | } else { |
5422 | bool wrapped = FALSE; |
5423 | do { |
5424 | necp_last_app_uuid_id++; |
5425 | if (necp_last_app_uuid_id < NECP_FIRST_VALID_APP_ID) { |
5426 | if (wrapped) { |
5427 | // Already wrapped, give up |
5428 | NECPLOG0(LOG_ERR, "Failed to find a free app UUID.\n" ); |
5429 | return (NECP_NULL_SERVICE_ID); |
5430 | } |
5431 | necp_last_app_uuid_id = NECP_FIRST_VALID_APP_ID; |
5432 | wrapped = TRUE; |
5433 | } |
5434 | newid = necp_last_app_uuid_id; |
5435 | } while (necp_uuid_lookup_uuid_with_app_id_locked(newid) != NULL); // If already used, keep trying |
5436 | } |
5437 | |
5438 | if (newid == NECP_NULL_SERVICE_ID) { |
5439 | NECPLOG0(LOG_ERR, "Allocate uuid ID failed.\n" ); |
5440 | return (NECP_NULL_SERVICE_ID); |
5441 | } |
5442 | |
5443 | return (newid); |
5444 | } |
5445 | |
5446 | static struct necp_uuid_id_mapping * |
5447 | necp_uuid_lookup_app_id_locked(uuid_t uuid) |
5448 | { |
5449 | struct necp_uuid_id_mapping *searchentry = NULL; |
5450 | struct necp_uuid_id_mapping *foundentry = NULL; |
5451 | |
5452 | LIST_FOREACH(searchentry, APPUUIDHASH(uuid), chain) { |
5453 | if (uuid_compare(searchentry->uuid, uuid) == 0) { |
5454 | foundentry = searchentry; |
5455 | break; |
5456 | } |
5457 | } |
5458 | |
5459 | return (foundentry); |
5460 | } |
5461 | |
5462 | static struct necp_uuid_id_mapping * |
5463 | necp_uuid_lookup_uuid_with_app_id_locked(u_int32_t local_id) |
5464 | { |
5465 | struct necp_uuid_id_mapping *searchentry = NULL; |
5466 | struct necp_uuid_id_mapping *foundentry = NULL; |
5467 | |
5468 | struct necp_uuid_id_mapping_head *uuid_list_head = NULL; |
5469 | for (uuid_list_head = &necp_uuid_app_id_hashtbl[necp_uuid_app_id_hash_num_buckets - 1]; uuid_list_head >= necp_uuid_app_id_hashtbl; uuid_list_head--) { |
5470 | LIST_FOREACH(searchentry, uuid_list_head, chain) { |
5471 | if (searchentry->id == local_id) { |
5472 | foundentry = searchentry; |
5473 | break; |
5474 | } |
5475 | } |
5476 | } |
5477 | |
5478 | return (foundentry); |
5479 | } |
5480 | |
5481 | static u_int32_t |
5482 | necp_create_uuid_app_id_mapping(uuid_t uuid, bool *allocated_mapping, bool uuid_policy_table) |
5483 | { |
5484 | u_int32_t local_id = 0; |
5485 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
5486 | |
5487 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5488 | |
5489 | if (allocated_mapping) { |
5490 | *allocated_mapping = FALSE; |
5491 | } |
5492 | |
5493 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid); |
5494 | if (existing_mapping != NULL) { |
5495 | local_id = existing_mapping->id; |
5496 | existing_mapping->refcount++; |
5497 | if (uuid_policy_table) { |
5498 | existing_mapping->table_refcount++; |
5499 | } |
5500 | } else { |
5501 | struct necp_uuid_id_mapping *new_mapping = NULL; |
5502 | MALLOC(new_mapping, struct necp_uuid_id_mapping *, sizeof(*new_mapping), M_NECP, M_WAITOK); |
5503 | if (new_mapping != NULL) { |
5504 | uuid_copy(new_mapping->uuid, uuid); |
5505 | new_mapping->id = necp_get_new_uuid_id(false); |
5506 | new_mapping->refcount = 1; |
5507 | if (uuid_policy_table) { |
5508 | new_mapping->table_refcount = 1; |
5509 | } else { |
5510 | new_mapping->table_refcount = 0; |
5511 | } |
5512 | |
5513 | LIST_INSERT_HEAD(APPUUIDHASH(uuid), new_mapping, chain); |
5514 | |
5515 | if (allocated_mapping) { |
5516 | *allocated_mapping = TRUE; |
5517 | } |
5518 | |
5519 | local_id = new_mapping->id; |
5520 | } |
5521 | } |
5522 | |
5523 | return (local_id); |
5524 | } |
5525 | |
5526 | static bool |
5527 | necp_remove_uuid_app_id_mapping(uuid_t uuid, bool *removed_mapping, bool uuid_policy_table) |
5528 | { |
5529 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
5530 | |
5531 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5532 | |
5533 | if (removed_mapping) { |
5534 | *removed_mapping = FALSE; |
5535 | } |
5536 | |
5537 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid); |
5538 | if (existing_mapping != NULL) { |
5539 | if (uuid_policy_table) { |
5540 | existing_mapping->table_refcount--; |
5541 | } |
5542 | if (--existing_mapping->refcount == 0) { |
5543 | LIST_REMOVE(existing_mapping, chain); |
5544 | FREE(existing_mapping, M_NECP); |
5545 | if (removed_mapping) { |
5546 | *removed_mapping = TRUE; |
5547 | } |
5548 | } |
5549 | return (TRUE); |
5550 | } |
5551 | |
5552 | return (FALSE); |
5553 | } |
5554 | |
5555 | static struct necp_uuid_id_mapping * |
5556 | necp_uuid_get_null_service_id_mapping(void) |
5557 | { |
5558 | static struct necp_uuid_id_mapping null_mapping; |
5559 | uuid_clear(null_mapping.uuid); |
5560 | null_mapping.id = NECP_NULL_SERVICE_ID; |
5561 | |
5562 | return (&null_mapping); |
5563 | } |
5564 | |
5565 | static struct necp_uuid_id_mapping * |
5566 | necp_uuid_lookup_service_id_locked(uuid_t uuid) |
5567 | { |
5568 | struct necp_uuid_id_mapping *searchentry = NULL; |
5569 | struct necp_uuid_id_mapping *foundentry = NULL; |
5570 | |
5571 | if (uuid_is_null(uuid)) { |
5572 | return necp_uuid_get_null_service_id_mapping(); |
5573 | } |
5574 | |
5575 | LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) { |
5576 | if (uuid_compare(searchentry->uuid, uuid) == 0) { |
5577 | foundentry = searchentry; |
5578 | break; |
5579 | } |
5580 | } |
5581 | |
5582 | return (foundentry); |
5583 | } |
5584 | |
5585 | static struct necp_uuid_id_mapping * |
5586 | necp_uuid_lookup_uuid_with_service_id_locked(u_int32_t local_id) |
5587 | { |
5588 | struct necp_uuid_id_mapping *searchentry = NULL; |
5589 | struct necp_uuid_id_mapping *foundentry = NULL; |
5590 | |
5591 | if (local_id == NECP_NULL_SERVICE_ID) { |
5592 | return necp_uuid_get_null_service_id_mapping(); |
5593 | } |
5594 | |
5595 | LIST_FOREACH(searchentry, &necp_uuid_service_id_list, chain) { |
5596 | if (searchentry->id == local_id) { |
5597 | foundentry = searchentry; |
5598 | break; |
5599 | } |
5600 | } |
5601 | |
5602 | return (foundentry); |
5603 | } |
5604 | |
5605 | static u_int32_t |
5606 | necp_create_uuid_service_id_mapping(uuid_t uuid) |
5607 | { |
5608 | u_int32_t local_id = 0; |
5609 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
5610 | |
5611 | if (uuid_is_null(uuid)) { |
5612 | return (NECP_NULL_SERVICE_ID); |
5613 | } |
5614 | |
5615 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5616 | |
5617 | existing_mapping = necp_uuid_lookup_service_id_locked(uuid); |
5618 | if (existing_mapping != NULL) { |
5619 | local_id = existing_mapping->id; |
5620 | existing_mapping->refcount++; |
5621 | } else { |
5622 | struct necp_uuid_id_mapping *new_mapping = NULL; |
5623 | MALLOC(new_mapping, struct necp_uuid_id_mapping *, sizeof(*new_mapping), M_NECP, M_WAITOK); |
5624 | if (new_mapping != NULL) { |
5625 | uuid_copy(new_mapping->uuid, uuid); |
5626 | new_mapping->id = necp_get_new_uuid_id(true); |
5627 | new_mapping->refcount = 1; |
5628 | |
5629 | LIST_INSERT_HEAD(&necp_uuid_service_id_list, new_mapping, chain); |
5630 | |
5631 | local_id = new_mapping->id; |
5632 | } |
5633 | } |
5634 | |
5635 | return (local_id); |
5636 | } |
5637 | |
5638 | static bool |
5639 | necp_remove_uuid_service_id_mapping(uuid_t uuid) |
5640 | { |
5641 | struct necp_uuid_id_mapping *existing_mapping = NULL; |
5642 | |
5643 | if (uuid_is_null(uuid)) { |
5644 | return (TRUE); |
5645 | } |
5646 | |
5647 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5648 | |
5649 | existing_mapping = necp_uuid_lookup_app_id_locked(uuid); |
5650 | if (existing_mapping != NULL) { |
5651 | if (--existing_mapping->refcount == 0) { |
5652 | LIST_REMOVE(existing_mapping, chain); |
5653 | FREE(existing_mapping, M_NECP); |
5654 | } |
5655 | return (TRUE); |
5656 | } |
5657 | |
5658 | return (FALSE); |
5659 | } |
5660 | |
5661 | |
5662 | static bool |
5663 | necp_kernel_socket_policies_update_uuid_table(void) |
5664 | { |
5665 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5666 | |
5667 | if (necp_uuid_app_id_mappings_dirty) { |
5668 | if (proc_uuid_policy_kernel(PROC_UUID_POLICY_OPERATION_CLEAR, NULL, PROC_UUID_NECP_APP_POLICY) < 0) { |
5669 | NECPLOG0(LOG_DEBUG, "Error clearing uuids from policy table\n" ); |
5670 | return (FALSE); |
5671 | } |
5672 | |
5673 | if (necp_num_uuid_app_id_mappings > 0) { |
5674 | struct necp_uuid_id_mapping_head *uuid_list_head = NULL; |
5675 | for (uuid_list_head = &necp_uuid_app_id_hashtbl[necp_uuid_app_id_hash_num_buckets - 1]; uuid_list_head >= necp_uuid_app_id_hashtbl; uuid_list_head--) { |
5676 | struct necp_uuid_id_mapping *mapping = NULL; |
5677 | LIST_FOREACH(mapping, uuid_list_head, chain) { |
5678 | if (mapping->table_refcount > 0 && |
5679 | proc_uuid_policy_kernel(PROC_UUID_POLICY_OPERATION_ADD, mapping->uuid, PROC_UUID_NECP_APP_POLICY) < 0) { |
5680 | NECPLOG0(LOG_DEBUG, "Error adding uuid to policy table\n" ); |
5681 | } |
5682 | } |
5683 | } |
5684 | } |
5685 | |
5686 | necp_uuid_app_id_mappings_dirty = FALSE; |
5687 | } |
5688 | |
5689 | return (TRUE); |
5690 | } |
5691 | |
5692 | #define NECP_KERNEL_VALID_IP_OUTPUT_CONDITIONS (NECP_KERNEL_CONDITION_ALL_INTERFACES | NECP_KERNEL_CONDITION_BOUND_INTERFACE | NECP_KERNEL_CONDITION_PROTOCOL | NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX | NECP_KERNEL_CONDITION_POLICY_ID | NECP_KERNEL_CONDITION_LAST_INTERFACE) |
5693 | static necp_kernel_policy_id |
5694 | necp_kernel_ip_output_policy_add(necp_policy_order order, necp_policy_order suborder, u_int32_t session_order, int session_pid, u_int32_t condition_mask, u_int32_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, necp_kernel_policy_result result, necp_kernel_policy_result_parameter result_parameter) |
5695 | { |
5696 | struct necp_kernel_ip_output_policy *new_kernel_policy = NULL; |
5697 | struct necp_kernel_ip_output_policy *tmp_kernel_policy = NULL; |
5698 | |
5699 | MALLOC_ZONE(new_kernel_policy, struct necp_kernel_ip_output_policy *, sizeof(*new_kernel_policy), M_NECP_IP_POLICY, M_WAITOK); |
5700 | if (new_kernel_policy == NULL) { |
5701 | goto done; |
5702 | } |
5703 | |
5704 | memset(new_kernel_policy, 0, sizeof(*new_kernel_policy)); // M_ZERO is not supported for MALLOC_ZONE |
5705 | new_kernel_policy->id = necp_kernel_policy_get_new_id(false); |
5706 | new_kernel_policy->suborder = suborder; |
5707 | new_kernel_policy->order = order; |
5708 | new_kernel_policy->session_order = session_order; |
5709 | new_kernel_policy->session_pid = session_pid; |
5710 | |
5711 | // Sanitize condition mask |
5712 | new_kernel_policy->condition_mask = (condition_mask & NECP_KERNEL_VALID_IP_OUTPUT_CONDITIONS); |
5713 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE)) { |
5714 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_BOUND_INTERFACE; |
5715 | } |
5716 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX)) { |
5717 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_LOCAL_PREFIX; |
5718 | } |
5719 | if ((new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) && (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX)) { |
5720 | new_kernel_policy->condition_mask &= ~NECP_KERNEL_CONDITION_REMOTE_PREFIX; |
5721 | } |
5722 | new_kernel_policy->condition_negated_mask = condition_negated_mask & new_kernel_policy->condition_mask; |
5723 | |
5724 | // Set condition values |
5725 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) { |
5726 | new_kernel_policy->cond_policy_id = cond_policy_id; |
5727 | } |
5728 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
5729 | if (cond_bound_interface) { |
5730 | ifnet_reference(cond_bound_interface); |
5731 | } |
5732 | new_kernel_policy->cond_bound_interface = cond_bound_interface; |
5733 | } |
5734 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LAST_INTERFACE) { |
5735 | new_kernel_policy->cond_last_interface_index = cond_last_interface_index; |
5736 | } |
5737 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
5738 | new_kernel_policy->cond_protocol = cond_protocol; |
5739 | } |
5740 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
5741 | memcpy(&new_kernel_policy->cond_local_start, cond_local_start, cond_local_start->sa.sa_len); |
5742 | } |
5743 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
5744 | memcpy(&new_kernel_policy->cond_local_end, cond_local_end, cond_local_end->sa.sa_len); |
5745 | } |
5746 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
5747 | new_kernel_policy->cond_local_prefix = cond_local_prefix; |
5748 | } |
5749 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
5750 | memcpy(&new_kernel_policy->cond_remote_start, cond_remote_start, cond_remote_start->sa.sa_len); |
5751 | } |
5752 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
5753 | memcpy(&new_kernel_policy->cond_remote_end, cond_remote_end, cond_remote_end->sa.sa_len); |
5754 | } |
5755 | if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
5756 | new_kernel_policy->cond_remote_prefix = cond_remote_prefix; |
5757 | } |
5758 | |
5759 | new_kernel_policy->result = result; |
5760 | memcpy(&new_kernel_policy->result_parameter, &result_parameter, sizeof(result_parameter)); |
5761 | |
5762 | if (necp_debug) { |
5763 | NECPLOG(LOG_DEBUG, "Added kernel policy: ip output, id=%d, mask=%x\n" , new_kernel_policy->id, new_kernel_policy->condition_mask); |
5764 | } |
5765 | LIST_INSERT_SORTED_THRICE_ASCENDING(&necp_kernel_ip_output_policies, new_kernel_policy, chain, session_order, order, suborder, tmp_kernel_policy); |
5766 | done: |
5767 | return (new_kernel_policy ? new_kernel_policy->id : 0); |
5768 | } |
5769 | |
5770 | static struct necp_kernel_ip_output_policy * |
5771 | necp_kernel_ip_output_policy_find(necp_kernel_policy_id policy_id) |
5772 | { |
5773 | struct necp_kernel_ip_output_policy *kernel_policy = NULL; |
5774 | struct necp_kernel_ip_output_policy *tmp_kernel_policy = NULL; |
5775 | |
5776 | if (policy_id == 0) { |
5777 | return (NULL); |
5778 | } |
5779 | |
5780 | LIST_FOREACH_SAFE(kernel_policy, &necp_kernel_ip_output_policies, chain, tmp_kernel_policy) { |
5781 | if (kernel_policy->id == policy_id) { |
5782 | return (kernel_policy); |
5783 | } |
5784 | } |
5785 | |
5786 | return (NULL); |
5787 | } |
5788 | |
5789 | static bool |
5790 | necp_kernel_ip_output_policy_delete(necp_kernel_policy_id policy_id) |
5791 | { |
5792 | struct necp_kernel_ip_output_policy *policy = NULL; |
5793 | |
5794 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5795 | |
5796 | policy = necp_kernel_ip_output_policy_find(policy_id); |
5797 | if (policy) { |
5798 | LIST_REMOVE(policy, chain); |
5799 | |
5800 | if (policy->cond_bound_interface) { |
5801 | ifnet_release(policy->cond_bound_interface); |
5802 | policy->cond_bound_interface = NULL; |
5803 | } |
5804 | |
5805 | FREE_ZONE(policy, sizeof(*policy), M_NECP_IP_POLICY); |
5806 | return (TRUE); |
5807 | } |
5808 | |
5809 | return (FALSE); |
5810 | } |
5811 | |
5812 | static void |
5813 | necp_kernel_ip_output_policies_dump_all(void) |
5814 | { |
5815 | if (necp_debug) { |
5816 | struct necp_kernel_ip_output_policy *policy = NULL; |
5817 | int policy_i; |
5818 | int id_i; |
5819 | char result_string[MAX_RESULT_STRING_LEN]; |
5820 | char proc_name_string[MAXCOMLEN + 1]; |
5821 | memset(result_string, 0, MAX_RESULT_STRING_LEN); |
5822 | memset(proc_name_string, 0, MAXCOMLEN + 1); |
5823 | |
5824 | NECPLOG0(LOG_DEBUG, "NECP IP Output Policies:\n" ); |
5825 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
5826 | for (id_i = 0; id_i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; id_i++) { |
5827 | NECPLOG(LOG_DEBUG, " ID Bucket: %d\n" , id_i); |
5828 | for (policy_i = 0; necp_kernel_ip_output_policies_map[id_i] != NULL && (necp_kernel_ip_output_policies_map[id_i])[policy_i] != NULL; policy_i++) { |
5829 | policy = (necp_kernel_ip_output_policies_map[id_i])[policy_i]; |
5830 | proc_name(policy->session_pid, proc_name_string, MAXCOMLEN); |
5831 | NECPLOG(LOG_DEBUG, "\t%3d. Policy ID: %5d\tProcess: %10.10s\tOrder: %04d.%04d.%d\tMask: %5x\tResult: %s\n" , policy_i, policy->id, proc_name_string, policy->session_order, policy->order, policy->suborder, policy->condition_mask, necp_get_result_description(result_string, policy->result, policy->result_parameter)); |
5832 | } |
5833 | NECPLOG0(LOG_DEBUG, "-----------\n" ); |
5834 | } |
5835 | } |
5836 | } |
5837 | |
5838 | static inline bool |
5839 | necp_kernel_ip_output_policy_results_overlap(struct necp_kernel_ip_output_policy *upper_policy, struct necp_kernel_ip_output_policy *lower_policy) |
5840 | { |
5841 | if (upper_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5842 | if (upper_policy->session_order != lower_policy->session_order) { |
5843 | // A skip cannot override a policy of a different session |
5844 | return (FALSE); |
5845 | } else { |
5846 | if (upper_policy->result_parameter.skip_policy_order == 0 || |
5847 | lower_policy->order >= upper_policy->result_parameter.skip_policy_order) { |
5848 | // This policy is beyond the skip |
5849 | return (FALSE); |
5850 | } else { |
5851 | // This policy is inside the skip |
5852 | return (TRUE); |
5853 | } |
5854 | } |
5855 | } |
5856 | |
5857 | // All other IP Output policy results (drop, tunnel, hard pass) currently overlap |
5858 | return (TRUE); |
5859 | } |
5860 | |
5861 | static bool |
5862 | necp_kernel_ip_output_policy_is_unnecessary(struct necp_kernel_ip_output_policy *policy, struct necp_kernel_ip_output_policy **policy_array, int valid_indices) |
5863 | { |
5864 | bool can_skip = FALSE; |
5865 | u_int32_t highest_skip_session_order = 0; |
5866 | u_int32_t highest_skip_order = 0; |
5867 | int i; |
5868 | for (i = 0; i < valid_indices; i++) { |
5869 | struct necp_kernel_ip_output_policy *compared_policy = policy_array[i]; |
5870 | |
5871 | // For policies in a skip window, we can't mark conflicting policies as unnecessary |
5872 | if (can_skip) { |
5873 | if (highest_skip_session_order != compared_policy->session_order || |
5874 | (highest_skip_order != 0 && compared_policy->order >= highest_skip_order)) { |
5875 | // If we've moved on to the next session, or passed the skip window |
5876 | highest_skip_session_order = 0; |
5877 | highest_skip_order = 0; |
5878 | can_skip = FALSE; |
5879 | } else { |
5880 | // If this policy is also a skip, in can increase the skip window |
5881 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5882 | if (compared_policy->result_parameter.skip_policy_order > highest_skip_order) { |
5883 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
5884 | } |
5885 | } |
5886 | continue; |
5887 | } |
5888 | } |
5889 | |
5890 | if (compared_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
5891 | // This policy is a skip. Set the skip window accordingly |
5892 | can_skip = TRUE; |
5893 | highest_skip_session_order = compared_policy->session_order; |
5894 | highest_skip_order = compared_policy->result_parameter.skip_policy_order; |
5895 | } |
5896 | |
5897 | // The result of the compared policy must be able to block out this policy result |
5898 | if (!necp_kernel_ip_output_policy_results_overlap(compared_policy, policy)) { |
5899 | continue; |
5900 | } |
5901 | |
5902 | // If new policy matches All Interfaces, compared policy must also |
5903 | if ((policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES) && !(compared_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
5904 | continue; |
5905 | } |
5906 | |
5907 | // Default makes lower policies unecessary always |
5908 | if (compared_policy->condition_mask == 0) { |
5909 | return (TRUE); |
5910 | } |
5911 | |
5912 | // Compared must be more general than policy, and include only conditions within policy |
5913 | if ((policy->condition_mask & compared_policy->condition_mask) != compared_policy->condition_mask) { |
5914 | continue; |
5915 | } |
5916 | |
5917 | // Negative conditions must match for the overlapping conditions |
5918 | if ((policy->condition_negated_mask & compared_policy->condition_mask) != (compared_policy->condition_negated_mask & compared_policy->condition_mask)) { |
5919 | continue; |
5920 | } |
5921 | |
5922 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID && |
5923 | compared_policy->cond_policy_id != policy->cond_policy_id) { |
5924 | continue; |
5925 | } |
5926 | |
5927 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE && |
5928 | compared_policy->cond_bound_interface != policy->cond_bound_interface) { |
5929 | continue; |
5930 | } |
5931 | |
5932 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL && |
5933 | compared_policy->cond_protocol != policy->cond_protocol) { |
5934 | continue; |
5935 | } |
5936 | |
5937 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
5938 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
5939 | if (!necp_is_range_in_range((struct sockaddr *)&policy->cond_local_start, (struct sockaddr *)&policy->cond_local_end, (struct sockaddr *)&compared_policy->cond_local_start, (struct sockaddr *)&compared_policy->cond_local_end)) { |
5940 | continue; |
5941 | } |
5942 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
5943 | if (compared_policy->cond_local_prefix > policy->cond_local_prefix || |
5944 | !necp_is_addr_in_subnet((struct sockaddr *)&policy->cond_local_start, (struct sockaddr *)&compared_policy->cond_local_start, compared_policy->cond_local_prefix)) { |
5945 | continue; |
5946 | } |
5947 | } |
5948 | } |
5949 | |
5950 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
5951 | if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
5952 | if (!necp_is_range_in_range((struct sockaddr *)&policy->cond_remote_start, (struct sockaddr *)&policy->cond_remote_end, (struct sockaddr *)&compared_policy->cond_remote_start, (struct sockaddr *)&compared_policy->cond_remote_end)) { |
5953 | continue; |
5954 | } |
5955 | } else if (compared_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
5956 | if (compared_policy->cond_remote_prefix > policy->cond_remote_prefix || |
5957 | !necp_is_addr_in_subnet((struct sockaddr *)&policy->cond_remote_start, (struct sockaddr *)&compared_policy->cond_remote_start, compared_policy->cond_remote_prefix)) { |
5958 | continue; |
5959 | } |
5960 | } |
5961 | } |
5962 | |
5963 | return (TRUE); |
5964 | } |
5965 | |
5966 | return (FALSE); |
5967 | } |
5968 | |
5969 | static bool |
5970 | necp_kernel_ip_output_policies_reprocess(void) |
5971 | { |
5972 | int i; |
5973 | int bucket_allocation_counts[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
5974 | int bucket_current_free_index[NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS]; |
5975 | struct necp_kernel_ip_output_policy *kernel_policy = NULL; |
5976 | |
5977 | LCK_RW_ASSERT(&necp_kernel_policy_lock, LCK_RW_ASSERT_EXCLUSIVE); |
5978 | |
5979 | // Reset mask to 0 |
5980 | necp_kernel_ip_output_policies_condition_mask = 0; |
5981 | necp_kernel_ip_output_policies_count = 0; |
5982 | necp_kernel_ip_output_policies_non_id_count = 0; |
5983 | |
5984 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
5985 | if (necp_kernel_ip_output_policies_map[i] != NULL) { |
5986 | FREE(necp_kernel_ip_output_policies_map[i], M_NECP); |
5987 | necp_kernel_ip_output_policies_map[i] = NULL; |
5988 | } |
5989 | |
5990 | // Init counts |
5991 | bucket_allocation_counts[i] = 0; |
5992 | } |
5993 | |
5994 | LIST_FOREACH(kernel_policy, &necp_kernel_ip_output_policies, chain) { |
5995 | // Update mask |
5996 | necp_kernel_ip_output_policies_condition_mask |= kernel_policy->condition_mask; |
5997 | necp_kernel_ip_output_policies_count++; |
5998 | |
5999 | /* Update bucket counts: |
6000 | * Non-id and SKIP policies will be added to all buckets |
6001 | */ |
6002 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) || |
6003 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6004 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6005 | bucket_allocation_counts[i]++; |
6006 | } |
6007 | } |
6008 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID)) { |
6009 | necp_kernel_ip_output_policies_non_id_count++; |
6010 | } else { |
6011 | bucket_allocation_counts[NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(kernel_policy->cond_policy_id)]++; |
6012 | } |
6013 | } |
6014 | |
6015 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6016 | if (bucket_allocation_counts[i] > 0) { |
6017 | // Allocate a NULL-terminated array of policy pointers for each bucket |
6018 | MALLOC(necp_kernel_ip_output_policies_map[i], struct necp_kernel_ip_output_policy **, sizeof(struct necp_kernel_ip_output_policy *) * (bucket_allocation_counts[i] + 1), M_NECP, M_WAITOK); |
6019 | if (necp_kernel_ip_output_policies_map[i] == NULL) { |
6020 | goto fail; |
6021 | } |
6022 | |
6023 | // Initialize the first entry to NULL |
6024 | (necp_kernel_ip_output_policies_map[i])[0] = NULL; |
6025 | } |
6026 | bucket_current_free_index[i] = 0; |
6027 | } |
6028 | |
6029 | LIST_FOREACH(kernel_policy, &necp_kernel_ip_output_policies, chain) { |
6030 | // Insert pointers into map |
6031 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) || |
6032 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
6033 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6034 | if (!necp_kernel_ip_output_policy_is_unnecessary(kernel_policy, necp_kernel_ip_output_policies_map[i], bucket_current_free_index[i])) { |
6035 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = kernel_policy; |
6036 | bucket_current_free_index[i]++; |
6037 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = NULL; |
6038 | } |
6039 | } |
6040 | } else { |
6041 | i = NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(kernel_policy->cond_policy_id); |
6042 | if (!necp_kernel_ip_output_policy_is_unnecessary(kernel_policy, necp_kernel_ip_output_policies_map[i], bucket_current_free_index[i])) { |
6043 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = kernel_policy; |
6044 | bucket_current_free_index[i]++; |
6045 | (necp_kernel_ip_output_policies_map[i])[(bucket_current_free_index[i])] = NULL; |
6046 | } |
6047 | } |
6048 | } |
6049 | necp_kernel_ip_output_policies_dump_all(); |
6050 | return (TRUE); |
6051 | |
6052 | fail: |
6053 | // Free memory, reset mask to 0 |
6054 | necp_kernel_ip_output_policies_condition_mask = 0; |
6055 | necp_kernel_ip_output_policies_count = 0; |
6056 | necp_kernel_ip_output_policies_non_id_count = 0; |
6057 | for (i = 0; i < NECP_KERNEL_IP_OUTPUT_POLICIES_MAP_NUM_ID_BUCKETS; i++) { |
6058 | if (necp_kernel_ip_output_policies_map[i] != NULL) { |
6059 | FREE(necp_kernel_ip_output_policies_map[i], M_NECP); |
6060 | necp_kernel_ip_output_policies_map[i] = NULL; |
6061 | } |
6062 | } |
6063 | return (FALSE); |
6064 | } |
6065 | |
6066 | // Outbound Policy Matching |
6067 | // --------------------- |
6068 | struct substring { |
6069 | char *string; |
6070 | size_t length; |
6071 | }; |
6072 | |
6073 | static struct substring |
6074 | necp_trim_dots_and_stars(char *string, size_t length) |
6075 | { |
6076 | struct substring sub; |
6077 | sub.string = string; |
6078 | sub.length = string ? length : 0; |
6079 | |
6080 | while (sub.length && (sub.string[0] == '.' || sub.string[0] == '*')) { |
6081 | sub.string++; |
6082 | sub.length--; |
6083 | } |
6084 | |
6085 | while (sub.length && (sub.string[sub.length - 1] == '.' || sub.string[sub.length - 1] == '*')) { |
6086 | sub.length--; |
6087 | } |
6088 | |
6089 | return (sub); |
6090 | } |
6091 | |
6092 | static char * |
6093 | necp_create_trimmed_domain(char *string, size_t length) |
6094 | { |
6095 | char *trimmed_domain = NULL; |
6096 | struct substring sub = necp_trim_dots_and_stars(string, length); |
6097 | |
6098 | MALLOC(trimmed_domain, char *, sub.length + 1, M_NECP, M_WAITOK); |
6099 | if (trimmed_domain == NULL) { |
6100 | return (NULL); |
6101 | } |
6102 | |
6103 | memcpy(trimmed_domain, sub.string, sub.length); |
6104 | trimmed_domain[sub.length] = 0; |
6105 | |
6106 | return (trimmed_domain); |
6107 | } |
6108 | |
6109 | static inline int |
6110 | necp_count_dots(char *string, size_t length) |
6111 | { |
6112 | int dot_count = 0; |
6113 | size_t i = 0; |
6114 | |
6115 | for (i = 0; i < length; i++) { |
6116 | if (string[i] == '.') { |
6117 | dot_count++; |
6118 | } |
6119 | } |
6120 | |
6121 | return (dot_count); |
6122 | } |
6123 | |
6124 | static bool |
6125 | necp_check_suffix(struct substring parent, struct substring suffix, bool require_dot_before_suffix) |
6126 | { |
6127 | if (parent.length <= suffix.length) { |
6128 | return (FALSE); |
6129 | } |
6130 | |
6131 | size_t length_difference = (parent.length - suffix.length); |
6132 | |
6133 | if (require_dot_before_suffix) { |
6134 | if (((char *)(parent.string + length_difference - 1))[0] != '.') { |
6135 | return (FALSE); |
6136 | } |
6137 | } |
6138 | |
6139 | // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters) |
6140 | return (strncasecmp(parent.string + length_difference, suffix.string, suffix.length) == 0); |
6141 | } |
6142 | |
6143 | static bool |
6144 | necp_hostname_matches_domain(struct substring hostname_substring, u_int8_t hostname_dot_count, char *domain, u_int8_t domain_dot_count) |
6145 | { |
6146 | if (hostname_substring.string == NULL || domain == NULL) { |
6147 | return (hostname_substring.string == domain); |
6148 | } |
6149 | |
6150 | struct substring domain_substring; |
6151 | domain_substring.string = domain; |
6152 | domain_substring.length = strlen(domain); |
6153 | |
6154 | if (hostname_dot_count == domain_dot_count) { |
6155 | // strncasecmp does case-insensitive check for all UTF-8 strings (ignores non-ASCII characters) |
6156 | if (hostname_substring.length == domain_substring.length && |
6157 | strncasecmp(hostname_substring.string, domain_substring.string, hostname_substring.length) == 0) { |
6158 | return (TRUE); |
6159 | } |
6160 | } else if (domain_dot_count < hostname_dot_count) { |
6161 | if (necp_check_suffix(hostname_substring, domain_substring, TRUE)) { |
6162 | return (TRUE); |
6163 | } |
6164 | } |
6165 | |
6166 | return (FALSE); |
6167 | } |
6168 | |
6169 | static char * |
6170 | necp_copy_string(char *string, size_t length) |
6171 | { |
6172 | char *copied_string = NULL; |
6173 | |
6174 | MALLOC(copied_string, char *, length + 1, M_NECP, M_WAITOK); |
6175 | if (copied_string == NULL) { |
6176 | return (NULL); |
6177 | } |
6178 | |
6179 | memcpy(copied_string, string, length); |
6180 | copied_string[length] = 0; |
6181 | |
6182 | return (copied_string); |
6183 | } |
6184 | |
6185 | static u_int32_t |
6186 | necp_get_primary_direct_interface_index(void) |
6187 | { |
6188 | u_int32_t interface_index = IFSCOPE_NONE; |
6189 | |
6190 | ifnet_head_lock_shared(); |
6191 | struct ifnet *ordered_interface = NULL; |
6192 | TAILQ_FOREACH(ordered_interface, &ifnet_ordered_head, if_ordered_link) { |
6193 | const u_int8_t functional_type = if_functional_type(ordered_interface, TRUE); |
6194 | if (functional_type != IFRTYPE_FUNCTIONAL_UNKNOWN && |
6195 | functional_type != IFRTYPE_FUNCTIONAL_LOOPBACK) { |
6196 | // All known, non-loopback functional types represent direct physical interfaces (Wi-Fi, Cellular, Wired) |
6197 | interface_index = ordered_interface->if_index; |
6198 | break; |
6199 | } |
6200 | } |
6201 | ifnet_head_done(); |
6202 | |
6203 | return interface_index; |
6204 | } |
6205 | |
6206 | static inline void |
6207 | necp_get_parent_cred_result(proc_t proc, struct necp_socket_info *info) |
6208 | { |
6209 | task_t task = proc_task(proc ? proc : current_proc()); |
6210 | coalition_t coal = COALITION_NULL; |
6211 | Boolean is_leader = coalition_is_leader(task, COALITION_TYPE_JETSAM, &coal); |
6212 | |
6213 | if (is_leader == TRUE) { |
6214 | // No parent, nothing to do |
6215 | return; |
6216 | } |
6217 | |
6218 | if (coal != NULL) { |
6219 | task_t lead_task = coalition_get_leader(coal); |
6220 | if (lead_task != NULL) { |
6221 | proc_t lead_proc = get_bsdtask_info(lead_task); |
6222 | if (lead_proc != NULL) { |
6223 | kauth_cred_t lead_cred = kauth_cred_proc_ref(lead_proc); |
6224 | if (lead_cred != NULL) { |
6225 | errno_t cred_result = priv_check_cred(lead_cred, PRIV_NET_PRIVILEGED_NECP_MATCH, 0); |
6226 | kauth_cred_unref(&lead_cred); |
6227 | info->cred_result = cred_result; |
6228 | } |
6229 | } |
6230 | task_deallocate(lead_task); |
6231 | } |
6232 | } |
6233 | } |
6234 | |
6235 | #define NECP_KERNEL_ADDRESS_TYPE_CONDITIONS (NECP_KERNEL_CONDITION_LOCAL_START | NECP_KERNEL_CONDITION_LOCAL_END | NECP_KERNEL_CONDITION_LOCAL_PREFIX | NECP_KERNEL_CONDITION_REMOTE_START | NECP_KERNEL_CONDITION_REMOTE_END | NECP_KERNEL_CONDITION_REMOTE_PREFIX) |
6236 | static void |
6237 | necp_application_fillout_info_locked(uuid_t application_uuid, uuid_t real_application_uuid, char *account, char *domain, pid_t pid, uid_t uid, u_int16_t protocol, u_int32_t bound_interface_index, u_int32_t traffic_class, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr, proc_t proc, struct necp_socket_info *info) |
6238 | { |
6239 | memset(info, 0, sizeof(struct necp_socket_info)); |
6240 | |
6241 | info->pid = pid; |
6242 | info->uid = uid; |
6243 | info->protocol = protocol; |
6244 | info->bound_interface_index = bound_interface_index; |
6245 | info->traffic_class = traffic_class; |
6246 | |
6247 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT && proc != NULL) { |
6248 | info->cred_result = priv_check_cred(proc_ucred(proc), PRIV_NET_PRIVILEGED_NECP_MATCH, 0); |
6249 | if (info->cred_result != 0) { |
6250 | // Process does not have entitlement, check the parent process |
6251 | necp_get_parent_cred_result(proc, info); |
6252 | } |
6253 | } |
6254 | |
6255 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID && !uuid_is_null(application_uuid)) { |
6256 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(application_uuid); |
6257 | if (existing_mapping) { |
6258 | info->application_id = existing_mapping->id; |
6259 | } |
6260 | } |
6261 | |
6262 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID && !uuid_is_null(real_application_uuid)) { |
6263 | if (uuid_compare(application_uuid, real_application_uuid) == 0) { |
6264 | info->real_application_id = info->application_id; |
6265 | } else { |
6266 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(real_application_uuid); |
6267 | if (existing_mapping) { |
6268 | info->real_application_id = existing_mapping->id; |
6269 | } |
6270 | } |
6271 | } |
6272 | |
6273 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && account != NULL) { |
6274 | struct necp_string_id_mapping *existing_mapping = necp_lookup_string_to_id_locked(&necp_account_id_list, account); |
6275 | if (existing_mapping) { |
6276 | info->account_id = existing_mapping->id; |
6277 | } |
6278 | } |
6279 | |
6280 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
6281 | info->domain = domain; |
6282 | } |
6283 | |
6284 | if (necp_kernel_application_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS) { |
6285 | if (local_addr && local_addr->sa.sa_len > 0) { |
6286 | memcpy(&info->local_addr, local_addr, local_addr->sa.sa_len); |
6287 | } |
6288 | if (remote_addr && remote_addr->sa.sa_len > 0) { |
6289 | memcpy(&info->remote_addr, remote_addr, remote_addr->sa.sa_len); |
6290 | } |
6291 | } |
6292 | } |
6293 | |
6294 | static void |
6295 | necp_send_application_interface_denied_event(pid_t pid, uuid_t proc_uuid, u_int32_t if_functional_type) |
6296 | { |
6297 | struct kev_netpolicy_ifdenied ev_ifdenied; |
6298 | |
6299 | bzero(&ev_ifdenied, sizeof(ev_ifdenied)); |
6300 | |
6301 | ev_ifdenied.ev_data.epid = pid; |
6302 | uuid_copy(ev_ifdenied.ev_data.euuid, proc_uuid); |
6303 | ev_ifdenied.ev_if_functional_type = if_functional_type; |
6304 | |
6305 | netpolicy_post_msg(KEV_NETPOLICY_IFDENIED, &ev_ifdenied.ev_data, sizeof(ev_ifdenied)); |
6306 | } |
6307 | |
6308 | extern char *proc_name_address(void *p); |
6309 | |
6310 | #define NECP_VERIFY_DELEGATION_ENTITLEMENT(_p, _d) \ |
6311 | if (!has_checked_delegation_entitlement) { \ |
6312 | has_delegation_entitlement = (priv_check_cred(proc_ucred(_p), PRIV_NET_PRIVILEGED_SOCKET_DELEGATE, 0) == 0); \ |
6313 | has_checked_delegation_entitlement = TRUE; \ |
6314 | } \ |
6315 | if (!has_delegation_entitlement) { \ |
6316 | NECPLOG(LOG_ERR, "%s(%d) does not hold the necessary entitlement to delegate network traffic for other processes by %s", \ |
6317 | proc_name_address(_p), proc_pid(_p), _d); \ |
6318 | break; \ |
6319 | } |
6320 | |
6321 | int |
6322 | necp_application_find_policy_match_internal(proc_t proc, |
6323 | u_int8_t *parameters, |
6324 | u_int32_t parameters_size, |
6325 | struct necp_aggregate_result *returned_result, |
6326 | u_int32_t *flags, |
6327 | u_int required_interface_index, |
6328 | const union necp_sockaddr_union *override_local_addr, |
6329 | const union necp_sockaddr_union *override_remote_addr, |
6330 | struct rtentry **returned_route, bool ignore_address) |
6331 | { |
6332 | int error = 0; |
6333 | size_t offset = 0; |
6334 | |
6335 | struct necp_kernel_socket_policy *matched_policy = NULL; |
6336 | struct necp_socket_info info; |
6337 | necp_kernel_policy_filter filter_control_unit = 0; |
6338 | u_int32_t route_rule_id = 0; |
6339 | necp_kernel_policy_result service_action = 0; |
6340 | necp_kernel_policy_service service = { 0, 0 }; |
6341 | |
6342 | u_int16_t protocol = 0; |
6343 | u_int32_t bound_interface_index = required_interface_index; |
6344 | u_int32_t traffic_class = 0; |
6345 | u_int32_t client_flags = 0; |
6346 | union necp_sockaddr_union local_addr; |
6347 | union necp_sockaddr_union remote_addr; |
6348 | bool no_remote_addr = FALSE; |
6349 | u_int8_t remote_family = 0; |
6350 | bool no_local_addr = FALSE; |
6351 | |
6352 | if (override_local_addr) { |
6353 | memcpy(&local_addr, override_local_addr, sizeof(local_addr)); |
6354 | } else { |
6355 | memset(&local_addr, 0, sizeof(local_addr)); |
6356 | } |
6357 | if (override_remote_addr) { |
6358 | memcpy(&remote_addr, override_remote_addr, sizeof(remote_addr)); |
6359 | } else { |
6360 | memset(&remote_addr, 0, sizeof(remote_addr)); |
6361 | } |
6362 | |
6363 | // Initialize UID, PID, and UUIDs to the current process |
6364 | uid_t uid = kauth_cred_getuid(proc_ucred(proc)); |
6365 | pid_t pid = proc_pid(proc); |
6366 | uuid_t application_uuid; |
6367 | uuid_clear(application_uuid); |
6368 | uuid_t real_application_uuid; |
6369 | uuid_clear(real_application_uuid); |
6370 | proc_getexecutableuuid(proc, real_application_uuid, sizeof(real_application_uuid)); |
6371 | uuid_copy(application_uuid, real_application_uuid); |
6372 | |
6373 | char *domain = NULL; |
6374 | char *account = NULL; |
6375 | |
6376 | #define NECP_MAX_REQUIRED_AGENTS 16 |
6377 | u_int32_t num_required_agent_types = 0; |
6378 | struct necp_client_parameter_netagent_type required_agent_types[NECP_MAX_REQUIRED_AGENTS]; |
6379 | memset(&required_agent_types, 0, sizeof(required_agent_types)); |
6380 | |
6381 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
6382 | u_int32_t netagent_use_flags[NECP_MAX_NETAGENTS]; |
6383 | memset(&netagent_ids, 0, sizeof(netagent_ids)); |
6384 | memset(&netagent_use_flags, 0, sizeof(netagent_use_flags)); |
6385 | int netagent_cursor; |
6386 | |
6387 | bool has_checked_delegation_entitlement = FALSE; |
6388 | bool has_delegation_entitlement = FALSE; |
6389 | |
6390 | if (returned_result == NULL) { |
6391 | return (EINVAL); |
6392 | } |
6393 | |
6394 | memset(returned_result, 0, sizeof(struct necp_aggregate_result)); |
6395 | |
6396 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
6397 | if (necp_kernel_application_policies_count == 0) { |
6398 | if (necp_drop_all_order > 0) { |
6399 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
6400 | lck_rw_done(&necp_kernel_policy_lock); |
6401 | return (0); |
6402 | } |
6403 | } |
6404 | lck_rw_done(&necp_kernel_policy_lock); |
6405 | |
6406 | while ((offset + sizeof(u_int8_t) + sizeof(u_int32_t)) <= parameters_size) { |
6407 | u_int8_t type = necp_buffer_get_tlv_type(parameters, offset); |
6408 | u_int32_t length = necp_buffer_get_tlv_length(parameters, offset); |
6409 | |
6410 | if (length > (parameters_size - (offset + sizeof(u_int8_t) + sizeof(u_int32_t)))) { |
6411 | // If the length is larger than what can fit in the remaining parameters size, bail |
6412 | NECPLOG(LOG_ERR, "Invalid TLV length (%u)" , length); |
6413 | break; |
6414 | } |
6415 | |
6416 | if (length > 0) { |
6417 | u_int8_t *value = necp_buffer_get_tlv_value(parameters, offset, NULL); |
6418 | if (value != NULL) { |
6419 | switch (type) { |
6420 | case NECP_CLIENT_PARAMETER_APPLICATION: { |
6421 | if (length >= sizeof(uuid_t)) { |
6422 | if (uuid_compare(application_uuid, value) == 0) { |
6423 | // No delegation |
6424 | break; |
6425 | } |
6426 | |
6427 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "euuid" ); |
6428 | |
6429 | uuid_copy(application_uuid, value); |
6430 | } |
6431 | break; |
6432 | } |
6433 | case NECP_CLIENT_PARAMETER_REAL_APPLICATION: { |
6434 | if (length >= sizeof(uuid_t)) { |
6435 | if (uuid_compare(real_application_uuid, value) == 0) { |
6436 | // No delegation |
6437 | break; |
6438 | } |
6439 | |
6440 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "uuid" ); |
6441 | |
6442 | uuid_copy(real_application_uuid, value); |
6443 | } |
6444 | break; |
6445 | } |
6446 | case NECP_CLIENT_PARAMETER_PID: { |
6447 | if (length >= sizeof(pid_t)) { |
6448 | if (memcmp(&pid, value, sizeof(pid_t)) == 0) { |
6449 | // No delegation |
6450 | break; |
6451 | } |
6452 | |
6453 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "pid" ); |
6454 | |
6455 | memcpy(&pid, value, sizeof(pid_t)); |
6456 | } |
6457 | break; |
6458 | } |
6459 | case NECP_CLIENT_PARAMETER_UID: { |
6460 | if (length >= sizeof(uid_t)) { |
6461 | if (memcmp(&uid, value, sizeof(uid_t)) == 0) { |
6462 | // No delegation |
6463 | break; |
6464 | } |
6465 | |
6466 | NECP_VERIFY_DELEGATION_ENTITLEMENT(proc, "uid" ); |
6467 | |
6468 | memcpy(&uid, value, sizeof(uid_t)); |
6469 | } |
6470 | break; |
6471 | } |
6472 | case NECP_CLIENT_PARAMETER_DOMAIN: { |
6473 | domain = (char *)value; |
6474 | domain[length - 1] = 0; |
6475 | break; |
6476 | } |
6477 | case NECP_CLIENT_PARAMETER_ACCOUNT: { |
6478 | account = (char *)value; |
6479 | account[length - 1] = 0; |
6480 | break; |
6481 | } |
6482 | case NECP_CLIENT_PARAMETER_TRAFFIC_CLASS: { |
6483 | if (length >= sizeof(u_int32_t)) { |
6484 | memcpy(&traffic_class, value, sizeof(u_int32_t)); |
6485 | } |
6486 | break; |
6487 | } |
6488 | case NECP_CLIENT_PARAMETER_IP_PROTOCOL: { |
6489 | if (length >= sizeof(u_int16_t)) { |
6490 | memcpy(&protocol, value, sizeof(u_int16_t)); |
6491 | } |
6492 | break; |
6493 | } |
6494 | case NECP_CLIENT_PARAMETER_BOUND_INTERFACE: { |
6495 | if (length <= IFXNAMSIZ && length > 0) { |
6496 | ifnet_t bound_interface = NULL; |
6497 | char interface_name[IFXNAMSIZ]; |
6498 | memcpy(interface_name, value, length); |
6499 | interface_name[length - 1] = 0; // Make sure the string is NULL terminated |
6500 | if (ifnet_find_by_name(interface_name, &bound_interface) == 0) { |
6501 | bound_interface_index = bound_interface->if_index; |
6502 | ifnet_release(bound_interface); |
6503 | } |
6504 | } |
6505 | break; |
6506 | } |
6507 | case NECP_CLIENT_PARAMETER_LOCAL_ADDRESS: { |
6508 | if (ignore_address) { |
6509 | break; |
6510 | } |
6511 | |
6512 | if (length >= sizeof(struct necp_policy_condition_addr)) { |
6513 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value; |
6514 | if (necp_address_is_valid(&address_struct->address.sa)) { |
6515 | memcpy(&local_addr, &address_struct->address, sizeof(address_struct->address)); |
6516 | } |
6517 | } |
6518 | break; |
6519 | } |
6520 | case NECP_CLIENT_PARAMETER_REMOTE_ADDRESS: { |
6521 | if (ignore_address) { |
6522 | break; |
6523 | } |
6524 | |
6525 | if (length >= sizeof(struct necp_policy_condition_addr)) { |
6526 | struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)value; |
6527 | if (necp_address_is_valid(&address_struct->address.sa)) { |
6528 | memcpy(&remote_addr, &address_struct->address, sizeof(address_struct->address)); |
6529 | } |
6530 | } |
6531 | break; |
6532 | } |
6533 | case NECP_CLIENT_PARAMETER_FLAGS: { |
6534 | if (length >= sizeof(client_flags)) { |
6535 | memcpy(&client_flags, value, sizeof(client_flags)); |
6536 | } |
6537 | break; |
6538 | } |
6539 | case NECP_CLIENT_PARAMETER_REQUIRE_AGENT_TYPE: { |
6540 | if (num_required_agent_types >= NECP_MAX_REQUIRED_AGENTS) { |
6541 | break; |
6542 | } |
6543 | if (length >= sizeof(struct necp_client_parameter_netagent_type)) { |
6544 | memcpy(&required_agent_types[num_required_agent_types], value, sizeof(struct necp_client_parameter_netagent_type)); |
6545 | num_required_agent_types++; |
6546 | } |
6547 | break; |
6548 | } |
6549 | default: { |
6550 | break; |
6551 | } |
6552 | } |
6553 | } |
6554 | } |
6555 | |
6556 | offset += sizeof(u_int8_t) + sizeof(u_int32_t) + length; |
6557 | } |
6558 | |
6559 | // Lock |
6560 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
6561 | |
6562 | necp_application_fillout_info_locked(application_uuid, real_application_uuid, account, domain, pid, uid, protocol, bound_interface_index, traffic_class, &local_addr, &remote_addr, proc, &info); |
6563 | matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_app_layer_map, &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, netagent_use_flags, NECP_MAX_NETAGENTS, required_agent_types, num_required_agent_types, proc, NULL); |
6564 | if (matched_policy) { |
6565 | returned_result->policy_id = matched_policy->id; |
6566 | returned_result->routing_result = matched_policy->result; |
6567 | memcpy(&returned_result->routing_result_parameter, &matched_policy->result_parameter, sizeof(returned_result->routing_result_parameter)); |
6568 | } else if (necp_drop_all_order > 0) { |
6569 | // Mark socket as a drop if drop_all is set |
6570 | returned_result->policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
6571 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
6572 | } else { |
6573 | returned_result->policy_id = 0; |
6574 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_NONE; |
6575 | } |
6576 | returned_result->filter_control_unit = filter_control_unit; |
6577 | returned_result->service_action = service_action; |
6578 | |
6579 | // Handle trigger service |
6580 | if (service.identifier != 0) { |
6581 | struct necp_uuid_id_mapping *mapping = necp_uuid_lookup_uuid_with_service_id_locked(service.identifier); |
6582 | if (mapping != NULL) { |
6583 | struct necp_service_registration *service_registration = NULL; |
6584 | uuid_copy(returned_result->service_uuid, mapping->uuid); |
6585 | returned_result->service_data = service.data; |
6586 | if (service.identifier == NECP_NULL_SERVICE_ID) { |
6587 | // NULL service is always 'registered' |
6588 | returned_result->service_flags |= NECP_SERVICE_FLAGS_REGISTERED; |
6589 | } else { |
6590 | LIST_FOREACH(service_registration, &necp_registered_service_list, kernel_chain) { |
6591 | if (service.identifier == service_registration->service_id) { |
6592 | returned_result->service_flags |= NECP_SERVICE_FLAGS_REGISTERED; |
6593 | break; |
6594 | } |
6595 | } |
6596 | } |
6597 | } |
6598 | } |
6599 | |
6600 | // Handle netagents |
6601 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
6602 | struct necp_uuid_id_mapping *mapping = NULL; |
6603 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
6604 | if (netagent_id == 0) { |
6605 | break; |
6606 | } |
6607 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(netagent_id); |
6608 | if (mapping != NULL) { |
6609 | uuid_copy(returned_result->netagents[netagent_cursor], mapping->uuid); |
6610 | returned_result->netagent_use_flags[netagent_cursor] = netagent_use_flags[netagent_cursor]; |
6611 | } |
6612 | } |
6613 | |
6614 | // Do routing evaluation |
6615 | u_int output_bound_interface = bound_interface_index; |
6616 | if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED) { |
6617 | output_bound_interface = returned_result->routing_result_parameter.scoped_interface_index; |
6618 | } else if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL) { |
6619 | output_bound_interface = returned_result->routing_result_parameter.tunnel_interface_index; |
6620 | } else if (returned_result->routing_result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT) { |
6621 | output_bound_interface = necp_get_primary_direct_interface_index(); |
6622 | if (output_bound_interface == IFSCOPE_NONE) { |
6623 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
6624 | } else { |
6625 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED; |
6626 | returned_result->routing_result_parameter.scoped_interface_index = output_bound_interface; |
6627 | } |
6628 | } |
6629 | |
6630 | if (local_addr.sa.sa_len == 0 || |
6631 | (local_addr.sa.sa_family == AF_INET && local_addr.sin.sin_addr.s_addr == 0) || |
6632 | (local_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&local_addr.sin6.sin6_addr))) { |
6633 | no_local_addr = TRUE; |
6634 | } |
6635 | |
6636 | if (remote_addr.sa.sa_len == 0 || |
6637 | (remote_addr.sa.sa_family == AF_INET && remote_addr.sin.sin_addr.s_addr == 0) || |
6638 | (remote_addr.sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&remote_addr.sin6.sin6_addr))) { |
6639 | no_remote_addr = TRUE; |
6640 | remote_family = remote_addr.sa.sa_family; |
6641 | } |
6642 | |
6643 | returned_result->routed_interface_index = 0; |
6644 | struct rtentry *rt = NULL; |
6645 | if (!no_local_addr && (client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) != 0) { |
6646 | // Treat the output bound interface as the routed interface for local address |
6647 | // validation later. |
6648 | returned_result->routed_interface_index = output_bound_interface; |
6649 | } else { |
6650 | if (no_remote_addr) { |
6651 | memset(&remote_addr, 0, sizeof(remote_addr)); |
6652 | if (remote_family == AF_INET6) { |
6653 | // Reset address to :: |
6654 | remote_addr.sa.sa_family = AF_INET6; |
6655 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in6); |
6656 | } else { |
6657 | // Reset address to 0.0.0.0 |
6658 | remote_addr.sa.sa_family = AF_INET; |
6659 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in); |
6660 | } |
6661 | } |
6662 | |
6663 | rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, |
6664 | output_bound_interface); |
6665 | |
6666 | if (remote_addr.sa.sa_family == AF_INET && rt != NULL && |
6667 | IS_INTF_CLAT46(rt->rt_ifp)) { |
6668 | rtfree(rt); |
6669 | rt = NULL; |
6670 | returned_result->routed_interface_index = 0; |
6671 | } |
6672 | |
6673 | if (no_remote_addr && remote_family == 0 && |
6674 | (rt == NULL || rt->rt_ifp == NULL)) { |
6675 | // Route lookup for default IPv4 failed, try IPv6 |
6676 | |
6677 | // Cleanup old route if necessary |
6678 | if (rt != NULL) { |
6679 | rtfree(rt); |
6680 | rt = NULL; |
6681 | } |
6682 | |
6683 | // Reset address to :: |
6684 | memset(&remote_addr, 0, sizeof(remote_addr)); |
6685 | remote_addr.sa.sa_family = AF_INET6; |
6686 | remote_addr.sa.sa_len = sizeof(struct sockaddr_in6); |
6687 | |
6688 | // Get route |
6689 | rt = rtalloc1_scoped((struct sockaddr *)&remote_addr, 0, 0, |
6690 | output_bound_interface); |
6691 | } |
6692 | |
6693 | if (rt != NULL && |
6694 | rt->rt_ifp != NULL) { |
6695 | returned_result->routed_interface_index = rt->rt_ifp->if_index; |
6696 | /* |
6697 | * For local addresses, we allow the interface scope to be |
6698 | * either the loopback interface or the interface hosting the |
6699 | * local address. |
6700 | */ |
6701 | if (bound_interface_index != IFSCOPE_NONE && |
6702 | rt->rt_ifa != NULL && rt->rt_ifa->ifa_ifp && |
6703 | (output_bound_interface == lo_ifp->if_index || |
6704 | rt->rt_ifp->if_index == lo_ifp->if_index || |
6705 | rt->rt_ifa->ifa_ifp->if_index == bound_interface_index)) { |
6706 | struct sockaddr_storage dst; |
6707 | unsigned int ifscope = bound_interface_index; |
6708 | |
6709 | /* |
6710 | * Transform dst into the internal routing table form |
6711 | */ |
6712 | (void) sa_copy((struct sockaddr *)&remote_addr, |
6713 | &dst, &ifscope); |
6714 | |
6715 | if ((rt->rt_ifp->if_index == lo_ifp->if_index) || |
6716 | rt_ifa_is_dst((struct sockaddr *)&dst, rt->rt_ifa)) |
6717 | returned_result->routed_interface_index = |
6718 | bound_interface_index; |
6719 | } |
6720 | } |
6721 | } |
6722 | |
6723 | if (returned_result->routed_interface_index != 0 && |
6724 | returned_result->routed_interface_index != lo_ifp->if_index && // Loopback can accept any local address |
6725 | !no_local_addr) { |
6726 | |
6727 | // Transform local_addr into the ifaddr form |
6728 | // IPv6 Scope IDs are always embedded in the ifaddr list |
6729 | struct sockaddr_storage local_address_sanitized; |
6730 | u_int ifscope = IFSCOPE_NONE; |
6731 | (void)sa_copy(&local_addr.sa, &local_address_sanitized, &ifscope); |
6732 | SIN(&local_address_sanitized)->sin_port = 0; |
6733 | if (local_address_sanitized.ss_family == AF_INET6) { |
6734 | SIN6(&local_address_sanitized)->sin6_scope_id = 0; |
6735 | } |
6736 | |
6737 | // Validate local address on routed interface |
6738 | struct ifaddr *ifa = ifa_ifwithaddr_scoped((struct sockaddr *)&local_address_sanitized, returned_result->routed_interface_index); |
6739 | if (ifa == NULL) { |
6740 | // Interface address not found, reject route |
6741 | returned_result->routed_interface_index = 0; |
6742 | if (rt != NULL) { |
6743 | rtfree(rt); |
6744 | rt = NULL; |
6745 | } |
6746 | } else { |
6747 | ifaddr_release(ifa); |
6748 | ifa = NULL; |
6749 | } |
6750 | } |
6751 | |
6752 | if (flags != NULL) { |
6753 | if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_LISTENER) == 0) { |
6754 | // Check for local/direct |
6755 | bool is_local = FALSE; |
6756 | if (rt != NULL && (rt->rt_flags & RTF_LOCAL)) { |
6757 | is_local = TRUE; |
6758 | } else if (returned_result->routed_interface_index != 0 && |
6759 | !no_remote_addr) { |
6760 | // Clean up the address before comparison with interface addresses |
6761 | |
6762 | // Transform remote_addr into the ifaddr form |
6763 | // IPv6 Scope IDs are always embedded in the ifaddr list |
6764 | struct sockaddr_storage remote_address_sanitized; |
6765 | u_int ifscope = IFSCOPE_NONE; |
6766 | (void)sa_copy(&remote_addr.sa, &remote_address_sanitized, &ifscope); |
6767 | SIN(&remote_address_sanitized)->sin_port = 0; |
6768 | if (remote_address_sanitized.ss_family == AF_INET6) { |
6769 | SIN6(&remote_address_sanitized)->sin6_scope_id = 0; |
6770 | } |
6771 | |
6772 | // Check if remote address is an interface address |
6773 | struct ifaddr *ifa = ifa_ifwithaddr((struct sockaddr *)&remote_address_sanitized); |
6774 | if (ifa != NULL && ifa->ifa_ifp != NULL) { |
6775 | u_int if_index_for_remote_addr = ifa->ifa_ifp->if_index; |
6776 | if (if_index_for_remote_addr == returned_result->routed_interface_index || |
6777 | if_index_for_remote_addr == lo_ifp->if_index) { |
6778 | is_local = TRUE; |
6779 | } |
6780 | } |
6781 | if (ifa != NULL) { |
6782 | ifaddr_release(ifa); |
6783 | ifa = NULL; |
6784 | } |
6785 | } |
6786 | |
6787 | if (is_local) { |
6788 | *flags |= (NECP_CLIENT_RESULT_FLAG_IS_LOCAL | NECP_CLIENT_RESULT_FLAG_IS_DIRECT); |
6789 | } else { |
6790 | if (rt != NULL && |
6791 | !(rt->rt_flags & RTF_GATEWAY) && |
6792 | (rt->rt_ifa && rt->rt_ifa->ifa_ifp && !(rt->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) { |
6793 | // Route is directly accessible |
6794 | *flags |= NECP_CLIENT_RESULT_FLAG_IS_DIRECT; |
6795 | } |
6796 | } |
6797 | |
6798 | if (rt != NULL && |
6799 | rt->rt_ifp != NULL) { |
6800 | // Check probe status |
6801 | if (rt->rt_ifp->if_eflags & IFEF_PROBE_CONNECTIVITY) { |
6802 | *flags |= NECP_CLIENT_RESULT_FLAG_PROBE_CONNECTIVITY; |
6803 | } |
6804 | |
6805 | if (rt->rt_ifp->if_type == IFT_CELLULAR) { |
6806 | struct if_cellular_status_v1 *ifsr; |
6807 | |
6808 | ifnet_lock_shared(rt->rt_ifp); |
6809 | lck_rw_lock_exclusive(&rt->rt_ifp->if_link_status_lock); |
6810 | |
6811 | if (rt->rt_ifp->if_link_status != NULL) { |
6812 | ifsr = &rt->rt_ifp->if_link_status->ifsr_u.ifsr_cell.if_cell_u.if_status_v1; |
6813 | |
6814 | if (ifsr->valid_bitmask & IF_CELL_UL_MSS_RECOMMENDED_VALID) { |
6815 | if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_NONE) { |
6816 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_NONE; |
6817 | } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_MEDIUM) { |
6818 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_MEDIUM; |
6819 | } else if (ifsr->mss_recommended == IF_CELL_UL_MSS_RECOMMENDED_LOW) { |
6820 | returned_result->mss_recommended = NECP_CLIENT_RESULT_RECOMMENDED_MSS_LOW; |
6821 | } |
6822 | } |
6823 | } |
6824 | lck_rw_done(&rt->rt_ifp->if_link_status_lock); |
6825 | ifnet_lock_done(rt->rt_ifp); |
6826 | } |
6827 | |
6828 | // Check link quality |
6829 | if ((client_flags & NECP_CLIENT_PARAMETER_FLAG_DISCRETIONARY) && |
6830 | (rt->rt_ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
6831 | rt->rt_ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
6832 | *flags |= NECP_CLIENT_RESULT_FLAG_LINK_QUALITY_ABORT; |
6833 | } |
6834 | |
6835 | // Check QoS marking (fastlane) |
6836 | if (necp_update_qos_marking(rt->rt_ifp, route_rule_id)) { |
6837 | *flags |= NECP_CLIENT_RESULT_FLAG_ALLOW_QOS_MARKING; |
6838 | } |
6839 | |
6840 | if (IFNET_IS_LOW_POWER(rt->rt_ifp)) { |
6841 | *flags |= NECP_CLIENT_RESULT_FLAG_INTERFACE_LOW_POWER; |
6842 | } |
6843 | } |
6844 | } |
6845 | |
6846 | if (returned_result->routed_interface_index != 0) { |
6847 | union necp_sockaddr_union default_address; |
6848 | struct rtentry *v4Route = NULL; |
6849 | struct rtentry *v6Route = NULL; |
6850 | |
6851 | memset(&default_address, 0, sizeof(default_address)); |
6852 | |
6853 | // Reset address to 0.0.0.0 |
6854 | default_address.sa.sa_family = AF_INET; |
6855 | default_address.sa.sa_len = sizeof(struct sockaddr_in); |
6856 | v4Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0, |
6857 | returned_result->routed_interface_index); |
6858 | |
6859 | // Reset address to :: |
6860 | default_address.sa.sa_family = AF_INET6; |
6861 | default_address.sa.sa_len = sizeof(struct sockaddr_in6); |
6862 | v6Route = rtalloc1_scoped((struct sockaddr *)&default_address, 0, 0, |
6863 | returned_result->routed_interface_index); |
6864 | |
6865 | if (v4Route != NULL) { |
6866 | if (v4Route->rt_ifp != NULL && !IS_INTF_CLAT46(v4Route->rt_ifp)) { |
6867 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV4; |
6868 | } |
6869 | rtfree(v4Route); |
6870 | v4Route = NULL; |
6871 | } |
6872 | |
6873 | if (v6Route != NULL) { |
6874 | if (v6Route->rt_ifp != NULL) { |
6875 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_IPV6; |
6876 | |
6877 | if (ifnet_get_nat64prefix(v6Route->rt_ifp, NULL) == 0) { |
6878 | *flags |= NECP_CLIENT_RESULT_FLAG_HAS_NAT64; |
6879 | } |
6880 | } |
6881 | rtfree(v6Route); |
6882 | v6Route = NULL; |
6883 | } |
6884 | } |
6885 | } |
6886 | |
6887 | u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN; |
6888 | bool route_is_allowed = necp_route_is_allowed(rt, NULL, route_rule_id, &interface_type_denied); |
6889 | if (!route_is_allowed) { |
6890 | // If the route is blocked, treat the lookup as a drop |
6891 | returned_result->routing_result = NECP_KERNEL_POLICY_RESULT_DROP; |
6892 | memset(&returned_result->routing_result_parameter, 0, sizeof(returned_result->routing_result_parameter)); |
6893 | |
6894 | if (interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) { |
6895 | necp_send_application_interface_denied_event(pid, application_uuid, interface_type_denied); |
6896 | } |
6897 | } |
6898 | |
6899 | if (rt != NULL) { |
6900 | if (returned_route != NULL) { |
6901 | *returned_route = rt; |
6902 | } else { |
6903 | rtfree(rt); |
6904 | } |
6905 | rt = NULL; |
6906 | } |
6907 | // Unlock |
6908 | lck_rw_done(&necp_kernel_policy_lock); |
6909 | |
6910 | return (error); |
6911 | } |
6912 | |
6913 | static bool |
6914 | necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_app_id app_id, necp_app_id real_app_id, errno_t cred_result, u_int32_t account_id, struct substring domain, u_int8_t domain_dot_count, pid_t pid, uid_t uid, u_int32_t bound_interface_index, u_int32_t traffic_class, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote, struct necp_client_parameter_netagent_type *required_agent_types, u_int32_t num_required_agent_types, proc_t proc) |
6915 | { |
6916 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
6917 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
6918 | u_int32_t cond_bound_interface_index = kernel_policy->cond_bound_interface ? kernel_policy->cond_bound_interface->if_index : 0; |
6919 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
6920 | if (bound_interface_index == cond_bound_interface_index) { |
6921 | // No match, matches forbidden interface |
6922 | return (FALSE); |
6923 | } |
6924 | } else { |
6925 | if (bound_interface_index != cond_bound_interface_index) { |
6926 | // No match, does not match required interface |
6927 | return (FALSE); |
6928 | } |
6929 | } |
6930 | } else { |
6931 | if (bound_interface_index != 0) { |
6932 | // No match, requires a non-bound packet |
6933 | return (FALSE); |
6934 | } |
6935 | } |
6936 | } |
6937 | |
6938 | if (kernel_policy->condition_mask == 0) { |
6939 | return (TRUE); |
6940 | } |
6941 | |
6942 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
6943 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_APP_ID) { |
6944 | if (app_id == kernel_policy->cond_app_id) { |
6945 | // No match, matches forbidden application |
6946 | return (FALSE); |
6947 | } |
6948 | } else { |
6949 | if (app_id != kernel_policy->cond_app_id) { |
6950 | // No match, does not match required application |
6951 | return (FALSE); |
6952 | } |
6953 | } |
6954 | } |
6955 | |
6956 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
6957 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
6958 | if (real_app_id == kernel_policy->cond_real_app_id) { |
6959 | // No match, matches forbidden application |
6960 | return (FALSE); |
6961 | } |
6962 | } else { |
6963 | if (real_app_id != kernel_policy->cond_real_app_id) { |
6964 | // No match, does not match required application |
6965 | return (FALSE); |
6966 | } |
6967 | } |
6968 | } |
6969 | |
6970 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
6971 | if (cred_result != 0) { |
6972 | // Process is missing entitlement |
6973 | return (FALSE); |
6974 | } |
6975 | } |
6976 | |
6977 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) { |
6978 | if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_false) { |
6979 | // Process is missing entitlement based on previous check |
6980 | return (FALSE); |
6981 | } else if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_unknown) { |
6982 | if (kernel_policy->cond_custom_entitlement != NULL) { |
6983 | if (proc == NULL) { |
6984 | // No process found, cannot check entitlement |
6985 | return (FALSE); |
6986 | } |
6987 | task_t task = proc_task(proc); |
6988 | if (task == NULL || |
6989 | !IOTaskHasEntitlement(task, kernel_policy->cond_custom_entitlement)) { |
6990 | // Process is missing custom entitlement |
6991 | kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_false; |
6992 | return (FALSE); |
6993 | } else { |
6994 | kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_true; |
6995 | } |
6996 | } |
6997 | } |
6998 | } |
6999 | |
7000 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
7001 | bool domain_matches = necp_hostname_matches_domain(domain, domain_dot_count, kernel_policy->cond_domain, kernel_policy->cond_domain_dot_count); |
7002 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
7003 | if (domain_matches) { |
7004 | // No match, matches forbidden domain |
7005 | return (FALSE); |
7006 | } |
7007 | } else { |
7008 | if (!domain_matches) { |
7009 | // No match, does not match required domain |
7010 | return (FALSE); |
7011 | } |
7012 | } |
7013 | } |
7014 | |
7015 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
7016 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) { |
7017 | if (account_id == kernel_policy->cond_account_id) { |
7018 | // No match, matches forbidden account |
7019 | return (FALSE); |
7020 | } |
7021 | } else { |
7022 | if (account_id != kernel_policy->cond_account_id) { |
7023 | // No match, does not match required account |
7024 | return (FALSE); |
7025 | } |
7026 | } |
7027 | } |
7028 | |
7029 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PID) { |
7030 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PID) { |
7031 | if (pid == kernel_policy->cond_pid) { |
7032 | // No match, matches forbidden pid |
7033 | return (FALSE); |
7034 | } |
7035 | } else { |
7036 | if (pid != kernel_policy->cond_pid) { |
7037 | // No match, does not match required pid |
7038 | return (FALSE); |
7039 | } |
7040 | } |
7041 | } |
7042 | |
7043 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_UID) { |
7044 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_UID) { |
7045 | if (uid == kernel_policy->cond_uid) { |
7046 | // No match, matches forbidden uid |
7047 | return (FALSE); |
7048 | } |
7049 | } else { |
7050 | if (uid != kernel_policy->cond_uid) { |
7051 | // No match, does not match required uid |
7052 | return (FALSE); |
7053 | } |
7054 | } |
7055 | } |
7056 | |
7057 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
7058 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
7059 | if (traffic_class >= kernel_policy->cond_traffic_class.start_tc && |
7060 | traffic_class <= kernel_policy->cond_traffic_class.end_tc) { |
7061 | // No match, matches forbidden traffic class |
7062 | return (FALSE); |
7063 | } |
7064 | } else { |
7065 | if (traffic_class < kernel_policy->cond_traffic_class.start_tc || |
7066 | traffic_class > kernel_policy->cond_traffic_class.end_tc) { |
7067 | // No match, does not match required traffic class |
7068 | return (FALSE); |
7069 | } |
7070 | } |
7071 | } |
7072 | |
7073 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
7074 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
7075 | if (protocol == kernel_policy->cond_protocol) { |
7076 | // No match, matches forbidden protocol |
7077 | return (FALSE); |
7078 | } |
7079 | } else { |
7080 | if (protocol != kernel_policy->cond_protocol) { |
7081 | // No match, does not match required protocol |
7082 | return (FALSE); |
7083 | } |
7084 | } |
7085 | } |
7086 | |
7087 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_AGENT_TYPE) { |
7088 | bool matches_agent_type = FALSE; |
7089 | for (u_int32_t i = 0; i < num_required_agent_types; i++) { |
7090 | struct necp_client_parameter_netagent_type *required_agent_type = &required_agent_types[i]; |
7091 | if ((strlen(kernel_policy->cond_agent_type.agent_domain) == 0 || |
7092 | strncmp(required_agent_type->netagent_domain, kernel_policy->cond_agent_type.agent_domain, NETAGENT_DOMAINSIZE) == 0) && |
7093 | (strlen(kernel_policy->cond_agent_type.agent_type) == 0 || |
7094 | strncmp(required_agent_type->netagent_type, kernel_policy->cond_agent_type.agent_type, NETAGENT_TYPESIZE) == 0)) { |
7095 | // Found a required agent that matches |
7096 | matches_agent_type = TRUE; |
7097 | break; |
7098 | } |
7099 | } |
7100 | if (!matches_agent_type) { |
7101 | return (FALSE); |
7102 | } |
7103 | } |
7104 | |
7105 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
7106 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
7107 | bool inRange = necp_is_addr_in_range((struct sockaddr *)local, (struct sockaddr *)&kernel_policy->cond_local_start, (struct sockaddr *)&kernel_policy->cond_local_end); |
7108 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
7109 | if (inRange) { |
7110 | return (FALSE); |
7111 | } |
7112 | } else { |
7113 | if (!inRange) { |
7114 | return (FALSE); |
7115 | } |
7116 | } |
7117 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
7118 | bool inSubnet = necp_is_addr_in_subnet((struct sockaddr *)local, (struct sockaddr *)&kernel_policy->cond_local_start, kernel_policy->cond_local_prefix); |
7119 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
7120 | if (inSubnet) { |
7121 | return (FALSE); |
7122 | } |
7123 | } else { |
7124 | if (!inSubnet) { |
7125 | return (FALSE); |
7126 | } |
7127 | } |
7128 | } |
7129 | } |
7130 | |
7131 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
7132 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
7133 | bool inRange = necp_is_addr_in_range((struct sockaddr *)remote, (struct sockaddr *)&kernel_policy->cond_remote_start, (struct sockaddr *)&kernel_policy->cond_remote_end); |
7134 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
7135 | if (inRange) { |
7136 | return (FALSE); |
7137 | } |
7138 | } else { |
7139 | if (!inRange) { |
7140 | return (FALSE); |
7141 | } |
7142 | } |
7143 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
7144 | bool inSubnet = necp_is_addr_in_subnet((struct sockaddr *)remote, (struct sockaddr *)&kernel_policy->cond_remote_start, kernel_policy->cond_remote_prefix); |
7145 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
7146 | if (inSubnet) { |
7147 | return (FALSE); |
7148 | } |
7149 | } else { |
7150 | if (!inSubnet) { |
7151 | return (FALSE); |
7152 | } |
7153 | } |
7154 | } |
7155 | } |
7156 | |
7157 | return (TRUE); |
7158 | } |
7159 | |
7160 | static inline u_int32_t |
7161 | necp_socket_calc_flowhash_locked(struct necp_socket_info *info) |
7162 | { |
7163 | return (net_flowhash(info, sizeof(*info), necp_kernel_socket_policies_gencount)); |
7164 | } |
7165 | |
7166 | static void |
7167 | necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface, struct necp_socket_info *info) |
7168 | { |
7169 | struct socket *so = NULL; |
7170 | |
7171 | memset(info, 0, sizeof(struct necp_socket_info)); |
7172 | |
7173 | so = inp->inp_socket; |
7174 | |
7175 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_PID) { |
7176 | info->pid = ((so->so_flags & SOF_DELEGATED) ? so->e_pid : so->last_pid); |
7177 | } |
7178 | |
7179 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_UID) { |
7180 | info->uid = kauth_cred_getuid(so->so_cred); |
7181 | } |
7182 | |
7183 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_TRAFFIC_CLASS) { |
7184 | info->traffic_class = so->so_traffic_class; |
7185 | } |
7186 | |
7187 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
7188 | if (inp->inp_ip_p) { |
7189 | info->protocol = inp->inp_ip_p; |
7190 | } else { |
7191 | info->protocol = SOCK_PROTO(so); |
7192 | } |
7193 | } |
7194 | |
7195 | if (inp->inp_flags2 & INP2_WANT_APP_POLICY && necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_APP_ID) { |
7196 | struct necp_uuid_id_mapping *existing_mapping = necp_uuid_lookup_app_id_locked(((so->so_flags & SOF_DELEGATED) ? so->e_uuid : so->last_uuid)); |
7197 | if (existing_mapping) { |
7198 | info->application_id = existing_mapping->id; |
7199 | } |
7200 | |
7201 | if (!(so->so_flags & SOF_DELEGATED)) { |
7202 | info->real_application_id = info->application_id; |
7203 | } else if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_REAL_APP_ID) { |
7204 | struct necp_uuid_id_mapping *real_existing_mapping = necp_uuid_lookup_app_id_locked(so->last_uuid); |
7205 | if (real_existing_mapping) { |
7206 | info->real_application_id = real_existing_mapping->id; |
7207 | } |
7208 | } |
7209 | |
7210 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_ENTITLEMENT) { |
7211 | info->cred_result = priv_check_cred(so->so_cred, PRIV_NET_PRIVILEGED_NECP_MATCH, 0); |
7212 | if (info->cred_result != 0) { |
7213 | // Process does not have entitlement, check the parent process |
7214 | necp_get_parent_cred_result(NULL, info); |
7215 | } |
7216 | } |
7217 | } |
7218 | |
7219 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID && inp->inp_necp_attributes.inp_account != NULL) { |
7220 | struct necp_string_id_mapping *existing_mapping = necp_lookup_string_to_id_locked(&necp_account_id_list, inp->inp_necp_attributes.inp_account); |
7221 | if (existing_mapping) { |
7222 | info->account_id = existing_mapping->id; |
7223 | } |
7224 | } |
7225 | |
7226 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_CONDITION_DOMAIN) { |
7227 | info->domain = inp->inp_necp_attributes.inp_domain; |
7228 | } |
7229 | |
7230 | if (override_bound_interface) { |
7231 | info->bound_interface_index = override_bound_interface; |
7232 | } else { |
7233 | if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp) { |
7234 | info->bound_interface_index = inp->inp_boundifp->if_index; |
7235 | } |
7236 | } |
7237 | |
7238 | if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS) { |
7239 | if (inp->inp_vflag & INP_IPV4) { |
7240 | if (override_local_addr) { |
7241 | if (override_local_addr->sa_len <= sizeof(struct sockaddr_in)) { |
7242 | memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len); |
7243 | } |
7244 | } else { |
7245 | ((struct sockaddr_in *)&info->local_addr)->sin_family = AF_INET; |
7246 | ((struct sockaddr_in *)&info->local_addr)->sin_len = sizeof(struct sockaddr_in); |
7247 | ((struct sockaddr_in *)&info->local_addr)->sin_port = inp->inp_lport; |
7248 | memcpy(&((struct sockaddr_in *)&info->local_addr)->sin_addr, &inp->inp_laddr, sizeof(struct in_addr)); |
7249 | } |
7250 | |
7251 | if (override_remote_addr) { |
7252 | if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in)) { |
7253 | memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len); |
7254 | } |
7255 | } else { |
7256 | ((struct sockaddr_in *)&info->remote_addr)->sin_family = AF_INET; |
7257 | ((struct sockaddr_in *)&info->remote_addr)->sin_len = sizeof(struct sockaddr_in); |
7258 | ((struct sockaddr_in *)&info->remote_addr)->sin_port = inp->inp_fport; |
7259 | memcpy(&((struct sockaddr_in *)&info->remote_addr)->sin_addr, &inp->inp_faddr, sizeof(struct in_addr)); |
7260 | } |
7261 | } else if (inp->inp_vflag & INP_IPV6) { |
7262 | if (override_local_addr) { |
7263 | if (override_local_addr->sa_len <= sizeof(struct sockaddr_in6)) { |
7264 | memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len); |
7265 | } |
7266 | } else { |
7267 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_family = AF_INET6; |
7268 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_len = sizeof(struct sockaddr_in6); |
7269 | ((struct sockaddr_in6 *)&info->local_addr)->sin6_port = inp->inp_lport; |
7270 | memcpy(&((struct sockaddr_in6 *)&info->local_addr)->sin6_addr, &inp->in6p_laddr, sizeof(struct in6_addr)); |
7271 | } |
7272 | |
7273 | if (override_remote_addr) { |
7274 | if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in6)) { |
7275 | memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len); |
7276 | } |
7277 | } else { |
7278 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_family = AF_INET6; |
7279 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_len = sizeof(struct sockaddr_in6); |
7280 | ((struct sockaddr_in6 *)&info->remote_addr)->sin6_port = inp->inp_fport; |
7281 | memcpy(&((struct sockaddr_in6 *)&info->remote_addr)->sin6_addr, &inp->in6p_faddr, sizeof(struct in6_addr)); |
7282 | } |
7283 | } |
7284 | } |
7285 | } |
7286 | |
7287 | static inline struct necp_kernel_socket_policy * |
7288 | necp_socket_find_policy_match_with_info_locked(struct necp_kernel_socket_policy **policy_search_array, struct necp_socket_info *info, |
7289 | necp_kernel_policy_filter *return_filter, u_int32_t *return_route_rule_id, |
7290 | necp_kernel_policy_result *return_service_action, necp_kernel_policy_service *return_service, |
7291 | u_int32_t *return_netagent_array, u_int32_t *return_netagent_use_flags_array, size_t netagent_array_count, |
7292 | struct necp_client_parameter_netagent_type *required_agent_types, |
7293 | u_int32_t num_required_agent_types, proc_t proc, necp_kernel_policy_id *skip_policy_id) |
7294 | { |
7295 | struct necp_kernel_socket_policy *matched_policy = NULL; |
7296 | u_int32_t skip_order = 0; |
7297 | u_int32_t skip_session_order = 0; |
7298 | u_int32_t route_rule_id_array[MAX_AGGREGATE_ROUTE_RULES]; |
7299 | size_t route_rule_id_count = 0; |
7300 | int i; |
7301 | size_t netagent_cursor = 0; |
7302 | |
7303 | // Pre-process domain for quick matching |
7304 | struct substring domain_substring = necp_trim_dots_and_stars(info->domain, info->domain ? strlen(info->domain) : 0); |
7305 | u_int8_t domain_dot_count = necp_count_dots(domain_substring.string, domain_substring.length); |
7306 | |
7307 | if (return_filter) { |
7308 | *return_filter = 0; |
7309 | } |
7310 | |
7311 | if (return_route_rule_id) { |
7312 | *return_route_rule_id = 0; |
7313 | } |
7314 | |
7315 | if (return_service_action) { |
7316 | *return_service_action = 0; |
7317 | } |
7318 | |
7319 | if (return_service) { |
7320 | return_service->identifier = 0; |
7321 | return_service->data = 0; |
7322 | } |
7323 | |
7324 | if (policy_search_array != NULL) { |
7325 | for (i = 0; policy_search_array[i] != NULL; i++) { |
7326 | if (necp_drop_all_order != 0 && policy_search_array[i]->session_order >= necp_drop_all_order) { |
7327 | // We've hit a drop all rule |
7328 | break; |
7329 | } |
7330 | if (skip_session_order && policy_search_array[i]->session_order >= skip_session_order) { |
7331 | // Done skipping |
7332 | skip_order = 0; |
7333 | skip_session_order = 0; |
7334 | } |
7335 | if (skip_order) { |
7336 | if (policy_search_array[i]->order < skip_order) { |
7337 | // Skip this policy |
7338 | continue; |
7339 | } else { |
7340 | // Done skipping |
7341 | skip_order = 0; |
7342 | skip_session_order = 0; |
7343 | } |
7344 | } else if (skip_session_order) { |
7345 | // Skip this policy |
7346 | continue; |
7347 | } |
7348 | if (necp_socket_check_policy(policy_search_array[i], info->application_id, info->real_application_id, info->cred_result, info->account_id, domain_substring, domain_dot_count, info->pid, info->uid, info->bound_interface_index, info->traffic_class, info->protocol, &info->local_addr, &info->remote_addr, required_agent_types, num_required_agent_types, proc)) { |
7349 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SOCKET_FILTER) { |
7350 | if (return_filter && *return_filter == 0) { |
7351 | *return_filter = policy_search_array[i]->result_parameter.filter_control_unit; |
7352 | if (necp_debug > 1) { |
7353 | NECPLOG(LOG_DEBUG, "Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Filter %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result_parameter.filter_control_unit); |
7354 | } |
7355 | } |
7356 | continue; |
7357 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_ROUTE_RULES) { |
7358 | if (return_route_rule_id && route_rule_id_count < MAX_AGGREGATE_ROUTE_RULES) { |
7359 | route_rule_id_array[route_rule_id_count++] = policy_search_array[i]->result_parameter.route_rule_id; |
7360 | if (necp_debug > 1) { |
7361 | NECPLOG(LOG_DEBUG, "Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Route Rule %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result_parameter.route_rule_id); |
7362 | } |
7363 | } |
7364 | continue; |
7365 | } else if (necp_kernel_socket_result_is_trigger_service_type(policy_search_array[i])) { |
7366 | if (return_service_action && *return_service_action == 0) { |
7367 | *return_service_action = policy_search_array[i]->result; |
7368 | if (necp_debug > 1) { |
7369 | NECPLOG(LOG_DEBUG, "Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Service Action %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result); |
7370 | } |
7371 | } |
7372 | if (return_service && return_service->identifier == 0) { |
7373 | return_service->identifier = policy_search_array[i]->result_parameter.service.identifier; |
7374 | return_service->data = policy_search_array[i]->result_parameter.service.data; |
7375 | if (necp_debug > 1) { |
7376 | NECPLOG(LOG_DEBUG, "Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) Service ID %d Data %d" , info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, policy_search_array[i]->result_parameter.service.identifier, policy_search_array[i]->result_parameter.service.data); |
7377 | } |
7378 | } |
7379 | continue; |
7380 | } else if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT || |
7381 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
7382 | if (return_netagent_array != NULL && |
7383 | netagent_cursor < netagent_array_count) { |
7384 | return_netagent_array[netagent_cursor] = policy_search_array[i]->result_parameter.netagent_id; |
7385 | if (return_netagent_use_flags_array != NULL && |
7386 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_NETAGENT_SCOPED) { |
7387 | return_netagent_use_flags_array[netagent_cursor] |= NECP_AGENT_USE_FLAG_SCOPE; |
7388 | } |
7389 | netagent_cursor++; |
7390 | if (necp_debug > 1) { |
7391 | NECPLOG(LOG_DEBUG, "Socket Policy: (Application %d Real Application %d BoundInterface %d Proto %d) %s Netagent %d" , |
7392 | info->application_id, info->real_application_id, info->bound_interface_index, info->protocol, |
7393 | policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_USE_NETAGENT ? "Use" : "Scope" , |
7394 | policy_search_array[i]->result_parameter.netagent_id); |
7395 | } |
7396 | } |
7397 | continue; |
7398 | } |
7399 | |
7400 | // Matched policy is a skip. Do skip and continue. |
7401 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
7402 | skip_order = policy_search_array[i]->result_parameter.skip_policy_order; |
7403 | skip_session_order = policy_search_array[i]->session_order + 1; |
7404 | if (skip_policy_id) { |
7405 | *skip_policy_id = policy_search_array[i]->id; |
7406 | } |
7407 | continue; |
7408 | } |
7409 | |
7410 | // Passed all tests, found a match |
7411 | matched_policy = policy_search_array[i]; |
7412 | break; |
7413 | } |
7414 | } |
7415 | } |
7416 | |
7417 | if (route_rule_id_count == 1) { |
7418 | *return_route_rule_id = route_rule_id_array[0]; |
7419 | } else if (route_rule_id_count > 1) { |
7420 | *return_route_rule_id = necp_create_aggregate_route_rule(route_rule_id_array); |
7421 | } |
7422 | return (matched_policy); |
7423 | } |
7424 | |
7425 | static bool |
7426 | necp_socket_uses_interface(struct inpcb *inp, u_int32_t interface_index) |
7427 | { |
7428 | bool found_match = FALSE; |
7429 | errno_t result = 0; |
7430 | ifaddr_t *addresses = NULL; |
7431 | union necp_sockaddr_union address_storage; |
7432 | int i; |
7433 | int family = AF_INET; |
7434 | ifnet_t interface = ifindex2ifnet[interface_index]; |
7435 | |
7436 | if (inp == NULL || interface == NULL) { |
7437 | return (FALSE); |
7438 | } |
7439 | |
7440 | if (inp->inp_vflag & INP_IPV4) { |
7441 | family = AF_INET; |
7442 | } else if (inp->inp_vflag & INP_IPV6) { |
7443 | family = AF_INET6; |
7444 | } |
7445 | |
7446 | result = ifnet_get_address_list_family(interface, &addresses, family); |
7447 | if (result != 0) { |
7448 | NECPLOG(LOG_ERR, "Failed to get address list for %s%d" , ifnet_name(interface), ifnet_unit(interface)); |
7449 | return (FALSE); |
7450 | } |
7451 | |
7452 | for (i = 0; addresses[i] != NULL; i++) { |
7453 | if (ifaddr_address(addresses[i], &address_storage.sa, sizeof(address_storage)) == 0) { |
7454 | if (family == AF_INET) { |
7455 | if (memcmp(&address_storage.sin.sin_addr, &inp->inp_laddr, sizeof(inp->inp_laddr)) == 0) { |
7456 | found_match = TRUE; |
7457 | goto done; |
7458 | } |
7459 | } else if (family == AF_INET6) { |
7460 | if (memcmp(&address_storage.sin6.sin6_addr, &inp->in6p_laddr, sizeof(inp->in6p_laddr)) == 0) { |
7461 | found_match = TRUE; |
7462 | goto done; |
7463 | } |
7464 | } |
7465 | } |
7466 | } |
7467 | |
7468 | done: |
7469 | ifnet_free_address_list(addresses); |
7470 | addresses = NULL; |
7471 | return (found_match); |
7472 | } |
7473 | |
7474 | static inline bool |
7475 | necp_socket_is_connected(struct inpcb *inp) |
7476 | { |
7477 | return (inp->inp_socket->so_state & (SS_ISCONNECTING | SS_ISCONNECTED | SS_ISDISCONNECTING)); |
7478 | } |
7479 | |
7480 | static inline bool |
7481 | necp_socket_bypass(struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, struct inpcb *inp) |
7482 | { |
7483 | |
7484 | if (necp_pass_loopback > 0 && necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) { |
7485 | return (true); |
7486 | } else if (necp_is_intcoproc(inp, NULL)) { |
7487 | return (true); |
7488 | } |
7489 | |
7490 | return (false); |
7491 | } |
7492 | |
7493 | necp_kernel_policy_id |
7494 | necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface) |
7495 | { |
7496 | struct socket *so = NULL; |
7497 | necp_kernel_policy_filter filter_control_unit = 0; |
7498 | u_int32_t route_rule_id = 0; |
7499 | struct necp_kernel_socket_policy *matched_policy = NULL; |
7500 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
7501 | necp_kernel_policy_result service_action = 0; |
7502 | necp_kernel_policy_service service = { 0, 0 }; |
7503 | |
7504 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
7505 | memset(&netagent_ids, 0, sizeof(netagent_ids)); |
7506 | int netagent_cursor; |
7507 | |
7508 | struct necp_socket_info info; |
7509 | |
7510 | if (inp == NULL) { |
7511 | return (NECP_KERNEL_POLICY_ID_NONE); |
7512 | } |
7513 | |
7514 | // Ignore invalid addresses |
7515 | if (override_local_addr != NULL && |
7516 | !necp_address_is_valid(override_local_addr)) { |
7517 | override_local_addr = NULL; |
7518 | } |
7519 | if (override_remote_addr != NULL && |
7520 | !necp_address_is_valid(override_remote_addr)) { |
7521 | override_remote_addr = NULL; |
7522 | } |
7523 | |
7524 | so = inp->inp_socket; |
7525 | |
7526 | // Don't lock. Possible race condition, but we don't want the performance hit. |
7527 | if (necp_kernel_socket_policies_count == 0 || |
7528 | (!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0)) { |
7529 | if (necp_drop_all_order > 0) { |
7530 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7531 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7532 | inp->inp_policyresult.policy_gencount = 0; |
7533 | inp->inp_policyresult.app_id = 0; |
7534 | inp->inp_policyresult.flowhash = 0; |
7535 | inp->inp_policyresult.results.filter_control_unit = 0; |
7536 | inp->inp_policyresult.results.route_rule_id = 0; |
7537 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) { |
7538 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS; |
7539 | } else { |
7540 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
7541 | } |
7542 | } |
7543 | return (NECP_KERNEL_POLICY_ID_NONE); |
7544 | } |
7545 | |
7546 | // Check for loopback exception |
7547 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) { |
7548 | // Mark socket as a pass |
7549 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7550 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7551 | inp->inp_policyresult.policy_gencount = 0; |
7552 | inp->inp_policyresult.app_id = 0; |
7553 | inp->inp_policyresult.flowhash = 0; |
7554 | inp->inp_policyresult.results.filter_control_unit = 0; |
7555 | inp->inp_policyresult.results.route_rule_id = 0; |
7556 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS; |
7557 | return (NECP_KERNEL_POLICY_ID_NONE); |
7558 | } |
7559 | |
7560 | // Lock |
7561 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
7562 | |
7563 | necp_socket_fillout_info_locked(inp, override_local_addr, override_remote_addr, override_bound_interface, &info); |
7564 | inp->inp_policyresult.app_id = info.application_id; |
7565 | |
7566 | // Check info |
7567 | u_int32_t flowhash = necp_socket_calc_flowhash_locked(&info); |
7568 | if (inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE && |
7569 | inp->inp_policyresult.policy_gencount == necp_kernel_socket_policies_gencount && |
7570 | inp->inp_policyresult.flowhash == flowhash) { |
7571 | // If already matched this socket on this generation of table, skip |
7572 | |
7573 | // Unlock |
7574 | lck_rw_done(&necp_kernel_policy_lock); |
7575 | |
7576 | return (inp->inp_policyresult.policy_id); |
7577 | } |
7578 | |
7579 | // Match socket to policy |
7580 | necp_kernel_policy_id skip_policy_id; |
7581 | matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, &filter_control_unit, &route_rule_id, &service_action, &service, netagent_ids, NULL, NECP_MAX_NETAGENTS, NULL, 0, current_proc(), &skip_policy_id); |
7582 | // If the socket matched a scoped service policy, mark as Drop if not registered. |
7583 | // This covers the cases in which a service is required (on demand) but hasn't started yet. |
7584 | if ((service_action == NECP_KERNEL_POLICY_RESULT_TRIGGER_SCOPED || |
7585 | service_action == NECP_KERNEL_POLICY_RESULT_NO_TRIGGER_SCOPED) && |
7586 | service.identifier != 0 && |
7587 | service.identifier != NECP_NULL_SERVICE_ID) { |
7588 | bool service_is_registered = FALSE; |
7589 | struct necp_service_registration *service_registration = NULL; |
7590 | LIST_FOREACH(service_registration, &necp_registered_service_list, kernel_chain) { |
7591 | if (service.identifier == service_registration->service_id) { |
7592 | service_is_registered = TRUE; |
7593 | break; |
7594 | } |
7595 | } |
7596 | if (!service_is_registered) { |
7597 | // Mark socket as a drop if service is not registered |
7598 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7599 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7600 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
7601 | inp->inp_policyresult.flowhash = flowhash; |
7602 | inp->inp_policyresult.results.filter_control_unit = 0; |
7603 | inp->inp_policyresult.results.route_rule_id = 0; |
7604 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
7605 | |
7606 | if (necp_debug > 1) { |
7607 | NECPLOG(LOG_DEBUG, "Socket Policy: (BoundInterface %d Proto %d) Dropping packet because service is not registered" , info.bound_interface_index, info.protocol); |
7608 | } |
7609 | |
7610 | // Unlock |
7611 | lck_rw_done(&necp_kernel_policy_lock); |
7612 | return (NECP_KERNEL_POLICY_ID_NONE); |
7613 | } |
7614 | } |
7615 | // Verify netagents |
7616 | for (netagent_cursor = 0; netagent_cursor < NECP_MAX_NETAGENTS; netagent_cursor++) { |
7617 | struct necp_uuid_id_mapping *mapping = NULL; |
7618 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
7619 | if (netagent_id == 0) { |
7620 | break; |
7621 | } |
7622 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(netagent_id); |
7623 | if (mapping != NULL) { |
7624 | u_int32_t agent_flags = 0; |
7625 | agent_flags = netagent_get_flags(mapping->uuid); |
7626 | if (agent_flags & NETAGENT_FLAG_REGISTERED) { |
7627 | if (agent_flags & NETAGENT_FLAG_ACTIVE) { |
7628 | continue; |
7629 | } else if ((agent_flags & NETAGENT_FLAG_VOLUNTARY) == 0) { |
7630 | if (agent_flags & NETAGENT_FLAG_KERNEL_ACTIVATED) { |
7631 | int trigger_error = 0; |
7632 | trigger_error = netagent_kernel_trigger(mapping->uuid); |
7633 | if (necp_debug > 1) { |
7634 | NECPLOG(LOG_DEBUG, "Socket Policy: Triggering inactive agent, error %d" , trigger_error); |
7635 | } |
7636 | } |
7637 | |
7638 | // Mark socket as a drop if required agent is not active |
7639 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7640 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7641 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
7642 | inp->inp_policyresult.flowhash = flowhash; |
7643 | inp->inp_policyresult.results.filter_control_unit = 0; |
7644 | inp->inp_policyresult.results.route_rule_id = 0; |
7645 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
7646 | |
7647 | if (necp_debug > 1) { |
7648 | NECPLOG(LOG_DEBUG, "Socket Policy: (BoundInterface %d Proto %d) Dropping packet because agent is not active" , info.bound_interface_index, info.protocol); |
7649 | } |
7650 | |
7651 | // Unlock |
7652 | lck_rw_done(&necp_kernel_policy_lock); |
7653 | return (NECP_KERNEL_POLICY_ID_NONE); |
7654 | } |
7655 | } |
7656 | } |
7657 | } |
7658 | if (matched_policy) { |
7659 | matched_policy_id = matched_policy->id; |
7660 | inp->inp_policyresult.policy_id = matched_policy->id; |
7661 | inp->inp_policyresult.skip_policy_id = skip_policy_id; |
7662 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
7663 | inp->inp_policyresult.flowhash = flowhash; |
7664 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; |
7665 | inp->inp_policyresult.results.route_rule_id = route_rule_id; |
7666 | inp->inp_policyresult.results.result = matched_policy->result; |
7667 | memcpy(&inp->inp_policyresult.results.result_parameter, &matched_policy->result_parameter, sizeof(matched_policy->result_parameter)); |
7668 | |
7669 | if (necp_socket_is_connected(inp) && |
7670 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP || |
7671 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && !necp_socket_uses_interface(inp, matched_policy->result_parameter.tunnel_interface_index)))) { |
7672 | if (necp_debug) { |
7673 | NECPLOG(LOG_DEBUG, "Marking socket in state %d as defunct" , so->so_state); |
7674 | } |
7675 | sosetdefunct(current_proc(), so, SHUTDOWN_SOCKET_LEVEL_NECP | SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL, TRUE); |
7676 | } else if (necp_socket_is_connected(inp) && |
7677 | matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && |
7678 | info.protocol == IPPROTO_TCP) { |
7679 | // Reset MSS on TCP socket if tunnel policy changes |
7680 | tcp_mtudisc(inp, 0); |
7681 | } |
7682 | |
7683 | if (necp_debug > 1) { |
7684 | NECPLOG(LOG_DEBUG, "Socket Policy: %p (BoundInterface %d Proto %d) Policy %d Result %d Parameter %d" , inp->inp_socket, info.bound_interface_index, info.protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index); |
7685 | } |
7686 | } else if (necp_drop_all_order > 0) { |
7687 | // Mark socket as a drop if set |
7688 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7689 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7690 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
7691 | inp->inp_policyresult.flowhash = flowhash; |
7692 | inp->inp_policyresult.results.filter_control_unit = 0; |
7693 | inp->inp_policyresult.results.route_rule_id = 0; |
7694 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP; |
7695 | } else { |
7696 | // Mark non-matching socket so we don't re-check it |
7697 | inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7698 | inp->inp_policyresult.skip_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7699 | inp->inp_policyresult.policy_gencount = necp_kernel_socket_policies_gencount; |
7700 | inp->inp_policyresult.flowhash = flowhash; |
7701 | inp->inp_policyresult.results.filter_control_unit = filter_control_unit; // We may have matched a filter, so mark it! |
7702 | inp->inp_policyresult.results.route_rule_id = route_rule_id; // We may have matched a route rule, so mark it! |
7703 | inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_NONE; |
7704 | } |
7705 | |
7706 | // Unlock |
7707 | lck_rw_done(&necp_kernel_policy_lock); |
7708 | |
7709 | return (matched_policy_id); |
7710 | } |
7711 | |
7712 | static bool |
7713 | necp_ip_output_check_policy(struct necp_kernel_ip_output_policy *kernel_policy, necp_kernel_policy_id socket_policy_id, necp_kernel_policy_id socket_skip_policy_id, u_int32_t bound_interface_index, u_int32_t last_interface_index, u_int16_t protocol, union necp_sockaddr_union *local, union necp_sockaddr_union *remote) |
7714 | { |
7715 | if (!(kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ALL_INTERFACES)) { |
7716 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
7717 | u_int32_t cond_bound_interface_index = kernel_policy->cond_bound_interface ? kernel_policy->cond_bound_interface->if_index : 0; |
7718 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_BOUND_INTERFACE) { |
7719 | if (bound_interface_index == cond_bound_interface_index) { |
7720 | // No match, matches forbidden interface |
7721 | return (FALSE); |
7722 | } |
7723 | } else { |
7724 | if (bound_interface_index != cond_bound_interface_index) { |
7725 | // No match, does not match required interface |
7726 | return (FALSE); |
7727 | } |
7728 | } |
7729 | } else { |
7730 | if (bound_interface_index != 0) { |
7731 | // No match, requires a non-bound packet |
7732 | return (FALSE); |
7733 | } |
7734 | } |
7735 | } |
7736 | |
7737 | if (kernel_policy->condition_mask == 0) { |
7738 | return (TRUE); |
7739 | } |
7740 | |
7741 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_POLICY_ID) { |
7742 | necp_kernel_policy_id matched_policy_id = |
7743 | kernel_policy->result == NECP_KERNEL_POLICY_RESULT_SKIP ? socket_skip_policy_id : socket_policy_id; |
7744 | if (matched_policy_id != kernel_policy->cond_policy_id) { |
7745 | // No match, does not match required id |
7746 | return (FALSE); |
7747 | } |
7748 | } |
7749 | |
7750 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LAST_INTERFACE) { |
7751 | if (last_interface_index != kernel_policy->cond_last_interface_index) { |
7752 | return (FALSE); |
7753 | } |
7754 | } |
7755 | |
7756 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
7757 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_PROTOCOL) { |
7758 | if (protocol == kernel_policy->cond_protocol) { |
7759 | // No match, matches forbidden protocol |
7760 | return (FALSE); |
7761 | } |
7762 | } else { |
7763 | if (protocol != kernel_policy->cond_protocol) { |
7764 | // No match, does not match required protocol |
7765 | return (FALSE); |
7766 | } |
7767 | } |
7768 | } |
7769 | |
7770 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_START) { |
7771 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
7772 | bool inRange = necp_is_addr_in_range((struct sockaddr *)local, (struct sockaddr *)&kernel_policy->cond_local_start, (struct sockaddr *)&kernel_policy->cond_local_end); |
7773 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_END) { |
7774 | if (inRange) { |
7775 | return (FALSE); |
7776 | } |
7777 | } else { |
7778 | if (!inRange) { |
7779 | return (FALSE); |
7780 | } |
7781 | } |
7782 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
7783 | bool inSubnet = necp_is_addr_in_subnet((struct sockaddr *)local, (struct sockaddr *)&kernel_policy->cond_local_start, kernel_policy->cond_local_prefix); |
7784 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_LOCAL_PREFIX) { |
7785 | if (inSubnet) { |
7786 | return (FALSE); |
7787 | } |
7788 | } else { |
7789 | if (!inSubnet) { |
7790 | return (FALSE); |
7791 | } |
7792 | } |
7793 | } |
7794 | } |
7795 | |
7796 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_START) { |
7797 | if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
7798 | bool inRange = necp_is_addr_in_range((struct sockaddr *)remote, (struct sockaddr *)&kernel_policy->cond_remote_start, (struct sockaddr *)&kernel_policy->cond_remote_end); |
7799 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_END) { |
7800 | if (inRange) { |
7801 | return (FALSE); |
7802 | } |
7803 | } else { |
7804 | if (!inRange) { |
7805 | return (FALSE); |
7806 | } |
7807 | } |
7808 | } else if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
7809 | bool inSubnet = necp_is_addr_in_subnet((struct sockaddr *)remote, (struct sockaddr *)&kernel_policy->cond_remote_start, kernel_policy->cond_remote_prefix); |
7810 | if (kernel_policy->condition_negated_mask & NECP_KERNEL_CONDITION_REMOTE_PREFIX) { |
7811 | if (inSubnet) { |
7812 | return (FALSE); |
7813 | } |
7814 | } else { |
7815 | if (!inSubnet) { |
7816 | return (FALSE); |
7817 | } |
7818 | } |
7819 | } |
7820 | } |
7821 | |
7822 | return (TRUE); |
7823 | } |
7824 | |
7825 | static inline struct necp_kernel_ip_output_policy * |
7826 | necp_ip_output_find_policy_match_locked(necp_kernel_policy_id socket_policy_id, necp_kernel_policy_id socket_skip_policy_id, u_int32_t bound_interface_index, u_int32_t last_interface_index, u_int16_t protocol, union necp_sockaddr_union *local_addr, union necp_sockaddr_union *remote_addr) |
7827 | { |
7828 | u_int32_t skip_order = 0; |
7829 | u_int32_t skip_session_order = 0; |
7830 | int i; |
7831 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
7832 | struct necp_kernel_ip_output_policy **policy_search_array = necp_kernel_ip_output_policies_map[NECP_IP_OUTPUT_MAP_ID_TO_BUCKET(socket_policy_id)]; |
7833 | if (policy_search_array != NULL) { |
7834 | for (i = 0; policy_search_array[i] != NULL; i++) { |
7835 | if (necp_drop_all_order != 0 && policy_search_array[i]->session_order >= necp_drop_all_order) { |
7836 | // We've hit a drop all rule |
7837 | break; |
7838 | } |
7839 | if (skip_session_order && policy_search_array[i]->session_order >= skip_session_order) { |
7840 | // Done skipping |
7841 | skip_order = 0; |
7842 | skip_session_order = 0; |
7843 | } |
7844 | if (skip_order) { |
7845 | if (policy_search_array[i]->order < skip_order) { |
7846 | // Skip this policy |
7847 | continue; |
7848 | } else { |
7849 | // Done skipping |
7850 | skip_order = 0; |
7851 | skip_session_order = 0; |
7852 | } |
7853 | } else if (skip_session_order) { |
7854 | // Skip this policy |
7855 | continue; |
7856 | } |
7857 | if (necp_ip_output_check_policy(policy_search_array[i], socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, local_addr, remote_addr)) { |
7858 | // Passed all tests, found a match |
7859 | matched_policy = policy_search_array[i]; |
7860 | |
7861 | if (policy_search_array[i]->result == NECP_KERNEL_POLICY_RESULT_SKIP) { |
7862 | skip_order = policy_search_array[i]->result_parameter.skip_policy_order; |
7863 | skip_session_order = policy_search_array[i]->session_order + 1; |
7864 | continue; |
7865 | } |
7866 | |
7867 | break; |
7868 | } |
7869 | } |
7870 | } |
7871 | |
7872 | return (matched_policy); |
7873 | } |
7874 | |
7875 | static inline bool |
7876 | necp_output_bypass(struct mbuf *packet) |
7877 | { |
7878 | if (necp_pass_loopback > 0 && necp_is_loopback(NULL, NULL, NULL, packet)) { |
7879 | return (true); |
7880 | } |
7881 | if (necp_pass_keepalives > 0 && necp_get_is_keepalive_from_packet(packet)) { |
7882 | return (true); |
7883 | } |
7884 | if (necp_is_intcoproc(NULL, packet)) { |
7885 | return (true); |
7886 | } |
7887 | return (false); |
7888 | } |
7889 | |
7890 | necp_kernel_policy_id |
7891 | necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_args *ipoa, necp_kernel_policy_result *result, necp_kernel_policy_result_parameter *result_parameter) |
7892 | { |
7893 | struct ip *ip = NULL; |
7894 | int hlen = sizeof(struct ip); |
7895 | necp_kernel_policy_id socket_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
7896 | necp_kernel_policy_id socket_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
7897 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
7898 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
7899 | u_int16_t protocol = 0; |
7900 | u_int32_t bound_interface_index = 0; |
7901 | u_int32_t last_interface_index = 0; |
7902 | union necp_sockaddr_union local_addr; |
7903 | union necp_sockaddr_union remote_addr; |
7904 | |
7905 | if (result) { |
7906 | *result = 0; |
7907 | } |
7908 | |
7909 | if (result_parameter) { |
7910 | memset(result_parameter, 0, sizeof(*result_parameter)); |
7911 | } |
7912 | |
7913 | if (packet == NULL) { |
7914 | return (NECP_KERNEL_POLICY_ID_NONE); |
7915 | } |
7916 | |
7917 | socket_policy_id = necp_get_policy_id_from_packet(packet); |
7918 | socket_skip_policy_id = necp_get_skip_policy_id_from_packet(packet); |
7919 | |
7920 | // Exit early for an empty list |
7921 | // Don't lock. Possible race condition, but we don't want the performance hit. |
7922 | if (necp_kernel_ip_output_policies_count == 0 || |
7923 | ((socket_policy_id == NECP_KERNEL_POLICY_ID_NONE) && necp_kernel_ip_output_policies_non_id_count == 0)) { |
7924 | if (necp_drop_all_order > 0) { |
7925 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7926 | if (result) { |
7927 | if (necp_output_bypass(packet)) { |
7928 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
7929 | } else { |
7930 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
7931 | } |
7932 | } |
7933 | } |
7934 | |
7935 | return (matched_policy_id); |
7936 | } |
7937 | |
7938 | // Check for loopback exception |
7939 | if (necp_output_bypass(packet)) { |
7940 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
7941 | if (result) { |
7942 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
7943 | } |
7944 | return (matched_policy_id); |
7945 | } |
7946 | |
7947 | last_interface_index = necp_get_last_interface_index_from_packet(packet); |
7948 | |
7949 | // Process packet to get relevant fields |
7950 | ip = mtod(packet, struct ip *); |
7951 | #ifdef _IP_VHL |
7952 | hlen = _IP_VHL_HL(ip->ip_vhl) << 2; |
7953 | #else |
7954 | hlen = ip->ip_hl << 2; |
7955 | #endif |
7956 | |
7957 | protocol = ip->ip_p; |
7958 | |
7959 | if ((flags & IP_OUTARGS) && (ipoa != NULL) && |
7960 | (ipoa->ipoa_flags & IPOAF_BOUND_IF) && |
7961 | ipoa->ipoa_boundif != IFSCOPE_NONE) { |
7962 | bound_interface_index = ipoa->ipoa_boundif; |
7963 | } |
7964 | |
7965 | local_addr.sin.sin_family = AF_INET; |
7966 | local_addr.sin.sin_len = sizeof(struct sockaddr_in); |
7967 | memcpy(&local_addr.sin.sin_addr, &ip->ip_src, sizeof(ip->ip_src)); |
7968 | |
7969 | remote_addr.sin.sin_family = AF_INET; |
7970 | remote_addr.sin.sin_len = sizeof(struct sockaddr_in); |
7971 | memcpy(&((struct sockaddr_in *)&remote_addr)->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); |
7972 | |
7973 | switch (protocol) { |
7974 | case IPPROTO_TCP: { |
7975 | struct tcphdr th; |
7976 | if ((int)(hlen + sizeof(th)) <= packet->m_pkthdr.len) { |
7977 | m_copydata(packet, hlen, sizeof(th), (u_int8_t *)&th); |
7978 | ((struct sockaddr_in *)&local_addr)->sin_port = th.th_sport; |
7979 | ((struct sockaddr_in *)&remote_addr)->sin_port = th.th_dport; |
7980 | } |
7981 | break; |
7982 | } |
7983 | case IPPROTO_UDP: { |
7984 | struct udphdr uh; |
7985 | if ((int)(hlen + sizeof(uh)) <= packet->m_pkthdr.len) { |
7986 | m_copydata(packet, hlen, sizeof(uh), (u_int8_t *)&uh); |
7987 | ((struct sockaddr_in *)&local_addr)->sin_port = uh.uh_sport; |
7988 | ((struct sockaddr_in *)&remote_addr)->sin_port = uh.uh_dport; |
7989 | } |
7990 | break; |
7991 | } |
7992 | default: { |
7993 | ((struct sockaddr_in *)&local_addr)->sin_port = 0; |
7994 | ((struct sockaddr_in *)&remote_addr)->sin_port = 0; |
7995 | break; |
7996 | } |
7997 | } |
7998 | |
7999 | // Match packet to policy |
8000 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8001 | matched_policy = necp_ip_output_find_policy_match_locked(socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, &local_addr, &remote_addr); |
8002 | if (matched_policy) { |
8003 | matched_policy_id = matched_policy->id; |
8004 | if (result) { |
8005 | *result = matched_policy->result; |
8006 | } |
8007 | |
8008 | if (result_parameter) { |
8009 | memcpy(result_parameter, &matched_policy->result_parameter, sizeof(matched_policy->result_parameter)); |
8010 | } |
8011 | |
8012 | if (necp_debug > 1) { |
8013 | NECPLOG(LOG_DEBUG, "IP Output: (ID %d BoundInterface %d LastInterface %d Proto %d) Policy %d Result %d Parameter %d" , socket_policy_id, bound_interface_index, last_interface_index, protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index); |
8014 | } |
8015 | } else if (necp_drop_all_order > 0) { |
8016 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
8017 | if (result) { |
8018 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
8019 | } |
8020 | } |
8021 | |
8022 | lck_rw_done(&necp_kernel_policy_lock); |
8023 | |
8024 | return (matched_policy_id); |
8025 | } |
8026 | |
8027 | necp_kernel_policy_id |
8028 | necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out_args *ip6oa, necp_kernel_policy_result *result, necp_kernel_policy_result_parameter *result_parameter) |
8029 | { |
8030 | struct ip6_hdr *ip6 = NULL; |
8031 | int next = -1; |
8032 | int offset = 0; |
8033 | necp_kernel_policy_id socket_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8034 | necp_kernel_policy_id socket_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8035 | necp_kernel_policy_id matched_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8036 | struct necp_kernel_ip_output_policy *matched_policy = NULL; |
8037 | u_int16_t protocol = 0; |
8038 | u_int32_t bound_interface_index = 0; |
8039 | u_int32_t last_interface_index = 0; |
8040 | union necp_sockaddr_union local_addr; |
8041 | union necp_sockaddr_union remote_addr; |
8042 | |
8043 | if (result) { |
8044 | *result = 0; |
8045 | } |
8046 | |
8047 | if (result_parameter) { |
8048 | memset(result_parameter, 0, sizeof(*result_parameter)); |
8049 | } |
8050 | |
8051 | if (packet == NULL) { |
8052 | return (NECP_KERNEL_POLICY_ID_NONE); |
8053 | } |
8054 | |
8055 | socket_policy_id = necp_get_policy_id_from_packet(packet); |
8056 | socket_skip_policy_id = necp_get_skip_policy_id_from_packet(packet); |
8057 | |
8058 | // Exit early for an empty list |
8059 | // Don't lock. Possible race condition, but we don't want the performance hit. |
8060 | if (necp_kernel_ip_output_policies_count == 0 || |
8061 | ((socket_policy_id == NECP_KERNEL_POLICY_ID_NONE) && necp_kernel_ip_output_policies_non_id_count == 0)) { |
8062 | if (necp_drop_all_order > 0) { |
8063 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
8064 | if (result) { |
8065 | if (necp_output_bypass(packet)) { |
8066 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
8067 | } else { |
8068 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
8069 | } |
8070 | } |
8071 | } |
8072 | |
8073 | return (matched_policy_id); |
8074 | } |
8075 | |
8076 | // Check for loopback exception |
8077 | if (necp_output_bypass(packet)) { |
8078 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
8079 | if (result) { |
8080 | *result = NECP_KERNEL_POLICY_RESULT_PASS; |
8081 | } |
8082 | return (matched_policy_id); |
8083 | } |
8084 | |
8085 | last_interface_index = necp_get_last_interface_index_from_packet(packet); |
8086 | |
8087 | // Process packet to get relevant fields |
8088 | ip6 = mtod(packet, struct ip6_hdr *); |
8089 | |
8090 | if ((flags & IPV6_OUTARGS) && (ip6oa != NULL) && |
8091 | (ip6oa->ip6oa_flags & IP6OAF_BOUND_IF) && |
8092 | ip6oa->ip6oa_boundif != IFSCOPE_NONE) { |
8093 | bound_interface_index = ip6oa->ip6oa_boundif; |
8094 | } |
8095 | |
8096 | ((struct sockaddr_in6 *)&local_addr)->sin6_family = AF_INET6; |
8097 | ((struct sockaddr_in6 *)&local_addr)->sin6_len = sizeof(struct sockaddr_in6); |
8098 | memcpy(&((struct sockaddr_in6 *)&local_addr)->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src)); |
8099 | |
8100 | ((struct sockaddr_in6 *)&remote_addr)->sin6_family = AF_INET6; |
8101 | ((struct sockaddr_in6 *)&remote_addr)->sin6_len = sizeof(struct sockaddr_in6); |
8102 | memcpy(&((struct sockaddr_in6 *)&remote_addr)->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); |
8103 | |
8104 | offset = ip6_lasthdr(packet, 0, IPPROTO_IPV6, &next); |
8105 | if (offset >= 0 && packet->m_pkthdr.len >= offset) { |
8106 | protocol = next; |
8107 | switch (protocol) { |
8108 | case IPPROTO_TCP: { |
8109 | struct tcphdr th; |
8110 | if ((int)(offset + sizeof(th)) <= packet->m_pkthdr.len) { |
8111 | m_copydata(packet, offset, sizeof(th), (u_int8_t *)&th); |
8112 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = th.th_sport; |
8113 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = th.th_dport; |
8114 | } |
8115 | break; |
8116 | } |
8117 | case IPPROTO_UDP: { |
8118 | struct udphdr uh; |
8119 | if ((int)(offset + sizeof(uh)) <= packet->m_pkthdr.len) { |
8120 | m_copydata(packet, offset, sizeof(uh), (u_int8_t *)&uh); |
8121 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = uh.uh_sport; |
8122 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = uh.uh_dport; |
8123 | } |
8124 | break; |
8125 | } |
8126 | default: { |
8127 | ((struct sockaddr_in6 *)&local_addr)->sin6_port = 0; |
8128 | ((struct sockaddr_in6 *)&remote_addr)->sin6_port = 0; |
8129 | break; |
8130 | } |
8131 | } |
8132 | } |
8133 | |
8134 | // Match packet to policy |
8135 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8136 | matched_policy = necp_ip_output_find_policy_match_locked(socket_policy_id, socket_skip_policy_id, bound_interface_index, last_interface_index, protocol, &local_addr, &remote_addr); |
8137 | if (matched_policy) { |
8138 | matched_policy_id = matched_policy->id; |
8139 | if (result) { |
8140 | *result = matched_policy->result; |
8141 | } |
8142 | |
8143 | if (result_parameter) { |
8144 | memcpy(result_parameter, &matched_policy->result_parameter, sizeof(matched_policy->result_parameter)); |
8145 | } |
8146 | |
8147 | if (necp_debug > 1) { |
8148 | NECPLOG(LOG_DEBUG, "IP6 Output: (ID %d BoundInterface %d LastInterface %d Proto %d) Policy %d Result %d Parameter %d" , socket_policy_id, bound_interface_index, last_interface_index, protocol, matched_policy->id, matched_policy->result, matched_policy->result_parameter.tunnel_interface_index); |
8149 | } |
8150 | } else if (necp_drop_all_order > 0) { |
8151 | matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
8152 | if (result) { |
8153 | *result = NECP_KERNEL_POLICY_RESULT_DROP; |
8154 | } |
8155 | } |
8156 | |
8157 | lck_rw_done(&necp_kernel_policy_lock); |
8158 | |
8159 | return (matched_policy_id); |
8160 | } |
8161 | |
8162 | // Utilities |
8163 | static bool |
8164 | necp_is_addr_in_range(struct sockaddr *addr, struct sockaddr *range_start, struct sockaddr *range_end) |
8165 | { |
8166 | int cmp = 0; |
8167 | |
8168 | if (addr == NULL || range_start == NULL || range_end == NULL) { |
8169 | return (FALSE); |
8170 | } |
8171 | |
8172 | /* Must be greater than or equal to start */ |
8173 | cmp = necp_addr_compare(addr, range_start, 1); |
8174 | if (cmp != 0 && cmp != 1) { |
8175 | return (FALSE); |
8176 | } |
8177 | |
8178 | /* Must be less than or equal to end */ |
8179 | cmp = necp_addr_compare(addr, range_end, 1); |
8180 | if (cmp != 0 && cmp != -1) { |
8181 | return (FALSE); |
8182 | } |
8183 | |
8184 | return (TRUE); |
8185 | } |
8186 | |
8187 | static bool |
8188 | necp_is_range_in_range(struct sockaddr *inner_range_start, struct sockaddr *inner_range_end, struct sockaddr *range_start, struct sockaddr *range_end) |
8189 | { |
8190 | int cmp = 0; |
8191 | |
8192 | if (inner_range_start == NULL || inner_range_end == NULL || range_start == NULL || range_end == NULL) { |
8193 | return (FALSE); |
8194 | } |
8195 | |
8196 | /* Must be greater than or equal to start */ |
8197 | cmp = necp_addr_compare(inner_range_start, range_start, 1); |
8198 | if (cmp != 0 && cmp != 1) { |
8199 | return (FALSE); |
8200 | } |
8201 | |
8202 | /* Must be less than or equal to end */ |
8203 | cmp = necp_addr_compare(inner_range_end, range_end, 1); |
8204 | if (cmp != 0 && cmp != -1) { |
8205 | return (FALSE); |
8206 | } |
8207 | |
8208 | return (TRUE); |
8209 | } |
8210 | |
8211 | static bool |
8212 | necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subnet_addr, u_int8_t subnet_prefix) |
8213 | { |
8214 | if (addr == NULL || subnet_addr == NULL) { |
8215 | return (FALSE); |
8216 | } |
8217 | |
8218 | if (addr->sa_family != subnet_addr->sa_family || addr->sa_len != subnet_addr->sa_len) { |
8219 | return (FALSE); |
8220 | } |
8221 | |
8222 | switch (addr->sa_family) { |
8223 | case AF_INET: { |
8224 | if (satosin(subnet_addr)->sin_port != 0 && |
8225 | satosin(addr)->sin_port != satosin(subnet_addr)->sin_port) { |
8226 | return (FALSE); |
8227 | } |
8228 | return (necp_buffer_compare_with_bit_prefix((u_int8_t *)&satosin(addr)->sin_addr, (u_int8_t *)&satosin(subnet_addr)->sin_addr, subnet_prefix)); |
8229 | } |
8230 | case AF_INET6: { |
8231 | if (satosin6(subnet_addr)->sin6_port != 0 && |
8232 | satosin6(addr)->sin6_port != satosin6(subnet_addr)->sin6_port) { |
8233 | return (FALSE); |
8234 | } |
8235 | if (satosin6(addr)->sin6_scope_id && |
8236 | satosin6(subnet_addr)->sin6_scope_id && |
8237 | satosin6(addr)->sin6_scope_id != satosin6(subnet_addr)->sin6_scope_id) { |
8238 | return (FALSE); |
8239 | } |
8240 | return (necp_buffer_compare_with_bit_prefix((u_int8_t *)&satosin6(addr)->sin6_addr, (u_int8_t *)&satosin6(subnet_addr)->sin6_addr, subnet_prefix)); |
8241 | } |
8242 | default: { |
8243 | return (FALSE); |
8244 | } |
8245 | } |
8246 | |
8247 | return (FALSE); |
8248 | } |
8249 | |
8250 | /* |
8251 | * Return values: |
8252 | * -1: sa1 < sa2 |
8253 | * 0: sa1 == sa2 |
8254 | * 1: sa1 > sa2 |
8255 | * 2: Not comparable or error |
8256 | */ |
8257 | static int |
8258 | necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port) |
8259 | { |
8260 | int result = 0; |
8261 | int port_result = 0; |
8262 | |
8263 | if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len) { |
8264 | return (2); |
8265 | } |
8266 | |
8267 | if (sa1->sa_len == 0) { |
8268 | return (0); |
8269 | } |
8270 | |
8271 | switch (sa1->sa_family) { |
8272 | case AF_INET: { |
8273 | if (sa1->sa_len != sizeof(struct sockaddr_in)) { |
8274 | return (2); |
8275 | } |
8276 | |
8277 | result = memcmp(&satosin(sa1)->sin_addr.s_addr, &satosin(sa2)->sin_addr.s_addr, sizeof(satosin(sa1)->sin_addr.s_addr)); |
8278 | |
8279 | if (check_port) { |
8280 | if (satosin(sa1)->sin_port < satosin(sa2)->sin_port) { |
8281 | port_result = -1; |
8282 | } else if (satosin(sa1)->sin_port > satosin(sa2)->sin_port) { |
8283 | port_result = 1; |
8284 | } |
8285 | |
8286 | if (result == 0) { |
8287 | result = port_result; |
8288 | } else if ((result > 0 && port_result < 0) || (result < 0 && port_result > 0)) { |
8289 | return (2); |
8290 | } |
8291 | } |
8292 | |
8293 | break; |
8294 | } |
8295 | case AF_INET6: { |
8296 | if (sa1->sa_len != sizeof(struct sockaddr_in6)) { |
8297 | return (2); |
8298 | } |
8299 | |
8300 | if (satosin6(sa1)->sin6_scope_id != satosin6(sa2)->sin6_scope_id) { |
8301 | return (2); |
8302 | } |
8303 | |
8304 | result = memcmp(&satosin6(sa1)->sin6_addr.s6_addr[0], &satosin6(sa2)->sin6_addr.s6_addr[0], sizeof(struct in6_addr)); |
8305 | |
8306 | if (check_port) { |
8307 | if (satosin6(sa1)->sin6_port < satosin6(sa2)->sin6_port) { |
8308 | port_result = -1; |
8309 | } else if (satosin6(sa1)->sin6_port > satosin6(sa2)->sin6_port) { |
8310 | port_result = 1; |
8311 | } |
8312 | |
8313 | if (result == 0) { |
8314 | result = port_result; |
8315 | } else if ((result > 0 && port_result < 0) || (result < 0 && port_result > 0)) { |
8316 | return (2); |
8317 | } |
8318 | } |
8319 | |
8320 | break; |
8321 | } |
8322 | default: { |
8323 | result = memcmp(sa1, sa2, sa1->sa_len); |
8324 | break; |
8325 | } |
8326 | } |
8327 | |
8328 | if (result < 0) { |
8329 | result = (-1); |
8330 | } else if (result > 0) { |
8331 | result = (1); |
8332 | } |
8333 | |
8334 | return (result); |
8335 | } |
8336 | |
8337 | static bool |
8338 | necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits) |
8339 | { |
8340 | u_int8_t mask; |
8341 | |
8342 | /* Handle null pointers */ |
8343 | if (p1 == NULL || p2 == NULL) { |
8344 | return (p1 == p2); |
8345 | } |
8346 | |
8347 | while (bits >= 8) { |
8348 | if (*p1++ != *p2++) { |
8349 | return (FALSE); |
8350 | } |
8351 | bits -= 8; |
8352 | } |
8353 | |
8354 | if (bits > 0) { |
8355 | mask = ~((1<<(8-bits))-1); |
8356 | if ((*p1 & mask) != (*p2 & mask)) { |
8357 | return (FALSE); |
8358 | } |
8359 | } |
8360 | return (TRUE); |
8361 | } |
8362 | |
8363 | static bool |
8364 | necp_update_qos_marking(struct ifnet *ifp, u_int32_t route_rule_id) |
8365 | { |
8366 | bool qos_marking = FALSE; |
8367 | int exception_index = 0; |
8368 | struct necp_route_rule *route_rule = NULL; |
8369 | |
8370 | route_rule = necp_lookup_route_rule_locked(&necp_route_rules, route_rule_id); |
8371 | if (route_rule == NULL) { |
8372 | qos_marking = FALSE; |
8373 | goto done; |
8374 | } |
8375 | |
8376 | qos_marking = (route_rule->default_action == NECP_ROUTE_RULE_QOS_MARKING) ? TRUE : FALSE; |
8377 | |
8378 | if (ifp == NULL) { |
8379 | goto done; |
8380 | } |
8381 | |
8382 | for (exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
8383 | if (route_rule->exception_if_indices[exception_index] == 0) { |
8384 | break; |
8385 | } |
8386 | if (route_rule->exception_if_actions[exception_index] != NECP_ROUTE_RULE_QOS_MARKING) { |
8387 | continue; |
8388 | } |
8389 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index) { |
8390 | qos_marking = TRUE; |
8391 | if (necp_debug > 2) { |
8392 | NECPLOG(LOG_DEBUG, "QoS Marking : Interface match %d for Rule %d Allowed %d" , |
8393 | route_rule->exception_if_indices[exception_index], route_rule_id, qos_marking); |
8394 | } |
8395 | goto done; |
8396 | } |
8397 | } |
8398 | |
8399 | if ((route_rule->cellular_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_CELLULAR(ifp)) || |
8400 | (route_rule->wifi_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIFI(ifp)) || |
8401 | (route_rule->wired_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_WIRED(ifp)) || |
8402 | (route_rule->expensive_action == NECP_ROUTE_RULE_QOS_MARKING && IFNET_IS_EXPENSIVE(ifp))) { |
8403 | qos_marking = TRUE; |
8404 | if (necp_debug > 2) { |
8405 | NECPLOG(LOG_DEBUG, "QoS Marking: C:%d WF:%d W:%d E:%d for Rule %d Allowed %d" , |
8406 | route_rule->cellular_action, route_rule->wifi_action, route_rule->wired_action, |
8407 | route_rule->expensive_action, route_rule_id, qos_marking); |
8408 | } |
8409 | goto done; |
8410 | } |
8411 | done: |
8412 | if (necp_debug > 1) { |
8413 | NECPLOG(LOG_DEBUG, "QoS Marking: Rule %d ifp %s Allowed %d" , |
8414 | route_rule_id, ifp ? ifp->if_xname : "" , qos_marking); |
8415 | } |
8416 | return (qos_marking); |
8417 | } |
8418 | |
8419 | void |
8420 | necp_socket_update_qos_marking(struct inpcb *inp, struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id) |
8421 | { |
8422 | bool qos_marking = FALSE; |
8423 | struct ifnet *ifp = interface = NULL; |
8424 | |
8425 | if (net_qos_policy_restricted == 0) { |
8426 | return; |
8427 | } |
8428 | if (inp->inp_socket == NULL) { |
8429 | return; |
8430 | } |
8431 | if ((inp->inp_socket->so_flags1 & SOF1_QOSMARKING_POLICY_OVERRIDE)) { |
8432 | return; |
8433 | } |
8434 | /* |
8435 | * This is racy but we do not need the performance hit of taking necp_kernel_policy_lock |
8436 | */ |
8437 | if (inp->inp_policyresult.results.qos_marking_gencount == necp_kernel_socket_policies_gencount) { |
8438 | return; |
8439 | } |
8440 | |
8441 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8442 | |
8443 | if (ifp == NULL && route != NULL) { |
8444 | ifp = route->rt_ifp; |
8445 | } |
8446 | /* |
8447 | * By default, until we have a interface, do not mark and reevaluate the Qos marking policy |
8448 | */ |
8449 | if (ifp == NULL || route_rule_id == 0) { |
8450 | qos_marking = FALSE; |
8451 | goto done; |
8452 | } |
8453 | |
8454 | if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) { |
8455 | struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id); |
8456 | if (aggregate_route_rule != NULL) { |
8457 | int index = 0; |
8458 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
8459 | u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index]; |
8460 | if (sub_route_rule_id == 0) { |
8461 | break; |
8462 | } |
8463 | qos_marking = necp_update_qos_marking(ifp, sub_route_rule_id); |
8464 | if (qos_marking == TRUE) { |
8465 | break; |
8466 | } |
8467 | } |
8468 | } |
8469 | } else { |
8470 | qos_marking = necp_update_qos_marking(ifp, route_rule_id); |
8471 | } |
8472 | /* |
8473 | * Now that we have an interface we remember the gencount |
8474 | */ |
8475 | inp->inp_policyresult.results.qos_marking_gencount = necp_kernel_socket_policies_gencount; |
8476 | |
8477 | done: |
8478 | lck_rw_done(&necp_kernel_policy_lock); |
8479 | |
8480 | if (qos_marking == TRUE) { |
8481 | inp->inp_socket->so_flags1 |= SOF1_QOSMARKING_ALLOWED; |
8482 | } else { |
8483 | inp->inp_socket->so_flags1 &= ~SOF1_QOSMARKING_ALLOWED; |
8484 | } |
8485 | } |
8486 | |
8487 | static bool |
8488 | necp_route_is_lqm_abort(struct ifnet *ifp, struct ifnet *delegated_ifp) |
8489 | { |
8490 | if (ifp != NULL && |
8491 | (ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
8492 | ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
8493 | return true; |
8494 | } |
8495 | if (delegated_ifp != NULL && |
8496 | (delegated_ifp->if_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) && |
8497 | delegated_ifp->if_interface_state.lqm_state == IFNET_LQM_THRESH_ABORT) { |
8498 | return true; |
8499 | } |
8500 | return false; |
8501 | } |
8502 | |
8503 | static bool |
8504 | necp_route_is_allowed_inner(struct rtentry *route, struct ifnet *ifp, u_int32_t route_rule_id, u_int32_t *interface_type_denied) |
8505 | { |
8506 | bool default_is_allowed = TRUE; |
8507 | u_int8_t type_aggregate_action = NECP_ROUTE_RULE_NONE; |
8508 | int exception_index = 0; |
8509 | struct ifnet *delegated_ifp = NULL; |
8510 | struct necp_route_rule *route_rule = NULL; |
8511 | |
8512 | route_rule = necp_lookup_route_rule_locked(&necp_route_rules, route_rule_id); |
8513 | if (route_rule == NULL) { |
8514 | return (TRUE); |
8515 | } |
8516 | |
8517 | default_is_allowed = (route_rule->default_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? FALSE : TRUE; |
8518 | if (ifp == NULL) { |
8519 | ifp = route->rt_ifp; |
8520 | } |
8521 | if (ifp == NULL) { |
8522 | if (necp_debug > 1 && !default_is_allowed) { |
8523 | NECPLOG(LOG_DEBUG, "Route Allowed: No interface for route, using default for Rule %d Allowed %d" , route_rule_id, default_is_allowed); |
8524 | } |
8525 | return (default_is_allowed); |
8526 | } |
8527 | |
8528 | delegated_ifp = ifp->if_delegated.ifp; |
8529 | for (exception_index = 0; exception_index < MAX_ROUTE_RULE_INTERFACES; exception_index++) { |
8530 | if (route_rule->exception_if_indices[exception_index] == 0) { |
8531 | break; |
8532 | } |
8533 | if (route_rule->exception_if_indices[exception_index] == ifp->if_index || |
8534 | (delegated_ifp != NULL && route_rule->exception_if_indices[exception_index] == delegated_ifp->if_index)) { |
8535 | if (route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
8536 | const bool lqm_abort = necp_route_is_lqm_abort(ifp, delegated_ifp); |
8537 | if (necp_debug > 1 && lqm_abort) { |
8538 | NECPLOG(LOG_DEBUG, "Route Allowed: Interface match %d for Rule %d Deny LQM Abort" , |
8539 | route_rule->exception_if_indices[exception_index], route_rule_id); |
8540 | } |
8541 | return false; |
8542 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->exception_if_actions[exception_index])) { |
8543 | if (necp_debug > 1) { |
8544 | NECPLOG(LOG_DEBUG, "Route Allowed: Interface match %d for Rule %d Allowed %d" , route_rule->exception_if_indices[exception_index], route_rule_id, ((route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_DENY_INTERFACE) ? FALSE : TRUE)); |
8545 | } |
8546 | return ((route_rule->exception_if_actions[exception_index] == NECP_ROUTE_RULE_DENY_INTERFACE) ? FALSE : TRUE); |
8547 | } |
8548 | } |
8549 | } |
8550 | |
8551 | if (IFNET_IS_CELLULAR(ifp)) { |
8552 | if (route_rule->cellular_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
8553 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
8554 | if (interface_type_denied != NULL) { |
8555 | *interface_type_denied = IFRTYPE_FUNCTIONAL_CELLULAR; |
8556 | } |
8557 | // Mark aggregate action as deny |
8558 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
8559 | } |
8560 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->cellular_action)) { |
8561 | if (interface_type_denied != NULL) { |
8562 | *interface_type_denied = IFRTYPE_FUNCTIONAL_CELLULAR; |
8563 | } |
8564 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
8565 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
8566 | route_rule->cellular_action == NECP_ROUTE_RULE_DENY_INTERFACE)) { |
8567 | // Deny wins if there is a conflict |
8568 | type_aggregate_action = route_rule->cellular_action; |
8569 | } |
8570 | } |
8571 | } |
8572 | |
8573 | if (IFNET_IS_WIFI(ifp)) { |
8574 | if (route_rule->wifi_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
8575 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
8576 | if (interface_type_denied != NULL) { |
8577 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIFI_INFRA; |
8578 | } |
8579 | // Mark aggregate action as deny |
8580 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
8581 | } |
8582 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wifi_action)) { |
8583 | if (interface_type_denied != NULL) { |
8584 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIFI_INFRA; |
8585 | } |
8586 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
8587 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
8588 | route_rule->wifi_action == NECP_ROUTE_RULE_DENY_INTERFACE)) { |
8589 | // Deny wins if there is a conflict |
8590 | type_aggregate_action = route_rule->wifi_action; |
8591 | } |
8592 | } |
8593 | } |
8594 | |
8595 | if (IFNET_IS_WIRED(ifp)) { |
8596 | if (route_rule->wired_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
8597 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
8598 | if (interface_type_denied != NULL) { |
8599 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIRED; |
8600 | } |
8601 | // Mark aggregate action as deny |
8602 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
8603 | } |
8604 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->wired_action)) { |
8605 | if (interface_type_denied != NULL) { |
8606 | *interface_type_denied = IFRTYPE_FUNCTIONAL_WIRED; |
8607 | } |
8608 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
8609 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
8610 | route_rule->wired_action == NECP_ROUTE_RULE_DENY_INTERFACE)) { |
8611 | // Deny wins if there is a conflict |
8612 | type_aggregate_action = route_rule->wired_action; |
8613 | } |
8614 | } |
8615 | } |
8616 | |
8617 | if (IFNET_IS_EXPENSIVE(ifp)) { |
8618 | if (route_rule->expensive_action == NECP_ROUTE_RULE_DENY_LQM_ABORT) { |
8619 | if (necp_route_is_lqm_abort(ifp, delegated_ifp)) { |
8620 | // Mark aggregate action as deny |
8621 | type_aggregate_action = NECP_ROUTE_RULE_DENY_INTERFACE; |
8622 | } |
8623 | } else if (IS_NECP_ROUTE_RULE_ALLOW_OR_DENY(route_rule->expensive_action)) { |
8624 | if (type_aggregate_action == NECP_ROUTE_RULE_NONE || |
8625 | (type_aggregate_action == NECP_ROUTE_RULE_ALLOW_INTERFACE && |
8626 | route_rule->expensive_action == NECP_ROUTE_RULE_DENY_INTERFACE)) { |
8627 | // Deny wins if there is a conflict |
8628 | type_aggregate_action = route_rule->expensive_action; |
8629 | } |
8630 | } |
8631 | } |
8632 | |
8633 | if (type_aggregate_action != NECP_ROUTE_RULE_NONE) { |
8634 | if (necp_debug > 1) { |
8635 | NECPLOG(LOG_DEBUG, "Route Allowed: C:%d WF:%d W:%d E:%d for Rule %d Allowed %d" , route_rule->cellular_action, route_rule->wifi_action, route_rule->wired_action, route_rule->expensive_action, route_rule_id, ((type_aggregate_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? FALSE : TRUE)); |
8636 | } |
8637 | return ((type_aggregate_action == NECP_ROUTE_RULE_DENY_INTERFACE) ? FALSE : TRUE); |
8638 | } |
8639 | |
8640 | if (necp_debug > 1 && !default_is_allowed) { |
8641 | NECPLOG(LOG_DEBUG, "Route Allowed: Using default for Rule %d Allowed %d" , route_rule_id, default_is_allowed); |
8642 | } |
8643 | return (default_is_allowed); |
8644 | } |
8645 | |
8646 | static bool |
8647 | necp_route_is_allowed(struct rtentry *route, struct ifnet *interface, u_int32_t route_rule_id, u_int32_t *interface_type_denied) |
8648 | { |
8649 | if ((route == NULL && interface == NULL) || route_rule_id == 0) { |
8650 | if (necp_debug > 1) { |
8651 | NECPLOG(LOG_DEBUG, "Route Allowed: no route or interface, Rule %d Allowed %d" , route_rule_id, TRUE); |
8652 | } |
8653 | return (TRUE); |
8654 | } |
8655 | |
8656 | if (ROUTE_RULE_IS_AGGREGATE(route_rule_id)) { |
8657 | struct necp_aggregate_route_rule *aggregate_route_rule = necp_lookup_aggregate_route_rule_locked(route_rule_id); |
8658 | if (aggregate_route_rule != NULL) { |
8659 | int index = 0; |
8660 | for (index = 0; index < MAX_AGGREGATE_ROUTE_RULES; index++) { |
8661 | u_int32_t sub_route_rule_id = aggregate_route_rule->rule_ids[index]; |
8662 | if (sub_route_rule_id == 0) { |
8663 | break; |
8664 | } |
8665 | if (!necp_route_is_allowed_inner(route, interface, sub_route_rule_id, interface_type_denied)) { |
8666 | return (FALSE); |
8667 | } |
8668 | } |
8669 | } |
8670 | } else { |
8671 | return (necp_route_is_allowed_inner(route, interface, route_rule_id, interface_type_denied)); |
8672 | } |
8673 | |
8674 | return (TRUE); |
8675 | } |
8676 | |
8677 | bool |
8678 | necp_packet_is_allowed_over_interface(struct mbuf *packet, struct ifnet *interface) |
8679 | { |
8680 | bool is_allowed = TRUE; |
8681 | u_int32_t route_rule_id = necp_get_route_rule_id_from_packet(packet); |
8682 | if (route_rule_id != 0 && |
8683 | interface != NULL) { |
8684 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8685 | is_allowed = necp_route_is_allowed(NULL, interface, necp_get_route_rule_id_from_packet(packet), NULL); |
8686 | lck_rw_done(&necp_kernel_policy_lock); |
8687 | } |
8688 | return (is_allowed); |
8689 | } |
8690 | |
8691 | static bool |
8692 | necp_netagents_allow_traffic(u_int32_t *netagent_ids, size_t netagent_id_count) |
8693 | { |
8694 | size_t netagent_cursor; |
8695 | for (netagent_cursor = 0; netagent_cursor < netagent_id_count; netagent_cursor++) { |
8696 | struct necp_uuid_id_mapping *mapping = NULL; |
8697 | u_int32_t netagent_id = netagent_ids[netagent_cursor]; |
8698 | if (netagent_id == 0) { |
8699 | break; |
8700 | } |
8701 | mapping = necp_uuid_lookup_uuid_with_service_id_locked(netagent_id); |
8702 | if (mapping != NULL) { |
8703 | u_int32_t agent_flags = 0; |
8704 | agent_flags = netagent_get_flags(mapping->uuid); |
8705 | if (agent_flags & NETAGENT_FLAG_REGISTERED) { |
8706 | if (agent_flags & NETAGENT_FLAG_ACTIVE) { |
8707 | continue; |
8708 | } else if ((agent_flags & NETAGENT_FLAG_VOLUNTARY) == 0) { |
8709 | return (FALSE); |
8710 | } |
8711 | } |
8712 | } |
8713 | } |
8714 | return (TRUE); |
8715 | } |
8716 | |
8717 | static bool |
8718 | necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, ifnet_t interface, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id) |
8719 | { |
8720 | u_int32_t verifyifindex = interface ? interface->if_index : 0; |
8721 | bool allowed_to_receive = TRUE; |
8722 | struct necp_socket_info info; |
8723 | u_int32_t flowhash = 0; |
8724 | necp_kernel_policy_result service_action = 0; |
8725 | necp_kernel_policy_service service = { 0, 0 }; |
8726 | u_int32_t route_rule_id = 0; |
8727 | struct rtentry *route = NULL; |
8728 | u_int32_t interface_type_denied = IFRTYPE_FUNCTIONAL_UNKNOWN; |
8729 | |
8730 | u_int32_t netagent_ids[NECP_MAX_NETAGENTS]; |
8731 | memset(&netagent_ids, 0, sizeof(netagent_ids)); |
8732 | |
8733 | if (return_policy_id) { |
8734 | *return_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8735 | } |
8736 | if (return_skip_policy_id) { |
8737 | *return_skip_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8738 | } |
8739 | if (return_route_rule_id) { |
8740 | *return_route_rule_id = 0; |
8741 | } |
8742 | |
8743 | if (inp == NULL) { |
8744 | goto done; |
8745 | } |
8746 | |
8747 | route = inp->inp_route.ro_rt; |
8748 | |
8749 | // Don't lock. Possible race condition, but we don't want the performance hit. |
8750 | if (necp_kernel_socket_policies_count == 0 || |
8751 | (!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0)) { |
8752 | if (necp_drop_all_order > 0) { |
8753 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) { |
8754 | allowed_to_receive = TRUE; |
8755 | } else { |
8756 | allowed_to_receive = FALSE; |
8757 | } |
8758 | } |
8759 | goto done; |
8760 | } |
8761 | |
8762 | // If this socket is connected, or we are not taking addresses into account, try to reuse last result |
8763 | if ((necp_socket_is_connected(inp) || (override_local_addr == NULL && override_remote_addr == NULL)) && inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
8764 | bool policies_have_changed = FALSE; |
8765 | bool route_allowed = TRUE; |
8766 | |
8767 | if (inp->inp_policyresult.policy_gencount != necp_kernel_socket_policies_gencount) { |
8768 | policies_have_changed = TRUE; |
8769 | } else { |
8770 | if (inp->inp_policyresult.results.route_rule_id != 0) { |
8771 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8772 | if (!necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &interface_type_denied)) { |
8773 | route_allowed = FALSE; |
8774 | } |
8775 | lck_rw_done(&necp_kernel_policy_lock); |
8776 | } |
8777 | } |
8778 | |
8779 | if (!policies_have_changed) { |
8780 | if (!route_allowed || |
8781 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_DROP || |
8782 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
8783 | (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && interface && |
8784 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index != verifyifindex)) { |
8785 | allowed_to_receive = FALSE; |
8786 | } else { |
8787 | if (return_policy_id) { |
8788 | *return_policy_id = inp->inp_policyresult.policy_id; |
8789 | } |
8790 | if (return_skip_policy_id) { |
8791 | *return_skip_policy_id = inp->inp_policyresult.skip_policy_id; |
8792 | } |
8793 | if (return_route_rule_id) { |
8794 | *return_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
8795 | } |
8796 | } |
8797 | goto done; |
8798 | } |
8799 | } |
8800 | |
8801 | // Check for loopback exception |
8802 | if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) { |
8803 | allowed_to_receive = TRUE; |
8804 | goto done; |
8805 | } |
8806 | |
8807 | // Actually calculate policy result |
8808 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
8809 | necp_socket_fillout_info_locked(inp, override_local_addr, override_remote_addr, 0, &info); |
8810 | |
8811 | flowhash = necp_socket_calc_flowhash_locked(&info); |
8812 | if (inp->inp_policyresult.policy_id != NECP_KERNEL_POLICY_ID_NONE && |
8813 | inp->inp_policyresult.policy_gencount == necp_kernel_socket_policies_gencount && |
8814 | inp->inp_policyresult.flowhash == flowhash) { |
8815 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_DROP || |
8816 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
8817 | (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && interface && |
8818 | inp->inp_policyresult.results.result_parameter.tunnel_interface_index != verifyifindex) || |
8819 | (inp->inp_policyresult.results.route_rule_id != 0 && |
8820 | !necp_route_is_allowed(route, interface, inp->inp_policyresult.results.route_rule_id, &interface_type_denied))) { |
8821 | allowed_to_receive = FALSE; |
8822 | } else { |
8823 | if (return_policy_id) { |
8824 | *return_policy_id = inp->inp_policyresult.policy_id; |
8825 | } |
8826 | if (return_route_rule_id) { |
8827 | *return_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
8828 | } |
8829 | } |
8830 | lck_rw_done(&necp_kernel_policy_lock); |
8831 | goto done; |
8832 | } |
8833 | |
8834 | struct necp_kernel_socket_policy *matched_policy = necp_socket_find_policy_match_with_info_locked(necp_kernel_socket_policies_map[NECP_SOCKET_MAP_APP_ID_TO_BUCKET(info.application_id)], &info, NULL, &route_rule_id, &service_action, &service, netagent_ids, NULL, NECP_MAX_NETAGENTS, NULL, 0, current_proc(), return_skip_policy_id); |
8835 | if (matched_policy != NULL) { |
8836 | if (matched_policy->result == NECP_KERNEL_POLICY_RESULT_DROP || |
8837 | matched_policy->result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT || |
8838 | (matched_policy->result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && interface && |
8839 | matched_policy->result_parameter.tunnel_interface_index != verifyifindex) || |
8840 | ((service_action == NECP_KERNEL_POLICY_RESULT_TRIGGER_SCOPED || |
8841 | service_action == NECP_KERNEL_POLICY_RESULT_NO_TRIGGER_SCOPED) && |
8842 | service.identifier != 0 && service.identifier != NECP_NULL_SERVICE_ID) || |
8843 | (route_rule_id != 0 && |
8844 | !necp_route_is_allowed(route, interface, route_rule_id, &interface_type_denied)) || |
8845 | !necp_netagents_allow_traffic(netagent_ids, NECP_MAX_NETAGENTS)) { |
8846 | allowed_to_receive = FALSE; |
8847 | } else { |
8848 | if (return_policy_id) { |
8849 | *return_policy_id = matched_policy->id; |
8850 | } |
8851 | if (return_route_rule_id) { |
8852 | *return_route_rule_id = route_rule_id; |
8853 | } |
8854 | } |
8855 | lck_rw_done(&necp_kernel_policy_lock); |
8856 | |
8857 | if (necp_debug > 1 && matched_policy->id != inp->inp_policyresult.policy_id) { |
8858 | NECPLOG(LOG_DEBUG, "Socket Send/Recv Policy: Policy %d Allowed %d" , return_policy_id ? *return_policy_id : 0, allowed_to_receive); |
8859 | } |
8860 | goto done; |
8861 | } else if (necp_drop_all_order > 0) { |
8862 | allowed_to_receive = FALSE; |
8863 | } else { |
8864 | if (return_policy_id) { |
8865 | *return_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH; |
8866 | } |
8867 | if (return_route_rule_id) { |
8868 | *return_route_rule_id = route_rule_id; |
8869 | } |
8870 | } |
8871 | |
8872 | lck_rw_done(&necp_kernel_policy_lock); |
8873 | |
8874 | done: |
8875 | if (!allowed_to_receive && interface_type_denied != IFRTYPE_FUNCTIONAL_UNKNOWN) { |
8876 | soevent(inp->inp_socket, (SO_FILT_HINT_LOCKED | SO_FILT_HINT_IFDENIED)); |
8877 | } |
8878 | |
8879 | return (allowed_to_receive); |
8880 | } |
8881 | |
8882 | bool |
8883 | necp_socket_is_allowed_to_send_recv_v4(struct inpcb *inp, u_int16_t local_port, u_int16_t remote_port, struct in_addr *local_addr, struct in_addr *remote_addr, ifnet_t interface, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id) |
8884 | { |
8885 | struct sockaddr_in local = {}; |
8886 | struct sockaddr_in remote = {}; |
8887 | local.sin_family = remote.sin_family = AF_INET; |
8888 | local.sin_len = remote.sin_len = sizeof(struct sockaddr_in); |
8889 | local.sin_port = local_port; |
8890 | remote.sin_port = remote_port; |
8891 | memcpy(&local.sin_addr, local_addr, sizeof(local.sin_addr)); |
8892 | memcpy(&remote.sin_addr, remote_addr, sizeof(remote.sin_addr)); |
8893 | |
8894 | return (necp_socket_is_allowed_to_send_recv_internal(inp, (struct sockaddr *)&local, (struct sockaddr *)&remote, interface, |
8895 | return_policy_id, return_route_rule_id, return_skip_policy_id)); |
8896 | } |
8897 | |
8898 | bool |
8899 | necp_socket_is_allowed_to_send_recv_v6(struct inpcb *inp, u_int16_t local_port, u_int16_t remote_port, struct in6_addr *local_addr, struct in6_addr *remote_addr, ifnet_t interface, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, necp_kernel_policy_id *return_skip_policy_id) |
8900 | { |
8901 | struct sockaddr_in6 local = {}; |
8902 | struct sockaddr_in6 remote = {}; |
8903 | local.sin6_family = remote.sin6_family = AF_INET6; |
8904 | local.sin6_len = remote.sin6_len = sizeof(struct sockaddr_in6); |
8905 | local.sin6_port = local_port; |
8906 | remote.sin6_port = remote_port; |
8907 | memcpy(&local.sin6_addr, local_addr, sizeof(local.sin6_addr)); |
8908 | memcpy(&remote.sin6_addr, remote_addr, sizeof(remote.sin6_addr)); |
8909 | |
8910 | return (necp_socket_is_allowed_to_send_recv_internal(inp, (struct sockaddr *)&local, (struct sockaddr *)&remote, interface, |
8911 | return_policy_id, return_route_rule_id, return_skip_policy_id)); |
8912 | } |
8913 | |
8914 | bool |
8915 | necp_socket_is_allowed_to_send_recv(struct inpcb *inp, necp_kernel_policy_id *return_policy_id, u_int32_t *return_route_rule_id, |
8916 | necp_kernel_policy_id *return_skip_policy_id) |
8917 | { |
8918 | return (necp_socket_is_allowed_to_send_recv_internal(inp, NULL, NULL, NULL, return_policy_id, return_route_rule_id, return_skip_policy_id)); |
8919 | } |
8920 | |
8921 | int |
8922 | necp_mark_packet_from_socket(struct mbuf *packet, struct inpcb *inp, necp_kernel_policy_id policy_id, u_int32_t route_rule_id, |
8923 | necp_kernel_policy_id skip_policy_id) |
8924 | { |
8925 | if (packet == NULL || inp == NULL || !(packet->m_flags & M_PKTHDR)) { |
8926 | return (EINVAL); |
8927 | } |
8928 | |
8929 | // Mark ID for Pass and IP Tunnel |
8930 | if (policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
8931 | packet->m_pkthdr.necp_mtag.necp_policy_id = policy_id; |
8932 | } else if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_PASS || |
8933 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL) { |
8934 | packet->m_pkthdr.necp_mtag.necp_policy_id = inp->inp_policyresult.policy_id; |
8935 | } else { |
8936 | packet->m_pkthdr.necp_mtag.necp_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8937 | } |
8938 | packet->m_pkthdr.necp_mtag.necp_last_interface_index = 0; |
8939 | if (route_rule_id != 0) { |
8940 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = route_rule_id; |
8941 | } else { |
8942 | packet->m_pkthdr.necp_mtag.necp_route_rule_id = inp->inp_policyresult.results.route_rule_id; |
8943 | } |
8944 | packet->m_pkthdr.necp_mtag.necp_app_id = inp->inp_policyresult.app_id; |
8945 | |
8946 | if (skip_policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
8947 | packet->m_pkthdr.necp_mtag.necp_skip_policy_id = skip_policy_id; |
8948 | } |
8949 | |
8950 | return (0); |
8951 | } |
8952 | |
8953 | int |
8954 | necp_mark_packet_from_ip(struct mbuf *packet, necp_kernel_policy_id policy_id) |
8955 | { |
8956 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
8957 | return (EINVAL); |
8958 | } |
8959 | |
8960 | // Mark ID for Pass and IP Tunnel |
8961 | if (policy_id != NECP_KERNEL_POLICY_ID_NONE) { |
8962 | packet->m_pkthdr.necp_mtag.necp_policy_id = policy_id; |
8963 | } else { |
8964 | packet->m_pkthdr.necp_mtag.necp_policy_id = NECP_KERNEL_POLICY_ID_NONE; |
8965 | } |
8966 | |
8967 | return (0); |
8968 | } |
8969 | |
8970 | int |
8971 | necp_mark_packet_from_interface(struct mbuf *packet, ifnet_t interface) |
8972 | { |
8973 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
8974 | return (EINVAL); |
8975 | } |
8976 | |
8977 | // Mark ID for Pass and IP Tunnel |
8978 | if (interface != NULL) { |
8979 | packet->m_pkthdr.necp_mtag.necp_last_interface_index = interface->if_index; |
8980 | } |
8981 | |
8982 | return (0); |
8983 | } |
8984 | |
8985 | int |
8986 | necp_mark_packet_as_keepalive(struct mbuf *packet, bool is_keepalive) |
8987 | { |
8988 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
8989 | return (EINVAL); |
8990 | } |
8991 | |
8992 | if (is_keepalive) { |
8993 | packet->m_pkthdr.pkt_flags |= PKTF_KEEPALIVE; |
8994 | } else { |
8995 | packet->m_pkthdr.pkt_flags &= ~PKTF_KEEPALIVE; |
8996 | } |
8997 | |
8998 | return (0); |
8999 | } |
9000 | |
9001 | necp_kernel_policy_id |
9002 | necp_get_policy_id_from_packet(struct mbuf *packet) |
9003 | { |
9004 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9005 | return (NECP_KERNEL_POLICY_ID_NONE); |
9006 | } |
9007 | |
9008 | return (packet->m_pkthdr.necp_mtag.necp_policy_id); |
9009 | } |
9010 | |
9011 | necp_kernel_policy_id |
9012 | necp_get_skip_policy_id_from_packet(struct mbuf *packet) |
9013 | { |
9014 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9015 | return (NECP_KERNEL_POLICY_ID_NONE); |
9016 | } |
9017 | |
9018 | return (packet->m_pkthdr.necp_mtag.necp_skip_policy_id); |
9019 | } |
9020 | |
9021 | u_int32_t |
9022 | necp_get_last_interface_index_from_packet(struct mbuf *packet) |
9023 | { |
9024 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9025 | return (0); |
9026 | } |
9027 | |
9028 | return (packet->m_pkthdr.necp_mtag.necp_last_interface_index); |
9029 | } |
9030 | |
9031 | u_int32_t |
9032 | necp_get_route_rule_id_from_packet(struct mbuf *packet) |
9033 | { |
9034 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9035 | return (0); |
9036 | } |
9037 | |
9038 | return (packet->m_pkthdr.necp_mtag.necp_route_rule_id); |
9039 | } |
9040 | |
9041 | int |
9042 | necp_get_app_uuid_from_packet(struct mbuf *packet, |
9043 | uuid_t app_uuid) |
9044 | { |
9045 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9046 | return (EINVAL); |
9047 | } |
9048 | |
9049 | bool found_mapping = FALSE; |
9050 | if (packet->m_pkthdr.necp_mtag.necp_app_id != 0) { |
9051 | lck_rw_lock_shared(&necp_kernel_policy_lock); |
9052 | struct necp_uuid_id_mapping *entry = necp_uuid_lookup_uuid_with_app_id_locked(packet->m_pkthdr.necp_mtag.necp_app_id); |
9053 | if (entry != NULL) { |
9054 | uuid_copy(app_uuid, entry->uuid); |
9055 | found_mapping = true; |
9056 | } |
9057 | lck_rw_done(&necp_kernel_policy_lock); |
9058 | } |
9059 | if (!found_mapping) { |
9060 | uuid_clear(app_uuid); |
9061 | } |
9062 | return (0); |
9063 | } |
9064 | |
9065 | bool |
9066 | necp_get_is_keepalive_from_packet(struct mbuf *packet) |
9067 | { |
9068 | if (packet == NULL || !(packet->m_flags & M_PKTHDR)) { |
9069 | return (FALSE); |
9070 | } |
9071 | |
9072 | return (packet->m_pkthdr.pkt_flags & PKTF_KEEPALIVE); |
9073 | } |
9074 | |
9075 | u_int32_t |
9076 | necp_socket_get_content_filter_control_unit(struct socket *so) |
9077 | { |
9078 | struct inpcb *inp = sotoinpcb(so); |
9079 | |
9080 | if (inp == NULL) { |
9081 | return (0); |
9082 | } |
9083 | return (inp->inp_policyresult.results.filter_control_unit); |
9084 | } |
9085 | |
9086 | bool |
9087 | necp_socket_should_use_flow_divert(struct inpcb *inp) |
9088 | { |
9089 | if (inp == NULL) { |
9090 | return (FALSE); |
9091 | } |
9092 | |
9093 | return (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT); |
9094 | } |
9095 | |
9096 | u_int32_t |
9097 | necp_socket_get_flow_divert_control_unit(struct inpcb *inp) |
9098 | { |
9099 | if (inp == NULL) { |
9100 | return (0); |
9101 | } |
9102 | |
9103 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_DIVERT) { |
9104 | return (inp->inp_policyresult.results.result_parameter.flow_divert_control_unit); |
9105 | } |
9106 | |
9107 | return (0); |
9108 | } |
9109 | |
9110 | bool |
9111 | necp_socket_should_rescope(struct inpcb *inp) |
9112 | { |
9113 | if (inp == NULL) { |
9114 | return (FALSE); |
9115 | } |
9116 | |
9117 | return (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED || |
9118 | inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT); |
9119 | } |
9120 | |
9121 | u_int |
9122 | necp_socket_get_rescope_if_index(struct inpcb *inp) |
9123 | { |
9124 | if (inp == NULL) { |
9125 | return (0); |
9126 | } |
9127 | |
9128 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SOCKET_SCOPED) { |
9129 | return (inp->inp_policyresult.results.result_parameter.scoped_interface_index); |
9130 | } else if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_SCOPED_DIRECT) { |
9131 | return (necp_get_primary_direct_interface_index()); |
9132 | } |
9133 | |
9134 | return (0); |
9135 | } |
9136 | |
9137 | u_int32_t |
9138 | necp_socket_get_effective_mtu(struct inpcb *inp, u_int32_t current_mtu) |
9139 | { |
9140 | if (inp == NULL) { |
9141 | return (current_mtu); |
9142 | } |
9143 | |
9144 | if (inp->inp_policyresult.results.result == NECP_KERNEL_POLICY_RESULT_IP_TUNNEL && |
9145 | (inp->inp_flags & INP_BOUND_IF) && |
9146 | inp->inp_boundifp) { |
9147 | |
9148 | u_int bound_interface_index = inp->inp_boundifp->if_index; |
9149 | u_int tunnel_interface_index = inp->inp_policyresult.results.result_parameter.tunnel_interface_index; |
9150 | |
9151 | // The result is IP Tunnel, and is rescoping from one interface to another. Recalculate MTU. |
9152 | if (bound_interface_index != tunnel_interface_index) { |
9153 | ifnet_t tunnel_interface = NULL; |
9154 | |
9155 | ifnet_head_lock_shared(); |
9156 | tunnel_interface = ifindex2ifnet[tunnel_interface_index]; |
9157 | ifnet_head_done(); |
9158 | |
9159 | if (tunnel_interface != NULL) { |
9160 | u_int32_t direct_tunnel_mtu = tunnel_interface->if_mtu; |
9161 | u_int32_t delegate_tunnel_mtu = (tunnel_interface->if_delegated.ifp != NULL) ? tunnel_interface->if_delegated.ifp->if_mtu : 0; |
9162 | if (delegate_tunnel_mtu != 0 && |
9163 | strncmp(tunnel_interface->if_name, "ipsec" , strlen("ipsec" )) == 0) { |
9164 | // For ipsec interfaces, calculate the overhead from the delegate interface |
9165 | u_int32_t tunnel_overhead = (u_int32_t)(esp_hdrsiz(NULL) + sizeof(struct ip6_hdr)); |
9166 | if (delegate_tunnel_mtu > tunnel_overhead) { |
9167 | delegate_tunnel_mtu -= tunnel_overhead; |
9168 | } |
9169 | |
9170 | if (delegate_tunnel_mtu < direct_tunnel_mtu) { |
9171 | // If the (delegate - overhead) < direct, return (delegate - overhead) |
9172 | return (delegate_tunnel_mtu); |
9173 | } else { |
9174 | // Otherwise return direct |
9175 | return (direct_tunnel_mtu); |
9176 | } |
9177 | } else { |
9178 | // For non-ipsec interfaces, just return the tunnel MTU |
9179 | return (direct_tunnel_mtu); |
9180 | } |
9181 | } |
9182 | } |
9183 | } |
9184 | |
9185 | // By default, just return the MTU passed in |
9186 | return (current_mtu); |
9187 | } |
9188 | |
9189 | ifnet_t |
9190 | necp_get_ifnet_from_result_parameter(necp_kernel_policy_result_parameter *result_parameter) |
9191 | { |
9192 | if (result_parameter == NULL) { |
9193 | return (NULL); |
9194 | } |
9195 | |
9196 | return (ifindex2ifnet[result_parameter->tunnel_interface_index]); |
9197 | } |
9198 | |
9199 | bool |
9200 | necp_packet_can_rebind_to_ifnet(struct mbuf *packet, struct ifnet *interface, struct route *new_route, int family) |
9201 | { |
9202 | bool found_match = FALSE; |
9203 | errno_t result = 0; |
9204 | ifaddr_t *addresses = NULL; |
9205 | union necp_sockaddr_union address_storage; |
9206 | int i; |
9207 | |
9208 | if (packet == NULL || interface == NULL || new_route == NULL || (family != AF_INET && family != AF_INET6)) { |
9209 | return (FALSE); |
9210 | } |
9211 | |
9212 | result = ifnet_get_address_list_family(interface, &addresses, family); |
9213 | if (result != 0) { |
9214 | NECPLOG(LOG_ERR, "Failed to get address list for %s%d" , ifnet_name(interface), ifnet_unit(interface)); |
9215 | return (FALSE); |
9216 | } |
9217 | |
9218 | for (i = 0; addresses[i] != NULL; i++) { |
9219 | ROUTE_RELEASE(new_route); |
9220 | if (ifaddr_address(addresses[i], &address_storage.sa, sizeof(address_storage)) == 0) { |
9221 | if (family == AF_INET) { |
9222 | struct ip *ip = mtod(packet, struct ip *); |
9223 | if (memcmp(&address_storage.sin.sin_addr, &ip->ip_src, sizeof(ip->ip_src)) == 0) { |
9224 | struct sockaddr_in *dst4 = (struct sockaddr_in *)(void *)&new_route->ro_dst; |
9225 | dst4->sin_family = AF_INET; |
9226 | dst4->sin_len = sizeof(struct sockaddr_in); |
9227 | dst4->sin_addr = ip->ip_dst; |
9228 | rtalloc_scoped(new_route, interface->if_index); |
9229 | if (!ROUTE_UNUSABLE(new_route)) { |
9230 | found_match = TRUE; |
9231 | goto done; |
9232 | } |
9233 | } |
9234 | } else if (family == AF_INET6) { |
9235 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
9236 | if (memcmp(&address_storage.sin6.sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src)) == 0) { |
9237 | struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)(void *)&new_route->ro_dst; |
9238 | dst6->sin6_family = AF_INET6; |
9239 | dst6->sin6_len = sizeof(struct sockaddr_in6); |
9240 | dst6->sin6_addr = ip6->ip6_dst; |
9241 | rtalloc_scoped(new_route, interface->if_index); |
9242 | if (!ROUTE_UNUSABLE(new_route)) { |
9243 | found_match = TRUE; |
9244 | goto done; |
9245 | } |
9246 | } |
9247 | } |
9248 | } |
9249 | } |
9250 | |
9251 | done: |
9252 | ifnet_free_address_list(addresses); |
9253 | addresses = NULL; |
9254 | return (found_match); |
9255 | } |
9256 | |
9257 | static bool |
9258 | necp_addr_is_loopback(struct sockaddr *address) |
9259 | { |
9260 | if (address == NULL) { |
9261 | return (FALSE); |
9262 | } |
9263 | |
9264 | if (address->sa_family == AF_INET) { |
9265 | return (ntohl(((struct sockaddr_in *)(void *)address)->sin_addr.s_addr) == INADDR_LOOPBACK); |
9266 | } else if (address->sa_family == AF_INET6) { |
9267 | return IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)(void *)address)->sin6_addr); |
9268 | } |
9269 | |
9270 | return (FALSE); |
9271 | } |
9272 | |
9273 | static bool |
9274 | necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet) |
9275 | { |
9276 | // Note: This function only checks for the loopback addresses. |
9277 | // In the future, we may want to expand to also allow any traffic |
9278 | // going through the loopback interface, but until then, this |
9279 | // check is cheaper. |
9280 | |
9281 | if (local_addr != NULL && necp_addr_is_loopback(local_addr)) { |
9282 | return (TRUE); |
9283 | } |
9284 | |
9285 | if (remote_addr != NULL && necp_addr_is_loopback(remote_addr)) { |
9286 | return (TRUE); |
9287 | } |
9288 | |
9289 | if (inp != NULL) { |
9290 | if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp && (inp->inp_boundifp->if_flags & IFF_LOOPBACK)) { |
9291 | return (TRUE); |
9292 | } |
9293 | if (inp->inp_vflag & INP_IPV4) { |
9294 | if (ntohl(inp->inp_laddr.s_addr) == INADDR_LOOPBACK || |
9295 | ntohl(inp->inp_faddr.s_addr) == INADDR_LOOPBACK) { |
9296 | return (TRUE); |
9297 | } |
9298 | } else if (inp->inp_vflag & INP_IPV6) { |
9299 | if (IN6_IS_ADDR_LOOPBACK(&inp->in6p_laddr) || |
9300 | IN6_IS_ADDR_LOOPBACK(&inp->in6p_faddr)) { |
9301 | return (TRUE); |
9302 | } |
9303 | } |
9304 | } |
9305 | |
9306 | if (packet != NULL) { |
9307 | struct ip *ip = mtod(packet, struct ip *); |
9308 | if (ip->ip_v == 4) { |
9309 | if (ntohl(ip->ip_src.s_addr) == INADDR_LOOPBACK) { |
9310 | return (TRUE); |
9311 | } |
9312 | if (ntohl(ip->ip_dst.s_addr) == INADDR_LOOPBACK) { |
9313 | return (TRUE); |
9314 | } |
9315 | } else if (ip->ip_v == 6) { |
9316 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
9317 | if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src)) { |
9318 | return (TRUE); |
9319 | } |
9320 | if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { |
9321 | return (TRUE); |
9322 | } |
9323 | } |
9324 | } |
9325 | |
9326 | return (FALSE); |
9327 | } |
9328 | |
9329 | static bool |
9330 | necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet) |
9331 | { |
9332 | |
9333 | if (inp != NULL) { |
9334 | return (sflt_permission_check(inp) ? true : false); |
9335 | } |
9336 | if (packet != NULL) { |
9337 | struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *); |
9338 | if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION && |
9339 | IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) && |
9340 | ip6->ip6_dst.s6_addr32[2] == ntohl(0xaede48ff) && |
9341 | ip6->ip6_dst.s6_addr32[3] == ntohl(0xfe334455)) { |
9342 | return (true); |
9343 | } |
9344 | } |
9345 | |
9346 | return (false); |
9347 | } |
9348 | |