1/* 2 * Copyright (c) 2000-2004 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#include "SecPassword.h" 25#include "Password.h" 26 27#include "SecBridge.h" 28 29#include "KCExceptions.h" 30#include <Security/Authorization.h> 31#include <Security/AuthorizationTagsPriv.h> 32 33#if 0 34static CFTypeID 35SecPasswordGetTypeID(void) 36{ 37 BEGIN_SECAPI 38 39 return gTypes().PasswordImpl.typeID; 40 41 END_SECAPI1(_kCFRuntimeNotATypeID) 42} 43#endif 44 45OSStatus 46SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef) 47{ 48 BEGIN_SECAPI 49 KCThrowParamErrIf_( (itemRef == NULL) ); 50 KCThrowParamErrIf_( (searchAttrList == NULL) ^ (itemAttrList == NULL) ); // Both or neither 51 52 Password passwordItem(kSecGenericPasswordItemClass, searchAttrList, itemAttrList); 53 if (itemRef) 54 *itemRef = passwordItem->handle(); 55 56 END_SECAPI 57} 58 59OSStatus 60SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef) 61{ 62 BEGIN_SECAPI 63 PasswordImpl::required(itemRef)->setAccess(Access::required(accessRef)); 64 END_SECAPI 65} 66 67OSStatus 68SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data) 69{ 70 BEGIN_SECAPI 71 72 Password passwordRef = PasswordImpl::required(itemRef); 73 74 void *passwordData = NULL; 75 UInt32 passwordLength = 0; 76 bool gotPassword = false; 77 78 // no flags has no meaning, and there is no apparent default 79 assert( flags ); 80 81 // fail can only be combined with get or new 82 assert( (flags & kSecPasswordFail) ? ((flags & kSecPasswordGet) || (flags & kSecPasswordNew)) : true ); 83 84 // XXX/cs replace this with our CFString->UTF8 conversion 85 const char *messageData = NULL; 86 auto_array<char> messageBuffer; 87 88 if (message && (CFStringGetTypeID() == CFGetTypeID(message))) 89 { 90 messageData = CFStringGetCStringPtr(static_cast<CFStringRef>(message), kCFStringEncodingUTF8); 91 92 if (messageData == NULL) 93 { 94 CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef>(message)), kCFStringEncodingUTF8) + 1; 95 96 messageBuffer.allocate(maxLen); 97 if (CFStringGetCString(static_cast<CFStringRef>(message), messageBuffer.get(), maxLen, kCFStringEncodingUTF8)) 98 messageData = messageBuffer.get(); 99 } 100 } 101 102 if (passwordRef->useKeychain() && !(flags & kSecPasswordNew) && !(flags & kSecPasswordFail)) 103 { 104 // Pull out data and if it's successful return it 105 if (flags & kSecPasswordGet) 106 { 107 108 // XXX/cs if there are unsaved changes this doesn't work 109 // so doing a Get followed by a Get|Set will do the wrong thing 110 111 // check mItem whether it's got data 112 if (passwordRef->getData(length, data)) 113 return errSecSuccess; 114 } 115 116 // User might cancel here, immediately return that too (it will be thrown) 117 } 118 119 // If we're still here we're not using the keychain or it wasn't there yet 120 121 // Do the authorization call to get the password, unless only kSecPasswordSet is specified) 122 if ((flags & kSecPasswordNew) || (flags & kSecPasswordGet)) 123 { 124 AuthorizationRef authRef; 125 OSStatus status = AuthorizationCreate(NULL,NULL,0,&authRef); 126 if (status != errSecSuccess) 127 { 128 MacOSError::throwMe(status); 129 } 130 131 AuthorizationItem right = { NULL, 0, NULL, 0 }; 132 AuthorizationItemSet rightSet = { 1, &right }; 133 uint32_t reason, tries; 134 bool keychain = 0, addToKeychain = 0; 135 136 if (passwordRef->useKeychain()) 137 { 138 keychain = 1; 139 addToKeychain = passwordRef->rememberInKeychain(); 140 } 141 else 142 { 143 keychain = 0; 144 addToKeychain = 0; 145 } 146 147 // Get|Fail conceivable would have it enabled, but since the effect is that it will get overwritten 148 // we'll make the user explicitly do it 149 if (flags & kSecPasswordGet) 150 addToKeychain = 0; // turn it off for old items that weren't successfully retrieved from the keychain 151 152 if (flags & kSecPasswordFail) // set up retry to reflect failure 153 { 154 tries = 1; 155 if (flags & kSecPasswordNew) 156 reason = 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason 157 else 158 reason = 21; // invalidPassphrase = 21 passphrase was wrong 159 } 160 else 161 { 162 reason = 0; 163 tries = 0; 164 } 165 166 if (flags & kSecPasswordNew) // pick new passphrase 167 right.name = "com.apple.builtin.generic-new-passphrase"; 168 else 169 right.name = "com.apple.builtin.generic-unlock"; 170 171 bool showPassword = false; 172 173 AuthorizationItem envRights[6] = { { AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 }, 174 { AGENT_HINT_TRIES, sizeof(tries), &tries, 0 }, 175 { AGENT_HINT_CUSTOM_PROMPT, messageData ? strlen(messageData) : 0, const_cast<char*>(messageData), 0 }, 176 { AGENT_HINT_ALLOW_SHOW_PASSWORD, showPassword ? strlen("YES") : strlen("NO"), const_cast<char *>(showPassword ? "YES" : "NO"), 0 }, 177 { AGENT_HINT_SHOW_ADD_TO_KEYCHAIN, keychain ? strlen("YES") : strlen("NO"), const_cast<char *>(keychain ? "YES" : "NO"), 0 }, 178 { AGENT_ADD_TO_KEYCHAIN, addToKeychain ? strlen("YES") : strlen("NO"), const_cast<char *>(addToKeychain ? "YES" : "NO"), 0 } }; 179 180 AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights }; 181 182 secdebug("SecPassword", "dialog(%s)%s%s%s.", right.name, tries?" retry":"", keychain?" show-add-keychain":"", addToKeychain?" save-to-keychain":""); 183 184 status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL); 185 186 if (status) 187 { 188 AuthorizationFree(authRef, 0); 189 return status; 190 } 191 192 // if success pull the data 193 AuthorizationItemSet *returnedInfo; 194 status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo); 195 196 if (status) 197 { 198 AuthorizationFree(authRef, 0); 199 200 return status; 201 } 202 203 if (returnedInfo && (returnedInfo->count > 0)) 204 { 205 for (uint32_t index = 0; index < returnedInfo->count; index++) 206 { 207 AuthorizationItem &item = returnedInfo->items[index]; 208 209 if (!strcmp(AGENT_PASSWORD, item.name)) 210 { 211 gotPassword = true; 212 passwordLength = (UInt32)item.valueLength; 213 214 if (passwordLength) 215 { 216 Allocator &allocator = Allocator::standard(); 217 passwordData = allocator.malloc(passwordLength); 218 if (passwordData) 219 memcpy(passwordData, item.value, passwordLength); 220 } 221 222 if (length) 223 *length = passwordLength; 224 if (data) 225 *data = passwordData; 226 227 secdebug("SecPassword", "Got password (%u,%p).", (unsigned int)passwordLength, passwordData); 228 } 229 else if (!strcmp(AGENT_ADD_TO_KEYCHAIN, item.name)) 230 { 231 bool remember = (item.value && item.valueLength == strlen("YES") && !memcmp("YES", static_cast<char *>(item.value), item.valueLength)); 232 passwordRef->setRememberInKeychain(remember); 233 if (remember) 234 secdebug("SecPassword", "User wants to add the password to the Keychain."); 235 } 236 } 237 } 238 239 AuthorizationFreeItemSet(returnedInfo); 240 AuthorizationFree(authRef, 0); 241 242 } 243 244 // If we're still here the use gave us his password, store it if keychain is in use 245 if (passwordRef->useKeychain()) 246 { 247 if (passwordRef->rememberInKeychain()) { 248 if (gotPassword) 249 passwordRef->setData(passwordLength, passwordData); 250 if (flags & kSecPasswordSet) 251 { 252 passwordRef->save(); 253 gotPassword = true; 254 } 255 } 256 } 257 258 if (!gotPassword) 259 { 260 return errAuthorizationDenied; 261 } 262 263 END_SECAPI 264} 265