keychain_utilities.c [plain text]
#include "keychain_utilities.h"
#include <Security/cssmapi.h>
#include <Security/SecKeychainItem.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
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;
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;
}
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("<string>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
fputs("<sint32>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
fputs("<uint32>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM:
fputs("<bignum>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
fputs("<real>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
fputs("<timedate>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
fputs("<blob>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
fputs("<uint32>", stream);
break;
case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX:
fputs("<complex>", stream);
break;
default:
fprintf(stream, "<format: %ld>", format);
break;
}
fputs("=", stream);
if (!attribute->length && !attribute->data)
fputs("<NULL>", 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);
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);
}
}
static const
unsigned char bintoasc[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define ENC(c) (bintoasc[((c) & 0x3f)])
#define PAD '='
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; unsigned olen = 0; unsigned char *outbuf;
unsigned char endbuf[3];
unsigned i;
unsigned char *outp;
unsigned numLines;
unsigned thisLine;
outTextLen = ((inlen + 2) / 3) * 4;
if(linelen) {
if((linelen & 0x03) != 0) {
linelen = (linelen + 3) & 0xfffffffc;
}
numLines = (outTextLen + linelen - 1)/ linelen;
}
else {
numLines = 1;
}
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) {
*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);
}