1// Copyright (c) 2009 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 CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_
6#define CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_
7#pragma once
8
9#include <set>
10#include <string>
11#include <vector>
12
13#include "chrome/browser/keychain_mac.h"
14
15// Mock Keychain wrapper for testing code that interacts with the OS Keychain.
16// The basic idea of this mock is that it has a static array of data, and
17// SecKeychainItemRef values are just indexes into that array (offset by 1 to
18// prevent problems with clients that null-check refs), cast to pointers.
19//
20// Note that "const" is pretty much meaningless for this class; the const-ness
21// of MacKeychain doesn't apply to the actual keychain data, so all of the Mock
22// data is mutable; don't assume that it won't change over the life of tests.
23class MockKeychain : public MacKeychain {
24 public:
25  // Create a Mock Keychain capable of holding item_capacity keychain items.
26  explicit MockKeychain(unsigned int item_capacity);
27  virtual ~MockKeychain();
28  virtual OSStatus ItemCopyAttributesAndData(
29      SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
30      SecItemClass *itemClass, SecKeychainAttributeList **attrList,
31      UInt32 *length, void **outData) const;
32  // Pass "fail_me" as the data to get errSecAuthFailed.
33  virtual OSStatus ItemModifyAttributesAndData(
34      SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList,
35      UInt32 length, const void *data) const;
36  virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList *attrList,
37                                             void *data) const;
38  virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const;
39  virtual OSStatus SearchCreateFromAttributes(
40      CFTypeRef keychainOrArray, SecItemClass itemClass,
41      const SecKeychainAttributeList *attrList,
42      SecKeychainSearchRef *searchRef) const;
43  virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
44                                  SecKeychainItemRef *itemRef) const;
45  // If there are unused slots in the Mock Keychain's capacity, the new item
46  // will use the first free one, otherwise it will stomp the last item.
47  // Pass "some.domain.com" as the serverName to get errSecDuplicateItem.
48  virtual OSStatus AddInternetPassword(SecKeychainRef keychain,
49                                       UInt32 serverNameLength,
50                                       const char *serverName,
51                                       UInt32 securityDomainLength,
52                                       const char *securityDomain,
53                                       UInt32 accountNameLength,
54                                       const char *accountName,
55                                       UInt32 pathLength, const char *path,
56                                       UInt16 port, SecProtocolType protocol,
57                                       SecAuthenticationType authenticationType,
58                                       UInt32 passwordLength,
59                                       const void *passwordData,
60                                       SecKeychainItemRef *itemRef) const;
61  virtual OSStatus FindGenericPassword(CFTypeRef keychainOrArray,
62                                       UInt32 serviceNameLength,
63                                       const char *serviceName,
64                                       UInt32 accountNameLength,
65                                       const char *accountName,
66                                       UInt32 *passwordLength,
67                                       void **passwordData,
68                                       SecKeychainItemRef *itemRef) const;
69  virtual OSStatus ItemFreeContent(SecKeychainAttributeList *attrList,
70                                   void *data) const;
71  virtual OSStatus AddGenericPassword(SecKeychainRef keychain,
72                                      UInt32 serviceNameLength,
73                                      const char *serviceName,
74                                      UInt32 accountNameLength,
75                                      const char *accountName,
76                                      UInt32 passwordLength,
77                                      const void *passwordData,
78                                      SecKeychainItemRef *itemRef) const;
79  virtual void Free(CFTypeRef ref) const;
80
81  // Return the counts of objects returned by Create/Copy functions but never
82  // Free'd as they should have been.
83  int UnfreedSearchCount() const;
84  int UnfreedKeychainItemCount() const;
85  int UnfreedAttributeDataCount() const;
86
87  // Returns true if all items added with AddInternetPassword have a creator
88  // code set.
89  bool CreatorCodesSetForAddedItems() const;
90
91  struct KeychainTestData {
92    const SecAuthenticationType auth_type;
93    const char* server;
94    const SecProtocolType protocol;
95    const char* path;
96    const UInt32 port;
97    const char* security_domain;
98    const char* creation_date;
99    const char* username;
100    const char* password;
101    const bool negative_item;
102  };
103  // Adds a keychain item with the given info to the test set.
104  void AddTestItem(const KeychainTestData& item_data);
105
106  // |FindGenericPassword()| can return different results depending on user
107  // interaction with the system Keychain.  For mocking purposes we allow the
108  // user of this class to specify the result code of the
109  // |FindGenericPassword()| call so we can simulate the result of different
110  // user interactions.
111  void set_find_generic_result(OSStatus result) {
112    find_generic_result_ = result;
113  }
114
115  // Returns the true if |AddGenericPassword()| was called.
116  bool called_add_generic() const { return called_add_generic_; }
117
118  // Returns the value of the password set when |AddGenericPassword()| was
119  // called.
120  std::string add_generic_password() const { return add_generic_password_; }
121
122  // Returns the number of allocations - deallocations for password data.
123  int password_data_count() const { return password_data_count_; }
124
125 private:
126  // Sets the data and length of |tag| in the item-th test item.
127  void SetTestDataBytes(int item, UInt32 tag, const void* data, size_t length);
128  // Sets the data and length of |tag| in the item-th test item based on
129  // |value|. The null-terminator will not be included; the Keychain Services
130  // docs don't indicate whether it is or not, so clients should not assume
131  // that it will be.
132  void SetTestDataString(int item, UInt32 tag, const char* value);
133  // Sets the data of the corresponding attribute of the item-th test item to
134  // |value|. Assumes that the space has alread been allocated, and the length
135  // set.
136  void SetTestDataPort(int item, UInt32 value);
137  void SetTestDataProtocol(int item, SecProtocolType value);
138  void SetTestDataAuthType(int item, SecAuthenticationType value);
139  void SetTestDataNegativeItem(int item, Boolean value);
140  void SetTestDataCreator(int item, OSType value);
141  // Sets the password data and length for the item-th test item.
142  void SetTestDataPasswordBytes(int item, const void* data, size_t length);
143  // Sets the password for the item-th test item. As with SetTestDataString,
144  // the data will not be null-terminated.
145  void SetTestDataPasswordString(int item, const char* value);
146
147  // Returns the address of the attribute in attribute_list with tag |tag|.
148  static SecKeychainAttribute* AttributeWithTag(
149      const SecKeychainAttributeList& attribute_list, UInt32 tag);
150
151  static const int kDummySearchRef = 1000;
152
153  typedef struct  {
154    void* data;
155    UInt32 length;
156  } KeychainPasswordData;
157
158  SecKeychainAttributeList* keychain_attr_list_;
159  KeychainPasswordData* keychain_data_;
160  unsigned int item_capacity_;
161  mutable unsigned int item_count_;
162
163  // Tracks the items that should be returned in subsequent calls to
164  // SearchCopyNext, based on the last call to SearchCreateFromAttributes.
165  // We can't handle multiple active searches, since we don't track the search
166  // ref we return, but we don't need to for our mocking.
167  mutable std::vector<unsigned int> remaining_search_results_;
168
169  // Track copies and releases to make sure they balance. Really these should
170  // be maps to track per item, but this should be good enough to catch
171  // real mistakes.
172  mutable int search_copy_count_;
173  mutable int keychain_item_copy_count_;
174  mutable int attribute_data_copy_count_;
175
176  // Tracks which items (by index) were added with AddInternetPassword.
177  mutable std::set<unsigned int> added_via_api_;
178
179  // Result code for the |FindGenericPassword()| method.
180  OSStatus find_generic_result_;
181
182  // Records whether |AddGenericPassword()| gets called.
183  mutable bool called_add_generic_;
184
185  // Tracks the allocations and frees of password data in |FindGenericPassword|
186  // and |ItemFreeContent|.
187  mutable unsigned int password_data_count_;
188
189  // Records the password being set when |AddGenericPassword()| gets called.
190  mutable std::string add_generic_password_;
191};
192
193#endif  // CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_
194