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