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