static int
do_lock(const char *keychainName)
{
	SecKeychainRef keychain = NULL;
	OSStatus result;

	if (keychainName)
	{
		keychain = keychain_open(keychainName);
		if (!keychain)
		{
			result = 1;
			goto loser;
		}
	}

	result = SecKeychainLock(keychain);
	if (result)
	{
		sec_error("SecKeychainLock %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
	}

loser:
	if (keychain)
		CFRelease(keychain);

	return result;
}
static int
do_keychain_set_settings(const char *keychainName, SecKeychainSettings newKeychainSettings)
{
	SecKeychainRef keychain = NULL;
	OSStatus result;

	if (keychainName)
	{
		keychain = keychain_open(keychainName);
		if (!keychain)
		{
			result = 1;
			goto cleanup;
		}
	}
	result = SecKeychainSetSettings(keychain, &newKeychainSettings);
	if (result)
	{
		sec_error("SecKeychainSetSettings %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
	}

cleanup:
	if (keychain)
		CFRelease(keychain);

	return result;
}
static int
do_keychain_show_info(const char *keychainName)
{
	SecKeychainRef keychain = NULL;
    SecKeychainSettings keychainSettings = { SEC_KEYCHAIN_SETTINGS_VERS1 };
	OSStatus result;

	if (keychainName)
	{
		keychain = keychain_open(keychainName);
		if (!keychain)
		{
			result = 1;
			goto loser;
		}
	}

	result = SecKeychainCopySettings(keychain, &keychainSettings);
	if (result)
	{
		sec_error("SecKeychainCopySettings %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
		goto loser;
	}

    fprintf(stderr,"Keychain \"%s\"%s%s",
		keychainName ? keychainName : "<NULL>",
		keychainSettings.lockOnSleep ? " lock-on-sleep" : "",
		keychainSettings.useLockInterval ? " use-lock-interval" : "");
	if (keychainSettings.lockInterval == INT_MAX)
		fprintf(stderr," no-timeout\n");
	else
		fprintf(stderr," timeout=%ds\n", (int)keychainSettings.lockInterval);

loser:
	if (keychain)
		CFRelease(keychain);
	return result;
}
int
keychain_import(int argc, char * const *argv)
{
	int ch, result = 0;

	char *inFile = NULL;
	char *kcName = NULL;
	SecKeychainRef kcRef = NULL;
	SecExternalFormat externFormat = kSecFormatUnknown;
	SecExternalItemType itemType = kSecItemTypeUnknown;
	Boolean wrapped = FALSE;
	Boolean nonExtractable = FALSE;
	const char *passphrase = NULL;
	unsigned char *inFileData = NULL;
	unsigned inFileLen = 0;
	CFDataRef inData = NULL;
	unsigned numExtendedAttributes = 0;
	char **attrNames = NULL;
	char **attrValues = NULL;
	Boolean access_specified = FALSE;
	Boolean always_allow = FALSE;
	SecAccessRef access = NULL;
	CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

	if(argc < 2) {
		result = 2; /* @@@ Return 2 triggers usage message. */
		goto cleanup;
	}
	inFile = argv[1];
	if((argc == 2) && (inFile[0] == '-')) {
		result = 2;
		goto cleanup;
	}
	optind = 2;

    while ((ch = getopt(argc, argv, "k:t:f:P:wxa:hAT:")) != -1)
	{
		switch  (ch)
		{
		case 'k':
			kcName = optarg;
			break;
		case 't':
			if(!strcmp("pub", optarg)) {
				itemType = kSecItemTypePublicKey;
			}
			else if(!strcmp("priv", optarg)) {
				itemType = kSecItemTypePrivateKey;
			}
			else if(!strcmp("session", optarg)) {
				itemType = kSecItemTypeSessionKey;
			}
			else if(!strcmp("cert", optarg)) {
				itemType = kSecItemTypeCertificate;
			}
			else if(!strcmp("agg", optarg)) {
				itemType = kSecItemTypeAggregate;
			}
			else {
				result = 2; /* @@@ Return 2 triggers usage message. */
				goto cleanup;
			}
			break;
		case 'f':
			if(!strcmp("openssl", optarg)) {
				externFormat = kSecFormatOpenSSL;
			}
			else if(!strcmp("openssh1", optarg)) {
				externFormat = kSecFormatSSH;
			}
			else if(!strcmp("openssh2", optarg)) {
				externFormat = kSecFormatSSHv2;
			}
			else if(!strcmp("bsafe", optarg)) {
				externFormat = kSecFormatBSAFE;
			}
			else if(!strcmp("raw", optarg)) {
				externFormat = kSecFormatRawKey;
			}
			else if(!strcmp("pkcs7", optarg)) {
				externFormat = kSecFormatPKCS7;
			}
			else if(!strcmp("pkcs8", optarg)) {
				externFormat = kSecFormatWrappedPKCS8;
			}
			else if(!strcmp("pkcs12", optarg)) {
				externFormat = kSecFormatPKCS12;
			}
			else if(!strcmp("netscape", optarg)) {
				externFormat = kSecFormatNetscapeCertSequence;
			}
			else if(!strcmp("x509", optarg)) {
				externFormat = kSecFormatX509Cert;
			}
			else if(!strcmp("pemseq", optarg)) {
				externFormat = kSecFormatPEMSequence;
			}
			else {
				result = 2; /* @@@ Return 2 triggers usage message. */
				goto cleanup;
			}
			break;
		case 'w':
			wrapped = TRUE;
			break;
		case 'x':
			nonExtractable = TRUE;
			break;
		case 'P':
			passphrase = optarg;
			break;
		case 'a':
			/* this takes an additional argument */
			if(optind > (argc - 1)) {
				result = 2; /* @@@ Return 2 triggers usage message. */
				goto cleanup;
			}
			attrNames  = (char **)realloc(attrNames, numExtendedAttributes * sizeof(char *));
			attrValues = (char **)realloc(attrValues, numExtendedAttributes * sizeof(char *));
			attrNames[numExtendedAttributes]  = optarg;
			attrValues[numExtendedAttributes] = argv[optind];
			numExtendedAttributes++;
			optind++;
			break;
		case 'A':
			always_allow = TRUE;
			access_specified = TRUE;
			break;
		case 'T':
			if (optarg[0])
			{
				SecTrustedApplicationRef app = NULL;
				OSStatus status = noErr;
				/* check whether the argument specifies an application group */
				const char *groupPrefix = "group://";
				size_t prefixLen = strlen(groupPrefix);
				if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
					const char *groupName = &optarg[prefixLen];
					if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
						sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
					}
				} else {
					if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
						sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
					}
				}

				if (status) {
					result = 1;
					goto cleanup;
				}

				CFArrayAppendValue(trusted_list, app);
				CFRelease(app);
			}
			access_specified = TRUE;
			break;
		case '?':
		default:
			result = 2; /* @@@ Return 2 triggers usage message. */
			goto cleanup;
		}
	}

	if(wrapped) {
		switch(externFormat) {
			case kSecFormatOpenSSL:
			case kSecFormatUnknown:		// i.e., use default
				externFormat = kSecFormatWrappedOpenSSL;
				break;
			case kSecFormatSSH:
				externFormat = kSecFormatWrappedSSH;
				break;
			case kSecFormatSSHv2:
				/* there is no wrappedSSHv2 */
				externFormat = kSecFormatWrappedOpenSSL;
				break;
			case kSecFormatWrappedPKCS8:
				/* proceed */
				break;
			default:
				fprintf(stderr, "Don't know how to wrap in specified format/type\n");
				result = 2; /* @@@ Return 2 triggers usage message. */
				goto cleanup;
		}
	}

	if(kcName) {
		kcRef = keychain_open(kcName);
		if(kcRef == NULL) {
			return 1;
		}
	}
	if(readFile(inFile, &inFileData, &inFileLen)) {
		sec_error("Error reading infile %s: %s", inFile, strerror(errno));
		result = 1;
		goto cleanup;
	}
	inData = CFDataCreate(NULL, inFileData, inFileLen);
	if(inData == NULL) {
		result = 1;
		goto cleanup;
	}
	free(inFileData);

	if(access_specified)
	{
		char *accessName = NULL;
		CFStringRef fileStr = CFStringCreateWithCString(NULL, inFile, kCFStringEncodingUTF8);
		if (fileStr) {
			CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, fileStr, kCFURLPOSIXPathStyle, FALSE);
			if (fileURL) {
				CFStringRef nameStr = CFURLCopyLastPathComponent(fileURL);
				if (nameStr) {
					CFIndex nameLen = CFStringGetLength(nameStr);
					CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
					accessName = (char *)malloc(bufLen);
					if (!CFStringGetCString(nameStr, accessName, bufLen-1, kCFStringEncodingUTF8))
						accessName[0]=0;
					nameLen = strlen(accessName);
					char *p = &accessName[nameLen]; // initially points to terminating null
					while (--nameLen > 0) {
						if (*p == '.') { // strip extension, if any
							*p = '\0';
							break;
						}
						p--;
					}
					safe_CFRelease(&nameStr);
				}
				safe_CFRelease(&fileURL);
			}
			safe_CFRelease(&fileStr);
		}

		result = create_access(accessName, always_allow, trusted_list, &access);

		if (accessName) {
			free(accessName);
		}
		if (result != 0) {
			goto cleanup;
		}
	}

	result = do_keychain_import(kcRef, inData, externFormat, itemType, access,
								nonExtractable, passphrase, inFile, attrNames,
								attrValues, numExtendedAttributes);

cleanup:
	safe_CFRelease(&trusted_list);
	safe_CFRelease(&access);
	safe_CFRelease(&kcRef);
	safe_CFRelease(&inData);

	return result;
}
static int
do_recode(const char *keychainName1, const char *keychainName2)
{
	SecKeychainRef keychain1 = NULL, keychain2 = NULL;
	CFMutableArrayRef dbBlobArray = NULL;
	CFDataRef dbBlob = NULL, extraData = NULL;
	OSStatus result;

	if (keychainName1)
	{
		keychain1 = keychain_open(keychainName1);
		if (!keychain1)
		{
			result = 1;
			goto loser;
		}
	}

	keychain2 = keychain_open(keychainName2);
	if (!keychain2)
	{
		result = 1;
		goto loser;
	}

	result = SecKeychainCopyBlob(keychain2, &dbBlob);
	if (result)
	{
		sec_error("SecKeychainCopyBlob %s: %s", keychainName2,
			sec_errstr(result));
		goto loser;
	}

	extraData = CFDataCreate(NULL, NULL, 0);

	dbBlobArray = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
	if (dbBlobArray) {
		CFArrayAppendValue(dbBlobArray, dbBlob);
	}

#if !defined MAC_OS_X_VERSION_10_6 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
	result = SecKeychainRecodeKeychain(keychain1, dbBlob, extraData);
#else
	result = SecKeychainRecodeKeychain(keychain1, dbBlobArray, extraData);
#endif
	if (result)
		sec_error("SecKeychainRecodeKeychain %s, %s: %s", keychainName1,
			keychainName2, sec_errstr(result));

loser:
	if (dbBlobArray)
		CFRelease(dbBlobArray);
	if (dbBlob)
		CFRelease(dbBlob);
	if (extraData)
		CFRelease(extraData);
	if (keychain1)
		CFRelease(keychain1);
	if (keychain2)
		CFRelease(keychain2);

	return result;
}
int
keychain_export(int argc, char * const *argv)
{
	int ch, result = 0;

	char *outFile = NULL;
	char *kcName = NULL;
	SecKeychainRef kcRef = NULL;
	SecExternalFormat externFormat = kSecFormatUnknown;
	ItemSpec itemSpec = IS_All;
	int wrapped = 0;
	int doPem = 0;
	const char *passphrase = NULL;

    while ((ch = getopt(argc, argv, "k:o:t:f:P:wph")) != -1)
	{
		switch  (ch)
		{
		case 'k':
			kcName = optarg;
			break;
		case 'o':
			outFile = optarg;
			break;
		case 't':
			if(!strcmp("certs", optarg)) {
				itemSpec = IS_Certs;
			}
			else if(!strcmp("allKeys", optarg)) {
				itemSpec = IS_AllKeys;
			}
			else if(!strcmp("pubKeys", optarg)) {
				itemSpec = IS_PubKeys;
			}
			else if(!strcmp("privKeys", optarg)) {
				itemSpec = IS_PrivKeys;
			}
			else if(!strcmp("identities", optarg)) {
				itemSpec = IS_Identities;
			}
			else if(!strcmp("all", optarg)) {
				itemSpec = IS_All;
			}
			else {
				return 2; /* @@@ Return 2 triggers usage message. */
			}
			break;
		case 'f':
			if(!strcmp("openssl", optarg)) {
				externFormat = kSecFormatOpenSSL;
			}
			else if(!strcmp("openssh1", optarg)) {
				externFormat = kSecFormatSSH;
			}
			else if(!strcmp("openssh2", optarg)) {
				externFormat = kSecFormatSSHv2;
			}
			else if(!strcmp("bsafe", optarg)) {
				externFormat = kSecFormatBSAFE;
			}
			else if(!strcmp("raw", optarg)) {
				externFormat = kSecFormatRawKey;
			}
			else if(!strcmp("pkcs7", optarg)) {
				externFormat = kSecFormatPKCS7;
			}
			else if(!strcmp("pkcs8", optarg)) {
				externFormat = kSecFormatWrappedPKCS8;
			}
			else if(!strcmp("pkcs12", optarg)) {
				externFormat = kSecFormatPKCS12;
			}
			else if(!strcmp("netscape", optarg)) {
				externFormat = kSecFormatNetscapeCertSequence;
			}
			else if(!strcmp("x509", optarg)) {
				externFormat = kSecFormatX509Cert;
			}
			else if(!strcmp("pemseq", optarg)) {
				externFormat = kSecFormatPEMSequence;
			}
			else {
				return 2; /* @@@ Return 2 triggers usage message. */
			}
			break;
		case 'w':
			wrapped = 1;
			break;
		case 'p':
			doPem = 1;
			break;
		case 'P':
			passphrase = optarg;
			break;
		case '?':
		default:
			return 2; /* @@@ Return 2 triggers usage message. */
		}
	}

	if(wrapped) {
		switch(externFormat) {
			case kSecFormatOpenSSL:
			case kSecFormatUnknown:		// i.e., use default
				externFormat = kSecFormatWrappedOpenSSL;
				break;
			case kSecFormatSSH:
				externFormat = kSecFormatWrappedSSH;
				break;
			case kSecFormatSSHv2:
				/* there is no wrappedSSHv2 */
				externFormat = kSecFormatWrappedOpenSSL;
				break;
			case kSecFormatWrappedPKCS8:
				/* proceed */
				break;
			default:
				sec_error("Don't know how to wrap in specified format/type");
				return 2; /* @@@ Return 2 triggers usage message. */
		}
	}

	if(kcName) {
		kcRef = keychain_open(kcName);
		if(kcRef == NULL) {
			return 1;
		}
	}
	result = do_keychain_export(kcRef, externFormat, itemSpec,
		passphrase, doPem, outFile);

	if(kcRef) {
		CFRelease(kcRef);
	}
	return result;
}
static int
do_keychain_set_password(const char *keychainName, const char* oldPassword, const char* newPassword)
{
	SecKeychainRef keychain = NULL;
	OSStatus result = 1;
	UInt32 oldLen = (oldPassword) ? strlen(oldPassword) : 0;
	UInt32 newLen = (newPassword) ? strlen(newPassword) : 0;
	char *oldPass = (oldPassword) ? (char*)oldPassword : NULL;
	char *newPass = (newPassword) ? (char*)newPassword : NULL;
	char *oldBuf = NULL;
	char *newBuf = NULL;

	if (keychainName)
	{
		keychain = keychain_open(keychainName);
		if (!keychain)
		{
			result = 1;
			goto cleanup;
		}
	}

	if (!oldPass) {
		/* prompt for old password */
		char *pBuf = getpass("Old Password: "******"New Password: "******"Retype New Password: "******"try again");
		goto cleanup;
	}

	/* lock keychain first to remove existing credentials */
	(void)SecKeychainLock(keychain);

	/* change the password */
	result = SecKeychainChangePassword(keychain, oldLen, oldPass, newLen, newPass);
	if (result)
	{
		sec_error("error changing password for \"%s\": %s",
			keychainName ? keychainName : "<NULL>", sec_errstr(result));
	}

cleanup:
	/* if we allocated password buffers, zero and free them */
	if (oldBuf) {
		bzero(oldBuf, PW_BUF_SIZE);
		free(oldBuf);
	}
	if (newBuf) {
		bzero(newBuf, PW_BUF_SIZE);
		free(newBuf);
	}
	if (keychain) {
		CFRelease(keychain);
	}
	return result;
}