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