2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecPassword.h"
27 #include "SecBridge.h"
29 #include "KCExceptions.h"
30 #include <Security/Authorization.h>
31 #include <Security/AuthorizationTagsPriv.h>
33 #include <os/activity.h>
35 #include "LegacyAPICounts.h"
38 SecGenericPasswordCreate(SecKeychainAttributeList
*searchAttrList
, SecKeychainAttributeList
*itemAttrList
, SecPasswordRef
*itemRef
)
41 os_activity_t activity
= os_activity_create("SecGenericPasswordCreate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
42 os_activity_scope(activity
);
44 KCThrowParamErrIf_( (itemRef
== NULL
) );
45 KCThrowParamErrIf_( (searchAttrList
== NULL
) ^ (itemAttrList
== NULL
) ); // Both or neither
47 Password
passwordItem(kSecGenericPasswordItemClass
, searchAttrList
, itemAttrList
);
49 *itemRef
= passwordItem
->handle();
55 SecPasswordSetInitialAccess(SecPasswordRef itemRef
, SecAccessRef accessRef
)
58 os_activity_t activity
= os_activity_create("SecPasswordSetInitialAccess", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
59 os_activity_scope(activity
);
61 PasswordImpl::required(itemRef
)->setAccess(Access::required(accessRef
));
66 SecPasswordAction(SecPasswordRef itemRef
, CFTypeRef message
, UInt32 flags
, UInt32
*length
, const void **data
)
69 os_activity_t activity
= os_activity_create("SecPasswordAction", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
70 os_activity_scope(activity
);
73 Password passwordRef
= PasswordImpl::required(itemRef
);
75 void *passwordData
= NULL
;
76 UInt32 passwordLength
= 0;
77 bool gotPassword
= false;
79 // no flags has no meaning, and there is no apparent default
82 // fail can only be combined with get or new
83 assert( (flags
& kSecPasswordFail
) ? ((flags
& kSecPasswordGet
) || (flags
& kSecPasswordNew
)) : true );
85 // XXX/cs replace this with our CFString->UTF8 conversion
86 const char *messageData
= NULL
;
87 auto_array
<char> messageBuffer
;
89 if (message
&& (CFStringGetTypeID() == CFGetTypeID(message
)))
91 messageData
= CFStringGetCStringPtr(static_cast<CFStringRef
>(message
), kCFStringEncodingUTF8
);
93 if (messageData
== NULL
)
95 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef
>(message
)), kCFStringEncodingUTF8
) + 1;
97 messageBuffer
.allocate(maxLen
);
98 if (CFStringGetCString(static_cast<CFStringRef
>(message
), messageBuffer
.get(), maxLen
, kCFStringEncodingUTF8
))
99 messageData
= messageBuffer
.get();
103 if (passwordRef
->useKeychain() && !(flags
& kSecPasswordNew
) && !(flags
& kSecPasswordFail
))
105 // Pull out data and if it's successful return it
106 if (flags
& kSecPasswordGet
)
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
112 // check mItem whether it's got data
113 if (passwordRef
->getData(length
, data
))
114 return errSecSuccess
;
117 // User might cancel here, immediately return that too (it will be thrown)
120 // If we're still here we're not using the keychain or it wasn't there yet
122 // Do the authorization call to get the password, unless only kSecPasswordSet is specified)
123 if ((flags
& kSecPasswordNew
) || (flags
& kSecPasswordGet
))
125 AuthorizationRef authRef
;
126 OSStatus status
= AuthorizationCreate(NULL
,NULL
,0,&authRef
);
127 if (status
!= errSecSuccess
)
129 MacOSError::throwMe(status
);
132 AuthorizationItem right
= { NULL
, 0, NULL
, 0 };
133 AuthorizationItemSet rightSet
= { 1, &right
};
134 uint32_t reason
, tries
;
135 bool keychain
= 0, addToKeychain
= 0;
137 if (passwordRef
->useKeychain())
140 addToKeychain
= passwordRef
->rememberInKeychain();
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
153 if (flags
& kSecPasswordFail
) // set up retry to reflect failure
156 if (flags
& kSecPasswordNew
)
157 reason
= 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason
159 reason
= 21; // invalidPassphrase = 21 passphrase was wrong
167 if (flags
& kSecPasswordNew
) // pick new passphrase
168 right
.name
= "com.apple.builtin.generic-new-passphrase";
170 right
.name
= "com.apple.builtin.generic-unlock";
172 bool showPassword
= false;
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 } };
181 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
183 secinfo("SecPassword", "dialog(%s)%s%s%s.", right
.name
, tries
?" retry":"", keychain
?" show-add-keychain":"", addToKeychain
?" save-to-keychain":"");
185 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
189 AuthorizationFree(authRef
, 0);
193 // if success pull the data
194 AuthorizationItemSet
*returnedInfo
;
195 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
199 AuthorizationFree(authRef
, 0);
204 if (returnedInfo
&& (returnedInfo
->count
> 0))
206 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++)
208 AuthorizationItem
&item
= returnedInfo
->items
[index
];
210 if (!strcmp(AGENT_PASSWORD
, item
.name
))
213 passwordLength
= (UInt32
)item
.valueLength
;
217 Allocator
&allocator
= Allocator::standard();
218 passwordData
= allocator
.malloc(passwordLength
);
220 memcpy(passwordData
, item
.value
, passwordLength
);
224 *length
= passwordLength
;
226 *data
= passwordData
;
228 secinfo("SecPassword", "Got password (%u,%p).", (unsigned int)passwordLength
, passwordData
);
230 else if (!strcmp(AGENT_ADD_TO_KEYCHAIN
, item
.name
))
232 bool remember
= (item
.value
&& item
.valueLength
== strlen("YES") && !memcmp("YES", static_cast<char *>(item
.value
), item
.valueLength
));
233 passwordRef
->setRememberInKeychain(remember
);
235 secinfo("SecPassword", "User wants to add the password to the Keychain.");
241 AuthorizationFreeItemSet(returnedInfo
);
243 AuthorizationFree(authRef
, 0);
247 // If we're still here the user gave us their password, store it if keychain is in use
248 if (passwordRef
->useKeychain())
250 if (passwordRef
->rememberInKeychain()) {
252 passwordRef
->setData(passwordLength
, passwordData
);
253 if (flags
& kSecPasswordSet
)
263 return errAuthorizationDenied
;