1/* 2 * Copyright (c) 2000-2001,2011-2014 Apple 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// cssmclient - common client interface to CSSM and MDS. 21// 22// Locking Strategy (preliminary): 23// XXX This is obsolete update this --mb 24// A CssmObject is a CountingMutex. Its count represents the number of children that have registered 25// themselves (using addChild/removeChild). The lock controls the internal management fields of the 26// various subclasses to protect them against corruption. It does NOT control attribute and argument 27// fields and operations, not does it control object-constant fields. 28// This means that if you use an object from multiple threads, you (the caller) must lock the object 29// during set/get calls of attributes. Note that the CSSM operations themselves are safely multithreaded 30// and thus don't need to be interlocked explicitly. 31// 32#include <security_cdsa_client/cssmclient.h> 33#include <syslog.h> 34 35using namespace CssmClient; 36 37// 38// Exception model 39// 40const char * 41Error::what () const throw() 42{ 43 return "CSSM client library error"; 44} 45 46 47// 48// General utilities 49// 50void 51ObjectImpl::check(CSSM_RETURN status) 52{ 53 if (status != CSSM_OK) 54 { 55 CssmError::throwMe(status); 56 } 57} 58 59 60// 61// Common features of Objects 62// 63ObjectImpl::ObjectImpl() : mParent(), mChildCount(0) 64{ 65 mActive = false; // not activated 66 mAllocator = NULL; // allocator to be determined 67} 68 69ObjectImpl::ObjectImpl(const Object &mommy) : mParent(mommy.mImpl), mChildCount(0) 70{ 71 mActive = false; // not activated 72 mAllocator = NULL; // allocator to be determined 73 if (mParent) 74 mParent->addChild(); 75} 76 77ObjectImpl::~ObjectImpl() 78try 79{ 80 if (!isIdle()) 81 { 82 int i = mChildCount; 83 syslog(LOG_ALERT, "Object %p still has %d children at delete.\n", this, i); 84 } 85 86 // release parent from her obligations (if we still have one) 87 if (mParent) 88 mParent->removeChild(); 89} 90catch(...) 91{ 92 return; 93} 94 95void 96ObjectImpl::addChild() 97{ 98 mChildCount++; // atomic 99} 100 101void 102ObjectImpl::removeChild() 103{ 104 mChildCount--; // atomic 105} 106 107 108// 109// Manage allocators in the Object tree 110// 111Allocator & 112ObjectImpl::allocator() const 113{ 114 if (mAllocator == NULL) 115 { 116 // fix allocator now 117 if (mParent) 118 mAllocator = &mParent->allocator(); 119 else 120 mAllocator = &Allocator::standard(); 121 } 122 123 return *mAllocator; 124} 125 126void 127ObjectImpl::allocator(Allocator &alloc) 128{ 129 assert(mAllocator == NULL); // cannot redefine allocator once set 130 mAllocator = &alloc; 131} 132 133// Comparison operators use pointer comparison by default. Subclasses may override. 134bool 135ObjectImpl::operator <(const ObjectImpl &other) const 136{ 137 return this < &other; 138} 139 140bool 141ObjectImpl::operator ==(const ObjectImpl &other) const 142{ 143 return this == &other; 144} 145 146 147// 148// CSSMSession objects. 149// parent ::= NULL (none) 150// active ::= CSSM initialized 151// 152ModuleNexus<CssmImpl::StandardCssm> CssmImpl::mStandard; 153 154CssmImpl::CssmImpl() : ObjectImpl() 155{ 156 setup(); 157 mStandard().setCssm(this); 158} 159 160CssmImpl::CssmImpl(bool) : ObjectImpl() 161{ 162 setup(); 163 // implicitly constructed - caller responsible for standard session management 164} 165 166CssmImpl::~CssmImpl() 167{ 168 try 169 { 170 deactivate(); 171 } 172 catch(...) {} 173 174 // this may be the standard session... 175 mStandard().unsetCssm(this); 176} 177 178 179void 180CssmImpl::setup() 181{ 182 // set default configuration 183 mVersion.Major = 2; 184 mVersion.Minor = 0; 185 mScope = CSSM_PRIVILEGE_SCOPE_PROCESS; 186} 187 188 189Cssm 190CssmImpl::standard() 191{ 192 return Cssm(mStandard().get()); 193} 194 195 196void 197CssmImpl::activate() 198{ 199 StLock<Mutex> _(mActivateMutex); 200 if (!mActive) 201 { 202 // currently, no choices on PVC mode and key hierarchy 203 CSSM_PVC_MODE pvc = CSSM_PVC_NONE; 204 switch (CSSM_RETURN rc = CSSM_Init(&mVersion, 205 mScope, &mCallerGuid, 206 CSSM_KEY_HIERARCHY_NONE, &pvc, NULL)) { 207 case CSSMERR_CSSM_PVC_ALREADY_CONFIGURED: 208 case CSSM_OK: 209 break; 210 default: 211 check(rc); 212 } 213 mActive = true; 214 } 215} 216 217void 218CssmImpl::deactivate() 219{ 220 StLock<Mutex> _(mActivateMutex); 221 if (mActive) 222 { 223 mActive = false; 224 225 // clear module map (all gone now) 226 moduleMap.erase(moduleMap.begin(), moduleMap.end()); 227 228 // now terminate CSSM 229 check(CSSM_Terminate()); 230 } 231} 232 233void 234CssmImpl::atExitHandler() 235{ 236 try { 237 mStandard.reset(); 238 } catch (...) { 239 } 240} 241 242void 243CssmImpl::catchExit() 244{ 245 // @@@ Even though this is the "right thing" to do. This only causes 246 // exceptions during exit and doesn't really help cleanup correctly. 247#if 0 248 if (::atexit(atExitHandler)) 249 UnixError::throwMe(); 250#endif 251} 252 253 254// 255// Manage the automatic Cssm object. 256// This is a program global. 257// 258void CssmImpl::StandardCssm::setCssm(CssmImpl *cssm) 259{ 260 StLock<Mutex> _(*this); 261 if (mCssm == NULL) 262 mCssm = cssm; 263} 264 265void CssmImpl::StandardCssm::unsetCssm(CssmImpl *cssm) 266{ 267 StLock<Mutex> _(*this); 268 if (mCssm == cssm) 269 mCssm = NULL; 270} 271 272CssmImpl *CssmImpl::StandardCssm::get() 273{ 274 StLock<Mutex> _(*this); 275 if (mCssm == NULL) { // make the default instance 276 mCssm = new CssmImpl(true); 277 } 278 return mCssm; 279} 280 281CssmImpl::StandardCssm::~StandardCssm() 282{ 283 if (mCssm) { 284 mCssm->deactivate(); 285 delete mCssm; 286 } 287} 288 289 290// 291// Auto-module management 292// 293Module 294CssmImpl::autoModule(const Guid &guid) 295{ 296 StLock<Mutex> _(mapLock); 297 ModuleMap::iterator it = moduleMap.find(guid); 298 if (it == moduleMap.end()) 299 { 300 // no automodule for this guid yet, create one 301 Module module(guid, Cssm(this)); 302 moduleMap.insert(ModuleMap::value_type(guid, module)); 303 return module; 304 } 305 else 306 { 307 // existing automodule - use it 308 return it->second; 309 } 310} 311 312 313// 314// Module objects. 315// parent ::= the session object (usually Cssm::standard) 316// active ::= module is loaded. 317// 318ModuleImpl::ModuleImpl(const Guid &guid) : ObjectImpl(Cssm::standard()), 319 mAppNotifyCallback(NULL), 320 mAppNotifyCallbackCtx(NULL) 321{ 322 setGuid(guid); 323} 324 325ModuleImpl::ModuleImpl(const Guid &guid, const Cssm &session) : ObjectImpl(session), 326 mAppNotifyCallback(NULL), 327 mAppNotifyCallbackCtx(NULL) 328{ 329 setGuid(guid); 330} 331 332ModuleImpl::~ModuleImpl() 333{ 334 unload(); 335} 336 337 338// 339// RawModuleEvent objects encapsulate CSSM module callbacks 340// 341RawModuleEvents::~RawModuleEvents() 342{ } 343 344CSSM_RETURN RawModuleEvents::sendNotify(const CSSM_GUID *, void *context, 345 uint32 subService, CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event) 346{ 347 try { 348 reinterpret_cast<RawModuleEvents *>(context)->notify(subService, type, event); 349 return CSSM_OK; 350 } catch (const CommonError &error) { 351 return CssmError::cssmError(error, CSSM_CSSM_BASE_ERROR); 352 } catch (...) { 353 return CSSMERR_CSSM_INTERNAL_ERROR; // whatever... 354 } 355} 356 357 358// 359// ModuleEvents enhance RawModuleEvents by splitting the callback up by type 360// 361void ModuleEvents::notify(uint32 subService, 362 CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event) 363{ 364 switch (event) { 365 case CSSM_NOTIFY_INSERT: 366 insertion(subService, type); 367 break; 368 case CSSM_NOTIFY_REMOVE: 369 removal(subService, type); 370 break; 371 case CSSM_NOTIFY_FAULT: 372 fault(subService, type); 373 break; 374 } 375} 376 377// default callbacks do nothing 378void ModuleEvents::insertion(uint32 subService, CSSM_SERVICE_TYPE type) { } 379void ModuleEvents::removal(uint32 subService, CSSM_SERVICE_TYPE type) { } 380void ModuleEvents::fault(uint32 subService, CSSM_SERVICE_TYPE type) { } 381 382 383void 384ModuleImpl::appNotifyCallback(CSSM_API_ModuleEventHandler appNotifyCallback, void *appNotifyCallbackCtx) 385{ 386 secdebug("callback","In ModuleImpl::appNotifyCallback, appNotifyCallback=%p, appNotifyCallbackCtx=%p", 387 appNotifyCallback, appNotifyCallbackCtx); 388 if (mActive) 389 Error::throwMe(Error::objectBusy); 390 391 mAppNotifyCallback = appNotifyCallback; 392 mAppNotifyCallbackCtx = appNotifyCallbackCtx; 393} 394 395void 396ModuleImpl::appNotifyCallback(RawModuleEvents *handler) 397{ 398 appNotifyCallback(RawModuleEvents::sendNotify, handler); 399} 400 401void 402ModuleImpl::activate() 403{ 404 { 405 StLock<Mutex> _(mActivateMutex); 406 if (!mActive) 407 { 408 session()->init(); 409 // @@@ install handler here (use central dispatch with override) 410 secdebug("callback","In ModuleImpl::activate, mAppNotifyCallback=%p, mAppNotifyCallbackCtx=%p", 411 mAppNotifyCallback, mAppNotifyCallbackCtx); 412 check(CSSM_ModuleLoad(&guid(), CSSM_KEY_HIERARCHY_NONE, mAppNotifyCallback, mAppNotifyCallbackCtx)); 413 mActive = true; 414 } 415 } 416 417 session()->catchExit(); 418} 419 420void 421ModuleImpl::deactivate() 422{ 423 if (!isIdle()) 424 Error::throwMe(Error::objectBusy); 425 426 StLock<Mutex> _(mActivateMutex); 427 if (mActive) 428 { 429 mActive = false; 430 check(CSSM_ModuleUnload(&guid(), mAppNotifyCallback, mAppNotifyCallbackCtx)); 431 } 432} 433 434Cssm 435ModuleImpl::session() const 436{ 437 return parent<Cssm>(); 438} 439 440 441// 442// CssmAttachment objects. 443// parent ::= the loaded module object. 444// active ::= attached. 445// 446AttachmentImpl::AttachmentImpl(const Guid &guid, CSSM_SERVICE_TYPE subserviceType) 447: ObjectImpl(CssmImpl::standard()->autoModule(guid)) 448{ 449 make(subserviceType); 450} 451 452AttachmentImpl::AttachmentImpl(const Module &module, CSSM_SERVICE_TYPE subserviceType) 453: ObjectImpl(module) 454{ 455 make(subserviceType); 456} 457 458AttachmentImpl::~AttachmentImpl() 459{ 460 detach(); 461} 462 463void 464AttachmentImpl::make(CSSM_SERVICE_TYPE subserviceType) 465{ 466 // default configuration 467 mVersion.Major = 2; 468 mVersion.Minor = 0; 469 mSubserviceType = subserviceType; 470 mSubserviceId = 0; 471 mAttachFlags = 0; 472} 473 474void 475AttachmentImpl::activate() 476{ 477 StLock<Mutex> _(mActivateMutex); 478 if (!mActive) 479 { 480 module()->load(); 481 mMemoryFunctions = CssmAllocatorMemoryFunctions(allocator()); 482 check(CSSM_ModuleAttach(&guid(), &mVersion, 483 &mMemoryFunctions, 484 mSubserviceId, 485 mSubserviceType, 486 mAttachFlags, 487 CSSM_KEY_HIERARCHY_NONE, 488 NULL, 0, // no function pointer table return 489 NULL, // reserved 490 &mHandle)); 491 mActive = true; 492 } 493} 494 495void 496AttachmentImpl::deactivate() 497{ 498 StLock<Mutex> _(mActivateMutex); 499 if (mActive) 500 { 501 mActive = false; 502 check(CSSM_ModuleDetach(mHandle)); 503 } 504} 505 506CSSM_SERVICE_MASK 507AttachmentImpl::subserviceMask() const 508{ 509 return mSubserviceType; 510} 511 512void 513AttachmentImpl::subserviceId(uint32 id) 514{ 515 mSubserviceId = id; 516} 517 518CssmSubserviceUid 519AttachmentImpl::subserviceUid() const 520{ 521 return CssmSubserviceUid(guid(), &mVersion, mSubserviceId, subserviceMask()); 522} 523 524Module 525AttachmentImpl::module() const 526{ 527 return parent<Module>(); 528} 529