#include <Security/osxsigning.h>
#include <Security/osxsigner.h>
#include <Security/trackingallocator.h>
#include "aclsupport.h"
#include "keychainacl.h"
#include <memory>
using namespace CssmClient;
TrustedApplicationImpl::TrustedApplicationImpl(const CssmData &signature, const CssmData &comment, bool enabled) :
mSignature(CssmAllocator::standard(), signature),
mComment(CssmAllocator::standard(), comment),
mEnabled(enabled)
{
}
TrustedApplicationImpl::TrustedApplicationImpl(const char *path, const CssmData &comment, bool enabled) : mSignature(CssmAllocator::standard()),
mComment(CssmAllocator::standard(), comment),
mEnabled(enabled)
{
calcSignature(path, mSignature);
}
const CssmData & TrustedApplicationImpl::signature() const
{
return mSignature;
}
const CssmData & TrustedApplicationImpl::comment() const
{
return mComment;
}
bool TrustedApplicationImpl::enabled() const
{
return mEnabled;
}
void TrustedApplicationImpl::enabled(bool enabled)
{
mEnabled = enabled;
}
bool TrustedApplicationImpl::sameSignature(const char *path)
{
CssmAutoData otherSignature(CssmAllocator::standard());
calcSignature(path, otherSignature);
return (mSignature.get() == otherSignature);
}
void TrustedApplicationImpl::calcSignature(const char *path, CssmOwnedData &signature)
{
RefPointer<CodeSigning::OSXCode> objToVerify(CodeSigning::OSXCode::at(path));
CodeSigning::OSXSigner signer;
auto_ptr<CodeSigning::OSXSigner::OSXSignature> osxSignature(signer.sign(*objToVerify));
signature.copy(osxSignature->data(), osxSignature->length());
}
TrustedApplication::TrustedApplication()
{
}
TrustedApplication::TrustedApplication(
const char *path, const CssmData &comment, bool enabled) :
RefPointer<TrustedApplicationImpl>(new TrustedApplicationImpl(path, comment, enabled))
{
}
TrustedApplication::TrustedApplication(
const CssmData &signature, const CssmData &comment, bool enabled) :
RefPointer<TrustedApplicationImpl>(new TrustedApplicationImpl(signature, comment, enabled))
{
}
KeychainACL::KeychainACL(const Key &key) :
mLabel(CssmAllocator::standard()), mSelector(CssmAllocator::standard())
{
mKey = key;
initialize();
}
void KeychainACL::initialize()
{
mAnyAllow=false;
mAlwaysAskUser=false;
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR defaultSelector
= { CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0 };
mSelector.copy(&defaultSelector, sizeof(defaultSelector));
AutoAclEntryInfoList aclInfos;
mKey->getAcl(aclInfos);
mHandle = CSSM_INVALID_HANDLE;
const AclEntryInfo *theInfo = NULL;
for(uint32 entry=0; entry<aclInfos.size(); entry++)
{
const AclEntryInfo &info = aclInfos[entry];
const AuthorizationGroup &authorizationGroup=info.proto().authorization();
for(uint32 auth=0; auth<authorizationGroup.count(); auth++)
{
if(authorizationGroup[auth]==CSSM_ACL_AUTHORIZATION_DECRYPT || authorizationGroup[auth]==CSSM_ACL_AUTHORIZATION_ANY)
{
if (mHandle != CSSM_INVALID_HANDLE && mHandle != info.handle())
{
mIsCustomACL=true;
return;
}
mHandle = info.handle();
theInfo = &info;
}
}
}
if (!theInfo)
{
mIsCustomACL=true;
return;
}
TypedList subject=theInfo->proto().subject();
assert(subject.isProper());
const ListElement *element = subject.first();
switch(*element)
{
case CSSM_ACL_SUBJECT_TYPE_ANY:
assert(element->next() == NULL);
mAnyAllow=true;
return;
case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT:
mAlwaysAskUser=true;
assert(subject.length() == 3);
mSelector = subject[1].data();
mLabel = subject[2].data();
return;
case CSSM_ACL_SUBJECT_TYPE_THRESHOLD:
break;
default:
mIsCustomACL = true;
return;
}
element = element->next();
assert(element && element->type() == CSSM_LIST_ELEMENT_WORDID);
if (*element != 1) {
mIsCustomACL = true;
return;
}
element = element->next();
assert(element && element->type() == CSSM_LIST_ELEMENT_WORDID);
uint32 n = *element;
assert(n > 0);
int isEnabled=1;
for (uint32 ix = 0; ix < n; ++ix)
{
element = element->next();
assert(element && element->type() == CSSM_LIST_ELEMENT_SUBLIST);
const TypedList &subList = *element;
assert(subList.isProper());
const ListElement *subElement = subList.first();
switch(*subElement)
{
case CSSM_ACL_SUBJECT_TYPE_ANY:
assert(ix == 0 && subElement->next() == NULL);
mAnyAllow=true;
break;
case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT:
assert(ix == n - 1);
mAlwaysAskUser=true;
assert(subList.length() == 3);
mSelector = subList[1].data();
mLabel = subList[2].data();
break;
case CSSM_ACL_SUBJECT_TYPE_COMMENT:
case CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE:
{
if(*subElement==CSSM_ACL_SUBJECT_TYPE_COMMENT)
{
isEnabled=0;
subElement = subElement->next();
}
subElement = subElement->next();
assert(subElement && subElement->type() == CSSM_LIST_ELEMENT_WORDID);
uint32 sigType = *subElement;
subElement = subElement->next();
assert(subElement && subElement->type() == CSSM_LIST_ELEMENT_DATUM);
const CssmData &sig = subElement->data();
subElement = subElement->next();
assert(subElement && subElement->type() == CSSM_LIST_ELEMENT_DATUM && subElement->next() == NULL);
const CssmData &comment = subElement->data();
push_back(TrustedApplication(sig, comment, (sigType == CSSM_ACL_CODE_SIGNATURE_OSX) && isEnabled));
break;
}
default:
mIsCustomACL = true;
return;
}
}
assert(element->next() == NULL);
}
void KeychainACL::commit()
{
TrackingAllocator allocator(CssmAllocator::standard());
AclFactory aclFactory;
CssmList &list = *new(allocator) CssmList();
list.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_THRESHOLD));
list.append(new(allocator) ListElement(1));
list.append(new(allocator) ListElement(size()+mAnyAllow+mAlwaysAskUser));
if(mAnyAllow)
{
CssmList &sublist = *new(allocator) CssmList();
sublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_ANY));
list.append(new(allocator) ListElement(sublist));
}
for (uint32 ix = 0; ix < size(); ++ix)
{
TrustedApplication app = at(ix);
CssmList &sublist = *new(allocator) CssmList();
if(!app->enabled()) sublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_COMMENT));
sublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE));
sublist.append(new(allocator) ListElement(CSSM_ACL_CODE_SIGNATURE_OSX));
sublist.append(new(allocator) ListElement(app->signature()));
sublist.append(new(allocator) ListElement(app->comment()));
list.append(new(allocator) ListElement(sublist));
}
if(mAlwaysAskUser)
{
CssmList &sublist = *new(allocator) CssmList();
sublist.append(new(allocator) ListElement(CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT));
sublist.append(new(allocator) ListElement(mSelector.get()));
sublist.append(new(allocator) ListElement(mLabel.get()));
list.append(new(allocator) ListElement(sublist));
}
AclEntryPrototype aclEntry(list);
AuthorizationGroup &anyDecryptAuthGroup = aclEntry.authorization();
CSSM_ACL_AUTHORIZATION_TAG decryptTag = CSSM_ACL_AUTHORIZATION_DECRYPT;
anyDecryptAuthGroup.NumberOfAuthTags = 1;
anyDecryptAuthGroup.AuthTags = &decryptTag;
const AccessCredentials *promptCred = aclFactory.promptCred ();
AclEdit edit(mHandle, aclEntry);
mKey->changeAcl(edit, promptCred);
}
void KeychainACL::anyAllow(bool allow)
{
mAnyAllow=allow;
}
bool KeychainACL::anyAllow() const
{
return mAnyAllow;
}
void KeychainACL::alwaysAskUser(bool ask)
{
mAlwaysAskUser=ask;
}
bool KeychainACL::alwaysAskUser() const
{
return mAlwaysAskUser;
}
bool KeychainACL::isCustomACL() const
{
return mIsCustomACL;
}
void KeychainACL::label(const CssmData &label)
{
mLabel = label;
}