]> git.saurik.com Git - apple/security.git/blob - Security/authd/agent.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / authd / agent.c
1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */
2
3 #include "agent.h"
4 #include "authitems.h"
5 #include "authd_private.h"
6 #include "process.h"
7 #include "mechanism.h"
8 #include "authutilities.h"
9 #include "authtoken.h"
10 #include "session.h"
11 #include "debugging.h"
12 #include "engine.h"
13
14 #import <xpc/private.h>
15 #include <Security/AuthorizationPlugin.h>
16
17 #include <mach/mach.h>
18 #include <servers/bootstrap.h>
19 #include <bootstrap_priv.h>
20
21 #define SECURITYAGENT_BOOTSTRAP_NAME_BASE "com.apple.security.agentMain"
22 #define SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE "com.apple.security.agentStub"
23 //#define SECURITYAGENT_LOGINWINDOW_BOOTSTRAP_NAME_BASE "com.apple.security.agent.login"
24 #define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE "com.apple.security.authhost"
25
26 #define UUID_INITIALIZER_FROM_SESSIONID(sessionid) \
27 { 0,0,0,0, 0,0,0,0, 0,0,0,0, (unsigned char)((0xff000000 & (sessionid))>>24), (unsigned char)((0x00ff0000 & (sessionid))>>16), (unsigned char)((0x0000ff00 & (sessionid))>>8), (unsigned char)((0x000000ff & (sessionid))) }
28
29 struct _agent_s {
30 __AUTH_BASE_STRUCT_HEADER__;
31
32 auth_items_t hints;
33 auth_items_t context;
34 mechanism_t mech;
35 engine_t engine;
36 PluginState pluginState;
37 uint64_t status;
38
39 xpc_connection_t agentConnection;
40 xpc_connection_t agentStubConnection;
41 dispatch_queue_t eventQueue;
42 dispatch_queue_t actionQueue;
43
44 pid_t agentPid;
45 };
46
47 static void
48 _agent_finalize(CFTypeRef value)
49 {
50 agent_t agent = (agent_t)value;
51
52 // If this ever becomes a concurrent queue, then this would need to be a barrier sync
53 dispatch_sync(agent->eventQueue, ^{
54 // Mark the agent as dead
55 agent->pluginState = dead;
56 });
57
58 // We're going away, which means no outside references exist. It's safe to dispose of the XPC connection.
59 if (NULL != agent->agentConnection) {
60 xpc_release(agent->agentConnection);
61 agent->agentConnection = NULL;
62 }
63
64 if (NULL != agent->agentStubConnection) {
65 xpc_release(agent->agentStubConnection);
66 agent->agentStubConnection = NULL;
67 }
68
69 // Now that we've released any XPC connection that may (or may not) be present
70 // it's safe to go ahead and free our memory. This is provided that all other
71 // blocks that were added to the event queue before the axe came down on the
72 // xpc connection have been executed.
73
74 // If this ever becomes a concurrent queue, then this would need to be a barrier sync
75 dispatch_sync(agent->eventQueue, ^{
76 CFReleaseSafe(agent->hints);
77 CFReleaseSafe(agent->context);
78 CFReleaseSafe(agent->mech);
79 CFReleaseSafe(agent->engine);
80 dispatch_release(agent->actionQueue);
81 });
82
83 dispatch_release(agent->eventQueue);
84
85 LOGD("agent[%i]: _agent_finalize called", agent->agentPid);
86 }
87
88 AUTH_TYPE_INSTANCE(agent,
89 .init = NULL,
90 .copy = NULL,
91 .finalize = _agent_finalize,
92 .equal = NULL,
93 .hash = NULL,
94 .copyFormattingDesc = NULL,
95 .copyDebugDesc = NULL
96 );
97
98 static CFTypeID agent_get_type_id() {
99 static CFTypeID type_id = _kCFRuntimeNotATypeID;
100 static dispatch_once_t onceToken;
101
102 dispatch_once(&onceToken, ^{
103 type_id = _CFRuntimeRegisterClass(&_auth_type_agent);
104 });
105
106 return type_id;
107 }
108
109 agent_t
110 agent_create(engine_t engine, mechanism_t mech, auth_token_t auth, process_t proc, bool firstMech)
111 {
112 bool doSwitchAudit = false;
113 bool doSwitchBootstrap = false;
114 agent_t agent = NULL;
115
116 agent = (agent_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, agent_get_type_id(), AUTH_CLASS_SIZE(agent), NULL);
117 require(agent != NULL, done);
118
119 agent->mech = (mechanism_t)CFRetain(mech);
120 agent->engine = (engine_t)CFRetain(engine);
121 agent->hints = auth_items_create();
122 agent->context = auth_items_create();
123 agent->pluginState = init;
124 agent->agentPid = process_get_pid(proc);
125 agent->agentStubConnection = NULL;
126
127 const audit_info_s *audit_info = auth_token_get_audit_info(auth);
128
129 auditinfo_addr_t tempAddr;
130 tempAddr.ai_asid = audit_info->asid;
131 auditon(A_GETSINFO_ADDR, &tempAddr, sizeof(tempAddr));
132
133 LOGV("agent[%i]: Stored auid %d fetched auid %d", agent->agentPid, audit_info->auid, tempAddr.ai_auid);
134 uid_t auid = tempAddr.ai_auid;
135 uuid_t sessionUUID = UUID_INITIALIZER_FROM_SESSIONID((uint32_t)audit_info->asid);
136
137 agent->eventQueue = dispatch_queue_create("Agent Event Queue", 0);
138 agent->actionQueue = dispatch_queue_create("Agent Action Queue", 0);
139
140 mach_port_t bootstrapPort = process_get_bootstrap(proc);
141 if (!mechanism_is_privileged(mech)) {
142 if ((int32_t)auid != -1) {
143 agent->agentStubConnection = xpc_connection_create_mach_service(SECURITYAGENT_STUB_BOOTSTRAP_NAME_BASE, NULL, 0);
144 xpc_connection_set_target_uid(agent->agentStubConnection, auid);
145 LOGV("agent[%i]: Creating a security agent stub", agent->agentPid);
146 xpc_connection_set_event_handler(agent->agentStubConnection, ^(xpc_object_t object){}); // Yes, this is a dummy handler, we never ever care about any responses from the stub. It can die in a fire for all I care.
147 xpc_connection_resume(agent->agentStubConnection);
148
149 xpc_object_t wakeupMessage = xpc_dictionary_create(NULL, NULL, 0);
150 xpc_dictionary_set_data(wakeupMessage, AUTH_XPC_SESSION_UUID, sessionUUID, sizeof(uuid_t));
151 xpc_object_t responseMessage = xpc_connection_send_message_with_reply_sync(agent->agentStubConnection, wakeupMessage);
152 if (xpc_get_type(responseMessage) == XPC_TYPE_DICTIONARY) {
153 LOGV("agent[%i]: Valid response received from stub", agent->agentPid);
154 } else {
155 LOGV("agent[%i]: Error response received from stub", agent->agentPid);
156 }
157 xpc_release(wakeupMessage);
158 xpc_release(responseMessage);
159
160 mach_port_t newBootstrapPort = auth_token_get_creator_bootstrap(auth);
161 if (newBootstrapPort != MACH_PORT_NULL) {
162 bootstrapPort = newBootstrapPort;
163 }
164 }
165
166 agent->agentConnection = xpc_connection_create_mach_service(SECURITYAGENT_BOOTSTRAP_NAME_BASE, NULL,0);
167 xpc_connection_set_instance(agent->agentConnection, sessionUUID);
168 LOGV("agent[%i]: Creating a security agent", agent->agentPid);
169 doSwitchAudit = true;
170 doSwitchBootstrap = true;
171 } else {
172 agent->agentConnection = xpc_connection_create_mach_service(AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE, NULL, 0);
173 xpc_connection_set_instance(agent->agentConnection, sessionUUID);
174 LOGV("agent[%i]: Creating a standard authhost", agent->agentPid);
175 doSwitchAudit = true;
176 doSwitchBootstrap = true;
177 }
178
179 // **************** Here's the event handler, since I can never find it
180 xpc_connection_set_target_queue(agent->agentConnection, agent->eventQueue);
181 xpc_connection_set_event_handler(agent->agentConnection, ^(xpc_object_t object) {
182 char* objectDesc = xpc_copy_description(object);
183 LOGV("agent[%i]: global xpc message received %s", agent->agentPid, objectDesc);
184 free(objectDesc);
185
186 if (agent->pluginState == dead) {
187 // If the agent is dead for some reason, drop this message before we hurt ourselves.
188 return;
189 }
190
191 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
192 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
193 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_INTERRUPT)) {
194 agent->pluginState = interrupting;
195 engine_interrupt_agent(agent->engine);
196 }
197 }
198 });
199
200 xpc_connection_resume(agent->agentConnection);
201
202 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
203 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_CREATE);
204 xpc_dictionary_set_string(requestObject, AUTH_XPC_PLUGIN_NAME, mechanism_get_plugin(mech));
205 xpc_dictionary_set_string(requestObject, AUTH_XPC_MECHANISM_NAME, mechanism_get_param(mech));
206 if (doSwitchAudit) {
207 mach_port_name_t jobPort;
208 if (audit_info != NULL) {
209 if (0 == audit_session_port(audit_info->asid, &jobPort)) {
210 LOGV("agent[%i]: attaching an audit session port", agent->agentPid);
211 xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_AUDIT_SESSION_PORT, jobPort);
212
213 if (mach_port_mod_refs(mach_task_self(), jobPort, MACH_PORT_RIGHT_SEND, -1) != KERN_SUCCESS) {
214 LOGE("unable to release send right for audit session, leaking");
215 }
216 }
217 }
218 }
219
220 if (doSwitchBootstrap) {
221 LOGV("agent[%i]: attaching a bootstrap port", agent->agentPid);
222 xpc_dictionary_set_mach_send(requestObject, AUTH_XPC_BOOTSTRAP_PORT, bootstrapPort);
223 }
224
225 // This loop will be repeated until we can get ahold of a SecurityAgent, or get a fatal error
226 // This will cause us to retry any CONNECTION_INTERRUPTED status messages
227 do {
228 xpc_object_t object = xpc_connection_send_message_with_reply_sync(agent->agentConnection, requestObject);
229
230 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
231 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
232 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_CREATE)) {
233 agent->status = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
234 if (agent->status == kAuthorizationResultAllow) {
235 // Only go here if we have an "allow" from the SecurityAgent (the plugin create might have failed)
236 // This is backwards compatible so that it goes gently into the build, as the default is "allow".
237 agent->pluginState = created;
238 } else {
239 agent->pluginState = dead;
240 }
241 }
242 } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
243 if (object == XPC_ERROR_CONNECTION_INVALID) {
244 agent->pluginState = dead;
245 }
246 }
247 xpc_release(object);
248 } while ((agent->pluginState == init) && (firstMech));
249
250 xpc_release(requestObject);
251
252 if (agent->pluginState != created) {
253 CFRelease(agent);
254 agent = NULL;
255 }
256 done:
257 return agent;
258 }
259
260 uint64_t
261 agent_run(agent_t agent, auth_items_t hints, auth_items_t context, auth_items_t immutable_hints)
262 {
263 xpc_object_t hintsArray = auth_items_export_xpc(hints);
264 xpc_object_t contextArray = auth_items_export_xpc(context);
265 xpc_object_t immutableHintsArray = auth_items_export_xpc(immutable_hints);
266
267 dispatch_semaphore_t replyWaiter = dispatch_semaphore_create(0);
268 dispatch_sync(agent->actionQueue, ^{
269 if (agent->pluginState != mechinterrupting) {
270 agent->pluginState = current;
271 agent->status = kAuthorizationResultUndefined; // Set this while the plugin chews on the user input.
272
273 auth_items_clear(agent->hints);
274 auth_items_clear(agent->context);
275
276 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
277 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_INVOKE);
278 xpc_dictionary_set_value(requestObject, AUTH_XPC_HINTS_NAME, hintsArray);
279 xpc_dictionary_set_value(requestObject, AUTH_XPC_CONTEXT_NAME, contextArray);
280 xpc_dictionary_set_value(requestObject, AUTH_XPC_IMMUTABLE_HINTS_NAME, immutableHintsArray);
281
282 xpc_connection_send_message_with_reply(agent->agentConnection, requestObject, agent->actionQueue, ^(xpc_object_t object){
283 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
284 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
285 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_RESULT)) {
286 if (agent->pluginState == current) {
287 xpc_object_t xpcContext = xpc_dictionary_get_value(object, AUTH_XPC_CONTEXT_NAME);
288 xpc_object_t xpcHints = xpc_dictionary_get_value(object, AUTH_XPC_HINTS_NAME);
289 auth_items_copy_xpc(agent->context, xpcContext);
290 auth_items_copy_xpc(agent->hints, xpcHints);
291 agent->status = xpc_dictionary_get_uint64(object, AUTH_XPC_REPLY_RESULT_VALUE);
292 agent->pluginState = active;
293 }
294 }
295 } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
296 if ((object == XPC_ERROR_CONNECTION_INVALID) || (object == XPC_ERROR_CONNECTION_INTERRUPTED)) {
297 agent->pluginState = dead;
298 }
299 }
300 dispatch_semaphore_signal(replyWaiter);
301 });
302 xpc_release(requestObject);
303 }
304 });
305
306 if (agent->pluginState == current) {
307 dispatch_semaphore_wait(replyWaiter, DISPATCH_TIME_FOREVER);
308 }
309 dispatch_release(replyWaiter);
310
311 LOGV("agent[%i]: Finished call to SecurityAgent", agent->agentPid);
312
313 xpc_release(hintsArray);
314 xpc_release(contextArray);
315 xpc_release(immutableHintsArray);
316
317 return agent->status;
318 }
319
320 auth_items_t
321 agent_get_hints(agent_t agent)
322 {
323 return agent->hints;
324 }
325
326 auth_items_t
327 agent_get_context(agent_t agent)
328 {
329 return agent->context;
330 }
331
332 mechanism_t
333 agent_get_mechanism(agent_t agent)
334 {
335 return agent->mech;
336 }
337
338 void
339 agent_deactivate(agent_t agent)
340 {
341 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
342 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_DEACTIVATE);
343
344 agent->pluginState = deactivating;
345
346 xpc_object_t object = xpc_connection_send_message_with_reply_sync(agent->agentConnection, requestObject);
347 if (xpc_get_type(object) == XPC_TYPE_DICTIONARY) {
348 const char *replyType = xpc_dictionary_get_string(object, AUTH_XPC_REPLY_METHOD_KEY);
349 if (0 == strcmp(replyType, AUTH_XPC_REPLY_METHOD_DEACTIVATE)) {
350 agent->pluginState = active; // This is strange, but true. We do actually want to set the state 'active'.
351 }
352 } else if (xpc_get_type(object) == XPC_TYPE_ERROR) {
353 if ((object == XPC_ERROR_CONNECTION_INVALID) || (object == XPC_ERROR_CONNECTION_INTERRUPTED)) {
354 agent->pluginState = dead;
355 }
356 }
357 xpc_release(object);
358 xpc_release(requestObject);
359 }
360
361 void agent_destroy(agent_t agent)
362 {
363 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
364 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_DESTROY);
365 xpc_connection_send_message(agent->agentConnection, requestObject);
366 xpc_release(requestObject);
367 }
368
369 PluginState
370 agent_get_state(agent_t agent)
371 {
372 return agent->pluginState;
373 }
374
375 void
376 agent_notify_interrupt(agent_t agent)
377 {
378 dispatch_sync(agent->actionQueue, ^{
379 if (agent->pluginState == current) {
380 xpc_object_t requestObject = xpc_dictionary_create(NULL, NULL, 0);
381 xpc_dictionary_set_string(requestObject, AUTH_XPC_REQUEST_METHOD_KEY, AUTH_XPC_REQUEST_METHOD_INTERRUPT);
382 xpc_connection_send_message(agent->agentConnection, requestObject);
383 xpc_release(requestObject);
384 } else if (agent->pluginState == active) {
385 agent->pluginState = mechinterrupting;
386 }
387 });
388 }
389
390 void
391 agent_clear_interrupt(agent_t agent)
392 {
393 dispatch_sync(agent->actionQueue, ^{
394 if (agent->pluginState == mechinterrupting) {
395 agent->pluginState = active;
396 }
397 });
398 }
399
400 void
401 agent_recieve(agent_t agent)
402 {
403 }