acltool.cpp   [plain text]


/*
 * acltool.cpp - display and manipluate ACLs on SecKeychainItems
 */
 
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <Security/Security.h>
#include "aclUtils.h"

static void usage(char **argv)
{
	printf("usage: %s op [options]\n", argv[0]);
	printf("op:\n");
	printf("   d   -- display ACL\n");
	printf("   a   -- add ACL\n");
	printf("   l   -- lookup, dump label; no ACL operation\n");
	printf("   m   -- modify data (only allowed for password items; must specify -p)\n");
	printf("Options:\n");
	printf("  -k keychain\n");
	printf("  -t itemType : k=privateKey, b=publicKey s=sessionKey, p=password; default is sessionKey\n");
	printf("  -l label_or_printName\n");
	printf("  -p new_password_data\n");
	printf("  -d   -- dump data\n");
	printf("  -e   -- edit ACL entries\n");
	printf("  -s   -- simulate StickyRecord ACL\n");
	/* etc. */
	exit(1);
}


/* print an item's label and (optionally) its data */
static OSStatus printItemLabelAndData(
	SecKeychainItemRef itemRef, 
	SecItemAttr labelAttr,
	bool dumpData)
{
	SecKeychainAttributeList	attrList;
	SecKeychainAttribute		attr;
	UInt32						length = 0;
	void						*outData = NULL;
	
	attr.tag = labelAttr;			
	attr.length = 0;
	attr.data = NULL;
	attrList.count = 1;
	attrList.attr = &attr;

	OSStatus ortn = SecKeychainItemCopyContent(itemRef,
		NULL,		// itemClass - we know
		&attrList,	// for label 
		dumpData ? &length   : 0,
		dumpData ? &outData : NULL);
	if(ortn) {
		cssmPerror("SecKeychainItemCopyContent", ortn);
		printf("***Error fetching label %s\n", dumpData ? "and data" : "");
		return ortn;
	}
	
	if(attr.data == NULL) {
		printf("**No label attribute found\n");
	}
	else {
		printf("Label: ");
		print_buffer(stdout, attr.length, attr.data);
		printf("\n");
	}
	if(dumpData) {
		if(outData == NULL) {
			printf("***Asked for data but none found\n");
		}
		else {
			printf("Data : ");
			print_buffer(stdout, length, outData);
			printf("\n");
		}
	}
	SecKeychainItemFreeContent(&attrList, outData);
	return noErr;
}

/* 
 * Lookup by itemClass and optional label. Then do one or more of:
 *
 * -- dump label (always done)
 * -- dump ACLs 
 * -- edit acl
 * -- dump data
 * -- set (modify) data
 */
static OSStatus dumpAcls(
	SecKeychainRef kcRef,
	
	/* item specification */
	SecItemClass itemClass,
	SecItemAttr labelAttr,		// to look up by label if specified
	const char *label,
	
	/* what we do with the item(s) found */
	bool dumpData,
	bool dumpAcl,
	bool editAcl,
	bool simulateStickyRecord,
	const unsigned char *newData,	// if non-NULL, set/modify new data
	unsigned newDataLen)
{
	OSStatus					ortn;
	SecKeychainSearchRef		srchRef;
	SecKeychainAttributeList	attrList;
	SecKeychainAttributeList	*attrListP = NULL;
	SecKeychainAttribute		attr;
	unsigned					numFound = 0;
	
	/* searching by label, or blindly? */
	if(label) {
		attr.tag = labelAttr;			
		attr.length = strlen(label);
		attr.data = (void *)label;
		attrList.count = 1;
		attrList.attr = &attr;
		attrListP = &attrList;
	}
	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
		itemClass,
		attrListP,	
		&srchRef);
	if(ortn) {
		cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
		return ortn;
	}
	for(;;) {
		SecKeychainItemRef itemRef;
		ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
		if(ortn) {
			if(ortn == errSecItemNotFound) {
				/* normal search end */
				ortn = noErr;
			}
			else {
				cssmPerror("SecKeychainSearchCopyNext", ortn);
			}
			break;
		}
		
		printf("Item %u:\n", numFound++);
		printItemLabelAndData(itemRef, labelAttr, dumpData);

		if(newData) {
			ortn = SecKeychainItemModifyAttributesAndData(itemRef, 
				NULL,		// attrList - we don't change any attrs 
				newDataLen,
				newData);
			if(ortn) {
				cssmPerror("SecKeychainItemModifyAttributesAndData", ortn);
				printf("***Cannot modify data of this item***\n");
				goto endOfLoop;
			}
		}
		if(dumpAcl) {
			SecAccessRef accessRef = nil;
			ortn = SecKeychainItemCopyAccess(itemRef, &accessRef);
			if(ortn) {
				cssmPerror("SecKeychainItemCopyAccess", ortn);
				printf("***No SecAccessRef found***\n");
				goto endOfLoop;
			}
			print_access(stdout, accessRef, editAcl);
			if(simulateStickyRecord) {
				ortn = stickyRecordUpdateAcl(accessRef);
				if(ortn) {
					goto endOfLoop;
				}
			}
			if(editAcl || simulateStickyRecord) {
				ortn = SecKeychainItemSetAccess(itemRef, accessRef);
				if(ortn) {
					cssmPerror("SecKeychainItemSetAccess", ortn);
				}
			}
		}
endOfLoop:
		CFRelease(itemRef);
		if(ortn) {
			break;
		}
	}
	CFRelease(srchRef);
	printf("...%u items found\n", numFound);
	return ortn;
}

typedef enum {
	AO_Dump,
	AO_Add,
	AO_Lookup,
	AO_ModifyPassword
} AclOp;

int main(int argc, char **argv)
{
	/* user spec'd variables */
	const char		*kcName = NULL;
	const char		*labelOrName = NULL;			// attribute type varies per 
	AclOp			op = AO_Dump;
	SecItemClass	itemClass = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
	/* FIXME why does this work like this for keys? doc says kSecKeyPrintName but that doesn't work */
	SecItemAttr		labelAttr = kSecLabelItemAttr; 
	bool			dumpData = false;
	bool			editAcl = false;
	bool			dumpAcl = true;
	bool			simulateStickyRecord = false;
	const char		*newPassword = NULL;
	unsigned		newPasswordLen = 0;
	
	SecKeychainRef kcRef = nil;
	OSStatus ortn;
	
	if(argc < 2) {
		usage(argv);
	}
	switch(argv[1][0]) {
		case 'd':
			op = AO_Dump;
			break;
		case 'a':
			op = AO_Add;
			break;
		case 'l':
			op = AO_Lookup;
			dumpAcl = false;
			break;
		case 'm':
			op = AO_ModifyPassword;
			dumpAcl = false;
			break;
		default:
			usage(argv);
	}
	
	extern char *optarg;
	int arg;
	extern int optind;
	optind = 2;
	while ((arg = getopt(argc, argv, "k:t:l:dep:sh")) != -1) {
		switch (arg) {
			case 'k':
				kcName = optarg;
				break;
			case 't':
				switch(optarg[0]) {
					case 'k':
						itemClass = CSSM_DL_DB_RECORD_PRIVATE_KEY;
						break;
					case 's':
						itemClass = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
						break;
					case 'b':
						itemClass = CSSM_DL_DB_RECORD_PUBLIC_KEY;
						break;
					case 'p':
						itemClass = kSecGenericPasswordItemClass;
						labelAttr = kSecLabelItemAttr;
						break;
					default:
						usage(argv);
				}
				break;
			case 'l':
				labelOrName = optarg;
				break;
			case 'd':
				dumpData = true;
				break;
			case 'e':
				editAcl = true;
				break;
			case 'p':
				newPassword = optarg;
				newPasswordLen = strlen(newPassword);
				break;
			case 's':
				simulateStickyRecord = true;
				break;
			case 'h':
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	if(op == AO_ModifyPassword) {
		if(itemClass != kSecGenericPasswordItemClass) {
			printf("***You can only modify data on a password item.\n");
			exit(1);
		}
		if(newPassword == NULL) {
			printf("***You must supply new password data for this operation.\n");
			exit(1);
		}
	}
	if(kcName) {
		ortn = SecKeychainOpen(kcName, &kcRef);
		if(ortn) {
			cssmPerror("SecKeychainOpen", ortn);
			printf("***Error opening keychain %s. Aborting.\n", kcName);
			exit(1);
		}
	}
	
	switch(op) {
		case AO_Dump:
		case AO_Lookup:
		case AO_ModifyPassword:
			ortn = dumpAcls(kcRef, itemClass, labelAttr, labelOrName, dumpData, dumpAcl, editAcl,
				simulateStickyRecord, (unsigned char *)newPassword, newPasswordLen);
			break;
		case AO_Add:
			printf("Add ACL op to be implemented real soon now\n");
			ortn = -1;
			break;
	}
	if(kcRef) {
		CFRelease(kcRef);
	}
	return (int)ortn;
}