package au.net.causal.maven.nativesecurity.encrypter;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import au.net.causal.maven.nativesecurity.encrypter.GnomeKeyringLibrary.GnomeKeyringFound;
import com.sun.jna.DefaultTypeMapper;
import com.sun.jna.FromNativeContext;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ToNativeContext;
import com.sun.jna.TypeConverter;
public class GnomeKeyring
{
private static final String ATTRIBUTE_KEY = "key";
private static final String LIB_NAME = "gnome-keyring";
private static final List<String> ALT_LIB_NAMES = Arrays.asList("/usr/lib/libgnome-keyring.so.0");
private static final GnomeKeyringLibrary LIBRARY;
private static final Throwable LIBRARY_INSTANTIATION_ERROR;
/**
* Can only set this once, so keep track of it.
*/
private static boolean applicationNameSet;
static
{
GnomeKeyringLibrary library;
Throwable libraryInstantiationError;
try
{
library = loadLibrary(Collections.singletonMap(Library.OPTION_TYPE_MAPPER, new BooleanTypeMapper()));
libraryInstantiationError = null;
}
catch (Throwable e)
{
library = null;
libraryInstantiationError = e;
}
LIBRARY = library;
LIBRARY_INSTANTIATION_ERROR = libraryInstantiationError;
}
private static GnomeKeyringLibrary loadLibrary(Map<?,?> options)
{
try
{
return (GnomeKeyringLibrary)Native.loadLibrary(LIB_NAME, GnomeKeyringLibrary.class, options);
}
catch (UnsatisfiedLinkError e)
{
for (String altLibName : ALT_LIB_NAMES)
{
if (new File(altLibName).isFile())
return (GnomeKeyringLibrary)Native.loadLibrary(altLibName, GnomeKeyringLibrary.class, options);
}
//No alternatives found
throw e;
}
}
public GnomeKeyring()
throws KeyringNotAvailableException
{
this(GnomeKeyring.class.getCanonicalName());
}
public GnomeKeyring(String applicationName)
throws KeyringNotAvailableException
{
if (applicationName == null)
throw new NullPointerException("applicationName == null");
//Check if static initialization was successful
if (LIBRARY_INSTANTIATION_ERROR != null)
throw new KeyringNotAvailableException(LIBRARY_INSTANTIATION_ERROR);
synchronized (GnomeKeyring.class)
{
//Application name can only be set once
//If trying to set it again just ignore
if (!applicationNameSet)
{
LIBRARY.g_set_application_name(applicationName);
applicationNameSet = true;
}
}
if (!LIBRARY.gnome_keyring_is_available())
throw new KeyringNotAvailableException("Gnome keyring not available in current session");
}
public String get(String key)
throws EncryptionException
{
if (key == null)
throw new NullPointerException("key == null");
Pointer[] found = new Pointer[1];
Pointer attributes = LIBRARY.g_array_new(false, false, GnomeKeyringLibrary.GnomeKeyringAttribute_SIZE);
try
{
LIBRARY.gnome_keyring_attribute_list_append_string(attributes, ATTRIBUTE_KEY, key);
int result = LIBRARY.gnome_keyring_find_items_sync(GnomeKeyringLibrary.GNOME_KEYRING_ITEM_GENERIC_SECRET, attributes, found);
checkForError(result, GnomeKeyringLibrary.GNOME_KEYRING_RESULT_NO_MATCH);
}
finally
{
LIBRARY.gnome_keyring_attribute_list_free(attributes);
}
if (found[0] != null)
{
try
{
if (LIBRARY.g_list_length(found[0]) > 0)
{
GnomeKeyringFound result = LIBRARY.g_list_nth_data(found[0], 0);
if (result != null && result.secret != null)
return(result.secret);
else
return(null);
}
else
return(null);
}
finally
{
LIBRARY.gnome_keyring_found_list_free(found[0]);
}
}
else
return(null);
}
public void put(String key, String value)
throws EncryptionException
{
put(key, value, key);
}
public void put(String key, String value, String description)
throws EncryptionException
{
if (key == null)
throw new NullPointerException("key == null");
if (value == null)
throw new NullPointerException("value == null");
if (description == null)
throw new NullPointerException("description == null");
Pointer attributes = LIBRARY.g_array_new(false, false, GnomeKeyringLibrary.GnomeKeyringAttribute_SIZE);
try
{
LIBRARY.gnome_keyring_attribute_list_append_string(attributes, ATTRIBUTE_KEY, key);
int[] itemIdHolder = new int[1];
int result = LIBRARY.gnome_keyring_item_create_sync(
null,
GnomeKeyringLibrary.GNOME_KEYRING_ITEM_GENERIC_SECRET,
description,
attributes,
value,
true,
itemIdHolder);
checkForError(result);
}
finally
{
LIBRARY.gnome_keyring_attribute_list_free(attributes);
}
}
public void remove(String key)
throws EncryptionException
{
Pointer[] foundHolder = new Pointer[1];
Pointer attributes = LIBRARY.g_array_new(false, false, GnomeKeyringLibrary.GnomeKeyringAttribute_SIZE);
try
{
LIBRARY.gnome_keyring_attribute_list_append_string(attributes, ATTRIBUTE_KEY, key);
int result = LIBRARY.gnome_keyring_find_items_sync(GnomeKeyringLibrary.GNOME_KEYRING_ITEM_GENERIC_SECRET, attributes, foundHolder);
checkForError(result, GnomeKeyringLibrary.GNOME_KEYRING_RESULT_NO_MATCH);
}
finally
{
LIBRARY.gnome_keyring_attribute_list_free(attributes);
}
if (foundHolder[0] == null)
return;
int id;
try
{
if (LIBRARY.g_list_length(foundHolder[0]) > 0)
{
GnomeKeyringFound result = LIBRARY.g_list_nth_data(foundHolder[0], 0);
id = result.item_id;
}
else
id = 0;
}
finally
{
LIBRARY.gnome_keyring_found_list_free(foundHolder[0]);
}
if (id > 0)
{
int result = LIBRARY.gnome_keyring_item_delete_sync(null, id);
checkForError(result);
}
}
private static void checkForError(int result, int... allowedErrorCodes)
throws EncryptionException
{
if (result == GnomeKeyringLibrary.GNOME_KEYRING_RESULT_OK)
return;
for (int allowedErrorCode : allowedErrorCodes)
{
if (result == allowedErrorCode)
return;
}
String message;
switch (result)
{
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
message = "GNOME_KEYRING_RESULT_NO_MATCH: No such match found.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
message = "GNOME_KEYRING_RESULT_BAD_ARGUMENTS: Bad arguments to function.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_CANCELLED:
message = "GNOME_KEYRING_RESULT_CANCELLED: Operation was cancelled.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_DENIED:
message = "GNOME_KEYRING_RESULT_DENIED: Either the user or daemon denied access.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_IO_ERROR:
message = "GNOME_KEYRING_RESULT_IO_ERROR: Problem communicating with daemon.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_KEYRING_ALREADY_EXISTS:
message = "GNOME_KEYRING_RESULT_KEYRING_ALREADY_EXISTS: The keyring already exists.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
message = "GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON: Keyring daemon is not available.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_NO_MATCH:
message = "GNOME_KEYRING_RESULT_NO_MATCH: No such match found.";
break;
case GnomeKeyringLibrary.GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
message = "GNOME_KEYRING_RESULT_NO_SUCH_KEYRING: No such keyring exists.";
break;
default:
message = "Unknown error " + result;
}
throw new EncryptionException(message);
}
private static class BooleanTypeMapper extends DefaultTypeMapper
{
public BooleanTypeMapper()
{
addTypeConverter(Boolean.TYPE, new TypeConverter()
{
@Override
public Object toNative(Object value, ToNativeContext context)
{
return Boolean.TRUE.equals(value) ? 1 : 0;
}
@Override
public Object fromNative(Object value, FromNativeContext context)
{
return ((Integer)value).intValue() != 0;
}
@Override
public Class<?> nativeType()
{
return Integer.class;
}
});
}
}
public static class KeyringNotAvailableException extends Exception
{
public KeyringNotAvailableException(String message)
{
super(message);
}
public KeyringNotAvailableException(Throwable t)
{
super(t);
}
}
}