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 */
56bitmap_t *restricted_port_bitmap = NULL;
57
58struct 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
73static 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
145SYSCTL_NODE(_net, OID_AUTO, restricted_port,
146 CTLFLAG_RW | CTLFLAG_LOCKED, 0, "restricted port");
147
148static int sysctl_restricted_port_bitmap SYSCTL_HANDLER_ARGS;
149static int sysctl_restricted_port_enforced SYSCTL_HANDLER_ARGS;
150static int sysctl_restricted_port_verbose SYSCTL_HANDLER_ARGS;
151
152SYSCTL_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)
161static int restricted_port_enforced = 1;
162SYSCTL_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) */
166const int restricted_port_enforced = 1;
167SYSCTL_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
172static int restricted_port_verbose = 0;
173SYSCTL_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 */
184static uint16_t restricted_port_test = 0;
185
186static int sysctl_restricted_port_test_entitlement SYSCTL_HANDLER_ARGS;
187static int sysctl_restricted_port_test_superuser SYSCTL_HANDLER_ARGS;
188
189SYSCTL_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
193SYSCTL_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
198static int
199sysctl_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
211static int
212sysctl_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
238static int
239sysctl_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
264static int
265sysctl_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
329static int
330sysctl_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
346static int
347sysctl_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
365void
366restricted_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
397static const char *
398port_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 */
420bool
421current_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