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