]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/SecPassword.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecPassword.cpp
diff --git a/Security/libsecurity_keychain/lib/SecPassword.cpp b/Security/libsecurity_keychain/lib/SecPassword.cpp
new file mode 100644 (file)
index 0000000..c5989d0
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * 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
+}