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