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