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