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