/ securityd / src / kckey.cpp
kckey.cpp
  1  /*
  2   * Copyright (c) 2000-2001,2004-2006,2008-2009 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  //
 26  // key - representation of securityd key objects
 27  //
 28  #include "kckey.h"
 29  #include "server.h"
 30  #include "database.h"
 31  #include <security_cdsa_utilities/acl_any.h>
 32  #include <security_cdsa_utilities/cssmendian.h>
 33  
 34  
 35  //
 36  // Create a Key object from a database-encoded blob.
 37  // Note that this doesn't decode the blob (yet).
 38  //
 39  KeychainKey::KeychainKey(Database &db, const KeyBlob *blob)
 40  	: LocalKey(db, n2h(blob->header.attributes()))
 41  {
 42      // perform basic validation on the incoming blob
 43  	if (blob == NULL) {
 44  		CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
 45  	}
 46      blob->validate(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
 47      if (blob->startCryptoBlob > blob->totalLength) {
 48          CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
 49      }
 50      switch (blob->version()) {
 51  #if defined(COMPAT_OSX_10_0)
 52      case KeyBlob::version_MacOS_10_0:
 53          break;
 54  #endif
 55      case KeyBlob::version_MacOS_10_1:
 56          break;
 57      case KeyBlob::version_partition:
 58          break;
 59      default:
 60          CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_KEY_BLOB);
 61      }
 62  
 63      // set it up
 64      mBlob = blob->copy(Allocator::standard());
 65  	mValidBlob = true;
 66  	db.addReference(*this);
 67      secinfo("SSkey", "%p (handle %#x) created from blob version %x",
 68  		this, handle(), blob->version());
 69  }
 70  
 71  
 72  //
 73  // Create a Key from an explicit CssmKey.
 74  //
 75  KeychainKey::KeychainKey(Database &db, const CssmKey &newKey, uint32 moreAttributes,
 76  	const AclEntryPrototype *owner)
 77  	: LocalKey(db, newKey, moreAttributes)
 78  {
 79  	assert(moreAttributes & CSSM_KEYATTR_PERMANENT);
 80  	setOwner(owner);
 81      mBlob = NULL;
 82  	mValidBlob = false;
 83  	db.addReference(*this);
 84  }
 85  
 86  
 87  KeychainKey::~KeychainKey()
 88  {
 89      Allocator::standard().free(mBlob);
 90      secinfo("SSkey", "%p destroyed", this);
 91  }
 92  
 93  
 94  KeychainDatabase &KeychainKey::database() const
 95  {
 96  	return referent<KeychainDatabase>();
 97  }
 98  
 99  
100  //
101  // Retrieve the actual CssmKey value for the key object.
102  // This will decode its blob if needed (and appropriate).
103  //
104  void KeychainKey::getKey()
105  {
106      decode();
107  }
108  
109  void KeychainKey::getHeader(CssmKey::Header &hdr)
110  {
111  	assert(mValidBlob);
112  	hdr = mBlob->header;
113  	n2hi(hdr);	// correct for endian-ness
114  }
115  
116  
117  //
118  // Ensure that a key is fully decoded.
119  // This makes the mKey key value available for use, as well as its ACL.
120  // Caller must hold the key object lock.
121  //
122  void KeychainKey::decode()
123  {
124  	if (!mValidKey) {
125  		assert(mValidBlob);	// must have a blob to decode
126          
127          // decode the key
128          void *publicAcl, *privateAcl;
129  		CssmKey key;
130          database().decodeKey(mBlob, key, publicAcl, privateAcl);
131  		mKey = CssmClient::Key(Server::csp(), key);
132          acl().importBlob(publicAcl, privateAcl);
133          // publicAcl points into the blob; privateAcl was allocated for us
134          Allocator::standard().free(privateAcl);
135          
136          // extract managed attribute bits
137          mAttributes = mKey.header().attributes() & managedAttributes;
138          mKey.header().clearAttribute(managedAttributes);
139  		mKey.header().setAttribute(forcedAttributes);
140  
141          // key is valid now
142  		mValidKey = true;
143  	}
144  }
145  
146  
147  //
148  // Encode a key into a blob.
149  // We'll have to ask our Database to do this - we don't have its keys.
150  // Note that this returns memory we own and keep.
151  //
152  KeyBlob *KeychainKey::blob()
153  {
154  	if (!mValidBlob) {
155          assert(mValidKey);		// must have valid key to encode
156  
157          // export Key ACL to blob form
158          CssmData pubAcl, privAcl;
159  		acl().exportBlob(pubAcl, privAcl);
160          
161          // assemble external key form
162          CssmKey externalKey = mKey;
163  		externalKey.clearAttribute(forcedAttributes);
164          externalKey.setAttribute(mAttributes);
165  
166          // encode the key and replace blob
167          KeyBlob *newBlob = database().encodeKey(externalKey, pubAcl, privAcl);
168          Allocator::standard().free(mBlob);
169          mBlob = newBlob;
170          mValidBlob = true;
171      
172          // clean up and go
173          acl().allocator.free(pubAcl);
174          acl().allocator.free(privAcl);
175  	}
176  	return mBlob;
177  }
178  
179  void KeychainKey::invalidateBlob()
180  {
181  	mValidBlob = false;
182  }
183  
184  
185  //
186  // Override ACL-related methods and events.
187  // Decode the key before ACL activity; invalidate the stored blob on ACL edits;
188  // and return the key's database as "related".
189  //
190  void KeychainKey::instantiateAcl()
191  {
192  	StLock<Mutex> _(*this);
193  	decode();
194  }
195  
196  void KeychainKey::changedAcl()
197  {
198  	invalidateBlob();
199  }
200  
201  
202  //
203  // Intercept Key validation and double-check that the keychain is (still) unlocked
204  //
205  void KeychainKey::validate(AclAuthorization auth, const AccessCredentials *cred,
206  	Database *relatedDatabase)
207  {
208  	if(!mBlob->isClearText()) {
209  		/* unlock not needed for cleartext keys */
210  		if (KeychainDatabase *db = dynamic_cast<KeychainDatabase *>(relatedDatabase))
211  			db->unlockDb(false);
212  	}
213  	SecurityServerAcl::validate(auth, cred, relatedDatabase);
214  
215      // Need the common lock some more. unlockDb and validate (in validatePartition) also take it, so must be down here.
216      StMaybeLock<Mutex> lock(relatedDatabase && relatedDatabase->hasCommon() ? &(relatedDatabase->common()) : NULL);
217  	database().activity();		// upon successful validation
218  }
219  
220  
221  //
222  // We're a key (duh)
223  //
224  AclKind KeychainKey::aclKind() const
225  {
226  	return keyAcl;
227  }
228  
229  
230  Database *KeychainKey::relatedDatabase()
231  {
232  	return &database();
233  }
234  
235  SecurityServerAcl &KeychainKey::acl()
236  {
237  	return *this;
238  }