]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper-main.c
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / helper-main.c
1 /*
2 * Copyright (c) 2007-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define _FORTIFY_SOURCE 2
18
19 #include <CoreFoundation/CoreFoundation.h>
20 #include <sys/cdefs.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <servers/bootstrap.h>
27 #include <launch.h>
28 #include <pwd.h>
29 #include <pthread.h>
30 #include <stdarg.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <Security/Security.h>
37 #include "helper.h"
38 #include "helper-server.h"
39 #include <xpc/private.h>
40
41 #if TARGET_OS_IPHONE
42 #define NO_SECURITYFRAMEWORK 1
43 #endif
44
45 #ifndef LAUNCH_JOBKEY_MACHSERVICES
46 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
47 #define LAUNCH_DATA_MACHPORT 10
48 #define launch_data_get_machport launch_data_get_fd
49 #endif
50
51
52 int mDNSHelperLogEnabled = 0;
53 os_log_t log_handle = NULL;
54
55 static dispatch_queue_t xpc_queue = NULL;
56 static int opt_debug;
57 static pthread_t idletimer_thread;
58
59 unsigned long maxidle = 15;
60 unsigned long actualidle = 3600;
61
62 CFRunLoopRef gRunLoop = NULL;
63 CFRunLoopTimerRef gTimer = NULL;
64
65
66 static void handle_sigterm(int sig)
67 {
68 // os_log_debug(log_handle,"entry sig=%d", sig); Can't use syslog from within a signal handler
69 assert(sig == SIGTERM);
70 helper_exit();
71 }
72
73 static void initialize_logging(void)
74 {
75 log_handle = os_log_create("com.apple.mDNSResponderHelper", "INFO");
76
77 if (!log_handle)
78 {
79 // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category
80 os_log_error(OS_LOG_DEFAULT, "Could NOT create log handle in mDNSResponderHelper");
81 }
82
83 }
84
85 static void initialize_id(void)
86 {
87 static char login[] = "_mdnsresponder";
88 struct passwd hardcode;
89 struct passwd *pwd = &hardcode; // getpwnam(login);
90 hardcode.pw_uid = 65;
91 hardcode.pw_gid = 65;
92
93 if (!pwd)
94 {
95 os_log(log_handle, "Could not find account name `%s'. I will only help root.", login);
96 return;
97 }
98 mDNSResponderUID = pwd->pw_uid;
99 mDNSResponderGID = pwd->pw_gid;
100 }
101
102 static void diediedie(CFRunLoopTimerRef timer, void *context)
103 {
104 os_log_info(log_handle, "entry %p %p %lu", timer, context, actualidle);
105
106 assert(gTimer == timer);
107 os_log_info(log_handle, "mDNSResponderHelper exiting after [%lu] seconds", actualidle);
108
109 if (actualidle)
110 helper_exit();
111 }
112
113 void pause_idle_timer(void)
114 {
115 os_log_debug(log_handle,"entry");
116 assert(gTimer);
117 assert(gRunLoop);
118 CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
119 }
120
121 void unpause_idle_timer(void)
122 {
123 os_log_debug(log_handle,"entry");
124 assert(gRunLoop);
125 assert(gTimer);
126 CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
127 }
128
129 void update_idle_timer(void)
130 {
131 os_log_debug(log_handle,"entry");
132 assert(gTimer);
133 CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle);
134 }
135
136 static void *idletimer(void *context)
137 {
138 os_log_debug(log_handle,"entry context=%p", context);
139 gRunLoop = CFRunLoopGetMain();
140
141 unpause_idle_timer();
142
143 for (;;)
144 {
145 // os_log_debug(log_handle,"Running CFRunLoop");
146 CFRunLoopRun();
147 sleep(1);
148 }
149
150 return NULL;
151 }
152
153 static int initialize_timer()
154 {
155 gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL);
156 int err = 0;
157 os_log_info(log_handle, "mDNSResponderHelper initialize_timer() started");
158
159 if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL)))
160 os_log(log_handle, "Could not start idletimer thread: %s", strerror(err));
161
162 return err;
163 }
164
165 /*
166 Reads the user's program arguments for mDNSResponderHelper
167 For now we have only one option: mDNSHelperDebugLogging which is used to turn on mDNSResponderHelperLogging
168
169 To turn ON mDNSResponderHelper Verbose Logging,
170 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponderHelper.plist mDNSHelperDebugLogging -bool YES
171 2] sudo reboot
172
173 To turn OFF mDNSResponderHelper Logging,
174 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponderHelper.plist
175
176 To view the current options set,
177 1] plutil -p /Library/Preferences/com.apple.mDNSResponderHelper.plist
178 OR
179 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponderHelper.plist
180 */
181
182 static mDNSBool HelperPrefsGetValueBool(CFStringRef key, mDNSBool defaultVal)
183 {
184 CFBooleanRef boolean;
185 mDNSBool result = defaultVal;
186
187 boolean = CFPreferencesCopyAppValue(key, kmDNSHelperProgramArgs);
188 if (boolean != NULL)
189 {
190 if (CFGetTypeID(boolean) == CFBooleanGetTypeID())
191 result = CFBooleanGetValue(boolean) ? mDNStrue : mDNSfalse;
192 CFRelease(boolean);
193 }
194
195 return result;
196 }
197
198
199 // Verify Client's Entitlement
200 static mDNSBool check_entitlement(xpc_connection_t conn, const char *password)
201 {
202 mDNSBool entitled = mDNSfalse;
203 xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password);
204
205 if (ent)
206 {
207 if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent))
208 {
209 entitled = mDNStrue;
210 }
211 xpc_release(ent);
212 }
213 else
214 {
215 os_log(log_handle, "client entitlement is NULL");
216 }
217
218 if (!entitled)
219 os_log(log_handle, "entitlement check failed -> client is missing entitlement!");
220
221 return entitled;
222 }
223
224
225 static void handle_request(xpc_object_t req)
226 {
227 mDNSu32 helper_mode = 0;
228 int error_code = 0;
229
230 xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req);
231 xpc_object_t response = xpc_dictionary_create_reply(req);
232
233 // switch here based on dictionary to handle different requests from mDNSResponder
234 if ((xpc_dictionary_get_uint64(req, kHelperMode)))
235 {
236 os_log_info(log_handle, "Getting mDNSResponder request mode");
237 helper_mode = (mDNSu32)(xpc_dictionary_get_uint64(req, kHelperMode));
238 }
239
240 switch (helper_mode)
241 {
242 case bpf_request:
243 {
244 os_log_info(log_handle, "Calling new RequestBPF()");
245 RequestBPF();
246 break;
247 }
248
249 case set_name:
250 {
251 const char *old_name;
252 const char *new_name;
253 int pref_key = 0;
254
255 pref_key = (int)(xpc_dictionary_get_uint64(req, kPrefsNameKey));
256 old_name = xpc_dictionary_get_string(req, kPrefsOldName);
257 new_name = xpc_dictionary_get_string(req, kPrefsNewName);
258
259 os_log_info(log_handle, "Calling new SetName() oldname: %s newname: %s key:%d", old_name, new_name, pref_key);
260 PreferencesSetName(pref_key, old_name, new_name);
261 break;
262 }
263
264 case p2p_packetfilter:
265 {
266 size_t count = 0;
267 pfArray_t pfports;
268 pfArray_t pfprotocols;
269 const char *if_name;
270 uint32_t cmd;
271 xpc_object_t xpc_obj_port_array;
272 size_t port_array_count = 0;
273 xpc_object_t xpc_obj_protocol_array;
274 size_t protocol_array_count = 0;
275
276 cmd = xpc_dictionary_get_uint64(req, "pf_opcode");
277 if_name = xpc_dictionary_get_string(req, "pf_ifname");
278 xpc_obj_port_array = xpc_dictionary_get_value(req, "xpc_obj_array_port");
279 if ((void *)xpc_obj_port_array != NULL)
280 port_array_count = xpc_array_get_count(xpc_obj_port_array);
281 xpc_obj_protocol_array = xpc_dictionary_get_value(req, "xpc_obj_array_protocol");
282 if ((void *)xpc_obj_protocol_array != NULL)
283 protocol_array_count = xpc_array_get_count(xpc_obj_protocol_array);
284 if (port_array_count != protocol_array_count)
285 break;
286 if (port_array_count > PFPortArraySize)
287 break;
288 count = port_array_count;
289
290 for (size_t i = 0; i < count; i++) {
291 pfports[i] = (uint16_t)xpc_array_get_uint64(xpc_obj_port_array, i);
292 pfprotocols[i] = (uint16_t)xpc_array_get_uint64(xpc_obj_protocol_array, i);
293 }
294
295 os_log_info(log_handle,"Calling new PacketFilterControl()");
296 PacketFilterControl(cmd, if_name, count, pfports, pfprotocols);
297 break;
298 }
299
300 case user_notify:
301 {
302 const char *title;
303 const char *msg;
304 title = xpc_dictionary_get_string(req, "notify_title");
305 msg = xpc_dictionary_get_string(req, "notify_msg");
306
307 os_log_info(log_handle,"Calling new UserNotify() title:%s msg:%s", title, msg);
308 UserNotify(title, msg);
309 break;
310 }
311
312 case power_req:
313 {
314 int key, interval;
315 key = xpc_dictionary_get_uint64(req, "powerreq_key");
316 interval = xpc_dictionary_get_uint64(req, "powerreq_interval");
317
318 os_log_info(log_handle,"Calling new PowerRequest() key[%d] interval[%d]", key, interval);
319 PowerRequest(key, interval, &error_code);
320 break;
321 }
322
323 case send_wakepkt:
324 {
325 const char *ether_addr;
326 const char *ip_addr;
327 int iteration;
328 unsigned int if_id;
329
330 if_id = (unsigned int)xpc_dictionary_get_uint64(req, "interface_index");
331 ether_addr = xpc_dictionary_get_string(req, "ethernet_address");
332 ip_addr = xpc_dictionary_get_string(req, "ip_address");
333 iteration = (int)xpc_dictionary_get_uint64(req, "swp_iteration");
334
335 os_log_info(log_handle, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]",
336 ether_addr, ip_addr, if_id, iteration);
337 SendWakeupPacket(if_id, ether_addr, ip_addr, iteration);
338 break;
339 }
340
341 case set_localaddr_cacheentry:
342 {
343 int if_index, family;
344 size_t ip_len, eth_len;
345
346 if_index = xpc_dictionary_get_uint64(req, "slace_ifindex");
347 family = xpc_dictionary_get_uint64(req, "slace_family");
348
349 const uint8_t * const ip = (const uint8_t *)xpc_dictionary_get_data(req, "slace_ip", &ip_len);
350 if (ip_len != sizeof(v6addr_t))
351 {
352 error_code = kHelperErr_ParamErr;
353 break;
354 }
355
356 const uint8_t * const eth = (const uint8_t *)xpc_dictionary_get_data(req, "slace_eth", &eth_len);
357 if (eth_len != sizeof(ethaddr_t))
358 {
359 error_code = kHelperErr_ParamErr;
360 break;
361 }
362
363 os_log_info(log_handle, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index, family);
364
365 SetLocalAddressCacheEntry(if_index, family, ip, eth, &error_code);
366 break;
367 }
368
369 case send_keepalive:
370 {
371 uint16_t lport, rport, win;
372 uint32_t seq, ack;
373 size_t sadd6_len, dadd6_len;
374
375 lport = xpc_dictionary_get_uint64(req, "send_keepalive_lport");
376 rport = xpc_dictionary_get_uint64(req, "send_keepalive_rport");
377 seq = xpc_dictionary_get_uint64(req, "send_keepalive_seq");
378 ack = xpc_dictionary_get_uint64(req, "send_keepalive_ack");
379 win = xpc_dictionary_get_uint64(req, "send_keepalive_win");
380
381 const uint8_t * const sadd6 = (const uint8_t *)xpc_dictionary_get_data(req, "send_keepalive_sadd", &sadd6_len);
382 const uint8_t * const dadd6 = (const uint8_t *)xpc_dictionary_get_data(req, "send_keepalive_dadd", &dadd6_len);
383 if ((sadd6_len != sizeof(v6addr_t)) || (dadd6_len != sizeof(v6addr_t)))
384 {
385 error_code = kHelperErr_ParamErr;
386 break;
387 }
388
389 os_log_info(log_handle, "helper-main: handle_request: send_keepalive: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]",
390 lport, rport, seq, ack, win);
391
392 SendKeepalive(sadd6, dadd6, lport, rport, seq, ack, win);
393 break;
394 }
395
396 case retreive_tcpinfo:
397 {
398 uint16_t lport, rport;
399 int family;
400 uint32_t seq, ack;
401 uint16_t win;
402 int32_t intfid;
403 size_t laddr_len, raddr_len;
404
405 lport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_lport");
406 rport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_rport");
407 family = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_family");
408
409 const uint8_t * const laddr = (const uint8_t *)xpc_dictionary_get_data(req, "retreive_tcpinfo_laddr", &laddr_len);
410 const uint8_t * const raddr = (const uint8_t *)xpc_dictionary_get_data(req, "retreive_tcpinfo_raddr", &raddr_len);
411 if ((laddr_len != sizeof(v6addr_t)) || (raddr_len != sizeof(v6addr_t)))
412 {
413 error_code = kHelperErr_ParamErr;
414 break;
415 }
416
417 os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]",
418 lport, rport, family);
419
420 RetrieveTCPInfo(family, laddr, lport, raddr, rport, &seq, &ack, &win, &intfid, &error_code);
421
422 if (response)
423 {
424 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_seq", seq);
425 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ack", ack);
426 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_win", win);
427 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ifid", intfid);
428 }
429
430 os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]",
431 seq, ack, win, intfid);
432
433 break;
434 }
435
436 case keychain_getsecrets:
437 {
438 unsigned int num_sec = 0;
439 unsigned long secrets = 0;
440 unsigned int sec_cnt = 0;
441
442 os_log_info(log_handle,"Calling new KeyChainGetSecrets()");
443
444 KeychainGetSecrets(&num_sec, &secrets, &sec_cnt, &error_code);
445
446 if (response)
447 {
448 xpc_dictionary_set_uint64(response, "keychain_num_secrets", num_sec);
449 xpc_dictionary_set_data(response, "keychain_secrets", (void *)secrets, sec_cnt);
450 }
451
452 os_log_info(log_handle,"helper-main: handle_request: keychain_getsecrets: num_secrets is %u, secrets is %lu, secrets_Cnt is %u",
453 num_sec, secrets, sec_cnt);
454
455 if (secrets)
456 vm_deallocate(mach_task_self(), secrets, sec_cnt);
457
458 break;
459 }
460
461 default:
462 {
463 os_log(log_handle, "handle_request: Unrecognized mode!");
464 error_code = kHelperErr_UndefinedMode;
465 break;
466 }
467 }
468
469 // Return Response Status back to the client (essentially ACKing the request)
470 if (response)
471 {
472 xpc_dictionary_set_uint64(response, kHelperReplyStatus, kHelperReply_ACK);
473 xpc_dictionary_set_int64(response, kHelperErrCode, error_code);
474 xpc_connection_send_message(remote_conn, response);
475 xpc_release(response);
476 }
477 else
478 {
479 os_log(log_handle, "handle_requests: Response Dictionary could not be created!");
480 return;
481 }
482
483 }
484
485 static void accept_client(xpc_connection_t conn)
486 {
487 int c_pid = xpc_connection_get_pid(conn);
488
489 if (!(check_entitlement(conn, kHelperService)))
490 {
491 os_log(log_handle, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid);
492 xpc_connection_cancel(conn);
493 return;
494 }
495
496 xpc_retain(conn);
497 xpc_connection_set_target_queue(conn, xpc_queue);
498 xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg)
499 {
500 xpc_type_t type = xpc_get_type(req_msg);
501
502 if (type == XPC_TYPE_DICTIONARY)
503 {
504 os_log_info(log_handle,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn, c_pid);
505 handle_request(req_msg);
506 }
507 else // We hit this case ONLY if Client Terminated Connection OR Crashed
508 {
509 os_log(log_handle, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn, c_pid);
510 // handle_termination();
511 xpc_release(conn);
512 }
513 });
514
515 xpc_connection_resume(conn);
516 }
517
518
519 static void init_helper_service(const char *service_name)
520 {
521
522 xpc_connection_t xpc_listener = xpc_connection_create_mach_service(service_name, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
523 if (!xpc_listener || xpc_get_type(xpc_listener) != XPC_TYPE_CONNECTION)
524 {
525 os_log(log_handle, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!");
526 return;
527 }
528
529 os_log_info(log_handle,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening");
530
531 xpc_queue = dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL);
532
533 xpc_connection_set_event_handler(xpc_listener, ^(xpc_object_t eventmsg)
534 {
535 xpc_type_t type = xpc_get_type(eventmsg);
536
537 if (type == XPC_TYPE_CONNECTION)
538 {
539 os_log_info(log_handle,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg);
540 accept_client(eventmsg);
541 }
542 else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases below
543 {
544 os_log(log_handle, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
545 return;
546 }
547 else
548 {
549 os_log(log_handle, "init_helper_service: Unknown EventMsg type");
550 return;
551 }
552 });
553
554 xpc_connection_resume(xpc_listener);
555 }
556
557
558 int main(int ac, char *av[])
559 {
560 char *p = NULL;
561 long n;
562 int ch;
563
564 while ((ch = getopt(ac, av, "dt:")) != -1)
565 {
566 switch (ch)
567 {
568 case 'd':
569 opt_debug = 1;
570 break;
571 case 't':
572 n = strtol(optarg, &p, 0);
573 if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0)
574 {
575 fprintf(stderr, "Invalid idle timeout: %s\n", optarg);
576 exit(EXIT_FAILURE);
577 }
578 maxidle = n;
579 break;
580 case '?':
581 default:
582 fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
583 exit(EXIT_FAILURE);
584 }
585 }
586 ac -= optind;
587 av += optind;
588 (void)ac; // Unused
589 (void)av; // Unused
590
591 initialize_logging();
592 initialize_id();
593
594 mDNSHelperLogEnabled = HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog, mDNSHelperLogEnabled);
595
596 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
597 // Hence mDNSHelperLogEnabled is set to true below by default.
598 mDNSHelperLogEnabled = 1;
599
600 os_log_info(log_handle,"mDNSResponderHelper Starting to run");
601
602 #ifndef NO_SECURITYFRAMEWORK
603 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
604 // Explicitly ensure that our Keychain operations utilize the system domain.
605 if (opt_debug)
606 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
607 #endif
608
609 if (maxidle)
610 actualidle = maxidle;
611
612 signal(SIGTERM, handle_sigterm);
613
614 if (initialize_timer())
615 exit(EXIT_FAILURE);
616 for (n=0; n<100000; n++)
617 if (!gRunLoop)
618 usleep(100);
619
620 if (!gRunLoop)
621 {
622 os_log(log_handle, "gRunLoop not set after waiting");
623 exit(EXIT_FAILURE);
624 }
625
626 init_helper_service(kHelperService);
627 os_log_info(log_handle,"mDNSResponderHelper is now running");
628 dispatch_main();
629
630 }
631
632 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
633 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
634 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
635 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
636 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
637
638 // For convenience when using the "strings" command, this is the last thing in the file
639 // The "@(#) " pattern is a special prefix the "what" command looks for
640 const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
641
642 #if _BUILDING_XCODE_PROJECT_
643 // If the process crashes, then this string will be magically included in the automatically-generated crash log
644 const char *__crashreporter_info__ = VersionString_SCCS + 5;
645 asm (".desc ___crashreporter_info__, 0x10");
646 #endif