static int necp_addr_compare(struct sockaddr *sa1, struct sockaddr *sa2, int check_port);
static bool necp_buffer_compare_with_bit_prefix(u_int8_t *p1, u_int8_t *p2, u_int32_t bits);
static bool necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, struct inpcb *inp, struct mbuf *packet);
+static bool necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet);
struct necp_uuid_id_mapping {
LIST_ENTRY(necp_uuid_id_mapping) chain;
curr_type = NECP_TLV_NIL;
}
curr_length = necp_buffer_get_tlv_length(buffer, cursor);
+ if (curr_length > buffer_length - ((u_int32_t)cursor + sizeof(curr_type) + sizeof(curr_length))) {
+ return (-1);
+ }
+
next_cursor = (cursor + sizeof(curr_type) + sizeof(curr_length) + curr_length);
if (curr_type == type) {
// check if entire TLV fits inside buffer
return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_APPLICATION);
}
+static inline bool
+necp_policy_condition_is_real_application(u_int8_t *buffer, u_int32_t length)
+{
+ return (necp_policy_condition_get_type_from_buffer(buffer, length) == NECP_POLICY_CONDITION_REAL_APPLICATION);
+}
+
static inline bool
necp_policy_condition_requires_application(u_int8_t *buffer, u_int32_t length)
{
return (type == NECP_POLICY_CONDITION_REAL_APPLICATION);
}
+static inline bool
+necp_policy_condition_requires_real_application(u_int8_t *buffer, u_int32_t length)
+{
+ u_int8_t type = necp_policy_condition_get_type_from_buffer(buffer, length);
+ return (type == NECP_POLICY_CONDITION_ENTITLEMENT);
+}
+
static bool
necp_policy_condition_is_valid(u_int8_t *buffer, u_int32_t length, u_int8_t policy_result_type)
{
bool has_default_condition = FALSE;
bool has_non_default_condition = FALSE;
bool has_application_condition = FALSE;
+ bool has_real_application_condition = FALSE;
bool requires_application_condition = FALSE;
+ bool requires_real_application_condition = FALSE;
u_int8_t *conditions_array = NULL;
u_int32_t conditions_array_size = 0;
int conditions_array_cursor;
has_application_condition = TRUE;
}
+ if (necp_policy_condition_is_real_application((conditions_array + conditions_array_cursor), condition_size)) {
+ has_real_application_condition = TRUE;
+ }
+
if (necp_policy_condition_requires_application((conditions_array + conditions_array_cursor), condition_size)) {
requires_application_condition = TRUE;
}
+ if (necp_policy_condition_requires_real_application((conditions_array + conditions_array_cursor), condition_size)) {
+ requires_real_application_condition = TRUE;
+ }
+
conditions_array_cursor += condition_size;
}
}
goto fail;
}
+ if (requires_real_application_condition && !has_real_application_condition) {
+ NECPLOG0(LOG_ERR, "Failed to validate conditions; did not contain real application condition");
+ response_error = NECP_ERROR_POLICY_CONDITIONS_INVALID;
+ goto fail;
+ }
+
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) {
response_error = NECP_ERROR_INTERNAL;
goto fail;
}
if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
new_kernel_policy->cond_custom_entitlement = cond_custom_entitlement;
+ new_kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_unknown;
}
if (new_kernel_policy->condition_mask & NECP_KERNEL_CONDITION_ACCOUNT_ID) {
new_kernel_policy->cond_account_id = cond_account_id;
u_int8_t type = necp_buffer_get_tlv_type(parameters, offset);
u_int32_t length = necp_buffer_get_tlv_length(parameters, offset);
- if (length > 0 && (offset + sizeof(u_int8_t) + sizeof(u_int32_t) + length) <= parameters_size) {
+ if (length > (parameters_size - (offset + sizeof(u_int8_t) + sizeof(u_int32_t)))) {
+ // If the length is larger than what can fit in the remaining parameters size, bail
+ NECPLOG(LOG_ERR, "Invalid TLV length (%u)", length);
+ break;
+ }
+
+ if (length > 0) {
u_int8_t *value = necp_buffer_get_tlv_value(parameters, offset, NULL);
if (value != NULL) {
switch (type) {
}
if (kernel_policy->condition_mask & NECP_KERNEL_CONDITION_CUSTOM_ENTITLEMENT) {
- if (kernel_policy->cond_custom_entitlement != NULL) {
- if (proc == NULL) {
- // No process found, cannot check entitlement
- return (FALSE);
- }
- task_t task = proc_task(proc);
- if (task == NULL ||
- !IOTaskHasEntitlement(task, kernel_policy->cond_custom_entitlement)) {
- // Process is missing custom entitlement
- return (FALSE);
+ if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_false) {
+ // Process is missing entitlement based on previous check
+ return (FALSE);
+ } else if (kernel_policy->cond_custom_entitlement_matched == necp_boolean_state_unknown) {
+ if (kernel_policy->cond_custom_entitlement != NULL) {
+ if (proc == NULL) {
+ // No process found, cannot check entitlement
+ return (FALSE);
+ }
+ task_t task = proc_task(proc);
+ if (task == NULL ||
+ !IOTaskHasEntitlement(task, kernel_policy->cond_custom_entitlement)) {
+ // Process is missing custom entitlement
+ kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_false;
+ return (FALSE);
+ } else {
+ kernel_policy->cond_custom_entitlement_matched = necp_boolean_state_true;
+ }
}
}
}
return (inp->inp_socket->so_state & (SS_ISCONNECTING | SS_ISCONNECTED | SS_ISDISCONNECTING));
}
+static inline bool
+necp_socket_bypass(struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, struct inpcb *inp)
+{
+
+ if (necp_pass_loopback > 0 && necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+ return (true);
+ } else if (necp_is_intcoproc(inp, NULL)) {
+ return (true);
+ }
+
+ return (false);
+}
+
necp_kernel_policy_id
necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int32_t override_bound_interface)
{
inp->inp_policyresult.flowhash = 0;
inp->inp_policyresult.results.filter_control_unit = 0;
inp->inp_policyresult.results.route_rule_id = 0;
- if (necp_pass_loopback > 0 &&
- necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+ if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_PASS;
} else {
inp->inp_policyresult.results.result = NECP_KERNEL_POLICY_RESULT_DROP;
}
// Check for loopback exception
- if (necp_pass_loopback > 0 &&
- necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+ if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
// Mark socket as a pass
inp->inp_policyresult.policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
inp->inp_policyresult.policy_gencount = 0;
return (matched_policy);
}
+static inline bool
+necp_output_bypass(struct mbuf *packet)
+{
+ if (necp_pass_loopback > 0 && necp_is_loopback(NULL, NULL, NULL, packet)) {
+ return (true);
+ }
+ if (necp_pass_keepalives > 0 && necp_get_is_keepalive_from_packet(packet)) {
+ return (true);
+ }
+ if (necp_is_intcoproc(NULL, packet)) {
+ return (true);
+ }
+ return (false);
+}
+
necp_kernel_policy_id
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)
{
if (necp_drop_all_order > 0) {
matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
if (result) {
- if ((necp_pass_loopback > 0 &&
- necp_is_loopback(NULL, NULL, NULL, packet)) ||
- (necp_pass_keepalives > 0 &&
- necp_get_is_keepalive_from_packet(packet))) {
+ if (necp_output_bypass(packet)) {
*result = NECP_KERNEL_POLICY_RESULT_PASS;
} else {
*result = NECP_KERNEL_POLICY_RESULT_DROP;
}
// Check for loopback exception
- if ((necp_pass_loopback > 0 &&
- necp_is_loopback(NULL, NULL, NULL, packet)) ||
- (necp_pass_keepalives > 0 &&
- necp_get_is_keepalive_from_packet(packet))) {
+ if (necp_output_bypass(packet)) {
matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
if (result) {
*result = NECP_KERNEL_POLICY_RESULT_PASS;
if (necp_drop_all_order > 0) {
matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
if (result) {
- if ((necp_pass_loopback > 0 &&
- necp_is_loopback(NULL, NULL, NULL, packet)) ||
- (necp_pass_keepalives > 0 &&
- necp_get_is_keepalive_from_packet(packet))) {
+ if (necp_output_bypass(packet)) {
*result = NECP_KERNEL_POLICY_RESULT_PASS;
} else {
*result = NECP_KERNEL_POLICY_RESULT_DROP;
}
// Check for loopback exception
- if ((necp_pass_loopback > 0 &&
- necp_is_loopback(NULL, NULL, NULL, packet)) ||
- (necp_pass_keepalives > 0 &&
- necp_get_is_keepalive_from_packet(packet))) {
+ if (necp_output_bypass(packet)) {
matched_policy_id = NECP_KERNEL_POLICY_ID_NO_MATCH;
if (result) {
*result = NECP_KERNEL_POLICY_RESULT_PASS;
if (necp_kernel_socket_policies_count == 0 ||
(!(inp->inp_flags2 & INP2_WANT_APP_POLICY) && necp_kernel_socket_policies_non_app_count == 0)) {
if (necp_drop_all_order > 0) {
- if (necp_pass_loopback > 0 &&
- necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+ if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
allowed_to_receive = TRUE;
} else {
allowed_to_receive = FALSE;
}
// Check for loopback exception
- if (necp_pass_loopback > 0 &&
- necp_is_loopback(override_local_addr, override_remote_addr, inp, NULL)) {
+ if (necp_socket_bypass(override_local_addr, override_remote_addr, inp)) {
allowed_to_receive = TRUE;
goto done;
}
return (FALSE);
}
+
+static bool
+necp_is_intcoproc(struct inpcb *inp, struct mbuf *packet)
+{
+
+ if (inp != NULL) {
+ return (sflt_permission_check(inp) ? true : false);
+ }
+ if (packet != NULL) {
+ struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *);
+ if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION &&
+ IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) &&
+ ip6->ip6_dst.s6_addr32[2] == ntohl(0xaede48ff) &&
+ ip6->ip6_dst.s6_addr32[3] == ntohl(0xfe334455)) {
+ return (true);
+ }
+ }
+
+ return (false);
+}