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