]> git.saurik.com Git - apple/securityd.git/blob - src/session.cpp
securityd-36489.tar.gz
[apple/securityd.git] / src / session.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // session - authentication session domains
27 //
28 // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
29 // hierarchical and inherited, so they work well for characterization of processes
30 // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
31 //
32 // Sessions are multi-threaded objects.
33 //
34 #include <pwd.h>
35 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
36
37 #include "session.h"
38 #include "connection.h"
39 #include "database.h"
40 #include "server.h"
41
42 //
43 // The static session map
44 //
45 PortMap<Session> Session::mSessions;
46
47 std::string Session::kUsername = "username";
48 std::string Session::kRealname = "realname";
49
50 //
51 // Create a Session object from initial parameters (create)
52 //
53 Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
54 : mBootstrap(bootstrap), mServicePort(servicePort),
55 mAttributes(attrs), mSecurityAgent(NULL), mAuthHost(NULL)
56 {
57 secdebug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
58 this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
59 }
60
61
62 //
63 // Destroy a Session
64 //
65 Session::~Session()
66 {
67 secdebug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
68 this, handle(), mBootstrap.port());
69 }
70
71
72 //
73 // Locate a session object by service port or (Session API) identifier
74 //
75 Session &Session::find(Port servicePort)
76 {
77 StLock<Mutex> _(mSessions);
78 PortMap<Session>::const_iterator it = mSessions.find(servicePort);
79 assert(it != mSessions.end());
80 return *it->second;
81 }
82
83 Session &Session::find(SecuritySessionId id)
84 {
85 switch (id) {
86 case callerSecuritySession:
87 return Server::session();
88 default:
89 return HandleObject::find<Session>(id, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
90 }
91 }
92
93
94 //
95 // Act on a death notification for a session's (sub)bootstrap port.
96 // We may not destroy the Session outright here (due to processes that use it),
97 // but we do clear out its accumulated wealth.
98 //
99 void Session::destroy(Port servPort)
100 {
101 // remove session from session map
102 StLock<Mutex> _(mSessions);
103 PortMap<Session>::iterator it = mSessions.find(servPort);
104 assert(it != mSessions.end());
105 RefPointer<Session> session = it->second;
106 mSessions.erase(it);
107 session->kill();
108 }
109
110 void Session::kill()
111 {
112 StLock<Mutex> _(*this);
113
114 // release authorization host objects
115 {
116 StLock<Mutex> _(mAuthHostLock);
117 mSecurityAgent = NULL;
118 mAuthHost = NULL;
119 }
120
121 // invalidate shared credentials
122 {
123 StLock<Mutex> _(mCredsLock);
124
125 IFDEBUG(if (!mSessionCreds.empty())
126 secdebug("SSauth", "session %p clearing %d shared credentials",
127 this, int(mSessionCreds.size())));
128 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
129 (*it)->invalidate();
130 }
131
132 // base kill processing
133 PerSession::kill();
134 }
135
136
137 //
138 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
139 //
140 void Session::processSystemSleep()
141 {
142 StLock<Mutex> _(mSessions);
143 for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
144 it->second->allReferences(&DbCommon::sleepProcessing);
145 }
146
147
148 //
149 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
150 //
151 void Session::processLockAll()
152 {
153 allReferences(&DbCommon::lockProcessing);
154 }
155
156
157 //
158 // The root session inherits the startup bootstrap and service port
159 //
160 RootSession::RootSession(Server &server, SessionAttributeBits attrs)
161 : Session(Bootstrap(), server.primaryServicePort(),
162 sessionIsRoot | sessionWasInitialized | attrs)
163 {
164 parent(server); // the Server is our parent
165 ref(); // eternalize
166
167 // self-install (no thread safety issues here)
168 mSessions[mServicePort] = this;
169 }
170
171 //
172 // Dynamic sessions use the given bootstrap and re-register in it
173 //
174 DynamicSession::DynamicSession(TaskPort taskPort)
175 : ReceivePort(Server::active().bootstrapName(), taskPort.bootstrap(), false),
176 Session(taskPort.bootstrap(), *this),
177 mOriginatorTask(taskPort), mHaveOriginatorUid(false)
178 {
179 // link to Server as the global nexus in the object mesh
180 parent(Server::active());
181
182 // tell the server to listen to our port
183 Server::active().add(*this);
184
185 // register for port notifications
186 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
187 Server::active().notifyIfUnused(*this);
188
189 // self-register
190 StLock<Mutex> _(mSessions);
191 assert(!mSessions[*this]); // can't be registered already (we just made it)
192 mSessions[*this] = this;
193
194 secdebug("SSsession", "%p dynamic session originator=%d (pid=%d)",
195 this, mOriginatorTask.port(), taskPort.pid());
196 }
197
198 DynamicSession::~DynamicSession()
199 {
200 // remove our service port from the server
201 Server::active().remove(*this);
202 }
203
204
205 void DynamicSession::kill()
206 {
207 StLock<Mutex> _(*this);
208 mBootstrap.destroy(); // release our bootstrap port
209 Session::kill(); // continue with parent kill
210 }
211
212
213 //
214 // Set up a DynamicSession.
215 // This call must be made from a process within the session, and it must be the first
216 // such process to make the call.
217 //
218 void DynamicSession::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
219 {
220 StLock<Mutex> _(*this);
221 secdebug("SSsession", "%p setup flags=0x%lx attrs=0x%lx", this, flags, attrs);
222 if (attrs & ~settableAttributes)
223 MacOSError::throwMe(errSessionInvalidAttributes);
224 checkOriginator();
225 if (attribute(sessionWasInitialized))
226 MacOSError::throwMe(errSessionAuthorizationDenied);
227 setAttributes(attrs | sessionWasInitialized);
228 }
229
230
231 //
232 // Check whether the calling process is the session originator.
233 // If it's not, throw.
234 //
235 void DynamicSession::checkOriginator()
236 {
237 if (mOriginatorTask != Server::process().taskPort())
238 MacOSError::throwMe(errSessionAuthorizationDenied);
239 }
240
241
242 //
243 // The "originator uid" is a uid value that can be provided by the session originator
244 // and retrieved by anyone. Securityd places no semantic meaning on this value.
245 //
246 uid_t DynamicSession::originatorUid() const
247 {
248 if (mHaveOriginatorUid)
249 return mOriginatorUid;
250 else
251 MacOSError::throwMe(errSessionValueNotSet);
252 }
253
254
255 void DynamicSession::originatorUid(uid_t uid)
256 {
257 checkOriginator();
258 if (mHaveOriginatorUid) // must not re-set this
259 MacOSError::throwMe(errSessionAuthorizationDenied);
260 mHaveOriginatorUid = true;
261 mOriginatorUid = uid;
262
263 Server::active().longTermActivity();
264 struct passwd *pw = getpwuid(uid);
265
266 if (pw != NULL) {
267
268 mOriginatorCredential = Credential(uid, pw->pw_name ? pw->pw_name : "", pw->pw_gecos ? pw->pw_gecos : "", true/*shared*/);
269 endpwent();
270 }
271
272 secdebug("SSsession", "%p session uid set to %d", this, uid);
273 }
274
275 //
276 // Authorization operations
277 //
278 OSStatus Session::authCreate(const AuthItemSet &rights,
279 const AuthItemSet &environment,
280 AuthorizationFlags flags,
281 AuthorizationBlob &newHandle,
282 const audit_token_t &auditToken)
283 {
284 // invoke the authorization computation engine
285 CredentialSet resultCreds;
286
287 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
288 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
289
290 // Make a copy of the mSessionCreds
291 CredentialSet sessionCreds;
292 {
293 StLock<Mutex> _(mCredsLock);
294 sessionCreds = mSessionCreds;
295 }
296
297 AuthItemSet outRights;
298 OSStatus result = Server::authority().authorize(rights, environment, flags,
299 &sessionCreds, &resultCreds, outRights, *auth);
300 newHandle = auth->handle();
301
302 // merge resulting creds into shared pool
303 if ((flags & kAuthorizationFlagExtendRights) &&
304 !(flags & kAuthorizationFlagDestroyRights))
305 {
306 StLock<Mutex> _(mCredsLock);
307 mergeCredentials(resultCreds);
308 auth->mergeCredentials(resultCreds);
309 }
310
311 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
312 // not be destroyed anymore since it's destructor asserts it has no processes
313 Server::process().addAuthorization(auth.get());
314 auth.release();
315 return result;
316 }
317
318 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
319 {
320 AuthorizationToken::Deleter deleter(authBlob);
321 AuthorizationToken &auth = deleter;
322 Process &process = Server::process();
323 process.checkAuthorization(&auth);
324
325 if (flags & kAuthorizationFlagDestroyRights) {
326 // explicitly invalidate all shared credentials and remove them from the session
327 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
328 if ((*it)->isShared())
329 (*it)->invalidate();
330 }
331
332 // now get rid of the authorization itself
333 if (process.removeAuthorization(&auth))
334 deleter.remove();
335 }
336
337 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
338 const AuthItemSet &rights, const AuthItemSet &environment,
339 AuthorizationFlags flags,
340 AuthItemSet &grantedRights)
341 {
342 AuthorizationToken &auth = authorization(authBlob);
343 return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
344 }
345
346 OSStatus Session::authGetRights(AuthorizationToken &auth,
347 const AuthItemSet &rights, const AuthItemSet &environment,
348 AuthorizationFlags flags,
349 AuthItemSet &grantedRights)
350 {
351 CredentialSet resultCreds;
352 CredentialSet effective;
353 {
354 StLock<Mutex> _(mCredsLock);
355 effective = auth.effectiveCreds();
356 }
357 OSStatus result = Server::authority().authorize(rights, environment, flags,
358 &effective, &resultCreds, grantedRights, auth);
359
360 // merge resulting creds into shared pool
361 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
362 {
363 StLock<Mutex> _(mCredsLock);
364 mergeCredentials(resultCreds);
365 auth.mergeCredentials(resultCreds);
366 }
367
368 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
369 &auth, int(rights.size()), int(grantedRights.size()));
370 return result;
371 }
372
373 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
374 const char *tag,
375 AuthItemSet &contextInfo)
376 {
377 AuthorizationToken &auth = authorization(authBlob);
378 secdebug("SSauth", "Authorization %p get-info", &auth);
379 contextInfo = auth.infoSet(tag);
380 return noErr;
381 }
382
383 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
384 AuthorizationExternalForm &extForm)
385 {
386 const AuthorizationToken &auth = authorization(authBlob);
387 StLock<Mutex> _(*this);
388 if (auth.mayExternalize(Server::process())) {
389 memset(&extForm, 0, sizeof(extForm));
390 AuthorizationExternalBlob &extBlob =
391 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
392 extBlob.blob = auth.handle();
393 extBlob.session = bootstrapPort();
394 secdebug("SSauth", "Authorization %p externalized", &auth);
395 return noErr;
396 } else
397 return errAuthorizationExternalizeNotAllowed;
398 }
399
400 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
401 AuthorizationBlob &authBlob)
402 {
403 // interpret the external form
404 const AuthorizationExternalBlob &extBlob =
405 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
406
407 // locate source authorization
408 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
409
410 // check for permission and do it
411 if (sourceAuth.mayInternalize(Server::process(), true)) {
412 StLock<Mutex> _(*this);
413 authBlob = extBlob.blob;
414 Server::process().addAuthorization(&sourceAuth);
415 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
416 return noErr;
417 } else
418 return errAuthorizationInternalizeNotAllowed;
419 }
420
421
422 //
423 // The default session setup operation always fails.
424 // Subclasses can override this to support session setup calls.
425 //
426 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
427 {
428 MacOSError::throwMe(errSessionAuthorizationDenied);
429 }
430
431
432 //
433 // Authorization database I/O
434 //
435 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
436 {
437 string rightName(inRightName);
438 return Server::authority().getRule(rightName, rightDict);
439 }
440
441
442 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
443 {
444 CredentialSet resultCreds;
445 AuthorizationToken &auth = authorization(authBlob);
446 CredentialSet effective;
447
448 {
449 StLock<Mutex> _(mCredsLock);
450 effective = auth.effectiveCreds();
451 }
452
453 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
454
455 {
456 StLock<Mutex> _(mCredsLock);
457 mergeCredentials(resultCreds);
458 auth.mergeCredentials(resultCreds);
459 }
460
461 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%ld)",
462 &authorization(authBlob), inRightName, result);
463 return result;
464 }
465
466
467 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
468 {
469 CredentialSet resultCreds;
470 AuthorizationToken &auth = authorization(authBlob);
471 CredentialSet effective;
472
473 {
474 StLock<Mutex> _(mCredsLock);
475 effective = auth.effectiveCreds();
476 }
477
478 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
479
480 {
481 StLock<Mutex> _(mCredsLock);
482 mergeCredentials(resultCreds);
483 auth.mergeCredentials(resultCreds);
484 }
485
486 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%ld)",
487 &authorization(authBlob), inRightName, result);
488 return result;
489 }
490
491
492 //
493 // Merge a set of credentials into the shared-session credential pool
494 //
495 // must hold mCredsLock
496 void Session::mergeCredentials(CredentialSet &creds)
497 {
498 secdebug("SSsession", "%p merge creds @%p", this, &creds);
499 CredentialSet updatedCredentials = creds;
500 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
501 if ((*it)->isShared() && (*it)->isValid()) {
502 CredentialSet::iterator old = mSessionCreds.find(*it);
503 if (old == mSessionCreds.end()) {
504 mSessionCreds.insert(*it);
505 } else {
506 // replace "new" with "old" in input set to retain synchronization
507 (*old)->merge(**it);
508 updatedCredentials.erase(*it);
509 updatedCredentials.insert(*old);
510 }
511 }
512 creds.swap(updatedCredentials);
513 }
514
515
516 //
517 // Locate an AuthorizationToken given a blob
518 //
519 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
520 {
521 AuthorizationToken &auth = AuthorizationToken::find(blob);
522 Server::process().checkAuthorization(&auth);
523 return auth;
524 }
525
526 RefPointer<AuthHostInstance>
527 Session::authhost(const AuthHostType hostType, const bool restart)
528 {
529 StLock<Mutex> _(mAuthHostLock);
530
531 if (hostType == privilegedAuthHost)
532 {
533 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
534 {
535 if (mAuthHost)
536 PerSession::kill(*mAuthHost);
537 mAuthHost = new AuthHostInstance(*this, hostType);
538 }
539 return mAuthHost;
540 }
541 else /* if (hostType == securityAgent) */
542 {
543 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
544 {
545 if (mSecurityAgent)
546 PerSession::kill(*mSecurityAgent);
547 mSecurityAgent = new AuthHostInstance(*this, hostType);
548 }
549 return mSecurityAgent;
550 }
551 }
552
553 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
554 {
555 checkOriginator();
556 if (Server::process().uid() != 0)
557 MacOSError::throwMe(errSessionAuthorizationDenied);
558 StLock<Mutex> _(*this);
559 mSessionAgentPrefs = userPrefsDict;
560 }
561
562 CFDataRef DynamicSession::copyUserPrefs()
563 {
564 StLock<Mutex> _(*this);
565 if (mSessionAgentPrefs)
566 CFRetain(mSessionAgentPrefs);
567 return mSessionAgentPrefs;
568 }
569
570
571 //
572 // Debug dumping
573 //
574 #if defined(DEBUGDUMP)
575
576 void Session::dumpNode()
577 {
578 PerSession::dumpNode();
579 Debug::dump(" boot=%d service=%d attrs=0x%lx authhost=%p securityagent=%p",
580 mBootstrap.port(), mServicePort.port(), mAttributes, mAuthHost, mSecurityAgent);
581 }
582
583 #endif //DEBUGDUMP