show_certificates.c   [plain text]


/*
 * Copyright (c) 2003-2007,2009-2010 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 * keychain_find.c
 */

#include <TargetConditionals.h>
#if TARGET_OS_EMBEDDED

#include "SecurityCommands.h"

#include "security.h"
#include "SecurityTool/print_cert.h"
#include "SecBase64.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include <SecurityTool/tool_errors.h>

#include <Security/SecItem.h>

#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDate.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>

#include <Security/SecCertificatePriv.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecInternal.h>

#include <SecurityTool/readline.h>

#include <utilities/SecCFWrappers.h>

typedef uint32_t SecProtocolType;
typedef uint32_t SecAuthenticationType;

static void
keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
    bool inkey = true;
    bool escaped = false;
    CFStringRef key = NULL;
    CFMutableStringRef str = CFStringCreateMutable(0, 0);
    CFRange rng = { .location = 0, .length = CFStringGetLength(s) };
    CFCharacterSetRef cs_key = CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
    CFCharacterSetRef cs_value = CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
    while (rng.length) {
        CFRange r;
        CFStringRef sub;
        bool complete = false;
        if (escaped) {
            r.location = rng.location;
            r.length = 1;
            sub = CFStringCreateWithSubstring(0, s, r);
            escaped = false;
        } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
            if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
                escaped = true;
            } else {
                complete = true;
            }
            CFIndex next = r.location + 1;
            r.length = r.location - rng.location;
            r.location = rng.location;
            sub = CFStringCreateWithSubstring(0, s, r);
            rng.length -= next - rng.location;
            rng.location = next;
        } else {
            sub = CFStringCreateWithSubstring(0, s, rng);
            rng.location += rng.length;
            rng.length = 0;
            complete = true;
        }
        CFStringAppend(str, sub);
        CFRelease(sub);
        if (complete) {
            CFStringRef value = CFStringCreateCopy(0, str);
            CFStringReplaceAll(str, CFSTR(""));
            if (inkey) {
                key = value;
            } else {
                CFDictionarySetValue(q, key, value);
                CFReleaseNull(value);
                CFReleaseNull(key);
            }
            inkey = !inkey;
        }
    }
    if (key) {
        /* Dangeling key value is true?. */
        CFDictionarySetValue(q, key, kCFBooleanTrue);
        CFRelease(key);
    }
    CFRelease(str);
}

static void
keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
    CFStringRef s;
    s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
    keychain_query_parse_string(q, s);
    CFRelease(s);
}

static void show_cert_eval(CFArrayRef certs, bool verbose) {
    SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
    SecTrustRef trust = NULL;
    OSStatus status = SecTrustCreateWithCertificates(certs, policy, &trust);
    SecTrustResultType trustResult;
    const char *trustResults[] = {
        "invalid",
        "proceed",
        "confirm",
        "deny",
        "unspecified",
        "recoverable trust failure",
        "fatal trust failure",
        "other error",
    };
    status = SecTrustEvaluate(trust, &trustResult);
    printf("* trust: %s *\n", trustResults[trustResult]);
    CFArrayRef properties = SecTrustCopyProperties(trust);
    print_plist(properties);
    CFReleaseNull(properties);
    CFIndex ix, count = SecTrustGetCertificateCount(trust);
    for (ix = 0; ix < count; ++ix) {
        printf("* cert %ld summary properties *\n", ix);
        properties = SecTrustCopySummaryPropertiesAtIndex(trust, ix);
        print_plist(properties);
        CFReleaseNull(properties);
        if (verbose) {
            printf("* cert %ld detail properties *\n", ix);
            properties = SecTrustCopyDetailedPropertiesAtIndex(trust, ix);
            print_plist(properties);
            CFReleaseNull(properties);
        }
    }

    CFDictionaryRef info = SecTrustCopyInfo(trust);
    if (info) {
        printf("* info *\n");
        CFShow(info);
        CFReleaseNull(info);
    }
}

static size_t print_buffer_pem(FILE *stream, const char *pem_name, size_t length,
                            const uint8_t *bytes) {
    size_t pem_name_len = strlen(pem_name);
    size_t b64_len = SecBase64Encode2(NULL, length, NULL, 0,
                                      kSecB64_F_LINE_LEN_USE_PARAM, 64, NULL);
    char *buffer = malloc(33 + 2 * pem_name_len + b64_len);
    char *p = buffer;
    p += sprintf(buffer, "-----BEGIN %s-----\n", pem_name);
    SecBase64Result result;
    p += SecBase64Encode2(bytes, length, p, b64_len,\
                          kSecB64_F_LINE_LEN_USE_PARAM, 64, &result);
    if (result) {
        free(buffer);
        return result;
    }
    p += sprintf(p, "\n-----END %s-----\n", pem_name);
    size_t res = fwrite(buffer, 1, p - buffer, stream);
    fflush(stream);
    bzero(buffer, p - buffer);
    free(buffer);
    return res;
}

int keychain_show_certificates(int argc, char * const *argv)
{
	int ch, result = 0;
	bool output_subject = false;
	bool verbose = false;
    bool trust_eval = false;
    bool keychain_certs = false;
    bool output_pem = false;
    bool output_finger_print = false;
    CFMutableArrayRef certs = NULL;
    CFMutableDictionaryRef query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

	while ((ch = getopt(argc, argv, "kfq:pstv")) != -1)
	{
		switch  (ch)
		{
        case 'k':
            keychain_certs = true;
            break;
        case 'p':
            output_pem = true;
            break;
		case 's':
			output_subject = true;
			break;
        case 'v':
            verbose = true;
            break;
		case 't':
			trust_eval = true;
			break;
        case 'f':
            output_finger_print = true;
            break;
        case 'q':
            keychain_query_parse_cstring(query, optarg);
            keychain_certs = true;
            break;
        case '?':
		default:
			return 2; /* @@@ Return 2 triggers usage message. */
		}
	}
  
	argc -= optind;
	argv += optind;

    if ((keychain_certs && argc > 0) || (!keychain_certs && argc < 1))
        result = 2;

    if (trust_eval)
        certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

    CFArrayRef kc_certs = NULL;
    int arg;
    if (keychain_certs) {
        for (arg = 0; arg < argc; ++arg) {
            keychain_query_parse_cstring(query, argv[arg]);
        }
        CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
        CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
        CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
        CFTypeRef results;
        if (!SecItemCopyMatching(query, &results)) {
            kc_certs = results;
            argc = (int) CFArrayGetCount(kc_certs);
        }
    }

    for (arg = 0; arg < argc; ++arg) {
        SecCertificateRef cert = NULL;
        if (keychain_certs) {
            cert = (SecCertificateRef)CFArrayGetValueAtIndex(kc_certs, arg);
        } else {
            CFDataRef data = copyFileContents(argv[arg]);
            if (data) {
                cert = SecCertificateCreateWithData(
                    kCFAllocatorDefault, data);
                if (!cert) {
                    /* DER failed, try PEM. */
                    cert = SecCertificateCreateWithPEM(kCFAllocatorDefault, data);
                }
                CFRelease(data);
            } else {
                result = 1;
            }
        }

        if (cert) {
            if (!keychain_certs) {
                printf(
                       "*******************************************************\n"
                       "%s\n"
                       "*******************************************************\n"
                       , argv[arg]);
            }
            if (trust_eval) {
                if (keychain_certs) {
                    CFArraySetValueAtIndex(certs, 0, cert);
                    show_cert_eval(certs, verbose);
                } else {
                    CFArrayAppendValue(certs, cert);
                }
            } else {
                if (verbose) {
                    print_cert(cert, verbose);
                } else if (output_subject) {
                    CFStringRef subject = SecCertificateCopySubjectString(cert);
                    if (subject) {
                        CFStringWriteToFileWithNewline(subject, stdout);
                        CFRelease(subject);
                    }
                } else if (!output_pem) {
                    print_cert(cert, verbose);
                }
            }
            if (output_finger_print) {
                CFDataRef key_fingerprint = SecCertificateCopyPublicKeySHA1Digest(cert);
                if (key_fingerprint) {
                    int i;
                    CFIndex j = CFDataGetLength(key_fingerprint);
                    const uint8_t *byte = CFDataGetBytePtr(key_fingerprint);

                    fprintf(stdout, "Key fingerprint:");
                    for (i = 0; i < j; i++) {
                        fprintf(stdout, " %02X", byte[i]);
                    }
                    fprintf(stdout, "\n");
                }
            }
            if (output_pem) {
                print_buffer_pem(stdout, "CERTIFICATE",
                                 SecCertificateGetLength(cert),
                                 SecCertificateGetBytePtr(cert));
            }
            if (!keychain_certs) {
                CFRelease(cert);
            }
        } else {
            result = 1;
            fprintf(stderr, "file %s: does not contain a valid certificate",
                argv[arg]);
        }
    }

    if (trust_eval && !keychain_certs)
        show_cert_eval(certs, verbose);

    CFReleaseSafe(kc_certs);
    CFReleaseSafe(certs);

	return result;
}

#endif // TARGET_OS_EMBEDDED