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; }
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); }
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 } }
// // 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 }
// // 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); }