]> git.saurik.com Git - apple/security.git/blob - SecurityServer/session.cpp
Security-54.1.tar.gz
[apple/security.git] / SecurityServer / session.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // session - authentication session domains
21 //
22 // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
23 // hierarchical and inherited, so they work well for characterization of processes
24 // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
25 //
26 // Sessions are multi-threaded objects.
27 //
28 #include "session.h"
29 #include "connection.h"
30 #include "server.h"
31
32
33 //
34 // The static session map
35 //
36 Session::SessionMap Session::sessionMap;
37 Mutex Session::sessionMapLock;
38
39
40 //
41 // Create a Session object from initial parameters (create)
42 //
43 Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
44 : mBootstrap(bootstrap), mServicePort(servicePort),
45 mAttributes(attrs), mProcessCount(0), mAuthCount(0), mDying(false)
46 {
47 debug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
48 this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
49 }
50
51
52 void Session::release()
53 {
54 // nothing by default
55 }
56
57
58 //
59 // The root session inherits the startup bootstrap and service port
60 //
61 RootSession::RootSession(Port servicePort, SessionAttributeBits attrs)
62 : Session(Bootstrap(), servicePort, sessionIsRoot | sessionWasInitialized | attrs)
63 {
64 // self-install (no thread safety issues here)
65 sessionMap[mServicePort] = this;
66 }
67
68
69 //
70 // Dynamic sessions use the given bootstrap and re-register in it
71 //
72 DynamicSession::DynamicSession(const Bootstrap &bootstrap)
73 : ReceivePort(Server::active().bootstrapName(), bootstrap),
74 Session(bootstrap, *this)
75 {
76 // tell the server to listen to our port
77 Server::active().add(*this);
78
79 // register for port notifications
80 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
81 Server::active().notifyIfUnused(*this);
82
83 // self-register
84 StLock<Mutex> _(sessionMapLock);
85 sessionMap[*this] = this;
86 }
87
88 DynamicSession::~DynamicSession()
89 {
90 // remove our service port from the server
91 Server::active().remove(*this);
92
93 // if this is a (the) graphic login session, lock all databases
94 if (attribute(sessionHasGraphicAccess))
95 Database::lockAllDatabases();
96 }
97
98
99 void DynamicSession::release()
100 {
101 mBootstrap.destroy();
102 }
103
104
105 //
106 // Destroy a Session
107 //
108 Session::~Session()
109 {
110 assert(mProcessCount == 0); // can't die with processes still alive
111 debug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
112 this, handle(), mBootstrap.port());
113 }
114
115
116 //
117 // Locate a session object by service port or (Session API) identifier
118 //
119 Session &Session::find(Port servicePort)
120 {
121 StLock<Mutex> _(sessionMapLock);
122 SessionMap::const_iterator it = sessionMap.find(servicePort);
123 assert(it != sessionMap.end());
124 return *it->second;
125 }
126
127 Session &Session::find(SecuritySessionId id)
128 {
129 switch (id) {
130 case callerSecuritySession:
131 return Server::connection().process.session;
132 default:
133 return findHandle<Session>(id);
134 }
135 }
136
137
138 //
139 // Act on a death notification for a session's (sub)bootstrap port.
140 // We may not destroy the Session outright here (due to processes that use it),
141 // but we do clear out its accumulated wealth.
142 //
143 void Session::eliminate(Port servPort)
144 {
145 // remove session from session map
146 StLock<Mutex> _(sessionMapLock);
147 SessionMap::iterator it = sessionMap.find(servPort);
148 assert(it != sessionMap.end());
149 Session *session = it->second;
150 sessionMap.erase(it);
151
152 // destroy the session service port (this releases mach_init to proceed)
153 session->release();
154
155 // clear resources
156 if (session->clearResources())
157 delete session;
158 else
159 debug("SSsession", "session %p zombified for %d processes and %d auths",
160 session, int(session->mProcessCount), int(session->mAuthCount));
161 }
162
163 bool Session::clearResources()
164 {
165 StLock<Mutex> _(mLock);
166
167 // this session is now officially dying
168 mDying = true;
169
170 // invalidate shared credentials
171 {
172 StLock<Mutex> _(mCredsLock);
173
174 IFDEBUG(if (!mSessionCreds.empty())
175 debug("SSauth", "session %p clearing %d shared credentials",
176 this, int(mSessionCreds.size())));
177 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
178 (*it)->invalidate();
179 }
180 // let the caller know if we are ready to die NOW
181 return mProcessCount == 0 && mAuthCount == 0;
182 }
183
184
185 //
186 // Process management
187 //
188 void Session::addProcess(Process *)
189 {
190 StLock<Mutex> _(mLock);
191 mProcessCount++;
192 }
193
194 bool Session::removeProcess(Process *)
195 {
196 StLock<Mutex> _(mLock);
197 assert(mProcessCount > 0);
198 return --mProcessCount == 0 && mDying && mAuthCount == 0;
199 }
200
201
202 //
203 // Authorization retention management.
204 //
205 void Session::addAuthorization(AuthorizationToken *)
206 {
207 StLock<Mutex> _(mLock);
208 mAuthCount++;
209 }
210
211 bool Session::removeAuthorization(AuthorizationToken *)
212 {
213 StLock<Mutex> _(mLock);
214 assert(mAuthCount > 0);
215 return --mAuthCount == 0 && mDying && mProcessCount == 0;
216 }
217
218
219 //
220 // Authorization operations
221 //
222 OSStatus Session::authCreate(const RightSet &rights,
223 const AuthorizationEnvironment *environment,
224 AuthorizationFlags flags,
225 AuthorizationBlob &newHandle)
226 {
227 // invoke the authorization computation engine
228 CredentialSet resultCreds;
229
230 // this will acquire mLock, so we delay acquiring it
231 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds));
232
233 // Make a copy of the mSessionCreds
234 CredentialSet sessionCreds;
235 {
236 StLock<Mutex> _(mCredsLock);
237 sessionCreds = mSessionCreds;
238 }
239
240 OSStatus result = Server::authority().authorize(rights, environment, flags,
241 &sessionCreds, &resultCreds, NULL, *auth);
242 newHandle = auth->handle();
243
244 // merge resulting creds into shared pool
245 if ((flags & kAuthorizationFlagExtendRights) &&
246 !(flags & kAuthorizationFlagDestroyRights))
247 {
248 StLock<Mutex> _(mCredsLock);
249 mergeCredentials(resultCreds);
250 auth->mergeCredentials(resultCreds);
251 }
252
253 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
254 // not be destroyed anymore since it's destructor asserts it has no processes
255 Server::connection().process.addAuthorization(auth.get());
256 auth.release();
257 return result;
258 }
259
260 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
261 {
262 AuthorizationToken::Deleter deleter(authBlob);
263 AuthorizationToken &auth = deleter;
264
265 if (flags & kAuthorizationFlagDestroyRights) {
266 // explicitly invalidate all shared credentials and remove them from the session
267 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
268 if ((*it)->isShared())
269 (*it)->invalidate();
270 }
271
272 // now get rid of the authorization itself
273 if (Server::connection().process.removeAuthorization(&auth))
274 deleter.remove();
275 }
276
277 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
278 const RightSet &rights, const AuthorizationEnvironment *environment,
279 AuthorizationFlags flags,
280 MutableRightSet &grantedRights)
281 {
282 CredentialSet resultCreds;
283 AuthorizationToken &auth = authorization(authBlob);
284 CredentialSet effective;
285 {
286 StLock<Mutex> _(mCredsLock);
287 effective = auth.effectiveCreds();
288 }
289 OSStatus result = Server::authority().authorize(rights, environment, flags,
290 &effective, &resultCreds, &grantedRights, auth);
291
292 // merge resulting creds into shared pool
293 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
294 {
295 StLock<Mutex> _(mCredsLock);
296 mergeCredentials(resultCreds);
297 auth.mergeCredentials(resultCreds);
298 }
299
300 IFDEBUG(debug("SSauth", "Authorization %p copyRights asked for %d got %d",
301 &authorization(authBlob), int(rights.size()), int(grantedRights.size())));
302 return result;
303 }
304
305 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
306 const char *tag,
307 AuthorizationItemSet *&contextInfo)
308 {
309 StLock<Mutex> _(mLock);
310 AuthorizationToken &auth = authorization(authBlob);
311 debug("SSauth", "Authorization %p get-info", &auth);
312 if (tag) { // @@@ no tag support yet
313 return errAuthorizationInvalidTag;
314 } else { // return all tags
315 contextInfo = &auth.infoSet();
316 return noErr;
317 }
318 }
319
320 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
321 AuthorizationExternalForm &extForm)
322 {
323 StLock<Mutex> _(mLock);
324 const AuthorizationToken &auth = authorization(authBlob);
325 if (auth.mayExternalize(Server::connection().process)) {
326 memset(&extForm, 0, sizeof(extForm));
327 AuthorizationExternalBlob &extBlob =
328 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
329 extBlob.blob = auth.handle();
330 extBlob.session = bootstrapPort();
331 debug("SSauth", "Authorization %p externalized", &auth);
332 return noErr;
333 } else
334 return errAuthorizationExternalizeNotAllowed;
335 }
336
337 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
338 AuthorizationBlob &authBlob)
339 {
340 StLock<Mutex> _(mLock);
341
342 // interpret the external form
343 const AuthorizationExternalBlob &extBlob =
344 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
345
346 // locate source authorization
347 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
348
349 // check for permission and do it
350 if (sourceAuth.mayInternalize(Server::connection().process, true)) {
351 authBlob = extBlob.blob;
352 Server::connection().process.addAuthorization(&sourceAuth);
353 mAuthCount++;
354 debug("SSauth", "Authorization %p internalized", &sourceAuth);
355 return noErr;
356 } else
357 return errAuthorizationInternalizeNotAllowed;
358 }
359
360
361 //
362 // Set up a (new-ish) Session.
363 // This call must be made from a process within the session, and it must be the first
364 // such process to make the call.
365 //
366 void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs)
367 {
368 // check current process object - it may have been cached before the client's bootstrap switch
369 Process *process = &Server::connection().process;
370 #if 0
371 if (process->taskPort().bootstrap() != process->session.bootstrapPort())
372 process = Server::active().resetConnection();
373 #endif
374 process->session.setupAttributes(attrs);
375 }
376
377
378 void Session::setupAttributes(SessionAttributeBits attrs)
379 {
380 debug("SSsession", "%p setup attrs=0x%lx", this, attrs);
381 if (attrs & ~settableAttributes)
382 MacOSError::throwMe(errSessionInvalidAttributes);
383 if (attribute(sessionWasInitialized))
384 MacOSError::throwMe(errSessionAuthorizationDenied);
385 setAttributes(attrs | sessionWasInitialized);
386 }
387
388
389 //
390 // Merge a set of credentials into the shared-session credential pool
391 //
392 // must hold mCredsLock
393 void Session::mergeCredentials(CredentialSet &creds)
394 {
395 debug("SSsession", "%p merge creds @%p", this, &creds);
396 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
397 if (((*it)->isShared() && (*it)->isValid())) {
398 CredentialSet::iterator old = mSessionCreds.find(*it);
399 if (old == mSessionCreds.end()) {
400 mSessionCreds.insert(*it);
401 } else {
402 // replace "new" with "old" in input set to retain synchronization
403 (*old)->merge(**it);
404 creds.erase(it);
405 creds.insert(*old);
406 }
407 }
408 }
409
410
411 //
412 // Locate an AuthorizationToken given a blob
413 //
414 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
415 {
416 return AuthorizationToken::find(blob);
417 }