]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/necp.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / bsd / net / necp.c
index 9ada6fe8aba57fbc9d8e81a5751353f5b569891b..cf7e64dbaf1b5c8ae4f7a5ab796d5aeab2f75781 100644 (file)
@@ -339,6 +339,7 @@ static bool necp_is_addr_in_subnet(struct sockaddr *addr, struct sockaddr *subne
 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;
@@ -887,6 +888,10 @@ necp_buffer_find_tlv(u_int8_t *buffer, u_int32_t buffer_length, int offset, u_in
                        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
@@ -1304,6 +1309,12 @@ necp_policy_condition_is_application(u_int8_t *buffer, u_int32_t length)
        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)
 {
@@ -1311,6 +1322,13 @@ 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)
 {
@@ -1611,7 +1629,9 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
        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;
@@ -1805,10 +1825,18 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                                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;
                }
        }
@@ -1819,6 +1847,12 @@ necp_handle_policy_add(struct necp_session *session, u_int32_t message_id, mbuf_
                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;
@@ -3301,6 +3335,7 @@ necp_kernel_socket_policy_add(necp_policy_id parent_policy_id, necp_policy_order
        }
        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;
@@ -5290,7 +5325,13 @@ necp_application_find_policy_match_internal(proc_t proc,
                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) {
@@ -5734,16 +5775,24 @@ necp_socket_check_policy(struct necp_kernel_socket_policy *kernel_policy, necp_a
        }
 
        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;
+                               }
                        }
                }
        }
@@ -6190,6 +6239,19 @@ necp_socket_is_connected(struct inpcb *inp)
        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)
 {
@@ -6233,8 +6295,7 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
                        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;
@@ -6244,8 +6305,7 @@ necp_socket_find_policy_match(struct inpcb *inp, struct sockaddr *override_local
        }
 
        // 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;
@@ -6564,6 +6624,21 @@ necp_ip_output_find_policy_match_locked(necp_kernel_policy_id socket_policy_id,
        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)
 {
@@ -6599,10 +6674,7 @@ necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_a
                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;
@@ -6614,10 +6686,7 @@ necp_ip_output_find_policy_match(struct mbuf *packet, int flags, struct ip_out_a
        }
 
        // 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;
@@ -6741,10 +6810,7 @@ necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out
                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;
@@ -6756,10 +6822,7 @@ necp_ip6_output_find_policy_match(struct mbuf *packet, int flags, struct ip6_out
        }
 
        // 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;
@@ -7376,8 +7439,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        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;
@@ -7421,8 +7483,7 @@ necp_socket_is_allowed_to_send_recv_internal(struct inpcb *inp, struct sockaddr
        }
 
        // 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;
        }
@@ -7927,3 +7988,23 @@ necp_is_loopback(struct sockaddr *local_addr, struct sockaddr *remote_addr, stru
 
        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);
+}