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