--- /dev/null
+/*
+ * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "SecPassword.h"
+#include "Password.h"
+
+#include "SecBridge.h"
+
+#include "KCExceptions.h"
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTagsPriv.h>
+
+#if 0
+static CFTypeID
+SecPasswordGetTypeID(void)
+{
+ BEGIN_SECAPI
+
+ return gTypes().PasswordImpl.typeID;
+
+ END_SECAPI1(_kCFRuntimeNotATypeID)
+}
+#endif
+
+OSStatus
+SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef)
+{
+ BEGIN_SECAPI
+ KCThrowParamErrIf_( (itemRef == NULL) );
+ KCThrowParamErrIf_( (searchAttrList == NULL) ^ (itemAttrList == NULL) ); // Both or neither
+
+ Password passwordItem(kSecGenericPasswordItemClass, searchAttrList, itemAttrList);
+ if (itemRef)
+ *itemRef = passwordItem->handle();
+
+ END_SECAPI
+}
+
+OSStatus
+SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef)
+{
+ BEGIN_SECAPI
+ PasswordImpl::required(itemRef)->setAccess(Access::required(accessRef));
+ END_SECAPI
+}
+
+OSStatus
+SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data)
+{
+ BEGIN_SECAPI
+
+ Password passwordRef = PasswordImpl::required(itemRef);
+
+ void *passwordData = NULL;
+ UInt32 passwordLength = 0;
+ bool gotPassword = false;
+
+ // no flags has no meaning, and there is no apparent default
+ assert( flags );
+
+ // fail can only be combined with get or new
+ assert( (flags & kSecPasswordFail) ? ((flags & kSecPasswordGet) || (flags & kSecPasswordNew)) : true );
+
+ // XXX/cs replace this with our CFString->UTF8 conversion
+ const char *messageData = NULL;
+ auto_array<char> messageBuffer;
+
+ if (message && (CFStringGetTypeID() == CFGetTypeID(message)))
+ {
+ messageData = CFStringGetCStringPtr(static_cast<CFStringRef>(message), kCFStringEncodingUTF8);
+
+ if (messageData == NULL)
+ {
+ CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef>(message)), kCFStringEncodingUTF8) + 1;
+
+ messageBuffer.allocate(maxLen);
+ if (CFStringGetCString(static_cast<CFStringRef>(message), messageBuffer.get(), maxLen, kCFStringEncodingUTF8))
+ messageData = messageBuffer.get();
+ }
+ }
+
+ if (passwordRef->useKeychain() && !(flags & kSecPasswordNew) && !(flags & kSecPasswordFail))
+ {
+ // Pull out data and if it's successful return it
+ if (flags & kSecPasswordGet)
+ {
+
+ // XXX/cs if there are unsaved changes this doesn't work
+ // so doing a Get followed by a Get|Set will do the wrong thing
+
+ // check mItem whether it's got data
+ if (passwordRef->getData(length, data))
+ return errSecSuccess;
+ }
+
+ // User might cancel here, immediately return that too (it will be thrown)
+ }
+
+ // If we're still here we're not using the keychain or it wasn't there yet
+
+ // Do the authorization call to get the password, unless only kSecPasswordSet is specified)
+ if ((flags & kSecPasswordNew) || (flags & kSecPasswordGet))
+ {
+ AuthorizationRef authRef;
+ OSStatus status = AuthorizationCreate(NULL,NULL,0,&authRef);
+ if (status != errSecSuccess)
+ {
+ MacOSError::throwMe(status);
+ }
+
+ AuthorizationItem right = { NULL, 0, NULL, 0 };
+ AuthorizationItemSet rightSet = { 1, &right };
+ uint32_t reason, tries;
+ bool keychain = 0, addToKeychain = 0;
+
+ if (passwordRef->useKeychain())
+ {
+ keychain = 1;
+ addToKeychain = passwordRef->rememberInKeychain();
+ }
+ else
+ {
+ keychain = 0;
+ addToKeychain = 0;
+ }
+
+ // Get|Fail conceivable would have it enabled, but since the effect is that it will get overwritten
+ // we'll make the user explicitly do it
+ if (flags & kSecPasswordGet)
+ addToKeychain = 0; // turn it off for old items that weren't successfully retrieved from the keychain
+
+ if (flags & kSecPasswordFail) // set up retry to reflect failure
+ {
+ tries = 1;
+ if (flags & kSecPasswordNew)
+ reason = 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason
+ else
+ reason = 21; // invalidPassphrase = 21 passphrase was wrong
+ }
+ else
+ {
+ reason = 0;
+ tries = 0;
+ }
+
+ if (flags & kSecPasswordNew) // pick new passphrase
+ right.name = "com.apple.builtin.generic-new-passphrase";
+ else
+ right.name = "com.apple.builtin.generic-unlock";
+
+ bool showPassword = false;
+
+ AuthorizationItem envRights[6] = { { AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 },
+ { AGENT_HINT_TRIES, sizeof(tries), &tries, 0 },
+ { AGENT_HINT_CUSTOM_PROMPT, messageData ? strlen(messageData) : 0, const_cast<char*>(messageData), 0 },
+ { AGENT_HINT_ALLOW_SHOW_PASSWORD, showPassword ? strlen("YES") : strlen("NO"), const_cast<char *>(showPassword ? "YES" : "NO"), 0 },
+ { AGENT_HINT_SHOW_ADD_TO_KEYCHAIN, keychain ? strlen("YES") : strlen("NO"), const_cast<char *>(keychain ? "YES" : "NO"), 0 },
+ { AGENT_ADD_TO_KEYCHAIN, addToKeychain ? strlen("YES") : strlen("NO"), const_cast<char *>(addToKeychain ? "YES" : "NO"), 0 } };
+
+ AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights };
+
+ secdebug("SecPassword", "dialog(%s)%s%s%s.", right.name, tries?" retry":"", keychain?" show-add-keychain":"", addToKeychain?" save-to-keychain":"");
+
+ status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL);
+
+ if (status)
+ {
+ AuthorizationFree(authRef, 0);
+ return status;
+ }
+
+ // if success pull the data
+ AuthorizationItemSet *returnedInfo;
+ status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo);
+
+ if (status)
+ {
+ AuthorizationFree(authRef, 0);
+
+ return status;
+ }
+
+ if (returnedInfo && (returnedInfo->count > 0))
+ {
+ for (uint32_t index = 0; index < returnedInfo->count; index++)
+ {
+ AuthorizationItem &item = returnedInfo->items[index];
+
+ if (!strcmp(AGENT_PASSWORD, item.name))
+ {
+ gotPassword = true;
+ passwordLength = (UInt32)item.valueLength;
+
+ if (passwordLength)
+ {
+ Allocator &allocator = Allocator::standard();
+ passwordData = allocator.malloc(passwordLength);
+ if (passwordData)
+ memcpy(passwordData, item.value, passwordLength);
+ }
+
+ if (length)
+ *length = passwordLength;
+ if (data)
+ *data = passwordData;
+
+ secdebug("SecPassword", "Got password (%u,%p).", (unsigned int)passwordLength, passwordData);
+ }
+ else if (!strcmp(AGENT_ADD_TO_KEYCHAIN, item.name))
+ {
+ bool remember = (item.value && item.valueLength == strlen("YES") && !memcmp("YES", static_cast<char *>(item.value), item.valueLength));
+ passwordRef->setRememberInKeychain(remember);
+ if (remember)
+ secdebug("SecPassword", "User wants to add the password to the Keychain.");
+ }
+ }
+ }
+
+ AuthorizationFreeItemSet(returnedInfo);
+ AuthorizationFree(authRef, 0);
+
+ }
+
+ // If we're still here the use gave us his password, store it if keychain is in use
+ if (passwordRef->useKeychain())
+ {
+ if (passwordRef->rememberInKeychain()) {
+ if (gotPassword)
+ passwordRef->setData(passwordLength, passwordData);
+ if (flags & kSecPasswordSet)
+ {
+ passwordRef->save();
+ gotPassword = true;
+ }
+ }
+ }
+
+ if (!gotPassword)
+ {
+ return errAuthorizationDenied;
+ }
+
+ END_SECAPI
+}