/ OSX / libsecurity_codesigning / lib / csdatabase.cpp
csdatabase.cpp
  1  /*
  2   * Copyright (c) 2006-2007,2011-2013 Apple Inc. All Rights Reserved.
  3   * 
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  //
 25  // csdb - system-supported Code Signing related database interfaces
 26  //
 27  #include "csdatabase.h"
 28  #include "detachedrep.h"
 29  
 30  namespace Security {
 31  namespace CodeSigning {
 32  
 33  using namespace SQLite;
 34  
 35  
 36  //
 37  // The one and only SignatureDatabase object.
 38  // It auto-adapts to readonly vs. writable use.
 39  //
 40  ModuleNexus<SignatureDatabase> signatureDatabase;
 41  ModuleNexus<SignatureDatabaseWriter> signatureDatabaseWriter;
 42  
 43  
 44  //
 45  // Default path to the signature database.
 46  //
 47  const char SignatureDatabase::defaultPath[] = "/var/db/DetachedSignatures";
 48  
 49  
 50  //
 51  // Creation commands to initialize the system database.
 52  //
 53  const char schema[] = "\
 54  	create table if not exists code ( \n\
 55  		id integer primary key on conflict replace autoincrement not null, \n\
 56  		global integer null references global (id), \n\
 57  		identifier text not null, \n\
 58  		architecture integer, \n\
 59  		identification blob not null unique on conflict replace, \n\
 60  		signature blob not null, \n\
 61  		created text default current_timestamp \n\
 62  	); \n\
 63  	create index if not exists identifier_index on code (identifier); \n\
 64  	create index if not exists architecture_index on code (architecture); \n\
 65  	create index if not exists id_index on code (identification); \n\
 66  	\n\
 67  	create table if not exists global ( \n\
 68  		id integer primary key on conflict replace autoincrement not null, \n\
 69  		sign_location text not null, \n\
 70  		signature blob null \n\
 71  	); \n\
 72  	create index if not exists location_index on global (sign_location); \n\
 73  ";
 74  
 75  
 76  
 77  //
 78  // Open the database (creating it if necessary and possible).
 79  // Note that this isn't creating the schema; we do that on first write.
 80  //
 81  SignatureDatabase::SignatureDatabase(const char *path, int flags)
 82  	: SQLite::Database(path, flags, true)	// lenient open
 83  {
 84  }
 85  
 86  SignatureDatabase::~SignatureDatabase()
 87  { /* virtual */ }
 88  
 89  
 90  //
 91  // Consult the database to find code by identification blob.
 92  // Return the signature and (optional) global data blobs.
 93  //
 94  FilterRep *SignatureDatabase::findCode(DiskRep *rep)
 95  {
 96  	if (CFRef<CFDataRef> identification = rep->identification())
 97  		if (!this->empty()) {
 98  			SQLite::Statement query(*this,
 99  				"select code.signature, global.signature from code, global \
100  				 where code.identification = ?1 and code.global = global.id;");
101  			query.bind(1) = identification.get();
102  			if (query.nextRow()) {
103  				CFRef<CFDataRef> sig = query[0].data();
104  				CFRef<CFDataRef> gsig = query[1].data();
105  				return new DetachedRep(sig, gsig, rep, "system");
106  			}
107  		}
108  
109  	// no joy
110  	return NULL;
111  }
112  
113  
114  //
115  // Given a unified detached signature blob, store its data in the database.
116  // This writes exactly one Global record, plus one Code record per architecture
117  // (where non-architectural code is treated as single-architecture).
118  //
119  void SignatureDatabaseWriter::storeCode(const BlobCore *sig, const char *location)
120  {
121  	if (!this->isOpen())	// failed database open or creation
122  		MacOSError::throwMe(errSecCSDBAccess);
123  	Transaction xa(*this, Transaction::exclusive);	// lock out everyone
124  	if (this->empty())
125  		this->execute(schema);					// initialize schema
126  	if (const EmbeddedSignatureBlob *esig = EmbeddedSignatureBlob::specific(sig)) {	// architecture-less
127  		int64 globid = insertGlobal(location, NULL);
128  		insertCode(globid, 0, esig);
129  		xa.commit();
130  		return;
131  	} else if (const DetachedSignatureBlob *dsblob = DetachedSignatureBlob::specific(sig)) {
132  		int64 globid = insertGlobal(location, dsblob->find(0));
133  		unsigned count = dsblob->count();
134  		for (unsigned n = 0; n < count; n++)
135  			if (uint32_t arch = dsblob->type(n))
136  				insertCode(globid, arch, EmbeddedSignatureBlob::specific(dsblob->blob(n)));
137  		xa.commit();
138  		return;
139  	}
140  	
141  	MacOSError::throwMe(errSecCSSignatureInvalid);
142  
143  }
144  
145  int64 SignatureDatabaseWriter::insertGlobal(const char *location, const BlobCore *blob)
146  {
147  	Statement insert(*this, "insert into global (sign_location, signature) values (?1, ?2);");
148  	insert.bind(1) = location;
149  	if (blob)
150  		insert.bind(2).blob(blob, blob->length(), true);
151  	insert();
152  	return lastInsert();
153  }
154  
155  void SignatureDatabaseWriter::insertCode(int64 globid, int arch, const EmbeddedSignatureBlob *sig)
156  {
157  	// retrieve binary identifier (was added by signer)
158  	const BlobWrapper *ident = BlobWrapper::specific(sig->find(cdIdentificationSlot));
159  	assert(ident);
160  	
161  	// extract CodeDirectory to get some information from it
162  	const CodeDirectory *cd = CodeDirectory::specific(sig->find(cdCodeDirectorySlot));
163  	assert(cd);
164  
165  	// write the record
166  	Statement insert(*this,
167  		"insert into code (global, identifier, architecture, identification, signature) values (?1, ?2, ?3, ?4, ?5);");
168  	insert.bind(1) = globid;
169  	insert.bind(2) = cd->identifier();
170  	if (arch)
171  		insert.bind(3) = arch;
172  	insert.bind(4).blob(ident->data(), ident->length(), true);
173  	insert.bind(5).blob(sig, sig->length(), true);
174  	insert();
175  }
176  
177  
178  
179  } // end namespace CodeSigning
180  } // end namespace Security