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