Home | History | Annotate | Download | only in crypto
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CRYPTO_MOCK_KEYCHAIN_MAC_H_
      6 #define CRYPTO_MOCK_KEYCHAIN_MAC_H_
      7 
      8 #include <stdint.h>
      9 
     10 #include <map>
     11 #include <set>
     12 #include <string>
     13 #include <vector>
     14 
     15 #include "base/compiler_specific.h"
     16 #include "crypto/apple_keychain.h"
     17 
     18 namespace crypto {
     19 
     20 // Mock Keychain wrapper for testing code that interacts with the OS X
     21 // Keychain.  Implemented by storing SecKeychainAttributeList and
     22 // KeychainPasswordData values in separate mutable containers and
     23 // mapping them to integer keys.
     24 //
     25 // Note that "const" is pretty much meaningless for this class; the const-ness
     26 // of AppleKeychain doesn't apply to the actual keychain data, so all of the
     27 // Mock data is mutable; don't assume that it won't change over the life of
     28 // tests.
     29 class CRYPTO_EXPORT MockAppleKeychain : public AppleKeychain {
     30  public:
     31   MockAppleKeychain();
     32   virtual ~MockAppleKeychain();
     33 
     34   // AppleKeychain implementation.
     35   virtual OSStatus FindGenericPassword(
     36       CFTypeRef keychainOrArray,
     37       UInt32 serviceNameLength,
     38       const char* serviceName,
     39       UInt32 accountNameLength,
     40       const char* accountName,
     41       UInt32* passwordLength,
     42       void** passwordData,
     43       SecKeychainItemRef* itemRef) const OVERRIDE;
     44   virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList,
     45                                    void* data) const OVERRIDE;
     46   virtual OSStatus AddGenericPassword(
     47       SecKeychainRef keychain,
     48       UInt32 serviceNameLength,
     49       const char* serviceName,
     50       UInt32 accountNameLength,
     51       const char* accountName,
     52       UInt32 passwordLength,
     53       const void* passwordData,
     54       SecKeychainItemRef* itemRef) const OVERRIDE;
     55 
     56 #if !defined(OS_IOS)
     57   virtual OSStatus ItemCopyAttributesAndData(
     58       SecKeychainItemRef itemRef,
     59       SecKeychainAttributeInfo* info,
     60       SecItemClass* itemClass,
     61       SecKeychainAttributeList** attrList,
     62       UInt32* length,
     63       void** outData) const OVERRIDE;
     64   // Pass "fail_me" as the data to get errSecAuthFailed.
     65   virtual OSStatus ItemModifyAttributesAndData(
     66       SecKeychainItemRef itemRef,
     67       const SecKeychainAttributeList* attrList,
     68       UInt32 length,
     69       const void* data) const OVERRIDE;
     70   virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList,
     71                                              void* data) const OVERRIDE;
     72   virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const OVERRIDE;
     73   virtual OSStatus SearchCreateFromAttributes(
     74       CFTypeRef keychainOrArray,
     75       SecItemClass itemClass,
     76       const SecKeychainAttributeList* attrList,
     77       SecKeychainSearchRef* searchRef) const OVERRIDE;
     78   virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
     79                                   SecKeychainItemRef* itemRef) const OVERRIDE;
     80   // Pass "some.domain.com" as the serverName to get errSecDuplicateItem.
     81   virtual OSStatus AddInternetPassword(
     82       SecKeychainRef keychain,
     83       UInt32 serverNameLength,
     84       const char* serverName,
     85       UInt32 securityDomainLength,
     86       const char* securityDomain,
     87       UInt32 accountNameLength,
     88       const char* accountName,
     89       UInt32 pathLength, const char* path,
     90       UInt16 port, SecProtocolType protocol,
     91       SecAuthenticationType authenticationType,
     92       UInt32 passwordLength,
     93       const void* passwordData,
     94       SecKeychainItemRef* itemRef) const OVERRIDE;
     95   virtual void Free(CFTypeRef ref) const OVERRIDE;
     96 
     97   // Return the counts of objects returned by Create/Copy functions but never
     98   // Free'd as they should have been.
     99   int UnfreedSearchCount() const;
    100   int UnfreedKeychainItemCount() const;
    101   int UnfreedAttributeDataCount() const;
    102 
    103   // Returns true if all items added with AddInternetPassword have a creator
    104   // code set.
    105   bool CreatorCodesSetForAddedItems() const;
    106 
    107   struct KeychainTestData {
    108     const SecAuthenticationType auth_type;
    109     const char* server;
    110     const SecProtocolType protocol;
    111     const char* path;
    112     const UInt32 port;
    113     const char* security_domain;
    114     const char* creation_date;
    115     const char* username;
    116     const char* password;
    117     const bool negative_item;
    118   };
    119   // Adds a keychain item with the given info to the test set.
    120   void AddTestItem(const KeychainTestData& item_data);
    121 #endif  // !defined(OS_IOS)
    122 
    123   // |FindGenericPassword()| can return different results depending on user
    124   // interaction with the system Keychain.  For mocking purposes we allow the
    125   // user of this class to specify the result code of the
    126   // |FindGenericPassword()| call so we can simulate the result of different
    127   // user interactions.
    128   void set_find_generic_result(OSStatus result) {
    129     find_generic_result_ = result;
    130   }
    131 
    132   // Returns the true if |AddGenericPassword()| was called.
    133   bool called_add_generic() const { return called_add_generic_; }
    134 
    135   // Returns the value of the password set when |AddGenericPassword()| was
    136   // called.
    137   std::string add_generic_password() const { return add_generic_password_; }
    138 
    139   // Returns the number of allocations - deallocations for password data.
    140   int password_data_count() const { return password_data_count_; }
    141 
    142  private:
    143 
    144   // Type used for the keys in the std::map(s) and MockAppleKeychain items.
    145   typedef uintptr_t MockKeychainItemType;
    146 
    147   // Type of the map holding the mock keychain attributes.
    148   typedef std::map<MockKeychainItemType, SecKeychainAttributeList>
    149       MockKeychainAttributesMap;
    150 
    151 #if !defined(OS_IOS)
    152   // Returns true if the keychain already contains a password that matches the
    153   // attributes provided.
    154   bool AlreadyContainsInternetPassword(
    155       UInt32 serverNameLength,
    156       const char* serverName,
    157       UInt32 securityDomainLength,
    158       const char* securityDomain,
    159       UInt32 accountNameLength,
    160       const char* accountName,
    161       UInt32 pathLength,
    162       const char* path,
    163       UInt16 port,
    164       SecProtocolType protocol,
    165       SecAuthenticationType authenticationType) const;
    166   // Initializes storage for keychain data at |key|.
    167   void InitializeKeychainData(MockKeychainItemType key) const;
    168   // Sets the data and length of |tag| in the item-th test item.
    169   void SetTestDataBytes(
    170       MockKeychainItemType item,
    171       UInt32 tag,
    172       const void* data,
    173       size_t length);
    174   // Sets the data and length of |tag| in the item-th test item based on
    175   // |value|. The null-terminator will not be included; the Keychain Services
    176   // docs don't indicate whether it is or not, so clients should not assume
    177   // that it will be.
    178   void SetTestDataString(MockKeychainItemType item,
    179                          UInt32 tag,
    180                          const char* value);
    181   // Sets the data of the corresponding attribute of the item-th test item to
    182   // |value|. Assumes that the space has alread been allocated, and the length
    183   // set.
    184   void SetTestDataPort(MockKeychainItemType item, UInt32 value);
    185   void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value);
    186   void SetTestDataAuthType(MockKeychainItemType item,
    187                            SecAuthenticationType value);
    188   void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value);
    189   void SetTestDataCreator(MockKeychainItemType item, OSType value);
    190   // Sets the password data and length for the item-th test item.
    191   void SetTestDataPasswordBytes(MockKeychainItemType item,
    192                                 const void* data,
    193                                 size_t length);
    194   // Sets the password for the item-th test item. As with SetTestDataString,
    195   // the data will not be null-terminated.
    196   void SetTestDataPasswordString(MockKeychainItemType item, const char* value);
    197 
    198   // Returns the address of the attribute in attribute_list with tag |tag|.
    199   static SecKeychainAttribute* AttributeWithTag(
    200       const SecKeychainAttributeList& attribute_list,
    201       UInt32 tag);
    202 
    203   static const SecKeychainSearchRef kDummySearchRef;
    204 
    205   typedef struct KeychainPasswordData {
    206     KeychainPasswordData() : data(NULL), length(0) {}
    207     void* data;
    208     UInt32 length;
    209   } KeychainPasswordData;
    210 
    211   // Mutable because the MockAppleKeychain API requires its internal keychain
    212   // storage to be modifiable by users of this class.
    213   mutable MockKeychainAttributesMap keychain_attr_list_;
    214   mutable std::map<MockKeychainItemType,
    215                    KeychainPasswordData> keychain_data_;
    216   mutable MockKeychainItemType next_item_key_;
    217 
    218   // Tracks the items that should be returned in subsequent calls to
    219   // SearchCopyNext, based on the last call to SearchCreateFromAttributes.
    220   // We can't handle multiple active searches, since we don't track the search
    221   // ref we return, but we don't need to for our mocking.
    222   mutable std::vector<MockKeychainItemType> remaining_search_results_;
    223 
    224   // Track copies and releases to make sure they balance. Really these should
    225   // be maps to track per item, but this should be good enough to catch
    226   // real mistakes.
    227   mutable int search_copy_count_;
    228   mutable int keychain_item_copy_count_;
    229   mutable int attribute_data_copy_count_;
    230 
    231   // Tracks which items (by key) were added with AddInternetPassword.
    232   mutable std::set<MockKeychainItemType> added_via_api_;
    233 #endif  // !defined(OS_IOS)
    234 
    235   // Result code for the |FindGenericPassword()| method.
    236   OSStatus find_generic_result_;
    237 
    238   // Records whether |AddGenericPassword()| gets called.
    239   mutable bool called_add_generic_;
    240 
    241   // Tracks the allocations and frees of password data in |FindGenericPassword|
    242   // and |ItemFreeContent|.
    243   mutable int password_data_count_;
    244 
    245   // Records the password being set when |AddGenericPassword()| gets called.
    246   mutable std::string add_generic_password_;
    247 };
    248 
    249 }  // namespace crypto
    250 
    251 #endif  // CRYPTO_MOCK_KEYCHAIN_MAC_H_
    252