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>
35 SecPasswordGetTypeID(void)
39 return gTypes().PasswordImpl
.typeID
;
41 END_SECAPI1(_kCFRuntimeNotATypeID
)
46 SecGenericPasswordCreate(SecKeychainAttributeList
*searchAttrList
, SecKeychainAttributeList
*itemAttrList
, SecPasswordRef
*itemRef
)
49 KCThrowParamErrIf_( (itemRef
== NULL
) );
50 KCThrowParamErrIf_( (searchAttrList
== NULL
) ^ (itemAttrList
== NULL
) ); // Both or neither
52 Password
passwordItem(kSecGenericPasswordItemClass
, searchAttrList
, itemAttrList
);
54 *itemRef
= passwordItem
->handle();
60 SecPasswordSetInitialAccess(SecPasswordRef itemRef
, SecAccessRef accessRef
)
63 PasswordImpl::required(itemRef
)->setAccess(Access::required(accessRef
));
68 SecPasswordAction(SecPasswordRef itemRef
, CFTypeRef message
, UInt32 flags
, UInt32
*length
, const void **data
)
72 Password passwordRef
= PasswordImpl::required(itemRef
);
74 void *passwordData
= NULL
;
75 UInt32 passwordLength
= 0;
76 bool gotPassword
= false;
78 // no flags has no meaning, and there is no apparent default
81 // fail can only be combined with get or new
82 assert( (flags
& kSecPasswordFail
) ? ((flags
& kSecPasswordGet
) || (flags
& kSecPasswordNew
)) : true );
84 // XXX/cs replace this with our CFString->UTF8 conversion
85 const char *messageData
= NULL
;
86 auto_array
<char> messageBuffer
;
88 if (message
&& (CFStringGetTypeID() == CFGetTypeID(message
)))
90 messageData
= CFStringGetCStringPtr(static_cast<CFStringRef
>(message
), kCFStringEncodingUTF8
);
92 if (messageData
== NULL
)
94 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef
>(message
)), kCFStringEncodingUTF8
) + 1;
96 messageBuffer
.allocate(maxLen
);
97 if (CFStringGetCString(static_cast<CFStringRef
>(message
), messageBuffer
.get(), maxLen
, kCFStringEncodingUTF8
))
98 messageData
= messageBuffer
.get();
102 if (passwordRef
->useKeychain() && !(flags
& kSecPasswordNew
) && !(flags
& kSecPasswordFail
))
104 // Pull out data and if it's successful return it
105 if (flags
& kSecPasswordGet
)
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
111 // check mItem whether it's got data
112 if (passwordRef
->getData(length
, data
))
113 return errSecSuccess
;
116 // User might cancel here, immediately return that too (it will be thrown)
119 // If we're still here we're not using the keychain or it wasn't there yet
121 // Do the authorization call to get the password, unless only kSecPasswordSet is specified)
122 if ((flags
& kSecPasswordNew
) || (flags
& kSecPasswordGet
))
124 AuthorizationRef authRef
;
125 OSStatus status
= AuthorizationCreate(NULL
,NULL
,0,&authRef
);
126 if (status
!= errSecSuccess
)
128 MacOSError::throwMe(status
);
131 AuthorizationItem right
= { NULL
, 0, NULL
, 0 };
132 AuthorizationItemSet rightSet
= { 1, &right
};
133 uint32_t reason
, tries
;
134 bool keychain
= 0, addToKeychain
= 0;
136 if (passwordRef
->useKeychain())
139 addToKeychain
= passwordRef
->rememberInKeychain();
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
152 if (flags
& kSecPasswordFail
) // set up retry to reflect failure
155 if (flags
& kSecPasswordNew
)
156 reason
= 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason
158 reason
= 21; // invalidPassphrase = 21 passphrase was wrong
166 if (flags
& kSecPasswordNew
) // pick new passphrase
167 right
.name
= "com.apple.builtin.generic-new-passphrase";
169 right
.name
= "com.apple.builtin.generic-unlock";
171 bool showPassword
= false;
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 } };
180 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
182 secdebug("SecPassword", "dialog(%s)%s%s%s.", right
.name
, tries
?" retry":"", keychain
?" show-add-keychain":"", addToKeychain
?" save-to-keychain":"");
184 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
188 AuthorizationFree(authRef
, 0);
192 // if success pull the data
193 AuthorizationItemSet
*returnedInfo
;
194 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
198 AuthorizationFree(authRef
, 0);
203 if (returnedInfo
&& (returnedInfo
->count
> 0))
205 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++)
207 AuthorizationItem
&item
= returnedInfo
->items
[index
];
209 if (!strcmp(AGENT_PASSWORD
, item
.name
))
212 passwordLength
= (UInt32
)item
.valueLength
;
216 Allocator
&allocator
= Allocator::standard();
217 passwordData
= allocator
.malloc(passwordLength
);
219 memcpy(passwordData
, item
.value
, passwordLength
);
223 *length
= passwordLength
;
225 *data
= passwordData
;
227 secdebug("SecPassword", "Got password (%u,%p).", (unsigned int)passwordLength
, passwordData
);
229 else if (!strcmp(AGENT_ADD_TO_KEYCHAIN
, item
.name
))
231 bool remember
= (item
.value
&& item
.valueLength
== strlen("YES") && !memcmp("YES", static_cast<char *>(item
.value
), item
.valueLength
));
232 passwordRef
->setRememberInKeychain(remember
);
234 secdebug("SecPassword", "User wants to add the password to the Keychain.");
239 AuthorizationFreeItemSet(returnedInfo
);
240 AuthorizationFree(authRef
, 0);
244 // If we're still here the use gave us his password, store it if keychain is in use
245 if (passwordRef
->useKeychain())
247 if (passwordRef
->rememberInKeychain()) {
249 passwordRef
->setData(passwordLength
, passwordData
);
250 if (flags
& kSecPasswordSet
)
260 return errAuthorizationDenied
;