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

net.snowflake.client.core.SecureStorageAppleManager Maven / Gradle / Ivy

There is a newer version: 3.21.0
Show newest version
/*
 * Copyright (c) 2012-2020 Snowflake Computing Inc. All rights reserved.
 */

package net.snowflake.client.core;

import com.google.common.base.Strings;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import java.nio.charset.StandardCharsets;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;

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 type, String cred) {
    if (Strings.isNullOrEmpty(cred)) {
      logger.info("No credential provided", false);
      return SecureStorageStatus.SUCCESS;
    }

    String target = SecureStorageManager.convertTarget(host, user, type);
    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 type) {
    String target = SecureStorageManager.convertTarget(host, user, type);
    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", false);
        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 type) {
    String target = SecureStorageManager.convertTarget(host, user, type);
    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;

    private static class ResourceHolder {
      private static final SecurityLib INSTANCE =
          (SecurityLib) Native.loadLibrary("Security", SecurityLib.class);
    }

    public static SecurityLib getInstance() {
      if (INSTANCE == null) {
        INSTANCE = ResourceHolder.INSTANCE;
      }
      return INSTANCE;
    }

    /** This function is used only for unit test */
    public static void setInstance(SecurityLib instance) {
      INSTANCE = instance;
    }

    /** This function is a helper function for testing */
    public static void resetInstance() {
      if (Constants.getOS() == Constants.OS.MAC) {
        INSTANCE = ResourceHolder.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