]>
Commit | Line | Data |
---|---|---|
866f8763 A |
1 | // |
2 | // manifeststresstest.m | |
3 | // Security | |
4 | // | |
5 | // Created by Ben Williamson on 6/1/17. | |
6 | // | |
7 | // | |
8 | ||
9 | #import <Foundation/Foundation.h> | |
10 | #import <stdlib.h> | |
11 | ||
12 | #import "Monkey.h" | |
13 | #import "Config.h" | |
14 | #import "Keychain.h" | |
15 | #import "mark.h" | |
16 | #import <Security/SecItemPriv.h> | |
17 | ||
18 | static const char *usage_message = | |
19 | "Usage: mainfeststresstest <command> [options...]\n" | |
20 | "\n" | |
21 | "Commands:\n" | |
22 | "\n" | |
23 | " reset Delete all items in the access group. Do this before starting the test.\n" | |
24 | "\n" | |
25 | " monkey Randomly add, update and delete items for a while. Then delete those items.\n" | |
26 | " --seed <n> Seed the random number generator\n" | |
27 | " --steps <n> Stop after n random actions. Default 1000.\n" | |
28 | " --maxitems <n> Limit number of items created to n. Default 20.\n" | |
29 | " --nocleanup Leave the items in place when finished.\n" | |
30 | " --dryrun Print actions to stdout but don't actually touch the keychain.\n" | |
31 | " --name Specialize items names (to isolate and avoid interfering changes)\n" | |
32 | " --view Keychain syncing view name. If not, specified \"Engram\" is used\n" | |
33 | "\n" | |
34 | " mark <id> [view] Write some items containing the string <id>, to mark the known finishing\n" | |
35 | " state for this device. The view argument takes a keychain syncing view name;\n" | |
36 | " If not specified, the view is \"Engram\".\n" | |
37 | "\n" | |
38 | " unmark <id> Delete the items that make up the mark for this id.\n" | |
39 | "\n" | |
40 | " update <id> Update the items that make up the mark for this id.\n" | |
41 | "\n" | |
42 | " verify <id>... Check that the access group contains only the marks for the\n" | |
43 | " given <id> list, corresponding to all the devices being finished.\n" | |
44 | " If given an empty id list this checks the access group is empty.\n" | |
45 | " Exits with nonzero status if verification fails.\n" | |
46 | "\n" | |
47 | " verify_update <id>... Check that the access group contains only the updated marks for\n" | |
48 | " the given <id> list, corresponding to all the devices being\n" | |
49 | " finished. If given an empty id list this checks the access group\n" | |
50 | " is empty. Exits with nonzero status if verification fails.\n" | |
51 | "\n" | |
52 | "Example:\n" | |
53 | "\n" | |
54 | " manifeststresstest reset\n" | |
55 | " manifeststresstest monkey --seed 12345 --steps 1000 --maxitems 20\n" | |
56 | " manifeststresstest mark foo\n" | |
57 | " manifeststresstest verify foo bar baz\n" | |
58 | "\n" | |
59 | " One device should run reset to clear the contents of the access group before the test\n" | |
60 | " begins. Then all devices should run monkey for a while. When each device is finished\n" | |
61 | " monkeying it should set a pattern with an id that uniquely identifies that device.\n" | |
62 | " When all devices are finished, one device can verify all the patterns by running the\n" | |
63 | " verify command with the ids of all of the devices, as it expects to see the patterns\n" | |
64 | " written by all devices, and no other items.\n" | |
65 | "\n" | |
66 | ; | |
67 | ||
68 | static void usage_exit(void) | |
69 | { | |
70 | printf("%s", usage_message); | |
71 | exit(1); | |
72 | } | |
73 | ||
74 | int main(int argc, const char ** argv) | |
75 | { | |
76 | @autoreleasepool { | |
77 | Keychain *keychain = [[Keychain alloc] init]; | |
78 | ||
79 | NSArray<NSString *> *args = [[NSProcessInfo processInfo] arguments]; | |
80 | if ([args count] < 2) { | |
81 | usage_exit(); | |
82 | } | |
83 | NSString *verb = args[1]; | |
84 | ||
85 | if ([verb isEqualToString:@"reset"]) { | |
86 | printf("Reseting\n"); | |
87 | NSLog(@"reset - deleteAllItems"); | |
88 | [keychain deleteAllItems]; | |
89 | ||
90 | } else if ([verb isEqualToString:@"monkey"]) { | |
91 | BOOL dryrun = NO; | |
92 | BOOL cleanup = YES; | |
93 | unsigned steps = 1000; | |
94 | Config *config = [[Config alloc] init]; | |
95 | config.maxItems = 20; | |
96 | config.distinctNames = 40; | |
97 | config.distinctValues = 10; | |
98 | config.addItemWeight = 20; | |
99 | config.deleteItemWeight = 10; | |
100 | config.updateNameWeight = 10; | |
101 | config.updateDataWeight = 10; | |
102 | config.updateNameAndDataWeight = 10; | |
103 | config.view = (__bridge NSString *)kSecAttrViewHintEngram; | |
104 | ||
105 | NSUInteger i = 2; | |
106 | while (i < [args count]) { | |
107 | NSString *opt = args[i++]; | |
108 | if ([opt isEqualToString:@"--seed"]) { | |
109 | if (i >= [args count]) { | |
110 | printf("error: --seed needs a value\n"); | |
111 | exit(1); | |
112 | } | |
113 | unsigned seed = (unsigned)[args[i++] integerValue]; | |
114 | NSLog(@"Seeding with %d", seed); | |
115 | srandom(seed); | |
116 | } else if ([opt isEqualToString:@"--steps"]) { | |
117 | if (i >= [args count]) { | |
118 | printf("error: --steps needs a value\n"); | |
119 | exit(1); | |
120 | } | |
121 | steps = (unsigned)[args[i++] integerValue]; | |
122 | } else if ([opt isEqualToString:@"--maxitems"]) { | |
123 | if (i >= [args count]) { | |
124 | printf("error: --maxitems needs a value\n"); | |
125 | exit(1); | |
126 | } | |
127 | config.maxItems = (unsigned)[args[i++] integerValue]; | |
128 | } else if ([opt isEqualToString:@"--nocleanup"]) { | |
129 | cleanup = NO; | |
130 | } else if ([opt isEqualToString:@"--dryrun"]) { | |
131 | dryrun = YES; | |
132 | } else if ([opt isEqualToString:@"--name"]) { | |
133 | if (i >= [args count]) { | |
134 | printf("error: --name needs a value\n"); | |
135 | exit(1); | |
136 | } | |
137 | config.name = args[i++]; | |
138 | } else if ([opt isEqualToString:@"--view"]) { | |
139 | if (i >= [args count]) { | |
140 | printf("error: --view needs a value\n"); | |
141 | exit(1); | |
142 | } | |
143 | config.view = args[i++]; | |
144 | } else { | |
145 | printf("Unrecognised argument %s\n", [opt UTF8String]); | |
146 | exit(1); | |
147 | } | |
148 | } | |
149 | NSLog(@"steps: %d", steps); | |
150 | NSLog(@"maxitems: %d", config.maxItems); | |
151 | NSLog(@"cleanup: %s", cleanup ? "yes" : "no"); | |
152 | NSLog(@"dryrun: %s", dryrun ? "yes" : "no"); | |
153 | ||
154 | Monkey *monkey = [[Monkey alloc] initWithConfig:config]; | |
155 | if (!dryrun) { | |
156 | monkey.keychain = keychain; | |
157 | } | |
158 | ||
159 | while (monkey.step < steps) { | |
160 | [monkey advanceOneStep]; | |
161 | } | |
162 | ||
163 | if (cleanup) { | |
164 | [monkey cleanup]; | |
165 | } | |
166 | ||
167 | } else if ([verb isEqualToString:@"mark"]) { | |
168 | if ([args count] < 3) { | |
169 | printf("mark command needs an identifier\n"); | |
170 | exit(1); | |
171 | } | |
172 | NSString *ident = args[2]; | |
173 | NSString *view = (__bridge NSString*)kSecAttrViewHintEngram; | |
174 | if ([args count] == 4) { | |
175 | view = args[3]; | |
176 | } | |
177 | NSLog(@"Writing mark %@", ident); | |
178 | writeMark(ident, view); | |
179 | ||
180 | } else if ([verb isEqualToString:@"unmark"]) { | |
181 | if ([args count] < 3) { | |
182 | printf("unmark command needs an identifier\n"); | |
183 | exit(1); | |
184 | } | |
185 | NSString *ident = args[2]; | |
186 | NSLog(@"Deleting mark %@", ident); | |
187 | deleteMark(ident); | |
188 | ||
189 | } else if ([verb isEqualToString:@"verify"]) { | |
190 | NSRange range; | |
191 | range.location = 2; | |
192 | range.length = args.count - 2; | |
193 | NSArray<NSString*> *idents = [args subarrayWithRange:range]; | |
194 | NSLog(@"Verifying, idents = %@", idents); | |
195 | if (!verifyMarks(idents)) { | |
196 | NSLog(@"Exiting nonzero status"); | |
197 | exit(1); | |
198 | } | |
199 | ||
200 | } else if ([verb isEqualToString:@"update"]) { | |
201 | if ([args count] < 3) { | |
202 | printf("unmark command needs an identifier\n"); | |
203 | exit(1); | |
204 | } | |
205 | NSString *ident = args[2]; | |
206 | NSLog(@"Updating mark %@", ident); | |
207 | updateMark(ident); | |
208 | ||
209 | } else if ([verb isEqualToString:@"verify_update"]) { | |
210 | NSRange range; | |
211 | range.location = 2; | |
212 | range.length = args.count - 2; | |
213 | NSArray<NSString*> *idents = [args subarrayWithRange:range]; | |
214 | NSLog(@"Verifying, idents = %@", idents); | |
215 | if (!verifyUpdateMarks(idents)) { | |
216 | NSLog(@"Exiting nonzero status"); | |
217 | exit(1); | |
218 | } | |
219 | ||
220 | } else { | |
221 | usage_exit(); | |
222 | } | |
223 | NSLog(@"Done."); | |
224 | } | |
225 | } |