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