net.snowflake.client.core.SecureStorageAppleManager Maven / Gradle / Ivy
/*
* Copyright (c) 2012-2020 Snowflake Computing Inc. All rights reserved.
*/
package net.snowflake.client.core;
import com.google.common.base.Strings;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import java.nio.charset.StandardCharsets;
public class SecureStorageAppleManager implements SecureStorageManager
{
private static final
SFLogger logger = SFLoggerFactory.getLogger(SecureStorageAppleManager.class);
private final SecurityLib securityLib;
private SecureStorageAppleManager()
{
securityLib = SecurityLibManager.getInstance();
}
public static SecureStorageAppleManager builder()
{
return new SecureStorageAppleManager();
}
public SecureStorageStatus setCredential(String host, String user, String cred)
{
if (Strings.isNullOrEmpty(cred))
{
logger.info("No credential provided");
return SecureStorageStatus.SUCCESS;
}
String target = SecureStorageManager.convertTarget(host, user);
byte[] targetBytes = target.getBytes(StandardCharsets.UTF_8);
byte[] userBytes = user.toUpperCase().getBytes(StandardCharsets.UTF_8);
byte[] credBytes = cred.getBytes(StandardCharsets.UTF_8);
Pointer[] itemRef = new Pointer[1];
int errCode = 0;
synchronized (securityLib)
{
errCode = securityLib.SecKeychainFindGenericPassword(
null, targetBytes.length, targetBytes, userBytes.length, userBytes, null, null, itemRef);
}
if (errCode != SecurityLib.ERR_SEC_SUCCESS && errCode != SecurityLib.ERR_SEC_ITEM_NOT_FOUND)
{
logger.info(String.format("Failed to check the existence of the item in keychain. Error code = %d", Native.getLastError()));
return SecureStorageStatus.FAILURE;
}
if (itemRef[0] != null)
{
synchronized (securityLib)
{
errCode = securityLib.SecKeychainItemModifyContent(
itemRef[0], null, credBytes.length, credBytes);
}
}
else
{
synchronized (securityLib)
{
errCode = securityLib.SecKeychainAddGenericPassword(
Pointer.NULL, targetBytes.length, targetBytes, userBytes.length, userBytes, credBytes.length, credBytes, null);
}
}
if (errCode != SecurityLib.ERR_SEC_SUCCESS)
{
logger.info(String.format("Failed to set/modify the item in keychain. Error code = %d", Native.getLastError()));
return SecureStorageStatus.FAILURE;
}
logger.debug("Set the item in keychain successfully");
return SecureStorageStatus.SUCCESS;
}
public String getCredential(String host, String user)
{
String target = SecureStorageManager.convertTarget(host, user);
byte[] targetBytes = target.getBytes(StandardCharsets.UTF_8);
byte[] userBytes = user.toUpperCase().getBytes(StandardCharsets.UTF_8);
int[] dataLength = new int[1];
Pointer[] data = new Pointer[1];
try
{
int errCode = 0;
synchronized (securityLib)
{
errCode = securityLib.SecKeychainFindGenericPassword(
null, targetBytes.length, targetBytes, userBytes.length, userBytes,
dataLength, data, null);
}
if (errCode != SecurityLib.ERR_SEC_SUCCESS)
{
logger.info(String.format("Failed to find the item in keychain or item not exists. Error code = %d", Native.getLastError()));
return null;
}
if (dataLength[0] == 0 || data[0] == null)
{
logger.info("Found empty item or no item is found");
return null;
}
byte[] credBytes = data[0].getByteArray(0, dataLength[0]);
String res = new String(credBytes, StandardCharsets.UTF_8);
logger.debug("Successfully read the credential. Will return it as String now");
return res;
}
finally
{
if (data[0] != null)
{
synchronized (securityLib)
{
securityLib.SecKeychainItemFreeContent(null, data[0]);
}
}
}
}
public SecureStorageStatus deleteCredential(String host, String user)
{
String target = SecureStorageManager.convertTarget(host, user);
byte[] targetBytes = target.getBytes(StandardCharsets.UTF_8);
byte[] userBytes = user.toUpperCase().getBytes(StandardCharsets.UTF_8);
Pointer[] itemRef = new Pointer[1];
int errCode = 0;
synchronized (securityLib)
{
errCode = securityLib.SecKeychainFindGenericPassword(
null, targetBytes.length, targetBytes, userBytes.length, userBytes, null, null, itemRef);
}
if (errCode != SecurityLib.ERR_SEC_SUCCESS && errCode != SecurityLib.ERR_SEC_ITEM_NOT_FOUND)
{
logger.info(String.format("Failed to delete the item in keychain. Error code = %d", Native.getLastError()));
return SecureStorageStatus.FAILURE;
}
if (itemRef[0] != null)
{
synchronized (securityLib)
{
errCode = securityLib.SecKeychainItemDelete(itemRef[0]);
}
if (errCode != SecurityLib.ERR_SEC_SUCCESS)
{
logger.info(String.format("Failed to delete the item in keychain. Error code = %d", Native.getLastError()));
return SecureStorageStatus.FAILURE;
}
}
return SecureStorageStatus.SUCCESS;
}
static class SecurityLibManager
{
private static SecurityLib INSTANCE = null;
public static SecurityLib getInstance()
{
if (INSTANCE == null)
{
synchronized (SecurityLibManager.class)
{
if (INSTANCE == null)
{
INSTANCE = (SecurityLib) Native.loadLibrary("Security", SecurityLib.class);
}
}
}
return INSTANCE;
}
/**
* This function is used only for unit test
*/
public static void setInstance(SecurityLib instance)
{
INSTANCE = instance;
}
}
/**
* the java mapping of OS X Security Library
*/
interface SecurityLib extends Library
{
//SecurityLib INSTANCE = (SecurityLib) Native.loadLibrary("Security", SecurityLib.class);
int ERR_SEC_SUCCESS = 0;
int ERR_SEC_ITEM_NOT_FOUND = -25300;
/**
* https://developer.apple.com/documentation/security/1397301-seckeychainfindgenericpassword
*
* func SecKeychainFindGenericPassword(_ keychainOrArray: CFTypeRef?,
* _ serviceNameLength: UInt32,
* _ serviceName: UnsafePointer?, const char*
* _ accountNameLength: UInt32,
* _ accountName: UnsafePointer?, const char*
* _ passwordLength: UnsafeMutablePointer?, UInt32*
* _ passwordData: UnsafeMutablePointer?, void**
* _ itemRef: UnsafeMutablePointer?), SecKeychainItemRef* -> OSStatus
*/
public int SecKeychainFindGenericPassword(
Pointer keychainOrArray,
int serviceNameLength,
byte[] serviceName,
int accountNameLength,
byte[] accountName,
int[] passwordLength,
Pointer[] passwordData,
Pointer[] itemRef);
/**
* func SecKeychainAddGenericPassword(_ keychain: SecKeychain?, SecKeychainRef
* _ serviceNameLength: UInt32,
* _ serviceName: UnsafePointer?, const char*
* _ accountNameLength: UInt32,
* _ accountName: UnsafePointer?, const char*
* _ passwordLength: UInt32,
* _ passwordData: UnsafeRawPointer, const void*
* _ itemRef: UnsafeMutablePointer?) SecKeychainItemRef* -> OSStatus
*/
public int SecKeychainAddGenericPassword(
Pointer keychain,
int serviceNameLength,
byte[] serviceName,
int accountNameLength,
byte[] accountName,
int passwordLength,
byte[] passwordData,
Pointer[] itemRef);
/**
* OSStatus SecKeychainItemModifyContent(SecKeychainItemRef itemRef,
* const SecKeychainAttributeList *attrList,
* UInt32 length,
* const void *data);
*/
public int SecKeychainItemModifyContent(
Pointer itemRef,
Pointer attrList,
int length,
byte[] data);
/**
* OSStatus SecKeychainItemDelete(SecKeychainItemRef itemRef);
*/
public int SecKeychainItemDelete(Pointer itemRef);
/**
* OSStatus SecKeychainItemFreeContent(SecKeychainAttributeList *attrList, void *data);
*/
public int SecKeychainItemFreeContent(Pointer[] attrList, Pointer data);
}
}