2  * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved. 
   4  * The contents of this file constitute Original Code as defined in and are 
   5  * subject to the Apple Public Source License Version 1.2 (the 'License'). 
   6  * You may not use this file except in compliance with the License. Please obtain 
   7  * a copy of the License at http://www.apple.com/publicsource and read it before 
  10  * This Original Code and all software distributed under the License are 
  11  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 
  12  * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 
  13  * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
  14  * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 
  15  * specific language governing rights and limitations under the License. 
  20 // codesigdb - code-hash equivalence database 
  22 #include "codesigdb.h" 
  25 #include "agentquery.h" 
  26 #include <Security/memutils.h> 
  30 // A self-constructing database key class. 
  31 // Key format is <t><uid|S><key data> 
  33 // <t> single ASCII character type code ('H' for hash links) 
  34 // <uid|S> decimal userid of owning user, or 'S' for system entries. Followed by null byte. 
  35 // <key data> variable length key value (binary). 
  37 class DbKey 
: public CssmAutoData 
{ 
  39         DbKey(char type
, const CssmData 
&key
, bool perUser 
= false, uid_t user 
= 0); 
  42 DbKey::DbKey(char type
, const CssmData 
&key
, bool perUser
, uid_t user
) 
  43         : CssmAutoData(CssmAllocator::standard()) 
  45         using namespace LowLevelMemoryUtilities
; 
  49                 headerLength 
= 1 + sprintf(header
, "%c%d", type
, user
); 
  51                 headerLength 
= 1 + sprintf(header
, "%cS", type
); 
  52         malloc(headerLength 
+ key
.length()); 
  53         memcpy(this->data(), header
, headerLength
); 
  54         memcpy(get().at(headerLength
), key
.data(), key
.length()); 
  59 // A subclass of Identity made of whole cloth (from a raw CodeSignature ACL information) 
  61 class AclIdentity 
: public CodeSignatures::Identity 
{ 
  63         AclIdentity(const CodeSigning::Signature 
*sig
, const char *comment
) 
  64                 : mHash(*sig
), mPath(comment 
? comment 
: "") { } 
  65         AclIdentity(const CssmData 
&hash
, const char *comment
) 
  66                 : mHash(hash
), mPath(comment 
? comment 
: "") { } 
  69         std::string 
getPath() const { return mPath
; } 
  70         const CssmData 
getHash(CodeSigning::OSXSigner 
&) const { return mHash
; } 
  79 // Construct a CodeSignatures objects 
  81 CodeSignatures::CodeSignatures(const char *path
) 
  84                 mDb
.open(path
, O_RDWR 
| O_CREAT
, 0644); 
  85         } catch (const CssmCommonError 
&err
) { 
  87                         mDb
.open(path
, O_RDONLY
, 0644); 
  88                         Syslog::warning("database %s opened READONLY (R/W failed errno=%d)", path
, err
.unixError()); 
  89                         secdebug("codesign", "database %s opened READONLY (R/W failed errno=%d)", path
, err
.unixError()); 
  91                         Syslog::warning("cannot open %s; using no code equivalents", path
); 
  92                         secdebug("codesign", "unable to open %s; using no code equivalents", path
); 
  96                 mDb
.flush();    // in case we just created it 
  97         IFDUMPING("equiv", debugDump("open")); 
 100 CodeSignatures::~CodeSignatures() 
 106 // (Re)open the equivalence database. 
 107 // This is useful to switch to database in another volume. 
 109 void CodeSignatures::open(const char *path
) 
 111         mDb
.open(path
, O_RDWR 
| O_CREAT
, 0644); 
 113         IFDUMPING("equiv", debugDump("reopen")); 
 118 // Basic Identity objects 
 120 CodeSignatures::Identity::Identity() : mState(untried
) 
 123 CodeSignatures::Identity::~Identity() 
 126 string 
CodeSignatures::Identity::canonicalName(const string 
&path
) 
 128         string::size_type slash 
= path
.rfind('/'); 
 129         if (slash 
== string::npos
)      // bloody unlikely, but whatever... 
 131         return path
.substr(slash
+1); 
 136 // Find and store database objects (primitive layer) 
 138 bool CodeSignatures::find(Identity 
&id
, uid_t user
) 
 140         if (id
.mState 
!= Identity::untried
) 
 141                 return id
.mState 
== Identity::valid
; 
 143                 DbKey 
userKey('H', id
.getHash(mSigner
), true, user
); 
 145                 if (mDb
.get(userKey
, linkValue
)) { 
 146                         id
.mName 
= string(linkValue
.interpretedAs
<const char>(), linkValue
.length()); 
 147                         IFDUMPING("equiv", id
.debugDump("found/user")); 
 148                         id
.mState 
= Identity::valid
; 
 151                 DbKey 
sysKey('H', id
.getHash(mSigner
)); 
 152                 if (mDb
.get(sysKey
, linkValue
)) { 
 153                         id
.mName 
= string(linkValue
.interpretedAs
<const char>(), linkValue
.length()); 
 154                         IFDUMPING("equiv", id
.debugDump("found/system")); 
 155                         id
.mState 
= Identity::valid
; 
 159                 secdebug("codesign", "exception validating identity for %s - marking failed", id
.path().c_str()); 
 160                 id
.mState 
= Identity::invalid
; 
 162         return id
.mState 
== Identity::valid
; 
 165 void CodeSignatures::makeLink(Identity 
&id
, const string 
&ident
, bool forUser
, uid_t user
) 
 167         DbKey 
key('H', id
.getHash(mSigner
), forUser
, user
); 
 168         if (!mDb
.put(key
, StringData(ident
))) 
 169                 UnixError::throwMe(); 
 172 void CodeSignatures::makeApplication(const std::string 
&name
, const std::string 
&path
) 
 174         //@@@ create app record and fill (later) 
 179 // Administrative manipulation calls 
 181 void CodeSignatures::addLink(const CssmData 
&oldHash
, const CssmData 
&newHash
, 
 182         const char *inName
, bool forSystem
) 
 184         string name 
= Identity::canonicalName(inName
); 
 185         uid_t user 
= Server::connection().process
.uid(); 
 186         if (forSystem 
&& user
)  // only root user can establish forSystem links 
 187                 UnixError::throwMe(EACCES
); 
 188         if (!forSystem
) // in fact, for now we don't allow per-user calls at all 
 189                 UnixError::throwMe(EACCES
); 
 190         AclIdentity 
oldCode(oldHash
, name
.c_str()); 
 191         AclIdentity 
newCode(newHash
, name
.c_str()); 
 192         secdebug("codesign", "addlink for name %s", name
.c_str()); 
 193         StLock
<Mutex
> _(mDatabaseLock
); 
 195                 if (oldCode
.trustedName() != name
) { 
 196                         secdebug("codesign", "addlink does not match existing name %s", 
 197                                 oldCode
.trustedName().c_str()); 
 198                         MacOSError::throwMe(CSSMERR_CSP_VERIFY_FAILED
); 
 201                 makeLink(oldCode
, name
, !forSystem
, user
); 
 204                 makeLink(newCode
, name
, !forSystem
, user
); 
 208 void CodeSignatures::removeLink(const CssmData 
&hash
, const char *name
, bool forSystem
) 
 210         AclIdentity 
code(hash
, name
); 
 211         uid_t user 
= Server::connection().process
.uid(); 
 212         if (forSystem 
&& user
)  // only root user can remove forSystem links 
 213                 UnixError::throwMe(EACCES
); 
 214         DbKey 
key('H', hash
, !forSystem
, user
); 
 215         StLock
<Mutex
> _(mDatabaseLock
); 
 222 // Verify signature matches 
 224 bool CodeSignatures::verify(Process 
&process
, 
 225         const CodeSigning::Signature 
*trustedSignature
, const CssmData 
*comment
) 
 227         secdebug("codesign", "start verify"); 
 229         // if we have no client code, we cannot possibly match this 
 230         if (!process
.clientCode()) { 
 231                 secdebug("codesign", "no code base: fail"); 
 235         // first of all, if the signature directly matches the client's code, we're obviously fine 
 236         // we don't even need the database for that... 
 237         Identity 
&clientIdentity 
= process
; 
 239                 if (clientIdentity
.getHash(mSigner
) == CssmData(*trustedSignature
)) { 
 240                         secdebug("codesign", "direct match: pass"); 
 244                 secdebug("codesign", "exception getting client code hash: fail"); 
 248         // ah well. Establish mediator objects for database signature links 
 249         AclIdentity 
aclIdentity(trustedSignature
, comment 
? comment
->interpretedAs
<const char>() : NULL
); 
 251         uid_t user 
= process
.uid(); 
 253                 StLock
<Mutex
> _(mDatabaseLock
); 
 254                 find(aclIdentity
, user
); 
 255                 find(clientIdentity
, user
); 
 258         // if both links exist, we can decide this right now 
 259         if (aclIdentity 
&& clientIdentity
) { 
 260                 if (aclIdentity
.trustedName() == clientIdentity
.trustedName()) { 
 261                         secdebug("codesign", "app references match: pass"); 
 264                         secdebug("codesign", "client/acl links exist but are unequal: fail"); 
 269         // check for name equality 
 270         secdebug("codesign", "matching client %s against acl %s", 
 271                 clientIdentity
.name().c_str(), aclIdentity
.name().c_str()); 
 272         if (aclIdentity
.name() != clientIdentity
.name()) { 
 273                 secdebug("codesign", "name/path mismatch: fail"); 
 277         // The names match - we have a possible update. 
 279         // Take the UI lock now to serialize "update rushes". 
 280         Server::active().longTermActivity(); 
 281         StLock
<Mutex
> uiLocker(mUILock
); 
 283         // re-read the database in case some other thread beat us to the update 
 285                 StLock
<Mutex
> _(mDatabaseLock
); 
 286                 find(aclIdentity
, user
); 
 287                 find(clientIdentity
, user
); 
 289         if (aclIdentity 
&& clientIdentity
) { 
 290                 if (aclIdentity
.trustedName() == clientIdentity
.trustedName()) { 
 291                         secdebug("codesign", "app references match: pass (on the rematch)"); 
 294                         secdebug("codesign", "client/acl links exist but are unequal: fail (on the rematch)"); 
 300         QueryCodeCheck query
; 
 301         query(aclIdentity
.path().c_str()); 
 302         if (!query
.allowAccess
) { 
 303                 secdebug("codesign", "user declined equivalence: fail"); 
 307         // take the database lock back for real 
 308         StLock
<Mutex
> _(mDatabaseLock
); 
 310         // user wants us to go ahead and establish trust (if possible) 
 312                 // acl is linked but new client: link the client to this application 
 313                 makeLink(clientIdentity
, aclIdentity
.trustedName(), true, user
); 
 315                 secdebug("codesign", "client %s linked to application %s: pass", 
 316                         clientIdentity
.path().c_str(), aclIdentity
.trustedName().c_str()); 
 320         if (clientIdentity
) {   // code link exists, acl link missing 
 321                 // client is linked but ACL (hash) never seen: link the ACL to this app 
 322                 makeLink(aclIdentity
, clientIdentity
.trustedName(), true, user
); 
 324                 secdebug("codesign", "acl %s linked to client %s: pass", 
 325                         aclIdentity
.path().c_str(), clientIdentity
.trustedName().c_str()); 
 329         // the De Novo case: no links, must create everything 
 330         string ident 
= clientIdentity
.name(); 
 331         makeApplication(ident
, clientIdentity
.path()); 
 332         makeLink(clientIdentity
, ident
, true, user
); 
 333         makeLink(aclIdentity
, ident
, true, user
); 
 335         secdebug("codesign", "new linkages established: pass"); 
 341 // Debug dumping support 
 343 #if defined(DEBUGDUMP) 
 345 void CodeSignatures::debugDump(const char *how
) const 
 347         using namespace Debug
; 
 348         using namespace LowLevelMemoryUtilities
; 
 352         if (!mDb
.first(key
, value
)) { 
 353                 dump("CODE EQUIVALENTS DATABASE IS EMPTY (%s)\n", how
); 
 355                 dump("CODE EQUIVALENTS DATABASE DUMP (%s)\n", how
); 
 357                         const char *header 
= key
.interpretedAs
<const char>(); 
 358                         size_t headerLength 
= strlen(header
) + 1; 
 360                         dumpData(key
.at(headerLength
), key
.length() - headerLength
); 
 364                 } while (mDb
.next(key
, value
)); 
 369 void CodeSignatures::Identity::debugDump(const char *how
) const 
 371         using namespace Debug
; 
 374         dump("IDENTITY (%s) path=%s", how
, getPath().c_str()); 
 375         dump(" name=%s hash=", mName
.empty() ? "(unset)" : mName
.c_str()); 
 376         CodeSigning::OSXSigner signer
; 
 377         dumpData(getHash(signer
));