All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy