]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_manifest/lib/SecureDownloadInternal.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_manifest / lib / SecureDownloadInternal.c
1 #include <CoreFoundation/CoreFoundation.h>
2
3 #include "SecureDownloadInternal.h"
4 #include "SecCFRelease.h"
5
6 #include "simulatecrash_assert.h"
7
8 //
9 // SecureDownloadXML: SecureDownloadXML.c
10 // cc -g -framework CoreFoundation -o $@ $^
11 //
12
13 #define LOCAL_DEBUG 0
14
15 #if LOCAL_DEBUG
16 extern CFDataRef read_data(char* path);
17 #endif
18
19 #define SD_XML_NAMESPACE CFSTR("http://www.apple.com/2006/SecureDownload/1")
20 #define SD_XML_ROOT CFSTR("SecureDownload")
21 #define SD_XML_RESOURCE CFSTR("resource")
22 #define SD_XML_SITES CFSTR("sites")
23 #define SD_XML_VERIFICATION CFSTR("verification")
24 #define SD_XML_ATTR_ALGORITHM CFSTR("algorithm")
25
26 struct parseState {
27 CFDictionaryRef namespaces; // array of dictionaries of namespace declarations
28 #if LOCAL_DEBUG
29 char* prefix;
30 #endif
31 CFMutableArrayRef plists; // array of all resource plists
32 CFMutableDictionaryRef plist; // most recent entry in the plists array
33 };
34
35
36 static inline unsigned char decode64(unsigned char c)
37 {
38 switch(c) {
39 case 'A': return 0;
40 case 'B': return 1;
41 case 'C': return 2;
42 case 'D': return 3;
43 case 'E': return 4;
44 case 'F': return 5;
45 case 'G': return 6;
46 case 'H': return 7;
47 case 'I': return 8;
48 case 'J': return 9;
49 case 'K': return 10;
50 case 'L': return 11;
51 case 'M': return 12;
52 case 'N': return 13;
53 case 'O': return 14;
54 case 'P': return 15;
55 case 'Q': return 16;
56 case 'R': return 17;
57 case 'S': return 18;
58 case 'T': return 19;
59 case 'U': return 20;
60 case 'V': return 21;
61 case 'W': return 22;
62 case 'X': return 23;
63 case 'Y': return 24;
64 case 'Z': return 25;
65 case 'a': return 26;
66 case 'b': return 27;
67 case 'c': return 28;
68 case 'd': return 29;
69 case 'e': return 30;
70 case 'f': return 31;
71 case 'g': return 32;
72 case 'h': return 33;
73 case 'i': return 34;
74 case 'j': return 35;
75 case 'k': return 36;
76 case 'l': return 37;
77 case 'm': return 38;
78 case 'n': return 39;
79 case 'o': return 40;
80 case 'p': return 41;
81 case 'q': return 42;
82 case 'r': return 43;
83 case 's': return 44;
84 case 't': return 45;
85 case 'u': return 46;
86 case 'v': return 47;
87 case 'w': return 48;
88 case 'x': return 49;
89 case 'y': return 50;
90 case 'z': return 51;
91 case '0': return 52;
92 case '1': return 53;
93 case '2': return 54;
94 case '3': return 55;
95 case '4': return 56;
96 case '5': return 57;
97 case '6': return 58;
98 case '7': return 59;
99 case '8': return 60;
100 case '9': return 61;
101 case '+': return 62;
102 case '/': return 63;
103 }
104 return 255;
105 }
106
107 // Decodes base64 data into a binary CFData object
108 // If first character on a line is not in the base64 alphabet, the line
109 // is ignored.
110 static CF_RETURNS_RETAINED CFDataRef decodeBase64Data(const UInt8* ptr, size_t len) {
111 CFMutableDataRef result = CFDataCreateMutable(NULL, len); // data can't exceed len bytes
112 if (!result) return NULL;
113
114 CFIndex i, j;
115
116 int skip = 0;
117
118 UInt8 triplet[3] = {0, 0, 0};
119
120 /*
121 http://www.faqs.org/rfcs/rfc3548.html
122 +--first octet--+-second octet--+--third octet--+
123 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
124 +-----------+---+-------+-------+---+-----------+
125 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
126 +--1.index--+--2.index--+--3.index--+--4.index--+
127 */
128
129 for (i = 0, j = 0; i < len; ++i) {
130 unsigned char c = ptr[i];
131 if (c == ' ') { continue; }
132 if (c == '\t') { continue; }
133 if (c == '\r') { continue; }
134 if (c == '\n') { skip = 0; continue; }
135 if (skip) { continue; }
136 if (!skip && c == '=') { --j; skip = 1; continue; }
137 unsigned char x = decode64(c);
138 if (x == 255) { skip = 1; continue; }
139
140 if (j == 0) {
141 triplet[0] |= ((x << 2) & 0xFC);
142 ++j;
143 } else if (j == 1) {
144 triplet[0] |= ((x >> 4) & 0x03);
145 triplet[1] |= ((x << 4) & 0xF0);
146 ++j;
147 } else if (j == 2) {
148 triplet[1] |= ((x >> 2) & 0x0F);
149 triplet[2] |= ((x << 6) & 0xC0);
150 ++j;
151 } else if (j == 3) {
152 triplet[2] |= ((x) & 0x3F);
153 CFDataAppendBytes(result, triplet, j);
154 memset(triplet, 0, sizeof(triplet));
155 j = 0;
156 }
157 }
158 if (j > 0) {
159 CFDataAppendBytes(result, triplet, j);
160 }
161 return result;
162 }
163
164 // Returns a CFString containing the base64 representation of the data.
165 // boolean argument for whether to line wrap at 64 columns or not.
166 static CF_RETURNS_RETAINED CFStringRef encodeBase64String(const UInt8* ptr, size_t len, int wrap) {
167 const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
168 "abcdefghijklmnopqrstuvwxyz"
169 "0123456789+/=";
170
171 // base64 encoded data uses 4 ASCII characters to represent 3 octets.
172 // There can be up to two == at the end of the base64 data for padding.
173 // If we are line wrapping then we need space for one newline character
174 // every 64 characters of output.
175 // Rounded 4/3 up to 2 to avoid floating point math.
176
177 //CFIndex max_len = (2*len) + 2;
178 //if (wrap) len = len + ((2*len) / 64) + 1;
179
180 CFMutableStringRef string = CFStringCreateMutable(NULL, 0);
181 if (!string) return NULL;
182
183 /*
184 http://www.faqs.org/rfcs/rfc3548.html
185 +--first octet--+-second octet--+--third octet--+
186 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
187 +-----------+---+-------+-------+---+-----------+
188 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
189 +--1.index--+--2.index--+--3.index--+--4.index--+
190 */
191 int i = 0; // octet offset into input data
192 int column = 0; // output column number (used for line wrapping)
193 for (;;) {
194 UniChar c[16]; // buffer of characters to add to output
195 int j = 0; // offset to place next character in buffer
196 int index; // index into output alphabet
197
198 #define ADDCHAR(_X_) do { c[j++] = _X_; if (wrap && (++column == 64)) { column = 0; c[j++] = '\n'; } } while (0);
199
200 // 1.index
201 index = (ptr[i] >> 2) & 0x3F;
202 ADDCHAR(alphabet[index]);
203
204 // 2.index
205 index = (ptr[i] << 4) & 0x30;
206 if ((i+1) < len) {
207 index = index | ((ptr[i+1] >> 4) & 0x0F);
208 ADDCHAR(alphabet[index]);
209 } else { // end of input, pad as necessary
210 ADDCHAR(alphabet[index]);
211 ADDCHAR('=');
212 ADDCHAR('=');
213 }
214
215 // 3.index
216 if ((i+1) < len) {
217 index = (ptr[i+1] << 2) & 0x3C;
218 if ((i+2) < len) {
219 index = index | ((ptr[i+2] >> 6) & 0x03);
220 ADDCHAR(alphabet[index]);
221 } else { // end of input, pad as necessary
222 ADDCHAR(alphabet[index]);
223 ADDCHAR('=');
224 }
225 }
226
227 // 4.index
228 if ((i+2) < len) {
229 index = (ptr[i+2]) & 0x3F;
230 ADDCHAR(alphabet[index]);
231 }
232
233 CFStringAppendCharacters(string, c, j);
234 i += 3; // we processed 3 bytes of input
235 if (i >= len) {
236 // end of data, append newline if we haven't already
237 if (wrap && c[j-1] != '\n') {
238 c[0] = '\n';
239 CFStringAppendCharacters(string, c, 1);
240 }
241 break;
242 }
243 }
244 return string;
245 }
246
247
248 // makes a copy of the current namespaces dictionary, adding in any
249 // namespaces defined by the current node.
250 static CFDictionaryRef copyNamespacesForNode(CFDictionaryRef namespaces, CFXMLNodeRef node) {
251 CFMutableDictionaryRef result = NULL;
252
253 CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
254
255 // careful, don't use the info unless we ensure type == kCFXMLNodeTypeElement
256 CFXMLElementInfo* info = (CFXMLElementInfo*)CFXMLNodeGetInfoPtr(node);
257
258 //
259 // create our result dictionary
260 // there are four possible configurations:
261 // 1. previous dictionary exists, this is an element, and has attributes:
262 // clone existing dictionary, we may be adding to it
263 // 2. previous dictionary exists, not an element or no attributes:
264 // retain existing dictionary and return
265 // 3. no previous dictionary, this is an element, and has attributes:
266 // create new dictionary, we may be adding to it
267 // 4. no previous dictionary, not an element or no attributes:
268 // create new dictionary and return
269 //
270 if (namespaces && type == kCFXMLNodeTypeElement && info->attributes && info->attributeOrder) {
271 result = CFDictionaryCreateMutableCopy(NULL, 0, namespaces);
272 } else if (namespaces) {
273 result = (CFMutableDictionaryRef)CFRetain(namespaces);
274 return result;
275 } else if (type == kCFXMLNodeTypeElement && info->attributes && info->attributeOrder) {
276 result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
277 if (result) CFDictionarySetValue(result, CFSTR(""), CFSTR(""));
278 } else {
279 result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
280 if (result) CFDictionarySetValue(result, CFSTR(""), CFSTR(""));
281 return result;
282 }
283 if (!result) return NULL;
284
285 //
286 // if we got this far, we're dealing with an XML element with
287 // attributes. check to see if any are xml namespace attributes.
288 //
289 CFArrayRef attrs = info->attributeOrder;
290 CFIndex i, count = CFArrayGetCount(attrs);
291 for (i = 0; i < count; ++i) {
292 CFStringRef attr = CFArrayGetValueAtIndex(attrs, i);
293
294 if (CFEqual(CFSTR("xmlns"), attr)) {
295 // default namespace
296 CFStringRef value = CFDictionaryGetValue(info->attributes, attr);
297 if (value) {
298 CFDictionarySetValue(result, CFSTR(""), value);
299 }
300 } else {
301 // if the attribute is in the "xmlns" namespace, then it's
302 // really a declaration of a new namespace. record it in our dictionary.
303 CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, attr, CFSTR(":"));
304 CFIndex numparts = parts ? CFArrayGetCount(parts) : 0;
305 if (numparts == 2) {
306 CFStringRef prefix = CFArrayGetValueAtIndex(parts, 0);
307 CFStringRef suffix = CFArrayGetValueAtIndex(parts, 1);
308 if (CFEqual(CFSTR("xmlns"), prefix)) {
309 CFStringRef value = CFDictionaryGetValue(info->attributes, attr);
310 if (value) {
311 CFDictionarySetValue(result, suffix, value);
312 }
313 }
314 }
315 if (parts) CFRelease(parts);
316 }
317 }
318 return result;
319 }
320
321 // returns the current node's element name and namespace URI
322 // based on the currently defined namespaces.
323 static void copyNodeNamespaceAndName(CFDictionaryRef namespaces, CFXMLNodeRef node, CFStringRef* namespace, CFStringRef* name) {
324 CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
325 *namespace = NULL;
326 *name = NULL;
327 if (type == kCFXMLNodeTypeElement) {
328 CFStringRef qname = CFXMLNodeGetString(node);
329 CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, qname, CFSTR(":"));
330 CFIndex numparts = parts ? CFArrayGetCount(parts) : 0;
331 if (numparts == 1) {
332 // default namespace
333 *namespace = CFRetain(CFDictionaryGetValue(namespaces, CFSTR("")));
334 *name = CFRetain(CFArrayGetValueAtIndex(parts, 0));
335 } else if (numparts == 2) {
336 CFStringRef prefix = CFArrayGetValueAtIndex(parts, 0);
337 CFStringRef ns = CFDictionaryGetValue(namespaces, prefix);
338 *namespace = ns ? CFRetain(ns) : NULL;
339 *name = CFRetain(CFArrayGetValueAtIndex(parts, 1));
340 } else {
341 // bogus xml
342 }
343 if (parts) CFRelease(parts);
344 }
345 }
346
347 // helper function for copyTreeString() below
348 // appends text nodes to the mutable string context
349 static void _appendTreeString(const void *value, void *context) {
350 CFXMLTreeRef tree = (CFXMLTreeRef)value;
351 CFMutableStringRef result = (CFMutableStringRef)context;
352
353 CFXMLNodeRef node = CFXMLTreeGetNode(tree);
354 CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
355 if (type == kCFXMLNodeTypeElement) {
356 CFTreeApplyFunctionToChildren(tree, _appendTreeString, result);
357 } else if (type == kCFXMLNodeTypeText) {
358 CFStringRef str = CFXMLNodeGetString(node);
359 if (str) CFStringAppend(result, str);
360 }
361 }
362
363 // equivalent to the XPATH string() function
364 // concatenates all text nodes into a single string
365 static CFMutableStringRef copyTreeString(CFXMLTreeRef tree) {
366 CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
367 CFTreeApplyFunctionToChildren(tree, _appendTreeString, result);
368 return result;
369 }
370
371
372 // returns an array of CFXMLTreeRef objects that are immediate
373 // children of the context node that match the specified element
374 // name and namespace URI
375 static CFArrayRef copyChildrenWithName(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
376 CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
377 tree = CFTreeGetFirstChild(tree);
378 while (tree) {
379 CFXMLNodeRef node = CFXMLTreeGetNode(tree);
380 CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
381 if (type == kCFXMLNodeTypeElement) {
382 CFDictionaryRef namespaces = copyNamespacesForNode(inNamespaces, node);
383 if (namespaces) {
384 CFStringRef ns, n;
385 copyNodeNamespaceAndName(namespaces, node, &ns, &n);
386
387 if (ns && n && CFEqual(ns, namespace) && CFEqual(n, name)) {
388 CFArrayAppendValue(result, tree);
389 }
390 if (ns) CFRelease(ns);
391 if (n) CFRelease(n);
392
393 CFRelease(namespaces);
394 }
395 }
396 tree = CFTreeGetNextSibling(tree);
397 }
398 return result;
399 }
400
401 // convenience function to find the first child element
402 // with the given element name and namespace URI
403 static CFXMLTreeRef getChildWithName(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
404 CFXMLTreeRef result = NULL;
405 CFArrayRef array = copyChildrenWithName(tree, inNamespaces, namespace, name);
406 if (array && CFArrayGetCount(array) > 0) {
407 result = (CFXMLTreeRef)CFArrayGetValueAtIndex(array, 0);
408 }
409 if (array) CFRelease(array);
410 return result;
411 }
412
413 // returns the string value of the specified child node
414 static CFStringRef copyChildWithNameAsString(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
415 CFStringRef result = NULL;
416 CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
417 if (child) result = copyTreeString(child);
418 return result;
419 }
420
421 // returns the integer value of the specified child node
422 static CFNumberRef copyChildWithNameAsInteger(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
423 CFNumberRef result = NULL;
424 CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
425 if (child) {
426 CFStringRef str = copyTreeString(child);
427 if (str) {
428 SInt32 size = CFStringGetIntValue(str);
429 result = CFNumberCreate(NULL, kCFNumberSInt32Type, &size);
430 CFRelease(str);
431 }
432 }
433 return result;
434 }
435
436 // returns an array of URLs aggregated from the child
437 // nodes matching the given name and namespace URI.
438 static CFArrayRef copyChildrenWithNameAsURLs(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
439 CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
440 CFArrayRef children = copyChildrenWithName(tree, inNamespaces, namespace, name);
441 if (children) {
442 CFIndex i, count = CFArrayGetCount(children);
443 for (i = 0; i < count; ++i) {
444 CFXMLTreeRef child = (CFXMLTreeRef)CFArrayGetValueAtIndex(children, i);
445 CFStringRef str = copyTreeString(child);
446 if (str) {
447 CFURLRef url = CFURLCreateWithString(NULL, str, NULL);
448 if (url) {
449 CFArrayAppendValue(result, url);
450 CFRelease(url);
451 }
452 CFRelease(str);
453 }
454 }
455 CFRelease(children);
456 }
457 return result;
458 }
459
460 // returns the base 64 decoded value of the specified child node
461 static CFDataRef copyChildWithNameAsData(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
462 CFDataRef result = NULL;
463 CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
464 if (child) {
465 CFStringRef str = copyTreeString(child);
466 if (str) {
467 CFIndex len = CFStringGetLength(str);
468 CFIndex used;
469 UInt8* buffer = malloc(len);
470 // ASCII is one byte per character. Any inconvertible characters
471 // are assigned to whitespace and skipped.
472 if (buffer) {
473 if (CFStringGetBytes(str, CFRangeMake(0, len), kCFStringEncodingASCII, ' ', 0, buffer, len, &used)) {
474 result = decodeBase64Data(buffer, used);
475 }
476 free(buffer);
477 }
478 CFRelease(str);
479 }
480 }
481 return result;
482 }
483
484 // returns the CFDate value of the specified child node
485 // whose string value is interpreted in W3C DateTime format
486 static CFDateRef copyChildWithNameAsDate(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
487 CFDateRef result = NULL;
488 CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
489 if (child) {
490 CFMutableStringRef str = copyTreeString(child);
491 if (str) {
492 CFStringTrimWhitespace(str);
493 if (CFStringGetLength(str) > 21) {
494 CFStringRef year = CFStringCreateWithSubstring(NULL, str, CFRangeMake(0, 4));
495 CFStringRef month = CFStringCreateWithSubstring(NULL, str, CFRangeMake(5, 2));
496 CFStringRef day = CFStringCreateWithSubstring(NULL, str, CFRangeMake(8, 2));
497 CFStringRef hour = CFStringCreateWithSubstring(NULL, str, CFRangeMake(11, 2));
498 CFStringRef minute = CFStringCreateWithSubstring(NULL, str, CFRangeMake(14, 2));
499 CFStringRef second = CFStringCreateWithSubstring(NULL, str, CFRangeMake(17, 2));
500 CFStringRef tenth = CFStringCreateWithSubstring(NULL, str, CFRangeMake(20, 1));
501
502 CFGregorianDate gregory;
503 memset(&gregory, 0, sizeof(gregory));
504 if (year) { gregory.year = CFStringGetIntValue(year); CFRelease(year); }
505 if (month) { gregory.month = CFStringGetIntValue(month); CFRelease(month); }
506 if (day) { gregory.day = CFStringGetIntValue(day); CFRelease(day); }
507 if (hour) { gregory.hour = CFStringGetIntValue(hour); CFRelease(hour); }
508 if (minute) { gregory.minute = CFStringGetIntValue(minute); CFRelease(minute); }
509 if (second) { gregory.second = (double)CFStringGetIntValue(second); CFRelease(second); }
510 if (tenth) { gregory.second += ((double)CFStringGetIntValue(tenth)/(double)10.0); CFRelease(tenth); }
511
512 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
513 if (tz) {
514 CFAbsoluteTime at = CFGregorianDateGetAbsoluteTime(gregory, tz);
515 result = CFDateCreate(NULL, at);
516 CFRelease(tz);
517 }
518 }
519 CFRelease(str);
520 }
521 }
522 return result;
523 }
524
525
526 // generic parser for XML nodes in our ticket format
527 static void _parseXMLNode(const void *value, void *context) {
528 CFXMLTreeRef tree = (CFXMLTreeRef)value;
529 struct parseState* state = (struct parseState*)context;
530
531 CFXMLNodeRef node = CFXMLTreeGetNode(tree);
532 assert(node);
533
534 CFDictionaryRef namespaces = copyNamespacesForNode(state->namespaces, node);
535
536 CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
537
538 int descend = 0;
539
540 if (type == kCFXMLNodeTypeElement) {
541 CFStringRef ns, name;
542 copyNodeNamespaceAndName(namespaces, node, &ns, &name);
543
544 if (ns && name) {
545
546 if (CFEqual(ns, SD_XML_NAMESPACE)) {
547 if (CFEqual(name, SD_XML_ROOT)) {
548
549 descend = 1;
550
551 } else if (CFEqual(name, SD_XML_RESOURCE)) {
552
553 state->plist = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
554 if (state->plist) {
555 CFArrayAppendValue(state->plists, state->plist);
556 CFRelease(state->plist);
557 }
558
559 CFStringRef name = copyChildWithNameAsString(tree, namespaces, SD_XML_NAMESPACE, SD_XML_NAME);
560 if (name && state->plist) {
561 CFDictionarySetValue(state->plist, SD_XML_NAME, name);
562 }
563 CFReleaseNull(name);
564
565 CFNumberRef size = copyChildWithNameAsInteger(tree, namespaces, SD_XML_NAMESPACE, SD_XML_SIZE);
566 if (size && state->plist) {
567 CFDictionarySetValue(state->plist, SD_XML_SIZE, size);
568 }
569 CFReleaseNull(size);
570
571 CFDateRef created = copyChildWithNameAsDate(tree, namespaces, SD_XML_NAMESPACE, SD_XML_CREATED);
572 if (created && state->plist) {
573 CFDictionarySetValue(state->plist, SD_XML_CREATED, created);
574 }
575 CFReleaseNull(created);
576
577 descend = 1;
578
579 } else if (CFEqual(name, SD_XML_SITES)) {
580 CFArrayRef urls = copyChildrenWithNameAsURLs(tree, namespaces, SD_XML_NAMESPACE, SD_XML_URL);
581 if (urls && state->plist) {
582 CFDictionarySetValue(state->plist, SD_XML_URL, urls);
583 }
584 CFReleaseNull(urls);
585
586 } else if (CFEqual(name, SD_XML_VERIFICATIONS)) {
587 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
588 if (dict && state->plist) {
589 CFDictionarySetValue(state->plist, SD_XML_VERIFICATIONS, dict);
590 CFRelease(dict);
591 descend = 1;
592 }
593
594 } else if (CFEqual(name, SD_XML_VERIFICATION) && state->plist) {
595 CFMutableDictionaryRef verifications = (CFMutableDictionaryRef)CFDictionaryGetValue(state->plist, SD_XML_VERIFICATIONS);
596 CFXMLElementInfo* info = (CFXMLElementInfo*)CFXMLNodeGetInfoPtr(node);
597 if (verifications && info && info->attributes) {
598 CFStringRef algorithm = CFDictionaryGetValue(info->attributes, SD_XML_ATTR_ALGORITHM);
599 if (algorithm) {
600 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
601 if (dict) {
602 CFXMLTreeRef child;
603 #pragma unused(child)
604
605 CFNumberRef sector_size = copyChildWithNameAsInteger(tree, namespaces, SD_XML_NAMESPACE, SD_XML_SECTOR_SIZE);
606 if (sector_size) {
607 CFDictionarySetValue(dict, SD_XML_SECTOR_SIZE, sector_size);
608 CFRelease(sector_size);
609 }
610
611 CFDataRef digest = copyChildWithNameAsData(tree, namespaces, SD_XML_NAMESPACE, SD_XML_DIGEST);
612 if (digest) {
613 CFDictionarySetValue(dict, SD_XML_DIGEST, digest);
614 CFRelease(digest);
615 }
616
617 CFDataRef digests = copyChildWithNameAsData(tree, namespaces, SD_XML_NAMESPACE, SD_XML_DIGESTS);
618 if (digests) {
619 CFDictionarySetValue(dict, SD_XML_DIGESTS, digests);
620 CFRelease(digests);
621 }
622
623 CFDictionarySetValue(verifications, algorithm, dict);
624 CFRelease(dict);
625 }
626 }
627 }
628 }
629 }
630 #if LOCAL_DEBUG
631 cfprintf(stderr, "%sELEM:\t%@\t[%@]\n", state->prefix, name, ns);
632 #endif
633 }
634 if (ns) CFRelease(ns);
635 if (name) CFRelease(name);
636 } else if (type == kCFXMLNodeTypeWhitespace) {
637 // do nothing
638 } else {
639 #if LOCAL_DEBUG
640 CFStringRef str = CFXMLNodeGetString(node);
641 cfprintf(stderr, "%s% 4d:\t%@\n", state->prefix, type, str);
642 #endif
643 }
644
645 // only recurse further if we have been specifically instructed to
646 // do so.
647 if (descend) {
648 struct parseState local;
649 memcpy(&local, state, sizeof(struct parseState));
650 local.namespaces = namespaces;
651 #if LOCAL_DEBUG
652 asprintf(&local.prefix, "%s ", state->prefix);
653 #endif
654 CFTreeApplyFunctionToChildren(tree, _parseXMLNode, &local);
655 #if LOCAL_DEBUG
656 free(local.prefix);
657 #endif
658 }
659 if (namespaces) CFRelease(namespaces);
660 }
661
662 CFPropertyListRef _SecureDownloadParseTicketXML(CFDataRef xmlData) {
663 if (!xmlData) return NULL;
664 CFURLRef url = NULL;
665
666 CFXMLTreeRef tree = CFXMLTreeCreateFromData(NULL, xmlData, url, kCFXMLParserNoOptions, kCFXMLNodeCurrentVersion);
667 if (!tree) return NULL;
668
669 struct parseState state;
670 memset(&state, 0, sizeof(state));
671 #if LOCAL_DEBUG
672 state.prefix = "";
673 #endif
674 state.plists = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
675 if (state.plists) {
676 CFTreeApplyFunctionToChildren(tree, _parseXMLNode, &state);
677 }
678 CFRelease(tree);
679
680 CFPropertyListRef result = NULL;
681 // For now, only return the first resource encountered
682 if (state.plists && CFArrayGetCount(state.plists) > 0) {
683 result = CFArrayGetValueAtIndex(state.plists, 0);
684 CFRetain(result);
685 }
686 if (state.plists) CFRelease(state.plists);
687 return result;
688 }
689
690 static void _appendCString(CFMutableDataRef data, const char* cstring) {
691 CFDataAppendBytes(data, (UInt8*)cstring, strlen(cstring));
692 }
693
694 static void _appendCFString(CFMutableDataRef data, CFStringRef string) {
695 CFDataRef utf8 = CFStringCreateExternalRepresentation(NULL, string, kCFStringEncodingUTF8, '?');
696 if (utf8) {
697 CFDataAppendBytes(data, CFDataGetBytePtr(utf8), CFDataGetLength(utf8));
698 CFRelease(utf8);
699 }
700 }
701
702 static void _appendCFNumber(CFMutableDataRef data, CFNumberRef number) {
703 CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), number);
704 if (str) {
705 _appendCFString(data, str);
706 CFRelease(str);
707 }
708 }
709
710 static void _appendCFURL(CFMutableDataRef data, CFURLRef url) {
711 CFURLRef abs = CFURLCopyAbsoluteURL(url);
712 if (abs) {
713 CFStringRef str = CFURLGetString(abs);
714 if (str) {
715 _appendCFString(data, str);
716 }
717 CFRelease(abs);
718 }
719 }
720
721 // appends data in base64 encoded form
722 static void _appendCFData(CFMutableDataRef data, CFDataRef moreData) {
723 CFStringRef str = encodeBase64String(CFDataGetBytePtr(moreData), CFDataGetLength(moreData), 0);
724 if (str) {
725 _appendCFString(data, str);
726 CFRelease(str);
727 }
728 }
729
730 // appends a date in W3C DateTime format
731 static void _appendCFDate(CFMutableDataRef data, CFDateRef date) {
732 CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US"));
733 if (locale) {
734 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
735 if (formatter) {
736 CFDateFormatterSetFormat(formatter, CFSTR("yyyy-MM-dd'T'HH:mm:ss.S'Z'"));
737 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
738 if (tz) {
739 CFDateFormatterSetProperty(formatter, kCFDateFormatterTimeZone, tz);
740 CFStringRef str = CFDateFormatterCreateStringWithDate(NULL, formatter, date);
741 if (str) {
742 _appendCFString(data, str);
743 CFRelease(str);
744 }
745 CFRelease(tz);
746 }
747 CFRelease(formatter);
748 }
749 CFRelease(locale);
750 }
751 }
752
753 static CFArrayRef dictionaryGetSortedKeys(CFDictionaryRef dictionary) {
754 CFIndex count = CFDictionaryGetCount(dictionary);
755
756 const void** keys = malloc(sizeof(CFTypeRef) * count);
757 CFDictionaryGetKeysAndValues(dictionary, keys, NULL);
758 CFArrayRef keysArray = CFArrayCreate(NULL, keys, count, &kCFTypeArrayCallBacks);
759 CFMutableArrayRef sortedKeys = CFArrayCreateMutableCopy(NULL, count, keysArray);
760 CFRelease(keysArray);
761 free(keys);
762
763 CFArraySortValues(sortedKeys, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, 0);
764 return sortedKeys;
765 }
766
767 CFDataRef _SecureDownloadCreateTicketXML(CFPropertyListRef plist) {
768 CFMutableDataRef data = CFDataCreateMutable(NULL, 0);
769 if (!data) return NULL;
770
771 _appendCString(data, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
772 _appendCString(data, "<SecureDownload xmlns=\"http://www.apple.com/2006/SecureDownload/1\">\n");
773 _appendCString(data, " <resource>\n");
774
775 CFStringRef name = CFDictionaryGetValue(plist, SD_XML_NAME);
776 if (name) {
777 _appendCString(data, "\t<name>");
778 _appendCFString(data, name);
779 _appendCString(data, "</name>\n");
780 }
781
782 CFNumberRef num = CFDictionaryGetValue(plist, SD_XML_SIZE);
783 if (num) {
784 _appendCString(data, "\t<size>");
785 _appendCFNumber(data, num);
786 _appendCString(data, "</size>\n");
787 }
788
789 CFDateRef created = CFDictionaryGetValue(plist, SD_XML_CREATED);
790 if (created) {
791 _appendCString(data, "\t<created>");
792 _appendCFDate(data, created);
793 _appendCString(data, "</created>\n");
794 }
795
796 _appendCString(data, "\t<sites>\n");
797 CFArrayRef urls = CFDictionaryGetValue(plist, SD_XML_URL);
798 if (urls) {
799 CFIndex i, count = CFArrayGetCount(urls);
800 for (i = 0; i < count; ++i) {
801 _appendCString(data, "\t\t<url>");
802 _appendCFURL(data, CFArrayGetValueAtIndex(urls, i));
803 _appendCString(data, "</url>\n");
804 }
805 }
806 _appendCString(data, "\t</sites>\n");
807
808 CFDictionaryRef verifications = CFDictionaryGetValue(plist, SD_XML_VERIFICATIONS);
809 if (verifications) {
810 _appendCString(data, "\t<verifications>\n");
811 CFArrayRef algorithms = dictionaryGetSortedKeys(verifications);
812 if (algorithms) {
813 CFIndex i, count = CFArrayGetCount(algorithms);
814 for (i = 0; i < count; ++i) {
815 CFStringRef algorithm = CFArrayGetValueAtIndex(algorithms, i);
816 if (algorithm) {
817 _appendCString(data, "\t\t<verification algorithm=\"");
818 _appendCFString(data, algorithm);
819 _appendCString(data, "\">\n");
820 CFDictionaryRef dict = CFDictionaryGetValue(verifications, algorithm);
821 if (dict) {
822 CFDataRef digest = CFDictionaryGetValue(dict, SD_XML_DIGEST);
823 if (digest) {
824 _appendCString(data, "\t\t\t<digest>");
825 _appendCFData(data, digest);
826 _appendCString(data, "</digest>\n");
827 }
828
829 CFNumberRef sector_size = CFDictionaryGetValue(dict, SD_XML_SECTOR_SIZE);
830 if (sector_size) {
831 _appendCString(data, "\t\t\t<sector_size>");
832 _appendCFNumber(data, sector_size);
833 _appendCString(data, "</sector_size>\n");
834 }
835
836 CFDataRef digests = CFDictionaryGetValue(dict, SD_XML_DIGESTS);
837 if (digest) {
838 _appendCString(data, "\t\t\t<digests>");
839 _appendCFData(data, digests);
840 _appendCString(data, "</digests>\n");
841 }
842 }
843 _appendCString(data, "\t\t</verification>\n");
844 }
845 }
846 CFRelease(algorithms);
847 }
848 _appendCString(data, "\t</verifications>\n");
849 }
850
851 _appendCString(data, " </resource>\n");
852 _appendCString(data, "</SecureDownload>\n");
853
854 return data;
855 }
856
857 #if LOCAL_DEBUG
858 #include <unistd.h>
859 int main(int argc, char* argv[]) {
860
861 CFDataRef data = read_data("/Users/kevin/Desktop/SecureDownloadXML/SecureDownload.xml");
862 if (data) {
863 CFPropertyListRef plist = _SecureDownloadParseTicketXML(data);
864 CFShow(plist);
865 if (plist) {
866 CFDataRef output = _SecureDownloadCreateTicketXML(plist);
867 if (output) {
868 write(STDOUT_FILENO, CFDataGetBytePtr(output), CFDataGetLength(output));
869 CFRelease(output);
870 }
871 CFRelease(plist);
872 }
873 CFRelease(data);
874 }
875
876 // help look for leaks
877 cfprintf(stderr, "pid = %d\n", getpid());
878 sleep(1000);
879 }
880 #endif