1/*
2 * Copyright (c) 2003-2008,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * printCert.c - utility functions for printing certificate info
26 */
27
28#include "printCert.h"
29#include <CoreFoundation/CoreFoundation.h>
30#include <Security/SecCertificatePriv.h>
31#include <Security/SecTrustPriv.h>
32
33void fprint_string(CFStringRef string, FILE *file) {
34    UInt8 buf[256];
35    CFRange range = { .location = 0 };
36    range.length = CFStringGetLength(string);
37    while (range.length > 0) {
38        CFIndex bytesUsed = 0;
39        CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed);
40        fwrite(buf, 1, bytesUsed, file);
41        range.length -= converted;
42        range.location += converted;
43    }
44}
45
46void print_line(CFStringRef line) {
47    fprint_string(line, stdout);
48    fputc('\n', stdout);
49}
50
51static void printPlist(CFArrayRef plist, CFIndex indent, CFIndex maxWidth) {
52    CFIndex count = CFArrayGetCount(plist);
53    CFIndex ix;
54    for (ix = 0; ix < count ; ++ix) {
55        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
56            ix);
57        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
58            kSecPropertyKeyType);
59        CFStringRef label = (CFStringRef)CFDictionaryGetValue(prop,
60            kSecPropertyKeyLabel);
61        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
62            kSecPropertyKeyLocalizedLabel);
63        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
64            kSecPropertyKeyValue);
65
66        bool isSection = CFEqual(pType, kSecPropertyTypeSection);
67        CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
68        CFIndex jx = 0;
69        for (jx = 0; jx < indent; ++jx) {
70            CFStringAppend(line, CFSTR("    "));
71        }
72        if (llabel) {
73            CFStringAppend(line, llabel);
74            if (!isSection) {
75                for (jx = CFStringGetLength(llabel) + indent * 4;
76                    jx < maxWidth; ++jx) {
77                    CFStringAppend(line, CFSTR(" "));
78                }
79                CFStringAppend(line, CFSTR(" : "));
80            }
81        }
82        if (CFEqual(pType, kSecPropertyTypeWarning)) {
83            CFStringAppend(line, CFSTR("*WARNING* "));
84            CFStringAppend(line, (CFStringRef)value);
85        } else if (CFEqual(pType, kSecPropertyTypeError)) {
86            CFStringAppend(line, CFSTR("*ERROR* "));
87            CFStringAppend(line, (CFStringRef)value);
88        } else if (CFEqual(pType, kSecPropertyTypeSuccess)) {
89            CFStringAppend(line, CFSTR("*OK* "));
90            CFStringAppend(line, (CFStringRef)value);
91        } else if (CFEqual(pType, kSecPropertyTypeTitle)) {
92            CFStringAppend(line, CFSTR("*"));
93            CFStringAppend(line, (CFStringRef)value);
94            CFStringAppend(line, CFSTR("*"));
95        } else if (CFEqual(pType, kSecPropertyTypeSection)) {
96        } else if (CFEqual(pType, kSecPropertyTypeData)) {
97            CFDataRef data = (CFDataRef)value;
98            CFIndex length = CFDataGetLength(data);
99            if (length > 20)
100                CFStringAppendFormat(line, NULL, CFSTR("[%d bytes] "), length);
101            const UInt8 *bytes = CFDataGetBytePtr(data);
102            for (jx = 0; jx < length; ++jx) {
103                if (jx == 0)
104                    CFStringAppendFormat(line, NULL, CFSTR("%02X"), bytes[jx]);
105                else if (jx < 15 || length <= 20)
106                    CFStringAppendFormat(line, NULL, CFSTR(" %02X"),
107                        bytes[jx]);
108                else {
109                    CFStringAppend(line, CFSTR(" ..."));
110                    break;
111                }
112            }
113        } else if (CFEqual(pType, kSecPropertyTypeString)) {
114            CFStringAppend(line, (CFStringRef)value);
115        } else if (CFEqual(pType, kSecPropertyTypeDate)) {
116            CFDateRef date = (CFDateRef)value;
117            CFLocaleRef lc = CFLocaleCopyCurrent();
118            CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc, kCFDateFormatterMediumStyle, kCFDateFormatterLongStyle);
119            CFStringRef ds;
120            if (df) {
121                CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
122                CFDateFormatterSetProperty(df, kCFDateFormatterTimeZone, tz);
123                CFRelease(tz);
124                ds = CFDateFormatterCreateStringWithDate(NULL, df, date);
125                CFRelease(df);
126            } else {
127                ds = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), CFDateGetAbsoluteTime(date));
128            }
129            CFStringAppend(line, ds);
130            CFRelease(ds);
131            CFRelease(lc);
132        } else if (CFEqual(pType, kSecPropertyTypeURL)) {
133            CFURLRef url = (CFURLRef)value;
134            CFStringAppend(line, CFSTR("<"));
135            CFStringAppend(line, CFURLGetString(url));
136            CFStringAppend(line, CFSTR(">"));
137        } else {
138            CFStringAppendFormat(line, NULL, CFSTR("*unknown type %@* = %@"),
139            pType, value);
140        }
141
142		if (!isSection || label)
143			print_line(line);
144		CFRelease(line);
145        if (isSection) {
146            printPlist((CFArrayRef)value, indent + 1, maxWidth);
147        }
148    }
149}
150
151static CFIndex maxLabelWidth(CFArrayRef plist, CFIndex indent) {
152    CFIndex count = CFArrayGetCount(plist);
153    CFIndex ix;
154    CFIndex maxWidth = 0;
155    for (ix = 0; ix < count ; ++ix) {
156        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
157            ix);
158        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
159            kSecPropertyKeyType);
160        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
161            kSecPropertyKeyLocalizedLabel);
162        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
163            kSecPropertyKeyValue);
164
165        if (CFEqual(pType, kSecPropertyTypeSection)) {
166            CFIndex width = maxLabelWidth((CFArrayRef)value, indent + 1);
167            if (width > maxWidth)
168                maxWidth = width;
169        } else if (llabel) {
170            CFIndex width = indent * 4 + CFStringGetLength(llabel);
171            if (width > maxWidth)
172                maxWidth = width;
173        }
174    }
175
176    return maxWidth;
177}
178
179void print_plist(CFArrayRef plist) {
180    if (plist)
181        printPlist(plist, 0, maxLabelWidth(plist, 0));
182    else
183        printf("NULL plist\n");
184}
185
186void print_cert(SecCertificateRef cert, bool verbose) {
187// TODO: merge these when all SecCertificate APIs are present on both iOS and OS X
188#if TARGET_OS_IOS
189    CFArrayRef plist;
190    if (verbose)
191        plist = SecCertificateCopyProperties(cert);
192    else {
193        CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
194        plist = SecCertificateCopySummaryProperties(cert, now);
195    }
196
197    CFStringRef subject = SecCertificateCopySubjectString(cert);
198    if (subject) {
199        print_line(subject);
200        CFRelease(subject);
201    } else {
202        print_line(CFSTR("no subject"));
203    }
204
205    print_plist(plist);
206    CFRelease(plist);
207#else
208    CFStringRef certName = NULL;
209    OSStatus status = SecCertificateInferLabel(cert, &certName);
210    if (certName) {
211        print_line(certName);
212        CFRelease(certName);
213    }
214    else {
215        fprintf(stdout, "ERROR: unable to read certificate name\n");
216    }
217#endif
218}
219