Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* ifpermit.c
      2  *
      3  * Copyright (c) 2024 Apple Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     https://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * Implementation of a permitted interface list object, which maintains a list of
     18  * interfaces on which we are permitted to provide some service.
     19  */
     20 
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <stdio.h>
     24 #include <unistd.h>
     25 #include <pwd.h>
     26 #include <errno.h>
     27 #include <sys/socket.h>
     28 #include <netinet/in.h>
     29 #include <arpa/inet.h>
     30 #include <fcntl.h>
     31 #include <time.h>
     32 #include <dns_sd.h>
     33 #include <net/if.h>
     34 #include <inttypes.h>
     35 #include <sys/resource.h>
     36 #include <netinet/icmp6.h>
     37 #include "srp.h"
     38 #include "ifpermit.h"
     39 #include "dns-msg.h"
     40 #include "ioloop.h"
     41 #include "srp-mdns-proxy.h"
     42 
     43 // If we aren't able to allocate a permitted interface list, we still need to return a non-NULL value so that we don't
     44 // fail open. So all of these functions need to treat this particular value as special but not dereference it.
     45 #define PERMITTED_INTERFACE_LIST_BLOCKED (ifpermit_list_t *)1
     46 
     47 typedef struct ifpermit_name ifpermit_name_t;
     48 struct ifpermit_name {
     49     ifpermit_name_t *next;
     50     char *name; // Interface name
     51     uint32_t ifindex; // Interface index
     52     int count;        // Number of permittors for this interface
     53 };
     54 
     55 struct ifpermit_list {
     56 	int ref_count;
     57     ifpermit_name_t *names;
     58 };
     59 
     60 void
     61 ifpermit_list_add(ifpermit_list_t *permits, const char *name)
     62 {
     63 	if (permits == PERMITTED_INTERFACE_LIST_BLOCKED) {
     64 		ERROR("blocked permit list when adding " PUB_S_SRP, name);
     65         return;
     66 	}
     67     ifpermit_name_t **pname = &permits->names;
     68     ifpermit_name_t *permit_name;
     69     while (*pname != NULL) {
     70         permit_name = *pname;
     71         if (!strcmp(name, permit_name->name)) {
     72         success:
     73             permit_name->count++;
     74             INFO("%d permits for interface " PUB_S_SRP " with index %d", permit_name->count, name, permit_name->ifindex);
     75             return;
     76         }
     77         pname = &permit_name->next;
     78     }
     79     permit_name = calloc(1, sizeof(*permit_name));
     80     if (permit_name != NULL) {
     81         permit_name->name = strdup(name);
     82         if (permit_name->name == NULL) {
     83             free(permit_name);
     84             permit_name = NULL;
     85         } else {
     86             permit_name->ifindex = if_nametoindex(name);
     87             if (permit_name->ifindex == 0) {
     88                 ERROR("if_nametoindex for interface " PUB_S_SRP " returned 0.", name);
     89                 free(permit_name->name);
     90                 free(permit_name);
     91                 return;
     92             }
     93             *pname = permit_name;
     94             goto success;
     95         }
     96     }
     97     ERROR("no memory to add permit for " PUB_S_SRP, name);
     98 }
     99 
    100 void
    101 ifpermit_list_remove(ifpermit_list_t *permits, const char *name)
    102 {
    103 	if (permits == PERMITTED_INTERFACE_LIST_BLOCKED) {
    104 		ERROR("blocked permit list when removing " PUB_S_SRP, name);
    105         return;
    106     }
    107     ifpermit_name_t **pname = &permits->names;
    108     ifpermit_name_t *permit_name;
    109     while (*pname != NULL) {
    110         permit_name = *pname;
    111         if (!strcmp(name, permit_name->name)) {
    112             permit_name->count--;
    113             INFO("%d permits for interface " PUB_S_SRP " with index %d", permit_name->count, name, permit_name->ifindex);
    114             if (permit_name->count == 0) {
    115                 *pname = permit_name->next;
    116                 free(permit_name->name);
    117                 free(permit_name);
    118             }
    119             return;
    120         }
    121         pname = &permit_name->next;
    122     }
    123 
    124     FAULT("permit remove for interface " PUB_S_SRP " which does not exist", name);
    125 }
    126 
    127 static void
    128 ifpermit_list_finalize(ifpermit_list_t *list)
    129 {
    130     if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) {
    131         ifpermit_name_t *names = list->names, *next = NULL;
    132         while (names != NULL) {
    133             next = names->next;
    134             free(names->name);
    135             free(names);
    136             names = next;
    137         }
    138         free(list);
    139     }
    140 }
    141 
    142 void
    143 ifpermit_list_retain_(ifpermit_list_t *list, const char *file, int line)
    144 {
    145     if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) {
    146         RETAIN(list, ifpermit_list);
    147     }
    148 }
    149 
    150 void
    151 ifpermit_list_release_(ifpermit_list_t *list, const char *file, int line)
    152 {
    153     if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) {
    154         RELEASE(list, ifpermit_list);
    155     }
    156 }
    157 
    158 ifpermit_list_t *
    159 ifpermit_list_create_(const char *file, int line)
    160 {
    161     ifpermit_list_t *permits = calloc(1, sizeof(*permits));
    162     if (permits == NULL) {
    163         return PERMITTED_INTERFACE_LIST_BLOCKED;
    164     }
    165     RETAIN(permits, ifpermit_list);
    166     return permits;
    167 }
    168 
    169 bool
    170 ifpermit_interface_is_permitted(ifpermit_list_t *permits, uint32_t ifindex)
    171 {
    172     if (permits != NULL && permits != PERMITTED_INTERFACE_LIST_BLOCKED) {
    173         for (ifpermit_name_t *name = permits->names; name != NULL; name = name->next) {
    174             if (name->ifindex == ifindex) {
    175                 return true;
    176             }
    177         }
    178     }
    179     return false;
    180 }
    181 
    182 void ifpermit_add_permitted_interface_to_server_(srp_server_t *NONNULL server_state, const char *NONNULL name,
    183                                                  const char *file, int line)
    184 {
    185     if (server_state->permitted_interfaces == NULL) {
    186         server_state->permitted_interfaces = ifpermit_list_create_(file, line);
    187     }
    188     ifpermit_list_add(server_state->permitted_interfaces, name);
    189 }
    190 
    191 // Local Variables:
    192 // mode: C
    193 // tab-width: 4
    194 // c-file-style: "bsd"
    195 // c-basic-offset: 4
    196 // fill-column: 120
    197 // indent-tabs-mode: nil
    198 // End:
    199