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>
37 SecPasswordGetTypeID(void)
41 return gTypes().PasswordImpl
.typeID
;
43 END_SECAPI1(_kCFRuntimeNotATypeID
)
48 SecGenericPasswordCreate(SecKeychainAttributeList
*searchAttrList
, SecKeychainAttributeList
*itemAttrList
, SecPasswordRef
*itemRef
)
51 os_activity_t activity
= os_activity_create("SecGenericPasswordCreate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
52 os_activity_scope(activity
);
54 KCThrowParamErrIf_( (itemRef
== NULL
) );
55 KCThrowParamErrIf_( (searchAttrList
== NULL
) ^ (itemAttrList
== NULL
) ); // Both or neither
57 Password
passwordItem(kSecGenericPasswordItemClass
, searchAttrList
, itemAttrList
);
59 *itemRef
= passwordItem
->handle();
65 SecPasswordSetInitialAccess(SecPasswordRef itemRef
, SecAccessRef accessRef
)
68 os_activity_t activity
= os_activity_create("SecPasswordSetInitialAccess", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
69 os_activity_scope(activity
);
71 PasswordImpl::required(itemRef
)->setAccess(Access::required(accessRef
));
76 SecPasswordAction(SecPasswordRef itemRef
, CFTypeRef message
, UInt32 flags
, UInt32
*length
, const void **data
)
79 os_activity_t activity
= os_activity_create("SecPasswordAction", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
80 os_activity_scope(activity
);
83 Password passwordRef
= PasswordImpl::required(itemRef
);
85 void *passwordData
= NULL
;
86 UInt32 passwordLength
= 0;
87 bool gotPassword
= false;
89 // no flags has no meaning, and there is no apparent default
92 // fail can only be combined with get or new
93 assert( (flags
& kSecPasswordFail
) ? ((flags
& kSecPasswordGet
) || (flags
& kSecPasswordNew
)) : true );
95 // XXX/cs replace this with our CFString->UTF8 conversion
96 const char *messageData
= NULL
;
97 auto_array
<char> messageBuffer
;
99 if (message
&& (CFStringGetTypeID() == CFGetTypeID(message
)))
101 messageData
= CFStringGetCStringPtr(static_cast<CFStringRef
>(message
), kCFStringEncodingUTF8
);
103 if (messageData
== NULL
)
105 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(static_cast<CFStringRef
>(message
)), kCFStringEncodingUTF8
) + 1;
107 messageBuffer
.allocate(maxLen
);
108 if (CFStringGetCString(static_cast<CFStringRef
>(message
), messageBuffer
.get(), maxLen
, kCFStringEncodingUTF8
))
109 messageData
= messageBuffer
.get();
113 if (passwordRef
->useKeychain() && !(flags
& kSecPasswordNew
) && !(flags
& kSecPasswordFail
))
115 // Pull out data and if it's successful return it
116 if (flags
& kSecPasswordGet
)
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
122 // check mItem whether it's got data
123 if (passwordRef
->getData(length
, data
))
124 return errSecSuccess
;
127 // User might cancel here, immediately return that too (it will be thrown)
130 // If we're still here we're not using the keychain or it wasn't there yet
132 // Do the authorization call to get the password, unless only kSecPasswordSet is specified)
133 if ((flags
& kSecPasswordNew
) || (flags
& kSecPasswordGet
))
135 AuthorizationRef authRef
;
136 OSStatus status
= AuthorizationCreate(NULL
,NULL
,0,&authRef
);
137 if (status
!= errSecSuccess
)
139 MacOSError::throwMe(status
);
142 AuthorizationItem right
= { NULL
, 0, NULL
, 0 };
143 AuthorizationItemSet rightSet
= { 1, &right
};
144 uint32_t reason
, tries
;
145 bool keychain
= 0, addToKeychain
= 0;
147 if (passwordRef
->useKeychain())
150 addToKeychain
= passwordRef
->rememberInKeychain();
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
163 if (flags
& kSecPasswordFail
) // set up retry to reflect failure
166 if (flags
& kSecPasswordNew
)
167 reason
= 34; // passphraseUnacceptable = 34 passphrase unacceptable for some other reason
169 reason
= 21; // invalidPassphrase = 21 passphrase was wrong
177 if (flags
& kSecPasswordNew
) // pick new passphrase
178 right
.name
= "com.apple.builtin.generic-new-passphrase";
180 right
.name
= "com.apple.builtin.generic-unlock";
182 bool showPassword
= false;
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 } };
191 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
193 secinfo("SecPassword", "dialog(%s)%s%s%s.", right
.name
, tries
?" retry":"", keychain
?" show-add-keychain":"", addToKeychain
?" save-to-keychain":"");
195 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
199 AuthorizationFree(authRef
, 0);
203 // if success pull the data
204 AuthorizationItemSet
*returnedInfo
;
205 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
209 AuthorizationFree(authRef
, 0);
214 if (returnedInfo
&& (returnedInfo
->count
> 0))
216 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++)
218 AuthorizationItem
&item
= returnedInfo
->items
[index
];
220 if (!strcmp(AGENT_PASSWORD
, item
.name
))
223 passwordLength
= (UInt32
)item
.valueLength
;
227 Allocator
&allocator
= Allocator::standard();
228 passwordData
= allocator
.malloc(passwordLength
);
230 memcpy(passwordData
, item
.value
, passwordLength
);
234 *length
= passwordLength
;
236 *data
= passwordData
;
238 secinfo("SecPassword", "Got password (%u,%p).", (unsigned int)passwordLength
, passwordData
);
240 else if (!strcmp(AGENT_ADD_TO_KEYCHAIN
, item
.name
))
242 bool remember
= (item
.value
&& item
.valueLength
== strlen("YES") && !memcmp("YES", static_cast<char *>(item
.value
), item
.valueLength
));
243 passwordRef
->setRememberInKeychain(remember
);
245 secinfo("SecPassword", "User wants to add the password to the Keychain.");
251 AuthorizationFreeItemSet(returnedInfo
);
253 AuthorizationFree(authRef
, 0);
257 // If we're still here the use gave us his password, store it if keychain is in use
258 if (passwordRef
->useKeychain())
260 if (passwordRef
->rememberInKeychain()) {
262 passwordRef
->setData(passwordLength
, passwordData
);
263 if (flags
& kSecPasswordSet
)
273 return errAuthorizationDenied
;