#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <strings.h>
#include <ctype.h>
#include <Security/cssm.h>
#include <Security/cssmapple.h>
#include <Security/cssmapplePriv.h>
#include "cspwrap.h"
#include "common.h"
#include "dbAttrs.h"
#include "dbCert.h"
#include "cspdlTesting.h"
static void usage(char **argv)
{
printf("usage: %s dbFileName command [options]\n", argv[0]);
printf("Commands:\n");
printf(" r Dump Schema Relations\n");
printf(" k Dump all keys\n");
printf(" c Dump certs\n");
printf(" a Dump all records\n");
printf(" d Delete records (interactively)\n");
printf(" D Delete records (noninteractively, requires really arg)\n");
printf(" i Import bad cert and its (good) private key\n");
printf("Options:\n");
printf(" v verbose\n");
printf(" q quiet\n");
printf(" R really! (for D command)\n");
printf(" d dump data\n");
printf(" c=certFile\n");
printf(" k=keyFile\n");
exit(1);
}
static unsigned indentVal = 0;
static void indentIncr()
{
indentVal += 3;
}
static void indentDecr()
{
if(indentVal) {
indentVal -= 3;
}
}
static void doIndent()
{
unsigned i;
for(i=0; i<indentVal; i++) {
printf(" ");
}
}
#define NORM_KEY_LEN 20
static void printName(
const CSSM_DB_ATTRIBUTE_INFO *attrInfo)
{
switch(attrInfo->AttributeNameFormat) {
case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
{
char *attrName = attrInfo->Label.AttributeName;
printf("%s", attrName);
int len = strlen(attrName);
if(len > NORM_KEY_LEN) {
return;
}
int numSpaces = NORM_KEY_LEN - len;
for(int i=0; i<numSpaces; i++) {
putchar(' ');
}
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
{
char *cp = (char *)&(attrInfo->Label.AttributeID);
for(unsigned i=0; i<4; i++) {
putchar(*cp++);
}
printf(" ");
break;
}
default:
printf("Unknown attribute name format (%u)\n",
(unsigned)attrInfo->AttributeNameFormat);
break;
}
}
static void printValueAsString(
unsigned val,
const NameValuePair *nameValues)
{
if(nameValues != NULL) {
while(nameValues->name != NULL) {
if(nameValues->value == val) {
printf("%s", nameValues->name);
return;
}
nameValues++;
}
}
printf("0x%x", val);
}
static void safePrint(
uint8 *cp,
uint32 len)
{
for(unsigned i=0; i<len; i++) {
printf("%c", *cp++);
}
}
bool isPrintable(
const CSSM_DATA *dp)
{
bool printable = true;
uint8 *cp = dp->Data;
for(unsigned i=0; i<dp->Length; i++) {
if(*cp == 0) {
if(i != (dp->Length - 1)) {
printable = false;
}
break;
}
if(!isprint(*cp)) {
printable = false;
break;
}
cp++;
}
return printable;
}
#define MAX_BLOB_TO_PRINT 12
static void printBlob(
const CSSM_DATA *data)
{
unsigned toPrint = data->Length;
if(toPrint > MAX_BLOB_TO_PRINT) {
toPrint = MAX_BLOB_TO_PRINT;
}
for(unsigned i=0; i<toPrint; i++) {
unsigned dat = data->Data[i];
printf("%02X ", dat);
}
if(toPrint < data->Length) {
printf("...");
}
}
static void printAttrData(
const CSSM_DB_ATTRIBUTE_INFO *attrInfo,
const CSSM_DATA *attrData,
const NameValuePair *nameValues) {
void *data = attrData->Data;
switch(attrInfo->AttributeFormat) {
case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
putchar('\'');
safePrint(attrData->Data, attrData->Length);
putchar('\'');
break;
case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
{
unsigned val = *(unsigned *)data;
printValueAsString(val, nameValues);
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
{
printf("BLOB length %u : ", (unsigned)attrData->Length);
if(isPrintable(attrData)) {
putchar('\'');
safePrint(attrData->Data, attrData->Length);
putchar('\'');
}
else {
printBlob(attrData);
}
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
{
printf("multi_int[");
uint32 numInts = attrData->Length / sizeof(uint32);
uint32 *uip = (uint32 *)data;
for(unsigned i=0; i<numInts; i++) {
if(i > 0) {
printf(", ");
}
printValueAsString(*uip++, nameValues);
}
printf("]");
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
putchar('\'');
safePrint(attrData->Data, attrData->Length);
putchar('\'');
break;
default:
printf("***UNKNOWN FORMAT (%u), Length %u",
(unsigned)attrInfo->AttributeFormat, (unsigned)attrData->Length);
break;
}
}
static void freeAttrs(
CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
{
unsigned i;
for(i=0; i<recordAttrs->NumberOfAttributes; i++) {
CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i];
if(attrData == NULL) {
printf("***freeAttrs screwup: NULL attrData\n");
return;
}
unsigned j;
for(j=0; j<attrData->NumberOfValues; j++) {
CSSM_DATA_PTR data = &attrData->Value[j];
if(data == NULL) {
printf("***freeAttrs screwup: NULL data\n");
return;
}
CSSM_FREE(data->Data);
data->Data = NULL;
data->Length = 0;
}
CSSM_FREE(attrData->Value);
attrData->Value = NULL;
}
}
static void dumpDataBlob(
const CSSM_DATA *datap)
{
doIndent();
printf("Record data length %lu ", datap->Length);
if(datap->Length != 0) {
printf(" : ");
printBlob(datap);
}
printf("\n");
}
static void dumpRecordAttrs(
const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs,
const NameValuePair **nameValues, const CSSM_DATA *recordData = NULL) {
unsigned valNum;
unsigned dex;
for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
doIndent();
printName(&attrData->Info);
printf(": ");
if(attrData->NumberOfValues == 0) {
printf("<<not present>>\n");
continue;
}
for(valNum=0; valNum<attrData->NumberOfValues; valNum++) {
printAttrData(&attrData->Info, &attrData->Value[valNum], nameValues[dex]);
if(valNum < (attrData->NumberOfValues - 1)) {
printf(", ");
}
}
printf("\n");
}
if(recordData) {
dumpDataBlob(recordData);
}
}
static void dumpRelation(
CSSM_DL_DB_HANDLE dlDbHand,
const RelationInfo *relInfo,
CSSM_BOOL dumpData)
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand;
CSSM_DB_ATTRIBUTE_DATA *attrs;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
unsigned attrDex;
unsigned recNum = 0;
uint32 numAttrs = relInfo->NumberOfAttributes;
CSSM_DATA data = {0, NULL};
CSSM_DATA_PTR datap = NULL;
if(dumpData) {
datap = &data;
}
attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC(
sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
for(attrDex=0; attrDex<numAttrs; attrDex++) {
attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
}
recordAttrs.DataRecordType = relInfo->DataRecordType;
recordAttrs.NumberOfAttributes = numAttrs;
recordAttrs.AttributeData = attrs;
query.RecordType = relInfo->DataRecordType;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 0;
query.SelectionPredicate = NULL;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
datap,
&record);
switch(crtn) {
case CSSM_OK:
break; case CSSMERR_DL_ENDOFDATA:
printf("%s: no record found\n", relInfo->relationName);
CSSM_FREE(attrs);
return;
default:
printError("DataGetFirst", crtn);
CSSM_FREE(attrs);
return;
}
printf("%s:\n", relInfo->relationName);
printf(" record %d; numAttrs %d:\n",
recNum++, (int)recordAttrs.NumberOfAttributes);
indentIncr();
dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap);
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
freeAttrs(&recordAttrs);
if(datap) {
CSSM_FREE(datap->Data);
}
for(;;) {
crtn = CSSM_DL_DataGetNext(dlDbHand,
resultHand,
&recordAttrs,
datap,
&record);
switch(crtn) {
case CSSM_OK:
printf(" record %d; numAttrs %d:\n",
recNum++, (int)recordAttrs.NumberOfAttributes);
dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap);
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
freeAttrs(&recordAttrs);
if(datap) {
CSSM_FREE(datap->Data);
}
break; case CSSMERR_DL_ENDOFDATA:
break;
default:
printError("DataGetNext", crtn);
break;
}
if(crtn != CSSM_OK) {
break;
}
}
indentDecr();
CSSM_FREE(attrs);
}
static void fetchParseRecord(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_DB_RECORD_ATTRIBUTE_DATA *inRecordAttrs,
CSSM_DB_UNIQUE_RECORD_PTR record,
const CSSM_DATA_PTR datap,
CSSM_BOOL dumpData)
{
const RelationInfo *relInfo = NULL;
switch(inRecordAttrs->DataRecordType) {
case CSSM_DL_DB_RECORD_PUBLIC_KEY:
case CSSM_DL_DB_RECORD_PRIVATE_KEY:
case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
relInfo = &allKeysRelation;
break;
case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
relInfo = &genericKcRelation;
break;
case CSSM_DL_DB_RECORD_CERT:
relInfo = &certRecordRelation;
break;
case CSSM_DL_DB_RECORD_X509_CERTIFICATE:
relInfo = &x509CertRecordRelation;
break;
case CSSM_DL_DB_RECORD_X509_CRL:
relInfo = &x509CrlRecordRelation;
break;
case CSSM_DL_DB_RECORD_USER_TRUST:
relInfo = &userTrustRelation;
break;
case CSSM_DL_DB_RECORD_UNLOCK_REFERRAL:
relInfo = &referralRecordRelation;
break;
case CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE:
relInfo = &extendedAttrRelation;
break;
case DBBlobRelationID:
relInfo = NULL;
doIndent();
printf("--- No attributes ---\n");
if(dumpData) {
dumpDataBlob(datap);
}
return;
default:
doIndent();
printf("<<unparsed>>\n");
if(dumpData) {
doIndent();
printf("Record blob (length %ld): ", datap->Length);
printBlob(datap);
printf("\n");
}
return;
}
CSSM_DB_ATTRIBUTE_DATA *attrs = NULL;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
unsigned attrDex;
uint32 numAttrs = relInfo->NumberOfAttributes;
CSSM_RETURN crtn;
CSSM_DATA recordData = {0, NULL};
CSSM_DATA_PTR recordDataP = dumpData ? &recordData : NULL;
attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC(
sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
for(attrDex=0; attrDex<numAttrs; attrDex++) {
attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
}
recordAttrs.DataRecordType = relInfo->DataRecordType;
recordAttrs.NumberOfAttributes = numAttrs;
recordAttrs.AttributeData = attrs;
crtn = CSSM_DL_DataGetFromUniqueRecordId(dlDbHand,
record,
&recordAttrs,
recordDataP);
if(crtn) {
printError("CSSM_DL_DataGetFromUniqueRecordId", crtn);
goto abort;
}
dumpRecordAttrs(&recordAttrs, relInfo->nameValues, recordDataP);
freeAttrs(&recordAttrs);
if(recordData.Data) {
CSSM_FREE(recordData.Data);
}
abort:
if(attrs) {
CSSM_FREE(attrs);
}
return;
}
static void deleteRecord(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_DB_UNIQUE_RECORD_PTR record,
CSSM_BOOL interact)
{
if(interact) {
fpurge(stdin);
printf("\nDelete this record [y/anything] ? ");
char resp = getchar();
if(resp != 'y') {
return;
}
}
CSSM_RETURN crtn;
crtn = CSSM_DL_DataDelete(dlDbHand, record);
if(crtn) {
printError("CSSM_DL_DataDelete", crtn);
}
else if(interact) {
printf("...record deleted\n\n");
}
}
static void dumpAllRecords(
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_BOOL deleteAll,
CSSM_BOOL interact,
CSSM_BOOL dumpData)
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DATA data = {0, NULL};
CSSM_DATA_PTR datap = NULL;
if(dumpData) {
datap = &data;
}
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
recordAttrs.NumberOfAttributes = 0;
recordAttrs.AttributeData = NULL;
query.RecordType = CSSM_DL_DB_RECORD_ANY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 0;
query.SelectionPredicate = NULL;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
datap,
&record);
switch(crtn) {
case CSSM_OK:
break; case CSSMERR_DL_ENDOFDATA:
printf("CSSM_DL_DB_RECORD_ANY: no record found\n");
return;
default:
printError("DataGetFirst", crtn);
return;
}
if(interact) {
doIndent();
printValueAsString(recordAttrs.DataRecordType, recordTypeNames);
printf("\n");
indentIncr();
fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData);
indentDecr();
}
if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) {
deleteRecord(dlDbHand, record, interact);
}
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
for(;;) {
crtn = CSSM_DL_DataGetNext(dlDbHand,
resultHand,
&recordAttrs,
datap,
&record);
switch(crtn) {
case CSSM_OK:
if(interact) {
doIndent();
printValueAsString(recordAttrs.DataRecordType, recordTypeNames);
printf("\n");
indentIncr();
fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData);
indentDecr();
}
if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) {
deleteRecord(dlDbHand, record, interact);
}
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
break; case CSSMERR_DL_ENDOFDATA:
break;
default:
printError("DataGetNext", crtn);
break;
}
if(crtn != CSSM_OK) {
break;
}
}
}
int main(
int argc,
char **argv)
{
int arg;
char *argp;
char *dbFileName;
char cmd;
CSSM_DL_DB_HANDLE dlDbHand;
CSSM_BOOL verbose = CSSM_FALSE;
CSSM_BOOL quiet = CSSM_FALSE;
char *certFile = NULL;
char *keyFile = NULL;
CSSM_BOOL interact = CSSM_TRUE;
CSSM_BOOL dumpData = CSSM_FALSE;
CSSM_ALGORITHMS keyAlg = CSSM_ALGID_RSA;
CSSM_BOOL pemFormat = CSSM_FALSE;
CSSM_KEYBLOB_FORMAT keyFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
CSSM_RETURN crtn = CSSM_OK;
if(argc < 3) {
usage(argv);
}
dbFileName = argv[1];
cmd = argv[2][0];
for(arg=3; arg<argc; arg++) {
argp = argv[arg];
switch(argp[0]) {
case 'v':
verbose = CSSM_TRUE;
break;
case 'q':
quiet = CSSM_TRUE;
break;
case 'R':
if(cmd == 'D') {
interact = CSSM_FALSE;
}
break;
case 'd':
dumpData = CSSM_TRUE;
break;
case 'c':
certFile = &argp[2];
break;
case 'k':
keyFile = &argp[2];
break;
case 'h':
default:
usage(argv);
}
}
dlDbHand.DLHandle = dlStartup();
if(dlDbHand.DLHandle == 0) {
exit(1);
}
if(cmd == 'i') {
crtn = importBadCert(dlDbHand.DLHandle, dbFileName, certFile,
keyFile, keyAlg, pemFormat, keyFormat, verbose);
goto done;
}
crtn = dbCreateOpen(dlDbHand.DLHandle, dbFileName,
CSSM_FALSE, CSSM_FALSE, NULL, &dlDbHand.DBHandle);
if(crtn) {
exit(1);
}
switch(cmd) {
case 'r':
dumpRelation(dlDbHand, &schemaInfoRelation, dumpData);
break;
case 'k':
dumpRelation(dlDbHand, &allKeysRelation, dumpData);
break;
case 'c':
dumpRelation(dlDbHand, &x509CertRecordRelation, dumpData);
break;
case 'a':
dumpAllRecords(dlDbHand, CSSM_FALSE, CSSM_TRUE, dumpData);
break;
case 'd':
case 'D':
dumpAllRecords(dlDbHand, CSSM_TRUE, interact, dumpData);
if(!interact) {
if(!quiet) {
printf("...DB %s wiped clean\n", dbFileName);
}
}
break;
default:
usage(argv);
}
CSSM_DL_DbClose(dlDbHand);
done:
CSSM_ModuleDetach(dlDbHand.DLHandle);
return crtn;
}