2 * Copyright (c) 2000-2004 Apple Computer, 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>
34 SecPasswordGetTypeID(void)
38 return gTypes().PasswordImpl
.typeID
;
40 END_SECAPI1(_kCFRuntimeNotATypeID
)
44 SecGenericPasswordCreate(SecKeychainAttributeList
*searchAttrList
, SecKeychainAttributeList
*itemAttrList
, SecPasswordRef
*itemRef
)
47 KCThrowParamErrIf_( (itemRef
== NULL
) );
48 KCThrowParamErrIf_( (searchAttrList
== NULL
) ^ (itemAttrList
== NULL
) ); // Both or neither
50 Password
passwordItem(kSecGenericPasswordItemClass
, searchAttrList
, itemAttrList
);
52 *itemRef
= passwordItem
->handle();
58 SecPasswordSetInitialAccess(SecPasswordRef itemRef
, SecAccessRef accessRef
)
61 PasswordImpl::required(itemRef
)->setAccess(Access::required(accessRef
));
66 SecPasswordAction(SecPasswordRef itemRef
, CFTypeRef message
, UInt32 flags
, UInt32
*length
, const void **data
)
70 Password passwordRef
= PasswordImpl::required(itemRef
);
72 void *passwordData
= NULL
;
73 uint32_t passwordLength
= 0;
74 bool gotPassword
= false;
76 // no flags has no meaning, and there is no apparent default
79 // fail can only be combined with get or new
80 assert( (flags
& kSecPasswordFail
) ? ((flags
& kSecPasswordGet
) || (flags
& kSecPasswordNew
)) : true );
82 // XXX/cs replace this with our CFString->UTF8 conversion
83 const char *messageData
= NULL
;
84 auto_array
<char> messageBuffer
;
86 if (message
&& (CFStringGetTypeID() == CFGetTypeID(message
)))
88 messageData
= CFStringGetCStringPtr(static_cast<CFStringRef
>(message
), kCFStringEncodingUTF8
);
90 if (messageData
== NULL
)
92 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef
>(message
)), kCFStringEncodingUTF8
) + 1;
94 messageBuffer
.allocate(maxLen
);
95 if (CFStringGetCString(static_cast<CFStringRef
>(message
), messageBuffer
.get(), maxLen
, kCFStringEncodingUTF8
))
96 messageData
= messageBuffer
.get();
100 if (passwordRef
->useKeychain() && !(flags
& kSecPasswordNew
) && !(flags
& kSecPasswordFail
))
102 // Pull out data and if it's successful return it
103 if (flags
& kSecPasswordGet
)
106 // XXX/cs if there are unsaved changes this doesn't work
107 // so doing a Get followed by a Get|Set will do the wrong thing
109 // check mItem whether it's got data
110 if (passwordRef
->getData(length
, data
))
114 // User might cancel here, immediately return that too (it will be thrown)
117 // If we're still here we're not using the keychain or it wasn't there yet
119 // Do the authorization call to get the password, unless only kSecPasswordSet is specified)
120 if ((flags
& kSecPasswordNew
) || (flags
& kSecPasswordGet
))
122 AuthorizationRef authRef
;
123 OSStatus status
= AuthorizationCreate(NULL
,NULL
,0,&authRef
);
124 AuthorizationItem right
= { NULL
, 0, NULL
, 0 };
125 AuthorizationItemSet rightSet
= { 1, &right
};
126 uint32_t reason
, tries
;
127 bool keychain
= 0, addToKeychain
= 0;
129 if (passwordRef
->useKeychain())
132 addToKeychain
= passwordRef
->rememberInKeychain();
140 // Get|Fail conceivable would have it enabled, but since the effect is that it will get overwritten
141 // we'll make the user explicitly do it
142 if (flags
& kSecPasswordGet
)
143 addToKeychain
= 0; // turn it off for old items that weren't successfully retrieved from the keychain
145 if (flags
& kSecPasswordFail
) // set up retry to reflect failure
148 if (flags
& kSecPasswordNew
)
149 reason
= 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason
151 reason
= 21; // invalidPassphrase = 21 passphrase was wrong
159 if (flags
& kSecPasswordNew
) // pick new passphrase
160 right
.name
= "com.apple.builtin.generic-new-passphrase";
162 right
.name
= "com.apple.builtin.generic-unlock";
164 AuthorizationItem envRights
[5] = { { AGENT_HINT_RETRY_REASON
, sizeof(reason
), &reason
, 0 },
165 { AGENT_HINT_TRIES
, sizeof(tries
), &tries
, 0 },
166 { AGENT_HINT_CUSTOM_PROMPT
, messageData
? strlen(messageData
) : 0, const_cast<char*>(messageData
), 0 },
167 { AGENT_HINT_SHOW_ADD_TO_KEYCHAIN
, keychain
? strlen("YES") : strlen("NO"), const_cast<char *>(keychain
? "YES" : "NO"), 0 },
168 { AGENT_ADD_TO_KEYCHAIN
, addToKeychain
? strlen("YES") : strlen("NO"), const_cast<char *>(addToKeychain
? "YES" : "NO"), 0 } };
170 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
172 secdebug("SecPassword", "dialog(%s)%s%s%s.", right
.name
, tries
?" retry":"", keychain
?" show-add-keychain":"", addToKeychain
?" save-to-keychain":"");
174 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
178 AuthorizationFree(authRef
, 0);
182 // if success pull the data
183 AuthorizationItemSet
*returnedInfo
;
184 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
188 AuthorizationFree(authRef
, 0);
193 if (returnedInfo
&& (returnedInfo
->count
> 0))
195 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++)
197 AuthorizationItem
&item
= returnedInfo
->items
[index
];
199 if (!strcmp(AGENT_PASSWORD
, item
.name
))
202 passwordLength
= item
.valueLength
;
206 Allocator
&allocator
= Allocator::standard();
207 passwordData
= allocator
.malloc(passwordLength
);
209 memcpy(passwordData
, item
.value
, passwordLength
);
213 *length
= passwordLength
;
215 *data
= passwordData
;
217 secdebug("SecPassword", "Got password (%d,%p).", passwordLength
, passwordData
);
219 else if (!strcmp(AGENT_ADD_TO_KEYCHAIN
, item
.name
))
221 bool remember
= (item
.value
&& item
.valueLength
== strlen("YES") && !memcmp("YES", static_cast<char *>(item
.value
), item
.valueLength
));
222 passwordRef
->setRememberInKeychain(remember
);
224 secdebug("SecPassword", "User wants to add the password to the Keychain.");
229 AuthorizationFreeItemSet(returnedInfo
);
230 AuthorizationFree(authRef
, 0);
234 // If we're still here the use gave us his password, store it if keychain is in use
235 if (passwordRef
->useKeychain())
237 if (passwordRef
->rememberInKeychain()) {
239 passwordRef
->setData(passwordLength
, passwordData
);
240 if (flags
& kSecPasswordSet
)