1 | /* |
2 | * Copyright (c) 2019-2021 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 | |
30 | #include <sys/types.h> |
31 | #include <sys/malloc.h> |
32 | #include <sys/proc.h> |
33 | #include <sys/sysctl.h> |
34 | #include <kern/task.h> |
35 | #include <IOKit/IOBSD.h> |
36 | #include <net/restricted_in_port.h> |
37 | #include <netinet/in.h> |
38 | #include <os/log.h> |
39 | #if SKYWALK |
40 | #include <skywalk/namespace/netns.h> |
41 | #endif /* SKYWALK */ |
42 | |
43 | /* |
44 | * Entitlement required for using the port of the test entry |
45 | */ |
46 | #define ENTITLEMENT_TEST_PORT "com.apple.private.network.restricted.port.test" |
47 | |
48 | /* |
49 | * Entitlement required for setting the test sysctl variables |
50 | */ |
51 | #define ENTITLEMENT_TEST_CONTROL "com.apple.private.network.restricted.port.control" |
52 | |
53 | /* |
54 | * Use a single bitmap for quickly checking if a TCP or UDP port is restricted |
55 | */ |
56 | bitmap_t *restricted_port_bitmap = NULL; |
57 | |
58 | struct restricted_port_entry { |
59 | const char *rpe_entitlement; // entitlement to check for this port |
60 | in_port_t rpe_port; // restricted port number (host byte order) |
61 | uint16_t rpe_flags; // RPE_FLAG_xxx |
62 | }; |
63 | |
64 | /* |
65 | * Possible values for the field rpe_flags |
66 | */ |
67 | #define RPE_FLAG_SUPERUSER 0x01 // superuser can use the port |
68 | #define RPE_FLAG_ENTITLEMENT 0x02 // can use the port with the required entitlement |
69 | #define RPE_FLAG_TCP 0x04 // require entitlement for TCP |
70 | #define RPE_FLAG_UDP 0x08 // require entitlement for TCP |
71 | #define RPE_FLAG_TEST 0x10 // entry for testing |
72 | |
73 | static struct restricted_port_entry restricted_port_list[] = { |
74 | #if !XNU_TARGET_OS_OSX |
75 | /* |
76 | * Network relay proxy |
77 | */ |
78 | { |
79 | .rpe_port = 62742, |
80 | .rpe_flags = RPE_FLAG_ENTITLEMENT | RPE_FLAG_TCP | RPE_FLAG_UDP, |
81 | .rpe_entitlement = "com.apple.private.network.restricted.port.nr_proxy" , |
82 | }, |
83 | |
84 | /* |
85 | * Network relay control |
86 | */ |
87 | { |
88 | .rpe_port = 62743, |
89 | .rpe_flags = RPE_FLAG_ENTITLEMENT | RPE_FLAG_UDP, |
90 | .rpe_entitlement = "com.apple.private.network.restricted.port.nr_control" , |
91 | }, |
92 | |
93 | /* |
94 | * Entries for identityservicesd |
95 | */ |
96 | { |
97 | .rpe_port = 61314, |
98 | .rpe_flags = RPE_FLAG_ENTITLEMENT | RPE_FLAG_TCP | RPE_FLAG_UDP, |
99 | .rpe_entitlement = "com.apple.private.network.restricted.port.ids_service_connector" , |
100 | }, |
101 | { |
102 | .rpe_port = 61315, |
103 | .rpe_flags = RPE_FLAG_ENTITLEMENT | RPE_FLAG_TCP | RPE_FLAG_UDP, |
104 | .rpe_entitlement = "com.apple.private.network.restricted.port.ids_cloud_service_connector" , |
105 | }, |
106 | #endif /* !XNU_TARGET_OS_OSX */ |
107 | |
108 | /* |
109 | * For RDC |
110 | */ |
111 | { |
112 | .rpe_port = 55555, |
113 | .rpe_flags = RPE_FLAG_ENTITLEMENT | RPE_FLAG_TCP, |
114 | .rpe_entitlement = "com.apple.private.network.restricted.port.lights_out_management" , |
115 | }, |
116 | |
117 | #if (DEBUG || DEVELOPMENT) |
118 | /* |
119 | * Entries reserved for unit testing |
120 | */ |
121 | { |
122 | .rpe_port = 0, |
123 | .rpe_flags = RPE_FLAG_TCP | RPE_FLAG_TEST, |
124 | .rpe_entitlement = ENTITLEMENT_TEST_PORT, |
125 | }, |
126 | { |
127 | .rpe_port = 0, |
128 | .rpe_flags = RPE_FLAG_UDP | RPE_FLAG_TEST, |
129 | .rpe_entitlement = ENTITLEMENT_TEST_PORT, |
130 | }, |
131 | #endif /* (DEBUG || DEVELOPMENT) */ |
132 | |
133 | /* |
134 | * Sentinel to mark the actual end of the list (rpe_entitlement == NULL) |
135 | */ |
136 | { |
137 | .rpe_port = 0, |
138 | .rpe_flags = 0, |
139 | .rpe_entitlement = NULL, |
140 | } |
141 | }; |
142 | |
143 | #define RPE_ENTRY_COUNT (sizeof(restricted_port_list) / sizeof(restricted_port_list[0])) |
144 | |
145 | SYSCTL_NODE(_net, OID_AUTO, restricted_port, |
146 | CTLFLAG_RW | CTLFLAG_LOCKED, 0, "restricted port" ); |
147 | |
148 | static int sysctl_restricted_port_bitmap SYSCTL_HANDLER_ARGS; |
149 | static int sysctl_restricted_port_enforced SYSCTL_HANDLER_ARGS; |
150 | static int sysctl_restricted_port_verbose SYSCTL_HANDLER_ARGS; |
151 | |
152 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, bitmap, |
153 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, |
154 | 0, 0, &sysctl_restricted_port_bitmap, "" , "" ); |
155 | |
156 | /* |
157 | * In order to set the following sysctl variables the process needs to run as superuser |
158 | * or have the entitlement ENTITLEMENT_TEST_CONTROL |
159 | */ |
160 | #if (DEBUG || DEVELOPMENT) |
161 | static int restricted_port_enforced = 1; |
162 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, enforced, |
163 | CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW | CTLFLAG_ANYBODY, |
164 | 0, 0, &sysctl_restricted_port_enforced, "I" , "" ); |
165 | #else /* (DEBUG || DEVELOPMENT) */ |
166 | const int restricted_port_enforced = 1; |
167 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, enforced, |
168 | CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RD, |
169 | 0, 0, &sysctl_restricted_port_enforced, "I" , "" ); |
170 | #endif /* (DEBUG || DEVELOPMENT) */ |
171 | |
172 | static int restricted_port_verbose = 0; |
173 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, verbose, |
174 | CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW | CTLFLAG_ANYBODY, |
175 | 0, 0, &sysctl_restricted_port_verbose, "I" , "" ); |
176 | |
177 | #if (DEBUG || DEVELOPMENT) |
178 | |
179 | /* |
180 | * Register dynamically a test port set by the unit test program to avoid conflict with |
181 | * a restricted port currently used by its legetimate process. |
182 | * The value must be passed is in host byte order. |
183 | */ |
184 | static uint16_t restricted_port_test = 0; |
185 | |
186 | static int sysctl_restricted_port_test_entitlement SYSCTL_HANDLER_ARGS; |
187 | static int sysctl_restricted_port_test_superuser SYSCTL_HANDLER_ARGS; |
188 | |
189 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, test_entitlement, |
190 | CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW | CTLFLAG_ANYBODY, |
191 | 0, 0, &sysctl_restricted_port_test_entitlement, "UI" , "" ); |
192 | |
193 | SYSCTL_PROC(_net_restricted_port, OID_AUTO, test_superuser, |
194 | CTLTYPE_INT | CTLFLAG_LOCKED | CTLFLAG_RW | CTLFLAG_ANYBODY, |
195 | 0, 0, &sysctl_restricted_port_test_superuser, "UI" , "" ); |
196 | #endif /* (DEBUG || DEVELOPMENT) */ |
197 | |
198 | static int |
199 | sysctl_restricted_port_bitmap SYSCTL_HANDLER_ARGS |
200 | { |
201 | #pragma unused(oidp, arg1, arg2) |
202 | |
203 | if (req->newptr) { |
204 | return EPERM; |
205 | } |
206 | int error = SYSCTL_OUT(req, restricted_port_bitmap, BITMAP_SIZE(UINT16_MAX)); |
207 | |
208 | return error; |
209 | } |
210 | |
211 | static int |
212 | sysctl_restricted_port_enforced SYSCTL_HANDLER_ARGS |
213 | { |
214 | #pragma unused(arg1, arg2) |
215 | int old_value = restricted_port_enforced; |
216 | int value = old_value; |
217 | |
218 | int error = sysctl_handle_int(oidp, arg1: &value, arg2: 0, req); |
219 | if (error != 0 || !req->newptr) { |
220 | return error; |
221 | } |
222 | #if (DEBUG || DEVELOPMENT) |
223 | if (proc_suser(current_proc()) != 0 && |
224 | !IOCurrentTaskHasEntitlement(ENTITLEMENT_TEST_CONTROL)) { |
225 | return EPERM; |
226 | } |
227 | restricted_port_enforced = value; |
228 | os_log(OS_LOG_DEFAULT, |
229 | "%s:%u sysctl net.restricted_port.enforced: %d -> %d" , |
230 | proc_best_name(current_proc()), proc_selfpid(), |
231 | old_value, restricted_port_enforced); |
232 | return error; |
233 | #else |
234 | return EPERM; |
235 | #endif /* (DEBUG || DEVELOPMENT) */ |
236 | } |
237 | |
238 | static int |
239 | sysctl_restricted_port_verbose SYSCTL_HANDLER_ARGS |
240 | { |
241 | #pragma unused(arg1, arg2) |
242 | int old_value = restricted_port_verbose; |
243 | int value = old_value; |
244 | |
245 | int error = sysctl_handle_int(oidp, arg1: &value, arg2: 0, req); |
246 | if (error != 0 || !req->newptr) { |
247 | return error; |
248 | } |
249 | if (proc_suser(p: current_proc()) != 0 && |
250 | !IOCurrentTaskHasEntitlement(ENTITLEMENT_TEST_CONTROL)) { |
251 | return EPERM; |
252 | } |
253 | restricted_port_verbose = value; |
254 | os_log(OS_LOG_DEFAULT, |
255 | "%s:%u sysctl net.restricted_port.verbose: %d -> %d)" , |
256 | proc_best_name(current_proc()), proc_selfpid(), |
257 | old_value, restricted_port_verbose); |
258 | |
259 | return error; |
260 | } |
261 | |
262 | #if (DEBUG || DEVELOPMENT) |
263 | |
264 | static int |
265 | sysctl_restricted_port_test_common(struct sysctl_oid *oidp, |
266 | struct sysctl_req *req, bool test_superuser) |
267 | { |
268 | uint16_t old_value = restricted_port_test; |
269 | int value = old_value; |
270 | unsigned int i; |
271 | |
272 | int error = sysctl_handle_int(oidp, &value, 0, req); |
273 | if (error != 0 || !req->newptr) { |
274 | return error; |
275 | } |
276 | if (proc_suser(current_proc()) != 0 && |
277 | !IOCurrentTaskHasEntitlement(ENTITLEMENT_TEST_CONTROL)) { |
278 | return EPERM; |
279 | } |
280 | if (value < 0 || value > UINT16_MAX) { |
281 | return EINVAL; |
282 | } |
283 | if (value == 0) { |
284 | /* |
285 | * Clear the current test port entries |
286 | */ |
287 | if (restricted_port_test != 0) { |
288 | for (i = 0; i < RPE_ENTRY_COUNT; i++) { |
289 | struct restricted_port_entry *rpe = &restricted_port_list[i]; |
290 | |
291 | if (rpe->rpe_entitlement == NULL) { |
292 | break; |
293 | } |
294 | if (!(rpe->rpe_flags & RPE_FLAG_TEST)) { |
295 | continue; |
296 | } |
297 | rpe->rpe_port = 0; |
298 | rpe->rpe_flags &= ~(RPE_FLAG_ENTITLEMENT | RPE_FLAG_SUPERUSER); |
299 | } |
300 | bitmap_clear(restricted_port_bitmap, restricted_port_test); |
301 | restricted_port_test = 0; |
302 | } |
303 | } else { |
304 | for (i = 0; i < RPE_ENTRY_COUNT; i++) { |
305 | struct restricted_port_entry *rpe = &restricted_port_list[i]; |
306 | |
307 | if (rpe->rpe_entitlement == NULL) { |
308 | break; |
309 | } |
310 | if (!(rpe->rpe_flags & RPE_FLAG_TEST)) { |
311 | continue; |
312 | } |
313 | rpe->rpe_port = (in_port_t)value; |
314 | if (test_superuser) { |
315 | rpe->rpe_flags |= RPE_FLAG_SUPERUSER; |
316 | rpe->rpe_flags &= ~RPE_FLAG_ENTITLEMENT; |
317 | } else { |
318 | rpe->rpe_flags |= RPE_FLAG_ENTITLEMENT; |
319 | rpe->rpe_flags &= ~RPE_FLAG_SUPERUSER; |
320 | } |
321 | } |
322 | restricted_port_test = (uint16_t)value; |
323 | bitmap_set(restricted_port_bitmap, restricted_port_test); |
324 | } |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | static int |
330 | sysctl_restricted_port_test_entitlement SYSCTL_HANDLER_ARGS |
331 | { |
332 | #pragma unused(arg1, arg2) |
333 | uint16_t old_value = restricted_port_test; |
334 | int error; |
335 | |
336 | error = sysctl_restricted_port_test_common(oidp, req, false); |
337 | if (error == 0) { |
338 | os_log(OS_LOG_DEFAULT, |
339 | "%s:%u sysctl net.restricted_port.test_entitlement: %u -> %u)" , |
340 | proc_best_name(current_proc()), proc_selfpid(), |
341 | old_value, restricted_port_test); |
342 | } |
343 | return error; |
344 | } |
345 | |
346 | static int |
347 | sysctl_restricted_port_test_superuser SYSCTL_HANDLER_ARGS |
348 | { |
349 | #pragma unused(arg1, arg2) |
350 | uint16_t old_value = restricted_port_test; |
351 | int error; |
352 | |
353 | error = sysctl_restricted_port_test_common(oidp, req, true); |
354 | if (error == 0) { |
355 | os_log(OS_LOG_DEFAULT, |
356 | "%s:%u sysctl net.restricted_port.test_superuser: %u -> %u)" , |
357 | proc_best_name(current_proc()), proc_selfpid(), |
358 | old_value, restricted_port_test); |
359 | } |
360 | return error; |
361 | } |
362 | |
363 | #endif /* (DEBUG || DEVELOPMENT) */ |
364 | |
365 | void |
366 | restricted_in_port_init(void) |
367 | { |
368 | unsigned int i; |
369 | |
370 | #if SKYWALK |
371 | _CASSERT(PORT_FLAGS_LISTENER == NETNS_LISTENER); |
372 | _CASSERT(PORT_FLAGS_SKYWALK == NETNS_SKYWALK); |
373 | _CASSERT(PORT_FLAGS_BSD == NETNS_BSD); |
374 | _CASSERT(PORT_FLAGS_PF == NETNS_PF); |
375 | _CASSERT(PORT_FLAGS_MAX == NETNS_OWNER_MAX); |
376 | #endif /* SKYWALK */ |
377 | |
378 | restricted_port_bitmap = bitmap_alloc(UINT16_MAX); |
379 | |
380 | if (restricted_port_bitmap == NULL) { |
381 | panic("restricted_port_init: bitmap allocation failed" ); |
382 | } |
383 | |
384 | for (i = 0; i < RPE_ENTRY_COUNT; i++) { |
385 | struct restricted_port_entry *rpe = &restricted_port_list[i]; |
386 | |
387 | if (rpe->rpe_entitlement == NULL) { |
388 | break; |
389 | } |
390 | if (rpe->rpe_port == 0) { |
391 | continue; |
392 | } |
393 | bitmap_set(map: restricted_port_bitmap, n: rpe->rpe_port); |
394 | } |
395 | } |
396 | |
397 | static const char * |
398 | port_flag_str(uint32_t port_flags) |
399 | { |
400 | switch (port_flags) { |
401 | case PORT_FLAGS_LISTENER: |
402 | return "listener" ; |
403 | #if SKYWALK |
404 | case PORT_FLAGS_SKYWALK: |
405 | return "skywalk" ; |
406 | #endif /* SKYWALK */ |
407 | case PORT_FLAGS_BSD: |
408 | return "bsd" ; |
409 | case PORT_FLAGS_PF: |
410 | return "pf" ; |
411 | default: |
412 | break; |
413 | } |
414 | return "?" ; |
415 | } |
416 | |
417 | /* |
418 | * The port is passed in network byte order |
419 | */ |
420 | bool |
421 | current_task_can_use_restricted_in_port(in_port_t port, uint8_t protocol, uint32_t port_flags) |
422 | { |
423 | unsigned int i; |
424 | struct proc *p = current_proc(); |
425 | pid_t pid = proc_pid(p); |
426 | |
427 | /* |
428 | * Quick check that does not take in account the protocol |
429 | */ |
430 | if (!IS_RESTRICTED_IN_PORT(port) || restricted_port_enforced == 0) { |
431 | if (restricted_port_verbose > 1) { |
432 | os_log(OS_LOG_DEFAULT, |
433 | "port %u for protocol %u via %s can be used by process %s:%u" , |
434 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid); |
435 | } |
436 | return true; |
437 | } |
438 | |
439 | for (i = 0; i < RPE_ENTRY_COUNT; i++) { |
440 | struct restricted_port_entry *rpe = &restricted_port_list[i]; |
441 | |
442 | if (rpe->rpe_entitlement == NULL) { |
443 | break; |
444 | } |
445 | if (rpe->rpe_port == 0) { |
446 | continue; |
447 | } |
448 | if ((protocol == IPPROTO_TCP && !(rpe->rpe_flags & RPE_FLAG_TCP)) || |
449 | (protocol == IPPROTO_UDP && !(rpe->rpe_flags & RPE_FLAG_UDP))) { |
450 | continue; |
451 | } |
452 | if (rpe->rpe_port != ntohs(port)) { |
453 | continue; |
454 | } |
455 | /* |
456 | * Found an entry in the list of restricted ports |
457 | * |
458 | * A process can use a restricted port if it meets at least one of |
459 | * the following conditions: |
460 | * - The process has the required entitlement |
461 | * - The port is marked as usable by root |
462 | */ |
463 | task_t task = current_task(); |
464 | if (rpe->rpe_flags & RPE_FLAG_SUPERUSER) { |
465 | if (task == kernel_task || proc_suser(p: current_proc()) == 0) { |
466 | os_log(OS_LOG_DEFAULT, |
467 | "root restricted port %u for protocol %u via %s can be used by superuser process %s:%u" , |
468 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid); |
469 | return true; |
470 | } |
471 | } |
472 | if (rpe->rpe_flags & RPE_FLAG_ENTITLEMENT) { |
473 | /* |
474 | * Do not let the kernel use the port because there is |
475 | * no entitlement for kernel extensions |
476 | */ |
477 | if (task == kernel_task) { |
478 | os_log(OS_LOG_DEFAULT, |
479 | "entitlement restricted port %u for protocol %u via %s cannot be used by kernel" , |
480 | ntohs(port), protocol, port_flag_str(port_flags)); |
481 | return false; |
482 | } |
483 | if (!IOCurrentTaskHasEntitlement(entitlement: rpe->rpe_entitlement)) { |
484 | os_log(OS_LOG_DEFAULT, |
485 | "entitlement restricted port %u for protocol %u via %s cannot be used by process %s:%u -- IOTaskHasEntitlement(%s) failed" , |
486 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid, rpe->rpe_entitlement); |
487 | return false; |
488 | } |
489 | os_log(OS_LOG_DEFAULT, |
490 | "entitlement restricted port %u for protocol %u via %s can be used by process %s:%u" , |
491 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid); |
492 | return true; |
493 | } |
494 | os_log(OS_LOG_DEFAULT, |
495 | "root restricted port %u for protocol %u via %s cannot be used by process %s:%u" , |
496 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid); |
497 | return false; |
498 | } |
499 | if (restricted_port_verbose > 1) { |
500 | os_log(OS_LOG_DEFAULT, |
501 | "port %u for protocol %u via %s can be used by process %s:%u" , |
502 | ntohs(port), protocol, port_flag_str(port_flags), proc_best_name(p), pid); |
503 | } |
504 | return true; |
505 | } |
506 | |