mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-CR-38256 wrap native store exceptions in PasswordSafe configurable
This commit is contained in:
@@ -112,9 +112,23 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
}
|
||||
|
||||
ProviderType.KEYCHAIN -> {
|
||||
// create here to ensure that user will get any error during native store creation.
|
||||
// well, unlikely will be any native error since usage is lazy, but at least ensure that created store is not null
|
||||
passwordSafe.currentProvider = createPersistentCredentialStore()!!
|
||||
// create here to ensure that user will get any error during native store creation
|
||||
try {
|
||||
val store = createPersistentCredentialStore()
|
||||
if (store == null) {
|
||||
throw ConfigurationException("Internal error, no available credential store implementation.")
|
||||
}
|
||||
passwordSafe.currentProvider = store
|
||||
}
|
||||
catch (e: UnsatisfiedLinkError) {
|
||||
LOG.warn(e)
|
||||
if (SystemInfo.isLinux) {
|
||||
throw ConfigurationException("Package libsecret-1-0 is not installed (to install: sudo apt-get install libsecret-1-0 gnome-keyring).")
|
||||
}
|
||||
else {
|
||||
throw ConfigurationException(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProviderType.KEEPASS -> createAndSaveKeePassDatabaseWithNewOptions(settings)
|
||||
|
||||
@@ -11,8 +11,6 @@ import gnu.trove.TIntObjectHashMap
|
||||
val isMacOsCredentialStoreSupported: Boolean
|
||||
get() = SystemInfo.isMacIntel64 && SystemInfo.isMacOSLeopard
|
||||
|
||||
private val LIBRARY by lazy { Native.loadLibrary("Security", MacOsKeychainLibrary::class.java) }
|
||||
|
||||
private const val errSecSuccess = 0
|
||||
private const val errSecItemNotFound = -25300
|
||||
private const val errSecInvalidRecord = -67701
|
||||
@@ -24,6 +22,69 @@ private const val kSecFormatUnknown = 0
|
||||
private const val kSecAccountItemAttr = (('a'.toInt() shl 8 or 'c'.toInt()) shl 8 or 'c'.toInt()) shl 8 or 't'.toInt()
|
||||
|
||||
internal class KeyChainCredentialStore : CredentialStore {
|
||||
companion object {
|
||||
private val library = Native.loadLibrary("Security", MacOsKeychainLibrary::class.java)
|
||||
|
||||
private fun findGenericPassword(serviceName: ByteArray, accountName: String?): Credentials? {
|
||||
val accountNameBytes = accountName?.toByteArray()
|
||||
val passwordSize = IntArray(1)
|
||||
val passwordRef = PointerByReference()
|
||||
val itemRef = PointerByReference()
|
||||
val errorCode = checkForError("find", library.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes?.size ?: 0, accountNameBytes, passwordSize, passwordRef, itemRef))
|
||||
if (errorCode == errSecUserCanceled) {
|
||||
return ACCESS_TO_KEY_CHAIN_DENIED
|
||||
}
|
||||
|
||||
val pointer = passwordRef.value ?: return null
|
||||
val password = OneTimeString(pointer.getByteArray(0, passwordSize.get(0)))
|
||||
library.SecKeychainItemFreeContent(null, pointer)
|
||||
|
||||
var effectiveAccountName = accountName
|
||||
if (effectiveAccountName == null) {
|
||||
val attributes = PointerByReference()
|
||||
checkForError("SecKeychainItemCopyAttributesAndData", library.SecKeychainItemCopyAttributesAndData(itemRef.value!!, SecKeychainAttributeInfo(kSecAccountItemAttr), null, attributes, null, null))
|
||||
val attributeList = SecKeychainAttributeList(attributes.value)
|
||||
try {
|
||||
attributeList.read()
|
||||
effectiveAccountName = readAttributes(attributeList).get(kSecAccountItemAttr)
|
||||
}
|
||||
finally {
|
||||
library.SecKeychainItemFreeAttributesAndData(attributeList, null)
|
||||
}
|
||||
}
|
||||
return Credentials(effectiveAccountName, password)
|
||||
}
|
||||
|
||||
private fun checkForError(message: String, code: Int): Int {
|
||||
if (code == errSecSuccess || code == errSecItemNotFound) {
|
||||
return code
|
||||
}
|
||||
|
||||
val translated = library.SecCopyErrorMessageString(code, null)
|
||||
val builder = StringBuilder(message).append(": ")
|
||||
if (translated == null) {
|
||||
builder.append(code)
|
||||
}
|
||||
else {
|
||||
val buf = CharArray(library.CFStringGetLength(translated).toInt())
|
||||
for (i in 0 until buf.size) {
|
||||
buf[i] = library.CFStringGetCharacterAtIndex(translated, i.toLong())
|
||||
}
|
||||
library.CFRelease(translated)
|
||||
builder.append(buf).append(" (").append(code).append(')')
|
||||
}
|
||||
|
||||
if (code == errUserNameNotCorrect || code == errSecUserCanceled || code == -25299 /* The specified item already exists in the keychain */) {
|
||||
LOG.warn(builder.toString())
|
||||
}
|
||||
else {
|
||||
LOG.error(builder.toString())
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(attributes: CredentialAttributes): Credentials? {
|
||||
return findGenericPassword(attributes.serviceName.toByteArray(), attributes.userName)
|
||||
}
|
||||
@@ -33,15 +94,15 @@ internal class KeyChainCredentialStore : CredentialStore {
|
||||
if (credentials.isEmpty()) {
|
||||
val itemRef = PointerByReference()
|
||||
val userName = attributes.userName?.toByteArray()
|
||||
val code = LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, userName?.size ?: 0, userName, null, null, itemRef)
|
||||
val code = library.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, userName?.size ?: 0, userName, null, null, itemRef)
|
||||
if (code == errSecItemNotFound || code == errSecInvalidRecord) {
|
||||
return
|
||||
}
|
||||
|
||||
checkForError("find (for delete)", code)
|
||||
itemRef.value?.let {
|
||||
checkForError("delete", LIBRARY.SecKeychainItemDelete(it))
|
||||
LIBRARY.CFRelease(it)
|
||||
checkForError("delete", library.SecKeychainItemDelete(it))
|
||||
library.CFRelease(it)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -49,7 +110,7 @@ internal class KeyChainCredentialStore : CredentialStore {
|
||||
val userName = (attributes.userName ?: credentials!!.userName)?.toByteArray()
|
||||
val searchUserName = if (attributes.serviceName == SERVICE_NAME_PREFIX) userName else null
|
||||
val itemRef = PointerByReference()
|
||||
val library = LIBRARY
|
||||
val library = library
|
||||
checkForError("find (for save)", library.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, searchUserName?.size ?: 0, searchUserName, null, null, itemRef))
|
||||
|
||||
val password = if (attributes.isPasswordMemoryOnly || credentials!!.password == null) null else credentials.password!!.toByteArray(false)
|
||||
@@ -79,36 +140,6 @@ internal class KeyChainCredentialStore : CredentialStore {
|
||||
}
|
||||
}
|
||||
|
||||
private fun findGenericPassword(serviceName: ByteArray, accountName: String?): Credentials? {
|
||||
val accountNameBytes = accountName?.toByteArray()
|
||||
val passwordSize = IntArray(1)
|
||||
val passwordRef = PointerByReference()
|
||||
val itemRef = PointerByReference()
|
||||
val errorCode = checkForError("find", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes?.size ?: 0, accountNameBytes, passwordSize, passwordRef, itemRef))
|
||||
if (errorCode == errSecUserCanceled) {
|
||||
return ACCESS_TO_KEY_CHAIN_DENIED
|
||||
}
|
||||
|
||||
val pointer = passwordRef.value ?: return null
|
||||
val password = OneTimeString(pointer.getByteArray(0, passwordSize.get(0)))
|
||||
LIBRARY.SecKeychainItemFreeContent(null, pointer)
|
||||
|
||||
var effectiveAccountName = accountName
|
||||
if (effectiveAccountName == null) {
|
||||
val attributes = PointerByReference()
|
||||
checkForError("SecKeychainItemCopyAttributesAndData", LIBRARY.SecKeychainItemCopyAttributesAndData(itemRef.value!!, SecKeychainAttributeInfo(kSecAccountItemAttr), null, attributes, null, null))
|
||||
val attributeList = SecKeychainAttributeList(attributes.value)
|
||||
try {
|
||||
attributeList.read()
|
||||
effectiveAccountName = readAttributes(attributeList).get(kSecAccountItemAttr)
|
||||
}
|
||||
finally {
|
||||
LIBRARY.SecKeychainItemFreeAttributesAndData(attributeList, null)
|
||||
}
|
||||
}
|
||||
return Credentials(effectiveAccountName, password)
|
||||
}
|
||||
|
||||
// https://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/index.html
|
||||
// It is very, very important to use CFRelease/SecKeychainItemFreeContent You must do it, otherwise you can get "An invalid record was encountered."
|
||||
@Suppress("FunctionName")
|
||||
@@ -150,7 +181,7 @@ private interface MacOsKeychainLibrary : Library {
|
||||
fun SecKeychainItemFreeContent(/*SecKeychainAttributeList*/attrList: Pointer?, data: Pointer?)
|
||||
}
|
||||
|
||||
internal class SecKeychainAttributeInfo : Structure() {
|
||||
private class SecKeychainAttributeInfo : Structure() {
|
||||
@JvmField
|
||||
var count: Int = 0
|
||||
@JvmField
|
||||
@@ -161,37 +192,8 @@ internal class SecKeychainAttributeInfo : Structure() {
|
||||
override fun getFieldOrder() = listOf("count", "tag", "format")
|
||||
}
|
||||
|
||||
private fun checkForError(message: String, code: Int): Int {
|
||||
if (code == errSecSuccess || code == errSecItemNotFound) {
|
||||
return code
|
||||
}
|
||||
|
||||
val translated = LIBRARY.SecCopyErrorMessageString(code, null)
|
||||
val builder = StringBuilder(message).append(": ")
|
||||
if (translated == null) {
|
||||
builder.append(code)
|
||||
}
|
||||
else {
|
||||
val buf = CharArray(LIBRARY.CFStringGetLength(translated).toInt())
|
||||
for (i in 0 until buf.size) {
|
||||
buf[i] = LIBRARY.CFStringGetCharacterAtIndex(translated, i.toLong())
|
||||
}
|
||||
LIBRARY.CFRelease(translated)
|
||||
builder.append(buf).append(" (").append(code).append(')')
|
||||
}
|
||||
|
||||
if (code == errUserNameNotCorrect || code == errSecUserCanceled || code == -25299 /* The specified item already exists in the keychain */) {
|
||||
LOG.warn(builder.toString())
|
||||
}
|
||||
else {
|
||||
LOG.error(builder.toString())
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
internal fun SecKeychainAttributeInfo(vararg ids: Int): SecKeychainAttributeInfo {
|
||||
private fun SecKeychainAttributeInfo(vararg ids: Int): SecKeychainAttributeInfo {
|
||||
val info = SecKeychainAttributeInfo()
|
||||
val length = ids.size
|
||||
info.count = length
|
||||
@@ -209,7 +211,7 @@ internal fun SecKeychainAttributeInfo(vararg ids: Int): SecKeychainAttributeInfo
|
||||
return info
|
||||
}
|
||||
|
||||
internal class SecKeychainAttributeList : Structure {
|
||||
private class SecKeychainAttributeList : Structure {
|
||||
@JvmField
|
||||
var count = 0
|
||||
@JvmField
|
||||
@@ -222,7 +224,7 @@ internal class SecKeychainAttributeList : Structure {
|
||||
override fun getFieldOrder() = listOf("count", "attr")
|
||||
}
|
||||
|
||||
internal class SecKeychainAttribute : Structure, Structure.ByReference {
|
||||
private class SecKeychainAttribute : Structure, Structure.ByReference {
|
||||
@JvmField
|
||||
var tag = 0
|
||||
@JvmField
|
||||
|
||||
Reference in New Issue
Block a user