haiku/src/servers/keystore/Keyring.cpp

532 lines
9.7 KiB
C++

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