1/* 2 * Copyright (c) 2004-2006 Apple Computer, 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 * keyPicker.cpp - select a key pair from a keychain 26 */ 27 28#include "keyPicker.h" 29#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 30#include <Security/Security.h> 31#include <stdexcept> 32#include <ctype.h> 33#include <clAppUtils/identPicker.h> /* for kcFileName() */ 34#include <vector> 35 36/* 37 * Obtain either public key hash or PrintName for a given SecKeychainItem. Works on public keys, 38 * private keys, identities, and certs. Caller must release the returned result. 39 */ 40OSStatus getKcItemAttr( 41 SecKeychainItemRef kcItem, 42 WhichAttr whichAttr, 43 CFDataRef *rtnAttr) /* RETURNED */ 44{ 45 /* main job is to figure out which attrType to ask for, and from what Sec item */ 46 SecKeychainItemRef attrFromThis; 47 SecKeychainAttrType attrType = 0; 48 OSStatus ortn; 49 bool releaseKcItem = false; 50 51 CFTypeID cfId = CFGetTypeID(kcItem); 52 if(cfId == SecIdentityGetTypeID()) { 53 /* switch over to cert */ 54 ortn = SecIdentityCopyCertificate((SecIdentityRef)kcItem, 55 (SecCertificateRef *)&attrFromThis); 56 if(ortn) 57 cssmPerror("SecIdentityCopyCertificate", ortn); 58 return ortn; 59 kcItem = attrFromThis; 60 releaseKcItem = true; 61 cfId = SecCertificateGetTypeID(); 62 } 63 64 if(cfId == SecCertificateGetTypeID()) { 65 switch(whichAttr) { 66 case WA_Hash: 67 attrType = kSecPublicKeyHashItemAttr; 68 break; 69 case WA_PrintName: 70 attrType = kSecLabelItemAttr; 71 break; 72 default: 73 printf("getKcItemAttr: WhichAttr\n"); 74 return paramErr; 75 } 76 } 77 else if(cfId == SecKeyGetTypeID()) { 78 switch(whichAttr) { 79 case WA_Hash: 80 attrType = kSecKeyLabel; 81 break; 82 case WA_PrintName: 83 attrType = kSecKeyPrintName; 84 break; 85 default: 86 printf("getKcItemAttr: WhichAttr\n"); 87 return paramErr; 88 } 89 } 90 91 SecKeychainAttributeInfo attrInfo; 92 attrInfo.count = 1; 93 attrInfo.tag = &attrType; 94 attrInfo.format = NULL; // ??? 95 SecKeychainAttributeList *attrList = NULL; 96 97 ortn = SecKeychainItemCopyAttributesAndData( 98 kcItem, 99 &attrInfo, 100 NULL, // itemClass 101 &attrList, 102 NULL, // don't need the data 103 NULL); 104 if(releaseKcItem) { 105 CFRelease(kcItem); 106 } 107 if(ortn) { 108 cssmPerror("SecKeychainItemCopyAttributesAndData", ortn); 109 return paramErr; 110 } 111 SecKeychainAttribute *attr = attrList->attr; 112 *rtnAttr = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length); 113 SecKeychainItemFreeAttributesAndData(attrList, NULL); 114 return noErr; 115} 116 117/* 118 * Class representing one key in the keychain. 119 */ 120class PickerKey 121{ 122public: 123 PickerKey(SecKeyRef keyRef); 124 ~PickerKey(); 125 126 bool isUsed() { return mIsUsed;} 127 void isUsed(bool u) { mIsUsed = u; } 128 bool isPrivate() { return mIsPrivate; } 129 CFDataRef getPrintName() { return mPrintName; } 130 CFDataRef getPubKeyHash() { return mPubKeyHash; } 131 SecKeyRef keyRef() { return mKeyRef; } 132 133 PickerKey *partnerKey() { return mPartner; } 134 void partnerKey(PickerKey *pk) { mPartner = pk; } 135 char *kcFile() { return mKcFile; } 136 137private: 138 SecKeyRef mKeyRef; 139 CFDataRef mPrintName; 140 CFDataRef mPubKeyHash; 141 bool mIsPrivate; // private/public key 142 bool mIsUsed; // has been spoken for 143 PickerKey *mPartner; // other member of public/private pair 144 char *mKcFile; // file name of keychain this lives on 145}; 146 147PickerKey::PickerKey(SecKeyRef keyRef) 148 : mKeyRef(NULL), 149 mPrintName(NULL), 150 mPubKeyHash(NULL), 151 mIsPrivate(false), 152 mIsUsed(false), 153 mPartner(NULL), 154 mKcFile(NULL) 155{ 156 if(CFGetTypeID(keyRef) != SecKeyGetTypeID()) { 157 throw std::invalid_argument("not a key"); 158 } 159 160 OSStatus ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_Hash, &mPubKeyHash); 161 if(ortn) { 162 throw std::invalid_argument("pub key hash not available"); 163 } 164 ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_PrintName, &mPrintName); 165 if(ortn) { 166 throw std::invalid_argument("pub key hash not available"); 167 } 168 169 const CSSM_KEY *cssmKey; 170 ortn = SecKeyGetCSSMKey(keyRef, &cssmKey); 171 if(ortn) { 172 /* should never happen */ 173 cssmPerror("SecKeyGetCSSMKey", ortn); 174 throw std::invalid_argument("SecKeyGetCSSMKey error"); 175 } 176 if(cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) { 177 mIsPrivate = true; 178 } 179 180 /* stash name of the keychain this lives on */ 181 SecKeychainRef kcRef; 182 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef); 183 if(ortn) { 184 cssmPerror("SecKeychainItemCopyKeychain", ortn); 185 mKcFile = strdup("Unnamed keychain"); 186 } 187 else { 188 mKcFile = kcFileName(kcRef); 189 } 190 191 mKeyRef = keyRef; 192 CFRetain(mKeyRef); 193} 194 195PickerKey::~PickerKey() 196{ 197 if(mKeyRef) { 198 CFRelease(mKeyRef); 199 } 200 if(mPubKeyHash) { 201 CFRelease(mPubKeyHash); 202 } 203 if(mPrintName) { 204 CFRelease(mPrintName); 205 } 206 if(mKcFile) { 207 free(mKcFile); 208 } 209} 210 211typedef std::vector<PickerKey *> KeyVector; 212 213/* 214 * add PickerKey objects of specified type to a KeyVector. 215 */ 216static void getPickerKeys( 217 SecKeychainRef kcRef, 218 SecItemClass itemClass, // actually CSSM_DL_DB_RECORD_{PRIVATE,PRIVATE}_KEY for now 219 KeyVector &keyVector) 220{ 221 SecKeychainSearchRef srchRef = NULL; 222 SecKeychainItemRef kcItem; 223 224 OSStatus ortn = SecKeychainSearchCreateFromAttributes(kcRef, 225 itemClass, 226 NULL, // any attrs 227 &srchRef); 228 if(ortn) { 229 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); 230 return; 231 } 232 do { 233 ortn = SecKeychainSearchCopyNext(srchRef, &kcItem); 234 if(ortn) { 235 break; 236 } 237 try { 238 PickerKey *pickerKey = new PickerKey((SecKeyRef)kcItem); 239 keyVector.push_back(pickerKey); 240 } 241 catch(...) { 242 printf("**** key item that failed PickerKey construct ***\n"); 243 /* but keep going */ 244 } 245 } while(ortn == noErr); 246 CFRelease(srchRef); 247} 248 249/* 250 * Print contents of a CFData assuming it's printable 251 */ 252static void printCfData(CFDataRef cfd) 253{ 254 CFIndex len = CFDataGetLength(cfd); 255 const UInt8 *cp = CFDataGetBytePtr(cfd); 256 for(CFIndex dex=0; dex<len; dex++) { 257 char c = cp[dex]; 258 if(isprint(c)) { 259 putchar(c); 260 } 261 else { 262 printf(".%02X.", c); 263 } 264 } 265} 266 267OSStatus keyPicker( 268 SecKeychainRef kcRef, // NULL means the default list 269 SecKeyRef *pubKey, // RETURNED 270 SecKeyRef *privKey) // RETURNED 271{ 272 273 /* First create a arrays of all of the keys, parsed and ready for use */ 274 275 std::vector<PickerKey *> privKeys; 276 std::vector<PickerKey *> pubKeys; 277 getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, privKeys); 278 getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, pubKeys); 279 280 /* now interate thru private keys, looking for a partner for each one */ 281 int numPairs = 0; 282 unsigned numPrivKeys = privKeys.size(); 283 unsigned numPubKeys = pubKeys.size(); 284 285 for(unsigned privDex=0; privDex<numPrivKeys; privDex++) { 286 PickerKey *privPk = privKeys[privDex]; 287 CFDataRef privHash = privPk->getPubKeyHash(); 288 for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) { 289 PickerKey *pubPk = pubKeys[pubDex]; 290 if(pubPk->isUsed()) { 291 /* already spoken for */ 292 continue; 293 } 294 if(!CFEqual(privHash, pubPk->getPubKeyHash())) { 295 /* public key hashes don't match */ 296 continue; 297 } 298 299 /* got a match */ 300 pubPk->partnerKey(privPk); 301 privPk->partnerKey(pubPk); 302 pubPk->isUsed(true); 303 privPk->isUsed(true); 304 305 /* display */ 306 printf("[%d] privKey : ", numPairs); printCfData(privPk->getPrintName()); printf("\n"); 307 printf(" pubKey : "); printCfData(pubPk->getPrintName());printf("\n"); 308 printf(" keychain : %s\n", privPk->kcFile()); 309 310 numPairs++; 311 } 312 } 313 314 if(numPairs == 0) { 315 printf("*** keyPicker: no key pairs found.\n"); 316 return paramErr; 317 } 318 319 OSStatus ortn = noErr; 320 int ires; 321 while(1) { 322 fpurge(stdin); 323 printf("\nEnter key pair number or CR to quit : "); 324 fflush(stdout); 325 char resp[64]; 326 getString(resp, sizeof(resp)); 327 if(resp[0] == '\0') { 328 ortn = CSSMERR_CSSM_USER_CANCELED; 329 break; 330 } 331 ires = atoi(resp); 332 if((ires < 0) || (ires >= numPairs)) { 333 printf("***Invalid entry. Type a number between 0 and %d\n", numPairs-1); 334 continue; 335 } 336 break; 337 } 338 339 if(ortn == noErr) { 340 /* find the ires'th partnered private key */ 341 int goodOnes = 0; 342 for(unsigned privDex=0; privDex<numPrivKeys; privDex++) { 343 PickerKey *privPk = privKeys[privDex]; 344 if(!privPk->isUsed()) { 345 continue; 346 } 347 if(goodOnes == ires) { 348 /* this is it */ 349 *privKey = privPk->keyRef(); 350 *pubKey = privPk->partnerKey()->keyRef(); 351 } 352 goodOnes++; 353 } 354 } 355 356 /* clean out PickerKey arrays */ 357 for(unsigned privDex=0; privDex<numPrivKeys; privDex++) { 358 delete privKeys[privDex]; 359 } 360 for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) { 361 delete pubKeys[pubDex]; 362 } 363 return ortn; 364} 365 366