1/*
2 * Copyright (c) 2003-2008,2011,2013-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 *  print_cert.c
24 *
25 */
26
27
28#include "print_cert.h"
29#include <CoreFoundation/CoreFoundation.h>
30#include <Security/SecCertificatePriv.h>
31#include <Security/SecTrustPriv.h>
32#include <utilities/SecIOFormat.h>
33#include <utilities/SecCFWrappers.h>
34
35
36static void printPlist(CFArrayRef plist, CFIndex indent, CFIndex maxWidth) {
37    CFIndex count = CFArrayGetCount(plist);
38    CFIndex ix;
39    for (ix = 0; ix < count ; ++ix) {
40        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
41            ix);
42        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
43            kSecPropertyKeyType);
44        CFStringRef label = (CFStringRef)CFDictionaryGetValue(prop,
45            kSecPropertyKeyLabel);
46        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
47            kSecPropertyKeyLocalizedLabel);
48        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
49            kSecPropertyKeyValue);
50
51        bool isSection = CFEqual(pType, kSecPropertyTypeSection);
52        CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
53        CFIndex jx = 0;
54        for (jx = 0; jx < indent; ++jx) {
55            CFStringAppend(line, CFSTR("    "));
56        }
57        if (llabel) {
58            CFStringAppend(line, llabel);
59            if (!isSection) {
60                for (jx = CFStringGetLength(llabel) + indent * 4;
61                    jx < maxWidth; ++jx) {
62                    CFStringAppend(line, CFSTR(" "));
63                }
64                CFStringAppend(line, CFSTR(" : "));
65            }
66        }
67        if (CFEqual(pType, kSecPropertyTypeWarning)) {
68            CFStringAppend(line, CFSTR("*WARNING* "));
69            CFStringAppend(line, (CFStringRef)value);
70        } else if (CFEqual(pType, kSecPropertyTypeError)) {
71            CFStringAppend(line, CFSTR("*ERROR* "));
72            CFStringAppend(line, (CFStringRef)value);
73        } else if (CFEqual(pType, kSecPropertyTypeSuccess)) {
74            CFStringAppend(line, CFSTR("*OK* "));
75            CFStringAppend(line, (CFStringRef)value);
76        } else if (CFEqual(pType, kSecPropertyTypeTitle)) {
77            CFStringAppend(line, CFSTR("*"));
78            CFStringAppend(line, (CFStringRef)value);
79            CFStringAppend(line, CFSTR("*"));
80        } else if (CFEqual(pType, kSecPropertyTypeSection)) {
81        } else if (CFEqual(pType, kSecPropertyTypeData)) {
82            CFDataRef data = (CFDataRef)value;
83            CFIndex length = CFDataGetLength(data);
84            if (length > 20)
85                CFStringAppendFormat(line, NULL, CFSTR("[%" PRIdCFIndex " bytes] "), length);
86            const UInt8 *bytes = CFDataGetBytePtr(data);
87            for (jx = 0; jx < length; ++jx) {
88                if (jx == 0)
89                    CFStringAppendFormat(line, NULL, CFSTR("%02X"), bytes[jx]);
90                else if (jx < 15 || length <= 20)
91                    CFStringAppendFormat(line, NULL, CFSTR(" %02X"),
92                        bytes[jx]);
93                else {
94                    CFStringAppend(line, CFSTR(" ..."));
95                    break;
96                }
97            }
98        } else if (CFEqual(pType, kSecPropertyTypeString)) {
99            CFStringAppend(line, (CFStringRef)value);
100        } else if (CFEqual(pType, kSecPropertyTypeDate)) {
101            CFDateRef date = (CFDateRef)value;
102            CFLocaleRef lc = CFLocaleCopyCurrent();
103            CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc, kCFDateFormatterMediumStyle, kCFDateFormatterLongStyle);
104            CFStringRef ds;
105            if (df) {
106                CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
107                CFDateFormatterSetProperty(df, kCFDateFormatterTimeZone, tz);
108                CFRelease(tz);
109                ds = CFDateFormatterCreateStringWithDate(NULL, df, date);
110                CFRelease(df);
111            } else {
112                ds = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), CFDateGetAbsoluteTime(date));
113            }
114            CFStringAppend(line, ds);
115            CFRelease(ds);
116            CFRelease(lc);
117        } else if (CFEqual(pType, kSecPropertyTypeURL)) {
118            CFURLRef url = (CFURLRef)value;
119            CFStringAppend(line, CFSTR("<"));
120            CFStringAppend(line, CFURLGetString(url));
121            CFStringAppend(line, CFSTR(">"));
122        } else {
123            CFStringAppendFormat(line, NULL, CFSTR("*unknown type %@* = %@"),
124            pType, value);
125        }
126
127		if (!isSection || label)
128			CFStringWriteToFileWithNewline(line, stdout);
129		CFRelease(line);
130        if (isSection) {
131            printPlist((CFArrayRef)value, indent + 1, maxWidth);
132        }
133    }
134}
135
136static CFIndex maxLabelWidth(CFArrayRef plist, CFIndex indent) {
137    CFIndex count = CFArrayGetCount(plist);
138    CFIndex ix;
139    CFIndex maxWidth = 0;
140    for (ix = 0; ix < count ; ++ix) {
141        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
142            ix);
143        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
144            kSecPropertyKeyType);
145        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
146            kSecPropertyKeyLocalizedLabel);
147        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
148            kSecPropertyKeyValue);
149
150        if (CFEqual(pType, kSecPropertyTypeSection)) {
151            CFIndex width = maxLabelWidth((CFArrayRef)value, indent + 1);
152            if (width > maxWidth)
153                maxWidth = width;
154        } else if (llabel) {
155            CFIndex width = indent * 4 + CFStringGetLength(llabel);
156            if (width > maxWidth)
157                maxWidth = width;
158        }
159    }
160
161    return maxWidth;
162}
163
164void print_plist(CFArrayRef plist) {
165    if (plist)
166        printPlist(plist, 0, maxLabelWidth(plist, 0));
167    else
168        printf("NULL plist\n");
169}
170
171void print_cert(SecCertificateRef cert, bool verbose) {
172    CFArrayRef plist;
173    if (verbose)
174        plist = SecCertificateCopyProperties(cert);
175    else {
176        CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
177        plist = SecCertificateCopySummaryProperties(cert, now);
178    }
179
180    CFStringRef subject = SecCertificateCopySubjectString(cert);
181    if (subject) {
182        CFStringWriteToFileWithNewline(subject, stdout);
183        CFRelease(subject);
184    } else {
185        CFStringWriteToFileWithNewline(CFSTR("no subject"), stdout);
186    }
187
188    print_plist(plist);
189    CFRelease(plist);
190}
191