/* * Copyright 2012, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved. * Distributed under the terms of the MIT License. */ #include "Keyring.h" Keyring::Keyring() : fHasUnlockKey(false), fUnlocked(false), fModified(false) { } Keyring::Keyring(const char* name) : fName(name), fHasUnlockKey(false), fUnlocked(false), fModified(false) { } Keyring::~Keyring() { } status_t Keyring::ReadFromMessage(const BMessage& message) { status_t result = message.FindString("name", &fName); if (result != B_OK) return result; result = message.FindBool("hasUnlockKey", &fHasUnlockKey); if (result != B_OK) return result; if (message.GetBool("noData", false)) { fFlatBuffer.SetSize(0); return B_OK; } ssize_t size; const void* data; result = message.FindData("data", B_RAW_TYPE, &data, &size); if (result != B_OK) return result; if (size < 0) return B_ERROR; fFlatBuffer.SetSize(0); ssize_t written = fFlatBuffer.WriteAt(0, data, size); if (written != size) { fFlatBuffer.SetSize(0); return written < 0 ? written : B_ERROR; } return B_OK; } status_t Keyring::WriteToMessage(BMessage& message) { status_t result = _EncryptToFlatBuffer(); if (result != B_OK) return result; if (fFlatBuffer.BufferLength() == 0) result = message.AddBool("noData", true); else { result = message.AddData("data", B_RAW_TYPE, fFlatBuffer.Buffer(), fFlatBuffer.BufferLength()); } if (result != B_OK) return result; result = message.AddBool("hasUnlockKey", fHasUnlockKey); if (result != B_OK) return result; return message.AddString("name", fName); } status_t Keyring::Unlock(const BMessage* keyMessage) { if (fUnlocked) return B_OK; if (fHasUnlockKey == (keyMessage == NULL)) return B_BAD_VALUE; if (keyMessage != NULL) fUnlockKey = *keyMessage; status_t result = _DecryptFromFlatBuffer(); if (result != B_OK) { fUnlockKey.MakeEmpty(); return result; } fUnlocked = true; return B_OK; } void Keyring::Lock() { if (!fUnlocked) return; _EncryptToFlatBuffer(); fUnlockKey.MakeEmpty(); fData.MakeEmpty(); fApplications.MakeEmpty(); fUnlocked = false; } bool Keyring::IsUnlocked() const { return fUnlocked; } bool Keyring::HasUnlockKey() const { return fHasUnlockKey; } const BMessage& Keyring::UnlockKey() const { return fUnlockKey; } status_t Keyring::SetUnlockKey(const BMessage& keyMessage) { if (!fUnlocked) return B_NOT_ALLOWED; fHasUnlockKey = true; fUnlockKey = keyMessage; fModified = true; return B_OK; } status_t Keyring::RemoveUnlockKey() { if (!fUnlocked) return B_NOT_ALLOWED; fUnlockKey.MakeEmpty(); fHasUnlockKey = false; fModified = true; return B_OK; } status_t Keyring::GetNextApplication(uint32& cookie, BString& signature, BString& path) { if (!fUnlocked) return B_NOT_ALLOWED; char* nameFound = NULL; status_t result = fApplications.GetInfo(B_MESSAGE_TYPE, cookie++, &nameFound, NULL); if (result != B_OK) return B_ENTRY_NOT_FOUND; BMessage appMessage; result = fApplications.FindMessage(nameFound, &appMessage); if (result != B_OK) return B_ENTRY_NOT_FOUND; result = appMessage.FindString("path", &path); if (result != B_OK) return B_ERROR; signature = nameFound; return B_OK; } status_t Keyring::FindApplication(const char* signature, const char* path, BMessage& appMessage) { if (!fUnlocked) return B_NOT_ALLOWED; int32 count; type_code type; if (fApplications.GetInfo(signature, &type, &count) != B_OK) return B_ENTRY_NOT_FOUND; for (int32 i = 0; i < count; i++) { if (fApplications.FindMessage(signature, i, &appMessage) != B_OK) continue; BString appPath; if (appMessage.FindString("path", &appPath) != B_OK) continue; if (appPath == path) return B_OK; } appMessage.MakeEmpty(); return B_ENTRY_NOT_FOUND; } status_t Keyring::AddApplication(const char* signature, const BMessage& appMessage) { if (!fUnlocked) return B_NOT_ALLOWED; status_t result = fApplications.AddMessage(signature, &appMessage); if (result != B_OK) return result; fModified = true; return B_OK; } status_t Keyring::RemoveApplication(const char* signature, const char* path) { if (!fUnlocked) return B_NOT_ALLOWED; if (path == NULL) { // We want all of the entries for this signature removed. status_t result = fApplications.RemoveName(signature); if (result != B_OK) return B_ENTRY_NOT_FOUND; fModified = true; return B_OK; } int32 count; type_code type; if (fApplications.GetInfo(signature, &type, &count) != B_OK) return B_ENTRY_NOT_FOUND; for (int32 i = 0; i < count; i++) { BMessage appMessage; if (fApplications.FindMessage(signature, i, &appMessage) != B_OK) return B_ERROR; BString appPath; if (appMessage.FindString("path", &appPath) != B_OK) continue; if (appPath == path) { fApplications.RemoveData(signature, i); fModified = true; return B_OK; } } return B_ENTRY_NOT_FOUND; } status_t Keyring::FindKey(const BString& identifier, const BString& secondaryIdentifier, bool secondaryIdentifierOptional, BMessage* _foundKeyMessage) const { if (!fUnlocked) return B_NOT_ALLOWED; int32 count; type_code type; if (fData.GetInfo(identifier, &type, &count) != B_OK) return B_ENTRY_NOT_FOUND; // We have a matching primary identifier, need to check for the secondary // identifier. for (int32 i = 0; i < count; i++) { BMessage candidate; if (fData.FindMessage(identifier, i, &candidate) != B_OK) return B_ERROR; BString candidateIdentifier; if (candidate.FindString("secondaryIdentifier", &candidateIdentifier) != B_OK) { candidateIdentifier = ""; } if (candidateIdentifier == secondaryIdentifier) { if (_foundKeyMessage != NULL) *_foundKeyMessage = candidate; return B_OK; } } // We didn't find an exact match. if (secondaryIdentifierOptional) { if (_foundKeyMessage == NULL) return B_OK; // The secondary identifier is optional, so we just return the // first entry. return fData.FindMessage(identifier, 0, _foundKeyMessage); } return B_ENTRY_NOT_FOUND; } status_t Keyring::FindKey(BKeyType type, BKeyPurpose purpose, uint32 index, BMessage& _foundKeyMessage) const { if (!fUnlocked) return B_NOT_ALLOWED; for (int32 keyIndex = 0;; keyIndex++) { int32 count = 0; char* identifier = NULL; if (fData.GetInfo(B_MESSAGE_TYPE, keyIndex, &identifier, NULL, &count) != B_OK) { break; } if (type == B_KEY_TYPE_ANY && purpose == B_KEY_PURPOSE_ANY) { // No need to inspect the actual keys. if ((int32)index >= count) { index -= count; continue; } return fData.FindMessage(identifier, index, &_foundKeyMessage); } // Go through the keys to check their type and purpose. for (int32 subkeyIndex = 0; subkeyIndex < count; subkeyIndex++) { BMessage subkey; if (fData.FindMessage(identifier, subkeyIndex, &subkey) != B_OK) return B_ERROR; bool match = true; if (type != B_KEY_TYPE_ANY) { BKeyType subkeyType; if (subkey.FindUInt32("type", (uint32*)&subkeyType) != B_OK) return B_ERROR; match = subkeyType == type; } if (match && purpose != B_KEY_PURPOSE_ANY) { BKeyPurpose subkeyPurpose; if (subkey.FindUInt32("purpose", (uint32*)&subkeyPurpose) != B_OK) { return B_ERROR; } match = subkeyPurpose == purpose; } if (match) { if (index == 0) { _foundKeyMessage = subkey; return B_OK; } index--; } } } return B_ENTRY_NOT_FOUND; } status_t Keyring::AddKey(const BString& identifier, const BString& secondaryIdentifier, const BMessage& keyMessage) { if (!fUnlocked) return B_NOT_ALLOWED; // Check for collisions. if (FindKey(identifier, secondaryIdentifier, false, NULL) == B_OK) return B_NAME_IN_USE; // We're fine, just add the new key. status_t result = fData.AddMessage(identifier, &keyMessage); if (result != B_OK) return result; fModified = true; return B_OK; } status_t Keyring::RemoveKey(const BString& identifier, const BMessage& keyMessage) { if (!fUnlocked) return B_NOT_ALLOWED; int32 count; type_code type; if (fData.GetInfo(identifier, &type, &count) != B_OK) return B_ENTRY_NOT_FOUND; for (int32 i = 0; i < count; i++) { BMessage candidate; if (fData.FindMessage(identifier, i, &candidate) != B_OK) return B_ERROR; // We require an exact match. if (!candidate.HasSameData(keyMessage)) continue; status_t result = fData.RemoveData(identifier, i); if (result != B_OK) return result; fModified = true; return B_OK; } return B_ENTRY_NOT_FOUND; } int Keyring::Compare(const Keyring* one, const Keyring* two) { return strcmp(one->Name(), two->Name()); } int Keyring::Compare(const BString* name, const Keyring* keyring) { return strcmp(name->String(), keyring->Name()); } status_t Keyring::_EncryptToFlatBuffer() { if (!fModified) return B_OK; if (!fUnlocked) return B_NOT_ALLOWED; BMessage container; status_t result = container.AddMessage("data", &fData); if (result != B_OK) return result; result = container.AddMessage("applications", &fApplications); if (result != B_OK) return result; fFlatBuffer.SetSize(0); fFlatBuffer.Seek(0, SEEK_SET); result = container.Flatten(&fFlatBuffer); if (result != B_OK) return result; if (fHasUnlockKey) { // TODO: Actually encrypt the flat buffer... } fModified = false; return B_OK; } status_t Keyring::_DecryptFromFlatBuffer() { if (fFlatBuffer.BufferLength() == 0) return B_OK; if (fHasUnlockKey) { // TODO: Actually decrypt the flat buffer... } BMessage container; fFlatBuffer.Seek(0, SEEK_SET); status_t result = container.Unflatten(&fFlatBuffer); if (result != B_OK) return result; result = container.FindMessage("data", &fData); if (result != B_OK) return result; result = container.FindMessage("applications", &fApplications); if (result != B_OK) { fData.MakeEmpty(); return result; } return B_OK; }