1/*
2 * Copyright (c) 2003-2007,2009-2010 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 * keychain_find.c
24 */
25
26#include <TargetConditionals.h>
27#if TARGET_OS_EMBEDDED
28
29#include "SecurityCommands.h"
30
31#include "security.h"
32#include "SecurityTool/print_cert.h"
33#include "SecBase64.h"
34#include <errno.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/types.h>
40#include <unistd.h>
41
42#include <SecurityTool/tool_errors.h>
43
44#include <Security/SecItem.h>
45
46#include <CoreFoundation/CFArray.h>
47#include <CoreFoundation/CFDate.h>
48#include <CoreFoundation/CFNumber.h>
49#include <CoreFoundation/CFString.h>
50
51#include <Security/SecCertificatePriv.h>
52#include <Security/SecPolicyPriv.h>
53#include <Security/SecTrustPriv.h>
54#include <Security/SecInternal.h>
55
56#include <SecurityTool/readline.h>
57
58#include <utilities/SecCFWrappers.h>
59
60typedef uint32_t SecProtocolType;
61typedef uint32_t SecAuthenticationType;
62
63static void
64keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
65    bool inkey = true;
66    bool escaped = false;
67    CFStringRef key = NULL;
68    CFMutableStringRef str = CFStringCreateMutable(0, 0);
69    CFRange rng = { .location = 0, .length = CFStringGetLength(s) };
70    CFCharacterSetRef cs_key = CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
71    CFCharacterSetRef cs_value = CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
72    while (rng.length) {
73        CFRange r;
74        CFStringRef sub;
75        bool complete = false;
76        if (escaped) {
77            r.location = rng.location;
78            r.length = 1;
79            sub = CFStringCreateWithSubstring(0, s, r);
80            escaped = false;
81        } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
82            if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
83                escaped = true;
84            } else {
85                complete = true;
86            }
87            CFIndex next = r.location + 1;
88            r.length = r.location - rng.location;
89            r.location = rng.location;
90            sub = CFStringCreateWithSubstring(0, s, r);
91            rng.length -= next - rng.location;
92            rng.location = next;
93        } else {
94            sub = CFStringCreateWithSubstring(0, s, rng);
95            rng.location += rng.length;
96            rng.length = 0;
97            complete = true;
98        }
99        CFStringAppend(str, sub);
100        CFRelease(sub);
101        if (complete) {
102            CFStringRef value = CFStringCreateCopy(0, str);
103            CFStringReplaceAll(str, CFSTR(""));
104            if (inkey) {
105                key = value;
106            } else {
107                CFDictionarySetValue(q, key, value);
108                CFReleaseNull(value);
109                CFReleaseNull(key);
110            }
111            inkey = !inkey;
112        }
113    }
114    if (key) {
115        /* Dangeling key value is true?. */
116        CFDictionarySetValue(q, key, kCFBooleanTrue);
117        CFRelease(key);
118    }
119    CFRelease(str);
120}
121
122static void
123keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
124    CFStringRef s;
125    s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
126    keychain_query_parse_string(q, s);
127    CFRelease(s);
128}
129
130static void show_cert_eval(CFArrayRef certs, bool verbose) {
131    SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
132    SecTrustRef trust = NULL;
133    OSStatus status = SecTrustCreateWithCertificates(certs, policy, &trust);
134    SecTrustResultType trustResult;
135    const char *trustResults[] = {
136        "invalid",
137        "proceed",
138        "confirm",
139        "deny",
140        "unspecified",
141        "recoverable trust failure",
142        "fatal trust failure",
143        "other error",
144    };
145    status = SecTrustEvaluate(trust, &trustResult);
146    printf("* trust: %s *\n", trustResults[trustResult]);
147    CFArrayRef properties = SecTrustCopyProperties(trust);
148    print_plist(properties);
149    CFReleaseNull(properties);
150    CFIndex ix, count = SecTrustGetCertificateCount(trust);
151    for (ix = 0; ix < count; ++ix) {
152        printf("* cert %ld summary properties *\n", ix);
153        properties = SecTrustCopySummaryPropertiesAtIndex(trust, ix);
154        print_plist(properties);
155        CFReleaseNull(properties);
156        if (verbose) {
157            printf("* cert %ld detail properties *\n", ix);
158            properties = SecTrustCopyDetailedPropertiesAtIndex(trust, ix);
159            print_plist(properties);
160            CFReleaseNull(properties);
161        }
162    }
163
164    CFDictionaryRef info = SecTrustCopyInfo(trust);
165    if (info) {
166        printf("* info *\n");
167        CFShow(info);
168        CFReleaseNull(info);
169    }
170}
171
172static size_t print_buffer_pem(FILE *stream, const char *pem_name, size_t length,
173                            const uint8_t *bytes) {
174    size_t pem_name_len = strlen(pem_name);
175    size_t b64_len = SecBase64Encode2(NULL, length, NULL, 0,
176                                      kSecB64_F_LINE_LEN_USE_PARAM, 64, NULL);
177    char *buffer = malloc(33 + 2 * pem_name_len + b64_len);
178    char *p = buffer;
179    p += sprintf(buffer, "-----BEGIN %s-----\n", pem_name);
180    SecBase64Result result;
181    p += SecBase64Encode2(bytes, length, p, b64_len,\
182                          kSecB64_F_LINE_LEN_USE_PARAM, 64, &result);
183    if (result) {
184        free(buffer);
185        return result;
186    }
187    p += sprintf(p, "\n-----END %s-----\n", pem_name);
188    size_t res = fwrite(buffer, 1, p - buffer, stream);
189    fflush(stream);
190    bzero(buffer, p - buffer);
191    free(buffer);
192    return res;
193}
194
195int keychain_show_certificates(int argc, char * const *argv)
196{
197	int ch, result = 0;
198	bool output_subject = false;
199	bool verbose = false;
200    bool trust_eval = false;
201    bool keychain_certs = false;
202    bool output_pem = false;
203    bool output_finger_print = false;
204    CFMutableArrayRef certs = NULL;
205    CFMutableDictionaryRef query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
206
207	while ((ch = getopt(argc, argv, "kfq:pstv")) != -1)
208	{
209		switch  (ch)
210		{
211        case 'k':
212            keychain_certs = true;
213            break;
214        case 'p':
215            output_pem = true;
216            break;
217		case 's':
218			output_subject = true;
219			break;
220        case 'v':
221            verbose = true;
222            break;
223		case 't':
224			trust_eval = true;
225			break;
226        case 'f':
227            output_finger_print = true;
228            break;
229        case 'q':
230            keychain_query_parse_cstring(query, optarg);
231            keychain_certs = true;
232            break;
233        case '?':
234		default:
235			return 2; /* @@@ Return 2 triggers usage message. */
236		}
237	}
238
239	argc -= optind;
240	argv += optind;
241
242    if ((keychain_certs && argc > 0) || (!keychain_certs && argc < 1))
243        result = 2;
244
245    if (trust_eval)
246        certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
247
248    CFArrayRef kc_certs = NULL;
249    int arg;
250    if (keychain_certs) {
251        for (arg = 0; arg < argc; ++arg) {
252            keychain_query_parse_cstring(query, argv[arg]);
253        }
254        CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
255        CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
256        CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
257        CFTypeRef results;
258        if (!SecItemCopyMatching(query, &results)) {
259            kc_certs = results;
260            argc = (int) CFArrayGetCount(kc_certs);
261        }
262    }
263
264    for (arg = 0; arg < argc; ++arg) {
265        SecCertificateRef cert = NULL;
266        if (keychain_certs) {
267            cert = (SecCertificateRef)CFArrayGetValueAtIndex(kc_certs, arg);
268        } else {
269            CFDataRef data = copyFileContents(argv[arg]);
270            if (data) {
271                cert = SecCertificateCreateWithData(
272                    kCFAllocatorDefault, data);
273                if (!cert) {
274                    /* DER failed, try PEM. */
275                    cert = SecCertificateCreateWithPEM(kCFAllocatorDefault, data);
276                }
277                CFRelease(data);
278            } else {
279                result = 1;
280            }
281        }
282
283        if (cert) {
284            if (!keychain_certs) {
285                printf(
286                       "*******************************************************\n"
287                       "%s\n"
288                       "*******************************************************\n"
289                       , argv[arg]);
290            }
291            if (trust_eval) {
292                if (keychain_certs) {
293                    CFArraySetValueAtIndex(certs, 0, cert);
294                    show_cert_eval(certs, verbose);
295                } else {
296                    CFArrayAppendValue(certs, cert);
297                }
298            } else {
299                if (verbose) {
300                    print_cert(cert, verbose);
301                } else if (output_subject) {
302                    CFStringRef subject = SecCertificateCopySubjectString(cert);
303                    if (subject) {
304                        CFStringWriteToFileWithNewline(subject, stdout);
305                        CFRelease(subject);
306                    }
307                } else if (!output_pem) {
308                    print_cert(cert, verbose);
309                }
310            }
311            if (output_finger_print) {
312                CFDataRef key_fingerprint = SecCertificateCopyPublicKeySHA1Digest(cert);
313                if (key_fingerprint) {
314                    int i;
315                    CFIndex j = CFDataGetLength(key_fingerprint);
316                    const uint8_t *byte = CFDataGetBytePtr(key_fingerprint);
317
318                    fprintf(stdout, "Key fingerprint:");
319                    for (i = 0; i < j; i++) {
320                        fprintf(stdout, " %02X", byte[i]);
321                    }
322                    fprintf(stdout, "\n");
323                }
324            }
325            if (output_pem) {
326                print_buffer_pem(stdout, "CERTIFICATE",
327                                 SecCertificateGetLength(cert),
328                                 SecCertificateGetBytePtr(cert));
329            }
330            if (!keychain_certs) {
331                CFRelease(cert);
332            }
333        } else {
334            result = 1;
335            fprintf(stderr, "file %s: does not contain a valid certificate",
336                argv[arg]);
337        }
338    }
339
340    if (trust_eval && !keychain_certs)
341        show_cert_eval(certs, verbose);
342
343    CFReleaseSafe(kc_certs);
344    CFReleaseSafe(certs);
345
346	return result;
347}
348
349#endif // TARGET_OS_EMBEDDED
350