1//
2//  si-12-item-stress.c
3//  sec
4//
5//  Created by Michael Brouwer on 8/1/13.
6//  Copyright (c) 2013 Apple Inc. All Rights Reserved.
7//
8//
9
10#include <CoreFoundation/CoreFoundation.h>
11#include <Security/SecCertificate.h>
12#include <Security/SecItem.h>
13#include <Security/SecItemPriv.h>
14#include <Security/SecBase.h>
15#include <utilities/array_size.h>
16#include <utilities/SecCFWrappers.h>
17#include <stdlib.h>
18#include <unistd.h>
19
20#include "Security_regressions.h"
21
22#if 0
23static void persistentRefIs(CFDataRef pref, CFDataRef data) {
24    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
25    CFTypeRef result = NULL;
26    CFDictionaryAddValue(dict, kSecValuePersistentRef, pref);
27    CFDictionaryAddValue(dict, kSecReturnData, kCFBooleanTrue);
28    ok_status(SecItemCopyMatching(dict, &result), "lookup item data by persistent ref");
29    ok(CFEqual(data, result), "result %@ equals expected data %@", result, data);
30    CFReleaseNull(result);
31    CFReleaseNull(dict);
32}
33#endif
34
35enum ItemAttrType {
36    kBoolItemAttr,
37    kNumberItemAttr,
38    kStringItemAttr,
39    kDataItemAttr,
40    kBlobItemAttr,
41    kDateItemAttr,
42    kAccessabilityItemAttr,
43    kAccessGroupItemAttr,
44};
45
46static void WithEachString(void(^each)(CFStringRef attr, enum ItemAttrType atype), ...) {
47    va_list ap;
48    va_start(ap, each);
49    CFStringRef attr;
50    while((attr = va_arg(ap, CFStringRef)) != NULL) {
51        enum ItemAttrType atype = va_arg(ap, enum ItemAttrType);
52        each(attr, atype);
53    }
54    va_end(ap);
55}
56
57#if 0
58static void ItemForEachAttr(CFMutableDictionaryRef item, void(^each)(CFStringRef attr, enum ItemAttrType atype)) {
59    CFStringRef iclass = CFDictionaryGetValue(item, kSecClass);
60    if (!iclass) {
61        return;
62    } else if (CFEqual(iclass, kSecClassGenericPassword)) {
63        WithEachString(each,
64                       kSecAttrAccessible,          kAccessabilityItemAttr,
65                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
66                       kSecAttrCreationDate,        kDateItemAttr,
67                       kSecAttrModificationDate,    kDateItemAttr,
68                       kSecAttrDescription,         kStringItemAttr,
69                       kSecAttrComment,             kStringItemAttr,
70                       kSecAttrCreator,             kNumberItemAttr,
71                       kSecAttrType,                kNumberItemAttr,
72                       kSecAttrLabel,               kStringItemAttr,
73                       kSecAttrIsInvisible,         kBoolItemAttr,
74                       kSecAttrIsNegative,          kBoolItemAttr,
75                       kSecAttrAccount,             kStringItemAttr,
76                       kSecAttrService,             kStringItemAttr,
77                       kSecAttrGeneric,             kDataItemAttr,
78                       kSecAttrSynchronizable,      kBoolItemAttr,
79                       NULL);
80    } else if (CFEqual(iclass, kSecClassInternetPassword)) {
81        WithEachString(each,
82                       kSecAttrAccessible,          kAccessabilityItemAttr,
83                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
84                       kSecAttrCreationDate,        kDateItemAttr,
85                       kSecAttrModificationDate,    kDateItemAttr,
86                       kSecAttrDescription,         kStringItemAttr,
87                       kSecAttrComment,             kStringItemAttr,
88                       kSecAttrCreator,             kNumberItemAttr,
89                       kSecAttrType,                kNumberItemAttr,
90                       kSecAttrLabel,               kStringItemAttr,
91                       kSecAttrIsInvisible,         kBoolItemAttr,
92                       kSecAttrIsNegative,          kBoolItemAttr,
93                       kSecAttrAccount,             kStringItemAttr,
94                       kSecAttrSecurityDomain,      kStringItemAttr,
95                       kSecAttrServer,              kStringItemAttr,
96                       kSecAttrProtocol,            kNumberItemAttr,
97                       kSecAttrAuthenticationType,  kNumberItemAttr,
98                       kSecAttrPort,                kNumberItemAttr,
99                       kSecAttrPath,                kStringItemAttr,
100                       kSecAttrSynchronizable,      kBoolItemAttr,
101                       NULL);
102    } else if (CFEqual(iclass, kSecClassCertificate)) {
103        WithEachString(each,
104                       kSecAttrAccessible,          kAccessabilityItemAttr,
105                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
106                       kSecAttrCertificateType,     kNumberItemAttr,
107                       kSecAttrCertificateEncoding, kNumberItemAttr,
108                       kSecAttrLabel,               kStringItemAttr,
109                       kSecAttrSubject,             kDataItemAttr,
110                       kSecAttrIssuer,              kDataItemAttr,
111                       kSecAttrSerialNumber,        kDataItemAttr,
112                       kSecAttrSubjectKeyID,        kDataItemAttr,
113                       kSecAttrPublicKeyHash,       kDataItemAttr,
114                       kSecAttrSynchronizable,      kBoolItemAttr,
115                       NULL);
116    } else if (CFEqual(iclass, kSecClassKey)) {
117        WithEachString(each,
118                       kSecAttrAccessible,          kAccessabilityItemAttr,
119                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
120                       kSecAttrKeyClass,            kStringItemAttr, // Might be Number on replies
121                       kSecAttrLabel,               kStringItemAttr,
122                       kSecAttrApplicationLabel,    kDataItemAttr,
123                       kSecAttrIsPermanent,         kBoolItemAttr,
124                       kSecAttrApplicationTag,      kDataItemAttr,
125                       kSecAttrKeyType,             kNumberItemAttr,
126                       kSecAttrKeySizeInBits,       kNumberItemAttr,
127                       kSecAttrEffectiveKeySize,    kNumberItemAttr,
128                       kSecAttrCanEncrypt,          kBoolItemAttr,
129                       kSecAttrCanDecrypt,          kBoolItemAttr,
130                       kSecAttrCanDerive,           kBoolItemAttr,
131                       kSecAttrCanSign,             kBoolItemAttr,
132                       kSecAttrCanVerify,           kBoolItemAttr,
133                       kSecAttrCanWrap,             kBoolItemAttr,
134                       kSecAttrCanUnwrap,           kBoolItemAttr,
135                       kSecAttrStartDate,           kDateItemAttr,
136                       kSecAttrEndDate,             kDateItemAttr,
137                       kSecAttrSynchronizable,      kBoolItemAttr,
138                       NULL);
139    } else if (CFEqual(iclass, kSecClassIdentity)) {
140        WithEachString(each,
141                       kSecAttrAccessible,          kAccessabilityItemAttr,
142                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
143                       kSecAttrCertificateType,     kNumberItemAttr,
144                       kSecAttrCertificateEncoding, kNumberItemAttr,
145                       kSecAttrLabel,               kStringItemAttr,
146                       kSecAttrSubject,             kDataItemAttr,
147                       kSecAttrIssuer,              kDataItemAttr,
148                       kSecAttrSerialNumber,        kDataItemAttr,
149                       kSecAttrSubjectKeyID,        kDataItemAttr,
150                       kSecAttrPublicKeyHash,       kDataItemAttr,
151                       kSecAttrKeyClass,            kStringItemAttr, // Might be Number on replies
152                       kSecAttrApplicationLabel,    kDataItemAttr,
153                       kSecAttrIsPermanent,         kBoolItemAttr,
154                       kSecAttrApplicationTag,      kDataItemAttr,
155                       kSecAttrKeyType,             kNumberItemAttr,
156                       kSecAttrKeySizeInBits,       kNumberItemAttr,
157                       kSecAttrEffectiveKeySize,    kNumberItemAttr,
158                       kSecAttrCanEncrypt,          kBoolItemAttr,
159                       kSecAttrCanDecrypt,          kBoolItemAttr,
160                       kSecAttrCanDerive,           kBoolItemAttr,
161                       kSecAttrCanSign,             kBoolItemAttr,
162                       kSecAttrCanVerify,           kBoolItemAttr,
163                       kSecAttrCanWrap,             kBoolItemAttr,
164                       kSecAttrCanUnwrap,           kBoolItemAttr,
165                       kSecAttrStartDate,           kDateItemAttr,
166                       kSecAttrEndDate,             kDateItemAttr,
167                       kSecAttrSynchronizable,      kBoolItemAttr,
168                       NULL);
169    }
170}
171#endif
172
173static void ItemForEachPKAttr(CFMutableDictionaryRef item, void(^each)(CFStringRef attr, enum ItemAttrType atype)) {
174    CFStringRef iclass = CFDictionaryGetValue(item, kSecClass);
175    if (!iclass) {
176        return;
177    } else if (CFEqual(iclass, kSecClassGenericPassword)) {
178        WithEachString(each,
179                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
180                       kSecAttrAccount,             kStringItemAttr,
181                       kSecAttrService,             kStringItemAttr,
182                       kSecAttrSynchronizable,      kBoolItemAttr,
183                       NULL);
184    } else if (CFEqual(iclass, kSecClassInternetPassword)) {
185        WithEachString(each,
186                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
187                       kSecAttrAccount,             kStringItemAttr,
188                       kSecAttrSecurityDomain,      kStringItemAttr,
189                       kSecAttrServer,              kStringItemAttr,
190                       kSecAttrProtocol,            kNumberItemAttr,
191                       kSecAttrAuthenticationType,  kNumberItemAttr,
192                       kSecAttrPort,                kNumberItemAttr,
193                       kSecAttrPath,                kStringItemAttr,
194                       kSecAttrSynchronizable,      kBoolItemAttr,
195                       NULL);
196    } else if (CFEqual(iclass, kSecClassCertificate)) {
197        WithEachString(each,
198                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
199                       kSecAttrCertificateType,     kNumberItemAttr,
200                       kSecAttrIssuer,              kDataItemAttr,
201                       kSecAttrSerialNumber,        kDataItemAttr,
202                       kSecAttrSynchronizable,      kBoolItemAttr,
203                       NULL);
204    } else if (CFEqual(iclass, kSecClassKey)) {
205        WithEachString(each,
206                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
207                       kSecAttrKeyClass,            kStringItemAttr, // kNumberItemAttr on replies
208                       kSecAttrApplicationLabel,    kDataItemAttr,
209                       kSecAttrApplicationTag,      kDataItemAttr,
210                       kSecAttrKeyType,             kNumberItemAttr,
211                       kSecAttrKeySizeInBits,       kNumberItemAttr,
212                       kSecAttrEffectiveKeySize,    kNumberItemAttr,
213                       kSecAttrStartDate,           kDateItemAttr,
214                       kSecAttrEndDate,             kDateItemAttr,
215                       kSecAttrSynchronizable,      kBoolItemAttr,
216                       NULL);
217    } else if (CFEqual(iclass, kSecClassIdentity)) {
218        WithEachString(each,
219                       kSecAttrAccessGroup,         kAccessGroupItemAttr,
220                       kSecAttrCertificateType,     kNumberItemAttr,
221                       kSecAttrIssuer,              kDataItemAttr,
222                       kSecAttrSerialNumber,        kDataItemAttr,
223                       kSecAttrSynchronizable,      kBoolItemAttr,
224                       kSecAttrKeyClass,            kStringItemAttr, // kNumberItemAttr on replies
225                       kSecAttrApplicationLabel,    kDataItemAttr,
226                       kSecAttrApplicationTag,      kDataItemAttr,
227                       kSecAttrKeyType,             kNumberItemAttr,
228                       kSecAttrKeySizeInBits,       kNumberItemAttr,
229                       kSecAttrEffectiveKeySize,    kNumberItemAttr,
230                       kSecAttrStartDate,           kDateItemAttr,
231                       kSecAttrEndDate,             kDateItemAttr,
232                       kSecAttrSynchronizable,      kBoolItemAttr,
233                       NULL);
234    }
235}
236
237static CFMutableDictionaryRef ItemCreate(int num) {
238    CFStringRef iclass = NULL;
239    switch (num % 4) {
240        case 0:
241            iclass = kSecClassInternetPassword;
242            break;
243        case 1:
244            iclass = kSecClassGenericPassword;
245            break;
246        case 2:
247            iclass = kSecClassKey;
248            break;
249        case 3:
250            iclass = kSecClassCertificate;
251            break;
252    }
253    return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, iclass, NULL);
254}
255
256/* Test add api in all it's variants. */
257static void tests(void)
258{
259    for (int num = 0 ; num < 8; ++num) {
260        CFMutableDictionaryRef item = ItemCreate(num);
261        ItemForEachPKAttr(item, ^(CFStringRef attr, enum ItemAttrType atype) {
262            CFTypeRef value = NULL;
263            switch (atype) {
264                case kBoolItemAttr:
265                    value = (num % 2 == 0 ? kCFBooleanTrue : kCFBooleanFalse);
266                    CFRetain(value);
267                    break;
268                case kNumberItemAttr:
269                    value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
270                    break;
271                case kStringItemAttr:
272                case kBlobItemAttr:
273                    value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("string-%d"), num);
274                    break;
275                case kDataItemAttr:
276                {
277                    char buf[10];
278                    int len = snprintf(buf, sizeof(buf), "data-%d", num);
279                    value = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buf, len);
280                    break;
281                }
282                case kDateItemAttr:
283                    value = NULL; // Don't mess with dates on create.
284                    break;
285                case kAccessabilityItemAttr:
286                {
287                    CFStringRef accessabilites[] = {
288                        kSecAttrAccessibleWhenUnlocked,
289                        kSecAttrAccessibleAfterFirstUnlock,
290                        kSecAttrAccessibleAlways,
291                        kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
292                        kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
293                        kSecAttrAccessibleAlwaysThisDeviceOnly,
294                    };
295                    value = accessabilites[num % array_size(accessabilites)];
296                    break;
297                }
298                case kAccessGroupItemAttr:
299                {
300                    CFStringRef accessGroups[] = {
301                        NULL,
302#if 0
303#if NO_SERVER
304                        CFSTR("test"),
305                        CFSTR("apple"),
306                        CFSTR("lockdown-identities"),
307#else
308                        CFSTR("sync"),
309#endif
310                        CFSTR("com.apple.security.sos"),          // Secd internally uses this
311
312                        CFSTR("com.apple.security.regressions"),  // SecurityTestApp is in this group.
313#endif
314                    };
315                    value = accessGroups[num % array_size(accessGroups)];
316                    break;
317                }
318            }
319            if (value)
320                CFDictionarySetValue(item, attr, value);
321            CFReleaseSafe(value);
322        });
323
324        CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
325        ok_status(SecItemAdd(item, NULL), "add sync");
326
327        CFDictionarySetValue(item, kSecUseTombstones, kCFBooleanTrue);
328        ok_status(SecItemDelete(item), "delete sync");
329
330        CFDictionarySetValue(item, kSecAttrTombstone, kCFBooleanTrue);
331        ok_status(SecItemCopyMatching(item, NULL), "find tombstone after delete sync");
332        ok_status(SecItemDelete(item), "delete sync tombstone");
333        CFDictionaryRemoveValue(item, kSecAttrTombstone);
334
335        ok_status(SecItemAdd(item, NULL), "add sync again");
336
337        CFDictionarySetValue(item, kSecUseTombstones, kCFBooleanFalse);
338        ok_status(SecItemDelete(item), "delete sync without leaving a tombstone behind");
339        CFDictionaryRemoveValue(item, kSecUseTombstones);
340
341        CFDictionarySetValue(item, kSecAttrTombstone, kCFBooleanTrue);
342        is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find tombstone after delete sync with kSecUseTombstones=false");
343
344        CFDictionaryRemoveValue(item, kSecAttrSynchronizable);
345        ok_status(SecItemAdd(item, NULL), "add local");
346        ok_status(SecItemDelete(item), "delete local");
347
348        CFDictionarySetValue(item, kSecAttrTombstone, kCFBooleanTrue);
349        is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find tombstone after delete local");
350        is_status(SecItemDelete(item), errSecItemNotFound, "do not delete tombstone after delete local");
351        CFDictionaryRemoveValue(item, kSecAttrTombstone);
352
353        ok_status(SecItemAdd(item, NULL), "add local again");
354
355        CFDictionarySetValue(item, kSecUseTombstones, kCFBooleanTrue);
356        ok_status(SecItemDelete(item), "delete local and leave a tombstone behind");
357        CFDictionaryRemoveValue(item, kSecUseTombstones);
358
359        CFDictionarySetValue(item, kSecAttrTombstone, kCFBooleanTrue);
360        ok_status(SecItemCopyMatching(item, NULL), "find tombstone after delete sync with kSecUseTombstones=true");
361
362        CFDictionarySetValue(item, kSecUseTombstones, kCFBooleanTrue);
363        ok_status(SecItemDelete(item), "delete local tombstone kSecUseTombstones=true");
364        CFDictionaryRemoveValue(item, kSecUseTombstones);
365
366        ok_status(SecItemCopyMatching(item, NULL), "find tombstone after delete local tombstone with kSecUseTombstones=true");
367        ok_status(SecItemDelete(item), "delete local tombstone");
368        is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find tombstone after delete local");
369
370        CFRelease(item);
371    }
372}
373
374int si_12_item_stress(int argc, char *const *argv)
375{
376	plan_tests(144);
377
378	tests();
379
380	return 0;
381}
382