Example #1
0
CssmKey::CssmKey(uint32 length, void *data)
{
	clearPod();
	KeyData = CssmData(data, length);
    KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
    KeyHeader.BlobType = CSSM_KEYBLOB_RAW;
    KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
}
Example #2
0
void CssmUniformDate::convertTo(CssmOwnedData &data) const
{
    Gregorian greg(mTime);
    char str[20];
    if (19 != snprintf(str, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
        int(greg.year), greg.month, greg.day, greg.hour, greg.minute, int(greg.second)))
        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
    data = CssmData(str, 19);
}
Example #3
0
const CssmData ClientIdentification::getHash() const
{
	if (GuestState *guest = current()) {
		if (!guest->gotHash) {
			RefPointer<OSXCode> clientCode = new OSXCodeWrap(guest->code);
			OSXVerifier::makeLegacyHash(clientCode, guest->legacyHash);
			guest->gotHash = true;
		}
		return CssmData::wrap(guest->legacyHash, SHA1::digestLength);
	} else
		return CssmData();
}
//
// State transition matrix for a reader, based on PCSC state changes
//
void Reader::update(const PCSC::ReaderState &state)
{
	// set new state
	unsigned long oldState = mState.state();
	mState = state;
	mState.name(mName.c_str());		// (fix name pointer, unchanged)
	
	try {
		if (state.state(SCARD_STATE_UNAVAILABLE)) {
			// reader is unusable (probably being removed)
			secdebug("reader", "%p (%s) unavailable (0x%lx)",
				this, name().c_str(), state.state());
			if (mToken)
				removeToken();
		} else if (state.state(SCARD_STATE_EMPTY)) {
			// reader is empty (no token present)
			secdebug("reader", "%p (%s) empty (0x%lx)",
				this, name().c_str(), state.state());
			if (mToken)
				removeToken();
		} else if (state.state(SCARD_STATE_PRESENT)) {
			// reader has a token inserted
			secdebug("reader", "%p (%s) card present (0x%lx)",
				this, name().c_str(), state.state());
			//@@@ is this hack worth it (with notifications in)??
			if (mToken && CssmData(state) != CssmData(pcscState()))
				removeToken();  // incomplete but better than nothing
			//@@@ or should we call some verify-still-the-same function of tokend?
			//@@@ (I think pcsc will return an error if the card changed?)
			if (!mToken)
				insertToken(NULL);
		} else {
			secdebug("reader", "%p (%s) unexpected state change (0x%lx to 0x%lx)",
				this, name().c_str(), oldState, state.state());
		}
	} catch (...) {
		secdebug("reader", "state update exception (ignored)");
	}
}
//
// Load root (anchor) certificates from disk
//
void TrustStore::loadRootCertificates()
{
	StLock<Mutex> _(mMutex);

	CFRef<CFArrayRef> anchors;
	OSStatus ortn;

	/*
	 * Get the current set of all positively trusted anchors.
	 */
	ortn = SecTrustSettingsCopyUnrestrictedRoots(
		true, true, true,		/* all domains */
		anchors.take());
	if(ortn) {
		MacOSError::throwMe(ortn);
	}

	// how many data bytes do we need?
	size_t size = 0;
	CFIndex numCerts = CFArrayGetCount(anchors);
	CSSM_RETURN crtn;
	for(CFIndex dex=0; dex<numCerts; dex++) {
		SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex);
		CSSM_DATA certData;
		crtn = SecCertificateGetData(certRef, &certData);
		if(crtn) {
			CssmError::throwMe(crtn);
		}
		size += certData.Length;
	}
	mRootBytes.length(size);

	// fill CssmData vector while copying data bytes together
	mRoots.clear();
	uint8 *base = mRootBytes.data<uint8>();
	for(CFIndex dex=0; dex<numCerts; dex++) {
		SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex);
		CSSM_DATA certData;
		SecCertificateGetData(certRef, &certData);
		memcpy(base, certData.Data, certData.Length);
		mRoots.push_back(CssmData(base, certData.Length));
		base += certData.Length;
	}

	secdebug("anchors", "%ld anchors loaded", (long)numCerts);

	mRootsValid = true;			// ready to roll
}
bool Directory::dlGetNext(CSSM_HANDLE handle, CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes,
	CSSM_DATA *data, CSSM_DB_UNIQUE_RECORD *&id)
{
	CSSM_RETURN rc = DataGetNext(cdsa(), handle, &attributes, NULL, &id);
	switch (rc) {
	case CSSM_OK:
		if (data)
			*data = CssmData();
		return true;
	case CSSMERR_DL_ENDOFDATA:
		return false;
	default:
		CssmError::throwMe(rc);
		return false;   // placebo
	}
}
//
// The DLAccess implementation for MDS.
// We don't ever return record data, of course; we just zero it out.
//
CSSM_HANDLE Directory::dlGetFirst(const CSSM_QUERY &query, CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes,
	CSSM_DATA *data, CSSM_DB_UNIQUE_RECORD *&id)
{
	CSSM_HANDLE result;
	switch (CSSM_RETURN rc = DataGetFirst(cdsa(), &query, &result, &attributes, NULL, &id)) {
	case CSSM_OK:
		if (data)
			*data = CssmData();
		return result;
	case CSSMERR_DL_ENDOFDATA:
		return CSSM_INVALID_HANDLE;
	default:
		CssmError::throwMe(rc);
		return CSSM_INVALID_HANDLE; // placebo
	}
}
Example #8
0
//
// Invoke the securityd_service to retrieve the keychain master
// key from the AppleFDEKeyStore.
//
void KeychainDatabase::stashDbCheck()
{    
    CssmAutoData data(Allocator::standard(Allocator::sensitive));

    // Fetch the key
    int rc = 0;
    void * stash_key = NULL;
    int stash_key_len = 0;
    service_context_t context = common().session().get_current_service_context();
    rc = service_client_stash_get_key(&context, &stash_key, &stash_key_len);
    if (rc == 0) {
        if (stash_key) {
            data.copy(CssmData((void *)stash_key,stash_key_len));
            memset(stash_key, 0, stash_key_len);
            free(stash_key);
        }
    } else {
        CssmError::throwMe(rc);
    }
    
    {
        StLock<Mutex> _(common());

        // Now establish it as the keychain master key
        CssmClient::Key key(Server::csp(), data.get());
        CssmKey::Header &hdr = key.header();
        hdr.keyClass(CSSM_KEYCLASS_SESSION_KEY);
        hdr.algorithm(CSSM_ALGID_3DES_3KEY_EDE);
        hdr.usage(CSSM_KEYUSE_ANY);
        hdr.blobType(CSSM_KEYBLOB_RAW);
        hdr.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
        common().setup(mBlob, key);

        if (!decode())
            CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
    }

    // when upgrading from pre-10.9 create a keybag if it doesn't exist with the master secret
    // only do this after we have verified the master key unlocks the login.keychain
    if (service_client_kb_load(&context) == KB_BagNotFound) {
        service_client_kb_create(&context, data.data(), (int)data.length());
    }
}
//
// Key derivation
//
void TokenDatabase::deriveKey(const Context &context, Key *sourceKey,
	const AccessCredentials *cred, const AclEntryPrototype *owner,
	CssmData *param, CSSM_KEYUSE usage, CSSM_KEYATTR_FLAGS attrs, RefPointer<Key> &derivedKey)
{
	Access access(token());
	InputKey cSourceKey(sourceKey);
	TRY
    if (sourceKey)
		sourceKey->validate(CSSM_ACL_AUTHORIZATION_DERIVE, cred);
	GUARD
	KeyHandle hKey;
	CssmKey *result;
	CssmData params = param ? *param : CssmData();
	access().deriveKey(noDb, context,
		cSourceKey, cSourceKey,
		usage, modattrs(attrs), params, cred, owner,
		hKey, result);
	if (param) {
		*param = params;
		//@@@ leak? what's the rule here?
	}
	derivedKey = makeKey(hKey, result, 0, owner);
	DONE
}
Example #10
0
//
// Given a (truncated) Database credentials TypedList specifying a master key,
// locate the key and return a reference to it.
//
CssmClient::Key KeychainDatabase::keyFromCreds(const TypedList &sample, unsigned int requiredLength)
{
	// decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
	assert(sample.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY || sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY);
	if (sample.length() != requiredLength
		|| sample[1].type() != CSSM_LIST_ELEMENT_DATUM
		|| sample[2].type() != CSSM_LIST_ELEMENT_DATUM
		|| (requiredLength == 4 && sample[3].type() != CSSM_LIST_ELEMENT_DATUM))
			CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
	KeyHandle &handle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
    // We used to be able to check the length but supporting multiple client
    // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
    // field-size differences).  The decoding in the transition layer should 
    // serve as a sufficient garbling check anyway.  
    if (sample[2].data().data() == NULL)
        CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
    CssmKey &key = *sample[2].data().interpretedAs<CssmKey>();

	if (key.header().cspGuid() == gGuidAppleCSPDL) {
		// handleOrKey is a SecurityServer KeyHandle; ignore key argument
		return safer_cast<LocalKey &>(*Server::key(handle));
	} else 
	if (sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY) {
		/*
			Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
			
			sample[0]	sample type
			sample[1]	csp handle for master or wrapping key; is really a keyhandle
			sample[2]	masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
			sample[3]	UnlockReferralRecord data, in this case the flattened symmetric key
		*/

		// RefPointer<Key> Server::key(KeyHandle key)
		KeyHandle keyhandle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
		CssmData &flattenedKey = sample[3].data();
		RefPointer<Key> unwrappingKey = Server::key(keyhandle);
		Database &db=unwrappingKey->database();
		
		CssmKey rawWrappedKey;
		unflattenKey(flattenedKey, rawWrappedKey);

		RefPointer<Key> masterKey;
		CssmData emptyDescriptiveData;
		const AccessCredentials *cred = NULL;
		const AclEntryPrototype *owner = NULL;
		CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
		CSSM_KEYATTR_FLAGS attrs = CSSM_KEYATTR_EXTRACTABLE;	//CSSM_KEYATTR_RETURN_REF | 

		// Get default credentials for unwrappingKey (the one on the token)
		// Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
		// Following KeyItem::getCredentials, one sees that the "operation" parameter
		// e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
		Allocator &alloc = Allocator::standard();
		AutoCredentials promptCred(alloc, 3);// enable interactive prompting
	
		// promptCred: a credential permitting user prompt confirmations
		// contains:
		//  a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
		//  a PROMPTED_PASSWORD sample
		promptCred.sample(0) = TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT);
		promptCred.sample(1) = TypedList(alloc, CSSM_SAMPLE_TYPE_THRESHOLD,
			new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT)));
		promptCred.sample(2) = TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD,
			new(alloc) ListElement(alloc, CssmData()));

		// This unwrap object is here just to provide a context
		CssmClient::UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);	//ok to lie about csp here
		unwrap.mode(CSSM_ALGMODE_NONE);
		unwrap.padding(CSSM_PADDING_PKCS1);
		unwrap.cred(promptCred);
		unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7));
		Security::Context *tmpContext;
		CSSM_CC_HANDLE CCHandle = unwrap.handle();
		/*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle, (CSSM_CONTEXT_PTR *)&tmpContext);
		
		// OK, this is skanky but necessary. We overwrite fields in the context struct

		tmpContext->ContextType = CSSM_ALGCLASS_ASYMMETRIC;
		tmpContext->AlgorithmType = CSSM_ALGID_RSA;
		
		db.unwrapKey(*tmpContext, cred, owner, unwrappingKey, NULL, usage, attrs,
			rawWrappedKey, masterKey, emptyDescriptiveData);

	    Allocator::standard().free(rawWrappedKey.KeyData.Data);

		return safer_cast<LocalKey &>(*masterKey).key();
	}
	else
	{
		// not a KeyHandle reference; use key as a raw key
		if (key.header().blobType() != CSSM_KEYBLOB_RAW)
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
		if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
		return CssmClient::Key(Server::csp(), key, true);
	}
}
bool
AuthItem::getCssmData(CssmAutoData &value)
{
	value = CssmData(static_cast<uint8_t*>(mValue.data), mValue.length);
	return true;
}	
//
// Poll PCSC for smartcard status.
// We are enumerating all readers on each call.
//
void PCSCMonitor::Watcher::action()
{
    // Associate this watching thread with the server, so that it is possible to call
    // Server::active() from inside code called from this thread.
    mServer.associateThread();

    try {
        // open PCSC session
        mSession.open();

        // Array of states, userData() points to associated Reader instance,
        // name points to string held by Reader::name attribute.
        vector<PCSC::ReaderState> states;

        for (;;) {
            // enumerate all current readers.
            vector<string> names;
            mSession.listReaders(names);
            secinfo("pcsc", "%ld reader(s) in system", names.size());

            // Update PCSC states array with new/removed readers.
            for (vector<PCSC::ReaderState>::iterator stateIt = states.begin(); stateIt != states.end(); ) {
                Reader *reader = stateIt->userData<Reader>();
                vector<string>::iterator nameIt = find(names.begin(), names.end(), reader->name());
                if (nameIt == names.end()) {
                    // Reader was removed from the system.
                    if (Reader *reader = stateIt->userData<Reader>()) {
                        secinfo("pcsc", "removing reader %s", stateIt->name());
                        Syslog::notice("Token reader %s removed from system", stateIt->name());
                        reader->kill();						// prepare to die
                        mReaders.erase(reader->name());		// remove from reader map
                        stateIt = states.erase(stateIt);
                    }
                } else {
                    // This reader is already tracked, copy its signalled state into the last known state.
                    stateIt->lastKnown(stateIt->state());
                    names.erase(nameIt);
                    stateIt++;
                }
            }

            // Add states for remaining (newly appeared) reader names.
            for (vector<string>::iterator it = names.begin(); it != names.end(); ++it) {
                PCSC::ReaderState state;
                state.clearPod();
                state.set(it->c_str());
                states.push_back(state);
            }

            // Now ask PCSC for status changes, and wait for them.
            mSession.statusChange(states, INFINITE);
            
            // Go through the states and notify changed readers.
            for (vector<PCSC::ReaderState>::iterator stateIt = states.begin(); stateIt != states.end(); stateIt++) {
                Reader *reader = stateIt->userData<Reader>();
                if (!reader) {
                    reader = new Reader(mTokenCache, *stateIt);
                    stateIt->userData<Reader>() = reader;
                    stateIt->name(reader->name().c_str());
                    mReaders.insert(make_pair(reader->name(), reader));
                    Syslog::notice("Token reader %s inserted into system", stateIt->name());
                }

                // if PCSC flags a change, notify the Reader
                if (stateIt->changed()) {
                    Syslog::notice("reader %s: state changed %lu -> %lu", stateIt->name(), stateIt->lastKnown(), stateIt->state());
                    try {
                        reader->update(*stateIt);
                    } catch (const exception &e) {
                        Syslog::notice("Token in reader %s: %s", stateIt->name(), e.what());
                    }
                }
            }

            //wakeup mach server to process notifications
            ClientSession session(Allocator::standard(), Allocator::standard());
            session.postNotification(kNotificationDomainPCSC, kNotificationPCSCStateChange, CssmData());
        }
    } catch (const exception &e) {
        Syslog::error("An error '%s' occured while tracking token readers", e.what());
    }
}
//
// Internal utilities
//
CssmData CSPFullPluginSession::makeBuffer(size_t size, Allocator &alloc)
{
	return CssmData(alloc.malloc(size), size);
}