Imported Upstream version 878.70.2
[platform/upstream/mdnsresponder.git] / mDNSMacOSX / Private / xpc_services.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
4  *
5  * xpc_services.c
6  * mDNSResponder 
7  *
8  * XPC as an IPC mechanism to communicate with Clients. Open only to Apple OSX/iOS clients
9  */
10
11 #include "xpc_services.h"
12 #include "dns_xpc.h"
13
14 #ifndef UNICAST_DISABLED
15
16 #include "dnsproxy.h"        // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback
17 #include "mDNSMacOSX.h"      // KQueueLock/KQueueUnlock
18 #include <xpc/private.h>     // xpc_connection_copy_entitlement_value
19
20 // ***************************************************************************
21 // Globals
22 extern mDNS mDNSStorage;
23 static int dps_client_pid; // To track current active client using DNS Proxy Service
24 static dispatch_queue_t dps_queue = NULL;
25 // ***************************************************************************
26
27 // prints current XPC Server State
28 mDNSexport void xpcserver_info(mDNS *const m)
29 {
30
31     LogMsg("----- Active XPC Clients -----");
32     if (dps_client_pid)
33        LogMsg("DNSProxy->Client[%d]: InputIntfs[%d, %d, %d, %d, %d] Output[%d]", dps_client_pid, m->dp_ipintf[0],
34                 m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); 
35 }
36
37
38 mDNSlocal void ActivateDNSProxy(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, mDNSBool proxy_off)
39 {
40
41     LogInfo("ActivateDNSProxy: InterfaceIndex List by Client: Input[%d, %d, %d, %d, %d] Output[%d] ", IpIfArr[0], IpIfArr[1],
42              IpIfArr[2], IpIfArr[3], IpIfArr[4], OpIf);
43  
44     KQueueLock();
45     DNSProxyInit(IpIfArr, OpIf);
46     if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts
47         mDNSPlatformInitDNSProxySkts(ProxyUDPCallback, ProxyTCPCallback);
48     KQueueUnlock("DNSProxy Activated");
49 }
50
51 mDNSlocal void handle_dps_terminate()
52 {
53
54     LogInfo("handle_dps_terminate: Client PID[%d] terminated connection or crashed. Proceed to terminate DNSProxy", dps_client_pid);
55     // Clear the Client's PID, so that we can now accept new DPS requests
56     dps_client_pid = 0;
57
58     KQueueLock();
59     mDNSPlatformCloseDNSProxySkts(&mDNSStorage);
60     // TBD: Close TCP Sockets
61     DNSProxyTerminate();
62     KQueueUnlock("DNSProxy Deactivated");
63 }
64
65 mDNSlocal void handle_dps_request(xpc_object_t req)
66 {
67     int dps_tmp_client;
68     mDNSBool proxy_off = mDNSfalse;
69     xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req);
70     dps_tmp_client = (int) xpc_connection_get_pid(remote_conn);
71
72     LogInfo("handle_dps_request: Handler for DNS Proxy Requests");
73
74     if (dps_client_pid <= 0)
75     {
76         LogInfo("handle_dps_request: DNSProxy is not engaged (New Client)");
77         // No Active Client, save new Client's PID (also indicates DNS Proxy was OFF)
78         dps_client_pid = dps_tmp_client;
79         proxy_off = mDNStrue;        
80     }
81     else
82     {
83         // We already have an active DNS Proxy Client and until that client does not terminate the connection
84         // or crashes, a new client cannot change/override the current DNS Proxy settings.
85         if (dps_client_pid != dps_tmp_client)
86         {
87             LogMsg("handle_dps_request: A Client is already using DNS Proxy and your request cannot be handled at this time");
88             // Return Engaged Status to the client
89             xpc_object_t reply = xpc_dictionary_create_reply(req); 
90             if (reply)
91             {   
92                 xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSMsg_Busy);
93                 xpc_connection_send_message(remote_conn, reply);
94                 xpc_release(reply);  
95             }   
96             else
97             {   
98                 LogMsg("handle_dps_request: Reply Dictionary could not be created");
99                 return;
100             }
101             // We do not really need to terminate the connection with the client
102             // as it may try again later which is fine
103             return;   
104         }
105     }
106  
107  
108     xpc_object_t response = xpc_dictionary_create_reply(req);
109     // Return Success Status to the client
110     if (response)
111     {
112         xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsg_NoError);
113         xpc_connection_send_message(remote_conn, response);
114         xpc_release(response);  
115     }
116     else
117     { 
118         LogMsg("handle_dps_request: Response Dictionary could not be created");
119         return;
120     }
121
122     // Proceed to get DNS Proxy Settings from the Client
123     if (xpc_dictionary_get_uint64(req, kDNSProxyParameters))
124     {
125         mDNSu32 inIf[MaxIp], outIf;
126         
127         inIf[0]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex0);
128         inIf[1]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex1);
129         inIf[2]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex2);
130         inIf[3]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex3);
131         inIf[4]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex4);
132         outIf     = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSOutIfindex);
133         
134         ActivateDNSProxy(inIf, outIf, proxy_off);
135     }
136     
137 }
138
139 // Verify Client's Entitlement
140 mDNSexport mDNSBool IsEntitled(xpc_connection_t conn, const char *password)
141 {
142     mDNSBool entitled = mDNSfalse;
143     xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password);
144
145     if (ent)
146     {
147         if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent))
148         {
149             entitled = mDNStrue;
150         }
151         xpc_release(ent);
152     }
153     else
154     {
155         LogMsg("IsEntitled: Client Entitlement is NULL");
156     }
157     
158     if (!entitled)
159         LogMsg("IsEntitled: Client is missing Entitlement!");
160     
161     return entitled;
162 }
163
164 mDNSlocal void accept_dps_client(xpc_connection_t conn)
165 {
166     uid_t c_euid;
167     int   c_pid;
168     c_euid  = xpc_connection_get_euid(conn);
169     c_pid   = xpc_connection_get_pid(conn);
170
171     if (c_euid != 0 || !IsEntitled(conn, kDNSProxyService))
172     {   
173         LogMsg("accept_dps_client: DNSProxyService Client PID[%d] is missing Entitlement or is not running as root!", c_pid);
174         xpc_connection_cancel(conn);
175         return; 
176     }
177     
178     xpc_retain(conn);
179     xpc_connection_set_target_queue(conn, dps_queue);
180     xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg)
181         {
182             xpc_type_t type = xpc_get_type(req_msg);
183
184             if (type == XPC_TYPE_DICTIONARY)
185             {
186                 handle_dps_request(req_msg);
187             }
188             else // We hit this case ONLY if Client Terminated DPS Connection OR Crashed
189             {
190                 LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn);
191                 // Only the Client that has activated DPS should be able to terminate it
192                 if (c_pid == dps_client_pid)
193                     handle_dps_terminate(); 
194                 xpc_release(conn);
195             }
196         });
197     
198     xpc_connection_resume(conn);
199                 
200 }
201
202 mDNSlocal void init_dnsproxy_service(void)
203 {
204     
205     xpc_connection_t dps_listener = xpc_connection_create_mach_service(kDNSProxyService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
206     if (!dps_listener || xpc_get_type(dps_listener) != XPC_TYPE_CONNECTION)
207     {
208         LogMsg("init_dnsproxy_service: Error Creating XPC Listener for DNSProxyService !!");
209         return;
210     }
211     
212     dps_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsproxyservice_queue", NULL);
213
214     xpc_connection_set_event_handler(dps_listener, ^(xpc_object_t eventmsg)
215         {
216             xpc_type_t type = xpc_get_type(eventmsg);
217
218             if (type == XPC_TYPE_CONNECTION)
219             {
220                 LogInfo("init_dnsproxy_service: New DNSProxyService Client %p", eventmsg);
221                 accept_dps_client(eventmsg);
222             }
223             else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases
224             {
225                 LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
226                 return;
227             }
228             else
229             {
230                 LogMsg("init_dnsproxy_service: Unknown EventMsg type");
231                 return;
232             }
233         });
234     
235     xpc_connection_resume(dps_listener);
236
237 }
238
239 mDNSexport void xpc_server_init()
240 {
241     // Add XPC Services here
242     init_dnsproxy_service();
243     init_dnsctl_service();
244 }
245
246
247 #else // !UNICAST_DISABLED
248
249 mDNSexport void xpc_server_init()
250 {
251     return;
252 }
253
254 mDNSexport void xpcserver_info(mDNS *const m)
255 {
256     (void) m;
257     
258     return;
259 }
260
261 #endif // !UNICAST_DISABLED
262