/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * keychain_utilities.c * security * * Created by Michael Brouwer on Tue May 07 2003. * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * */ #include "keychain_utilities.h" #include #include #include #include #include SecKeychainRef keychain_open(const char *name) { SecKeychainRef keychain = NULL; OSStatus result = SecKeychainOpen(name, &keychain); if (result) { fprintf(stderr, "SecKeychainOpen(%s) returned %ld(0x%lx)\n", name, result, result); } return keychain; } CFTypeRef keychain_create_array(int argc, char * const *argv) { if (argc == 0) return NULL; else if (argc == 1) return keychain_open(argv[0]); else { CFMutableArrayRef keychains = CFArrayCreateMutable(NULL, argc, &kCFTypeArrayCallBacks); int ix; for (ix = 0; ix < argc; ++ix) { SecKeychainRef keychain = keychain_open(argv[ix]); if (keychain) { CFArrayAppendValue(keychains, keychain); CFRelease(keychain); } } return keychains; } } int print_keychain_name(FILE *stream, SecKeychainRef keychain) { int result = 0; char pathName[MAXPATHLEN]; UInt32 ioPathLength = sizeof(pathName); OSStatus status = SecKeychainGetPath(keychain, &ioPathLength, pathName); if (status) { fprintf(stderr, "SecKeychainGetPath() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } print_buffer(stream, ioPathLength, pathName); loser: return result; } int print_keychain_item_attributes(FILE *stream, SecKeychainItemRef item, Boolean show_data, Boolean show_raw_data) { int result = 0; unsigned int ix; OSStatus status; SecKeychainRef keychain = NULL; SecItemClass itemClass = 0; UInt32 itemID; SecKeychainAttributeList *attrList = NULL; SecKeychainAttributeInfo *info = NULL; UInt32 length = 0; void *data = NULL; status = SecKeychainItemCopyKeychain(item, &keychain); if (status) { fprintf(stderr, "SecKeychainItemCopyKeychain() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } fputs("keychain: ", stream); result = print_keychain_name(stream, keychain); fputc('\n', stream); if (result) goto loser; /* First find out the item class. */ status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL); if (status) { fprintf(stderr, "SecKeychainItemCopyAttributesAndData() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } fputs("class: ", stream); print_buffer(stream, 4, &itemClass); fputs("\nattributes:\n", stream); switch (itemClass) { case kSecInternetPasswordItemClass: itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD; break; case kSecGenericPasswordItemClass: itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD; break; case kSecAppleSharePasswordItemClass: itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD; break; default: itemID = itemClass; break; } /* Now get the AttributeInfo for it. */ status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info); if (status) { fprintf(stderr, "SecKeychainAttributeInfoForItemID() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, show_data ? &length : NULL, show_data ? &data : NULL); if (status) { fprintf(stderr, "SecKeychainItemCopyAttributesAndData() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } if (info->count != attrList->count) { fprintf(stderr, "info count: %ld != attribute count: %ld\n", info->count, attrList->count); result = 1; goto loser; } for (ix = 0; ix < info->count; ++ix) { UInt32 tag = info->tag[ix]; UInt32 format = info->format[ix]; SecKeychainAttribute *attribute = &attrList->attr[ix]; if (tag != attribute->tag) { fprintf(stderr, "attribute %d of %ld info tag: %ld != attribute tag: %ld\n", ix, info->count, tag, attribute->tag); result = 1; goto loser; } fputs(" ", stream); print_buffer(stream, 4, &tag); switch (format) { case CSSM_DB_ATTRIBUTE_FORMAT_STRING: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_REAL: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: fputs("", stream); break; case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX: fputs("", stream); break; default: fprintf(stream, "", format); break; } fputs("=", stream); if (!attribute->length && !attribute->data) fputs("", stream); else print_buffer(stream, attribute->length, attribute->data); fputc('\n', stream); } if (show_data) { fputs("data:\n", stream); print_buffer(stream, length, data); fputc('\n', stream); } if (show_raw_data) { CSSM_DL_DB_HANDLE dldbHandle = {}; const CSSM_DB_UNIQUE_RECORD *uniqueRecordID = NULL; CSSM_DATA data = {}; status = SecKeychainItemGetDLDBHandle(item, &dldbHandle); if (status) { fprintf(stderr, "SecKeychainItemGetDLDBHandle() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } status = SecKeychainItemGetUniqueRecordID(item, &uniqueRecordID); if (status) { fprintf(stderr, "SecKeychainItemGetUniqueRecordID() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } status = CSSM_DL_DataGetFromUniqueRecordId(dldbHandle, uniqueRecordID, NULL, &data); if (status) { fprintf(stderr, "CSSM_DL_DataGetFromUniqueRecordId() returned %ld(0x%lx)\n", status, status); result = 1; goto loser; } fputs("raw data:\n", stream); print_buffer(stream, data.Length, data.Data); fputc('\n', stream); /* @@@ Hmm which allocators should we use here? */ free(data.Data); } loser: if (attrList) { status = SecKeychainItemFreeAttributesAndData(attrList, data); if (status) fprintf(stderr, "SecKeychainItemFreeAttributesAndData() returned %ld(0x%lx)\n", status, status); } if (info) { status = SecKeychainFreeAttributeInfo(info); if (status) fprintf(stderr, "SecKeychainFreeAttributeInfo() returned %ld(0x%lx)\n", status, status); } if (keychain) CFRelease(keychain); return result; } static void print_buffer_hex(FILE *stream, UInt32 length, void *data) { uint8 *p = (uint8 *) data; while (length--) { int ch = *p++; fprintf(stream, "%02X", ch); } } static void print_buffer_ascii(FILE *stream, UInt32 length, void *data) { uint8 *p = (uint8 *) data; while (length--) { int ch = *p++; if (ch >= ' ' && ch <= '~' && ch != '\\') { fputc(ch, stream); } else { fputc('\\', stream); fputc('0' + ((ch >> 6) & 7), stream); fputc('0' + ((ch >> 3) & 7), stream); fputc('0' + ((ch >> 0) & 7), stream); } } } void print_buffer(FILE *stream, UInt32 length, void *data) { uint8 *p = (uint8 *) data; Boolean hex = FALSE; Boolean ascii = FALSE; UInt32 ix; for (ix = 0; ix < length; ++ix) { int ch = *p++; if (ch >= ' ' && ch <= '~' && ch != '\\') ascii = TRUE; else hex = TRUE; } if (hex) { fputc('0', stream); fputc('x', stream); print_buffer_hex(stream, length, data); if (ascii) fputc(' ', stream); fputc(' ', stream); } if (ascii) { fputc('"', stream); print_buffer_ascii(stream, length, data); fputc('"', stream); } } /* * map a 6-bit binary value to a printable character. */ static const unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* * map 6 bits to a printing char */ #define ENC(c) (bintoasc[((c) & 0x3f)]) #define PAD '=' /* * map one group of up to 3 bytes at inp to 4 bytes at outp. * Count is number of valid bytes in *inp; if less than 3, the * 1 or two extras must be zeros. */ static void encChunk(const unsigned char *inp, unsigned char *outp, int count) { unsigned char c1, c2, c3, c4; c1 = *inp >> 2; c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf); c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3); c4 = inp[2] & 0x3f; *outp++ = ENC(c1); *outp++ = ENC(c2); if (count == 1) { *outp++ = PAD; *outp = PAD; } else { *outp++ = ENC(c3); if (count == 2) { *outp = PAD; } else { *outp = ENC(c4); } } } static unsigned char * malloc_enc64_with_lines(const unsigned char *inbuf, unsigned inlen, unsigned linelen, unsigned *outlen) { unsigned outTextLen; unsigned len; // to malloc, liberal unsigned olen = 0; // actual output size unsigned char *outbuf; unsigned char endbuf[3]; unsigned i; unsigned char *outp; unsigned numLines; unsigned thisLine; outTextLen = ((inlen + 2) / 3) * 4; if(linelen) { /* * linelen must be 0 mod 4 for this to work; round up... */ if((linelen & 0x03) != 0) { linelen = (linelen + 3) & 0xfffffffc; } numLines = (outTextLen + linelen - 1)/ linelen; } else { numLines = 1; } /* * Total output size = encoded text size plus one newline per * line of output, plus trailing NULL. We always generate newlines * as \n; when decoding, we tolerate \r\n (Microsoft) or \n. */ len = outTextLen + (2 * numLines) + 1; outbuf = (unsigned char*)malloc(len); outp = outbuf; thisLine = 0; while(inlen) { if(inlen < 3) { for(i=0; i<3; i++) { if(i < inlen) { endbuf[i] = inbuf[i]; } else { endbuf[i] = 0; } } encChunk(endbuf, outp, inlen); inlen = 0; } else { encChunk(inbuf, outp, 3); inlen -= 3; inbuf += 3; } outp += 4; thisLine += 4; olen += 4; if((linelen != 0) && (thisLine >= linelen) && inlen) { /* * last trailing newline added below * Note we don't split 4-byte output chunks over newlines */ *outp++ = '\n'; olen++; thisLine = 0; } } *outp++ = '\n'; olen += 1; *outlen = olen; return outbuf; } void print_buffer_pem(FILE *stream, const char *headerString, UInt32 length, void *data) { unsigned char *buf; unsigned bufLen; fprintf(stream, "-----BEGIN %s-----\n", headerString); buf = malloc_enc64_with_lines(data, length, 64, &bufLen); fwrite(buf, bufLen, 1, stream); free(buf); fprintf(stream, "-----END %s-----\n", headerString); }