Skip to content

Commit

Permalink
Clean-up and simplify LinuxPasswordProvider and WinCrypto
Browse files Browse the repository at this point in the history
And remove 'arch' attribute of 'o.e.equinox.security.linux' in the
'o.e.equinox.core.sdk' feature to reflect that the fragment is
architecture-independent.
  • Loading branch information
HannesWell committed Mar 24, 2024
1 parent 1b16627 commit a90c944
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 93 deletions.
Expand Up @@ -10,5 +10,4 @@ Bundle-Localization: fragment
Eclipse-PlatformFilter: (osgi.os=linux)
Export-Package: org.eclipse.equinox.internal.security.linux;x-internal:=true
Automatic-Module-Name: org.eclipse.equinox.security.linux
Eclipse-BundleShape: dir
Require-Bundle: com.sun.jna;bundle-version="[5.8.0,6.0.0)"
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2021 IBM Corporation and others.
* Copyright (c) 2017, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -17,7 +17,6 @@

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.spec.PBEKeySpec;
Expand All @@ -42,26 +41,14 @@ public class LinuxPasswordProvider extends PasswordProvider implements IValidati
private static final int PASSWORD_LENGTH = 64;

private static final String SECRET_COLLECTION_DEFAULT = "default"; //$NON-NLS-1$
private static final Map<String, Object> LIB_LOAD_OPTIONS = new HashMap<>();
// open flags = (RTLD_NODELETE | RTLD_GLOBAL | RTLD_LAZY)
private static final Map<String, Object> LIB_LOAD_OPTIONS = Map.of(Library.OPTION_OPEN_FLAGS, 0x1101);

private SecretSchema fEquinoxSchema;
private final SecretSchema fEquinoxSchema = new SecretSchema("org.eclipse.equinox", //$NON-NLS-1$
SecretSchemaFlags.SECRET_SCHEMA_NONE, new SecretSchemaAttribute(null, 0));
private LibSecret fLibSecret;
private LibGio fLibGio;

static {
// open flags = (RTLD_NODELETE | RTLD_GLOBAL | RTLD_LAZY)
LIB_LOAD_OPTIONS.put(Library.OPTION_OPEN_FLAGS, 0x1101);
}

public LinuxPasswordProvider() {
initEquinoxSchema();
}

private void initEquinoxSchema() {
fEquinoxSchema = new SecretSchema("org.eclipse.equinox", //$NON-NLS-1$
SecretSchemaFlags.SECRET_SCHEMA_NONE, new SecretSchemaAttribute(null, 0));
}

private interface LibGio extends Library {
Pointer g_bus_get_sync(int bus_type, Pointer cancellable, PointerByReference gerror);

Expand Down Expand Up @@ -97,31 +84,16 @@ private void unlockSecretService() {
PointerByReference gerror = new PointerByReference();
gerror.setValue(Pointer.NULL);
fLibGio.g_bus_get_sync(GBusType.G_BUS_TYPE_SESSION, Pointer.NULL, gerror);
if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = "Unable to get DBus session bus: " + error.message; //$NON-NLS-1$
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
}
requireNoError(gerror, "Unable to get DBus session bus: "); //$NON-NLS-1$

fLibSecret = Native.load("secret-1", LibSecret.class, LIB_LOAD_OPTIONS); //$NON-NLS-1$
Pointer secretService = fLibSecret.secret_service_get_sync(SecretServiceFlags.SECRET_SERVICE_LOAD_COLLECTIONS,
Pointer.NULL, gerror);
if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = "Unable to get secret service: " + error.message; //$NON-NLS-1$
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
}
requireNoError(gerror, "Unable to get secret service: "); //$NON-NLS-1$

Pointer defaultCollection = fLibSecret.secret_collection_for_alias_sync(secretService,
SECRET_COLLECTION_DEFAULT, SecretCollectionFlags.SECRET_COLLECTION_NONE, Pointer.NULL, gerror);
if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = "Unable to get secret collection: " + error.message; //$NON-NLS-1$
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
}
requireNoError(gerror, "Unable to get secret collection: "); //$NON-NLS-1$
if (defaultCollection == Pointer.NULL) {
throw new SecurityException("Unable to find default secret collection"); //$NON-NLS-1$
}
Expand All @@ -132,36 +104,19 @@ private void unlockSecretService() {
fLibSecret.secret_service_unlock_sync(secretService, list, Pointer.NULL, unlocked, gerror);
fLibGio.g_error_free(unlocked.getValue());
fLibGio.g_error_free(list.getPointer());
if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = "Unable to unlock: " + error.message; //$NON-NLS-1$
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
}
}

}

private boolean canUnlock() {
try {
unlockSecretService();
} catch (SecurityException e) {
return false;
requireNoError(gerror, "Unable to unlock: "); //$NON-NLS-1$
}
return true;

}

private String getMasterPassword() throws SecurityException {
unlockSecretService();
PointerByReference gerror = new PointerByReference();
String password = fLibSecret.secret_password_lookup_sync(fEquinoxSchema, Pointer.NULL, gerror, Pointer.NULL);

if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = error.message;
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
} else if (password == null) {
requireNoError(gerror, ""); //$NON-NLS-1$
if (password == null) {
throw new SecurityException("Unable to find password"); //$NON-NLS-1$
}

Expand All @@ -171,20 +126,22 @@ private String getMasterPassword() throws SecurityException {

private void saveMasterPassword(String password) throws SecurityException {
unlockSecretService();
String passwordUTF8 = password;
PointerByReference gerror = new PointerByReference();

byte[] utfbytes = password.getBytes();
passwordUTF8 = new String(utfbytes, StandardCharsets.UTF_8);
String passwordUTF8 = new String(password.getBytes(), StandardCharsets.UTF_8);

fLibSecret.secret_password_store_sync(fEquinoxSchema, SECRET_COLLECTION_DEFAULT, "Equinox master password", //$NON-NLS-1$
passwordUTF8, Pointer.NULL, gerror, Pointer.NULL);

requireNoError(gerror, ""); //$NON-NLS-1$
}

private void requireNoError(PointerByReference gerror, String details) {
if (gerror.getValue() != Pointer.NULL) {
GError error = new GError(gerror.getValue());
String message = error.message;
fLibGio.g_error_free(gerror.getValue());
throw new SecurityException(message);
throw new SecurityException(details + message);
}
}

Expand Down Expand Up @@ -224,7 +181,8 @@ public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType)
@Override
public boolean isValid() {
try {
return canUnlock();
unlockSecretService();
return true;
} catch (SecurityException e) {
return false;
}
Expand Down
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %fragmentName
Bundle-SymbolicName: org.eclipse.equinox.security.win32.x86_64;singleton:=true
Bundle-Version: 1.2.200.qualifier
Bundle-Version: 1.2.300.qualifier
Bundle-Vendor: %providerName
Fragment-Host: org.eclipse.equinox.security;bundle-version="[1.0.0,2.0.0)"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Expand Down
2 changes: 1 addition & 1 deletion bundles/org.eclipse.equinox.security.win32.x86_64/pom.xml
Expand Up @@ -18,7 +18,7 @@
<relativePath>../../</relativePath>
</parent>
<artifactId>org.eclipse.equinox.security.win32.x86_64</artifactId>
<version>1.2.200-SNAPSHOT</version>
<version>1.2.300-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>

<properties>
Expand Down
@@ -1,13 +1,13 @@
/*******************************************************************************
* Copyright (c) 2008, 2017 IBM Corporation and others.
* Copyright (c) 2008, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
Expand Down Expand Up @@ -41,21 +41,22 @@ public class WinCrypto extends PasswordProvider {
System.loadLibrary("jnicrypt64");
}

private final static String WIN_PROVIDER_NODE = "/org.eclipse.equinox.secure.storage/windows64";
private final static String PASSWORD_KEY = "encryptedPassword";
private static final String WIN_PROVIDER_NODE = "/org.eclipse.equinox.secure.storage/windows64";
private static final String PASSWORD_KEY = "encryptedPassword";

/**
* The length of the randomly generated password in bytes
*/
private final static int PASSWORD_LENGTH = 250;
private static final int PASSWORD_LENGTH = 250;

@Override
public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
byte[] encryptedPassword;
if ((passwordType & CREATE_NEW_PASSWORD) == 0)
if ((passwordType & CREATE_NEW_PASSWORD) == 0) {
encryptedPassword = getEncryptedPassword(container);
else
} else {
encryptedPassword = null;
}

if (encryptedPassword != null) {
byte[] decryptedPassword = windecrypt(encryptedPassword);
Expand All @@ -78,24 +79,24 @@ public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType)
random.setSeed(System.currentTimeMillis());
random.nextBytes(rawPassword);
String password = Base64.encode(rawPassword);
if (savePassword(password, container))
if (savePassword(password, container)) {
return new PBEKeySpec(password.toCharArray());
else
} else {
return null;
}
}

private byte[] getEncryptedPassword(IPreferencesContainer container) {
ISecurePreferences node = container.getPreferences().node(WIN_PROVIDER_NODE);
String passwordHint;
try {
passwordHint = node.get(PASSWORD_KEY, null);
String passwordHint = node.get(PASSWORD_KEY, null);
if (passwordHint != null) {
return Base64.decode(passwordHint);
}
} catch (StorageException e) { // should never happen in this scenario
AuthPlugin.getDefault().logError(WinCryptoMessages.decryptPasswordFailed, e);
return null;
}
if (passwordHint == null)
return null;
return Base64.decode(passwordHint);
return null;
}

private boolean savePassword(String password, IPreferencesContainer container) {
Expand Down
Expand Up @@ -31,7 +31,7 @@
* password provider module to the secure storage system.
* </p>
*/
abstract public class PasswordProvider {
public abstract class PasswordProvider {

/**
* Bit mask for the password type field of the
Expand All @@ -40,15 +40,15 @@ abstract public class PasswordProvider {
* otherwise this is a request for the password previously used for this secure
* storage.
*/
final public static int CREATE_NEW_PASSWORD = 1 << 0;
public static final int CREATE_NEW_PASSWORD = 1 << 0;

/**
* Bit mask for the password type field of the
* {@link #getPassword(IPreferencesContainer, int)} method. If value at this bit
* set to <code>1</code>, it indicates that a new password is requested as a
* part of the password change operation.
*/
final public static int PASSWORD_CHANGE = 1 << 1;
public static final int PASSWORD_CHANGE = 1 << 1;

/**
* This method should return the password used to encrypt entries in the secure
Expand All @@ -63,14 +63,7 @@ abstract public class PasswordProvider {
* @return password used to encrypt entries in the secure preferences,
* <code>null</code> if unable to obtain password
*/
abstract public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType);

/**
* Constructor.
*/
public PasswordProvider() {
// placeholder
}
public abstract PBEKeySpec getPassword(IPreferencesContainer container, int passwordType);

/**
* The framework might call this method if it suspects that the password is
Expand Down
2 changes: 0 additions & 2 deletions features/org.eclipse.equinox.core.sdk/feature.xml
Expand Up @@ -86,13 +86,11 @@
<plugin
id="org.eclipse.equinox.security.linux"
os="linux"
arch="x86_64"
version="0.0.0"/>

<plugin
id="org.eclipse.equinox.security.linux.source"
os="linux"
arch="x86_64"
version="0.0.0"/>

<plugin
Expand Down

0 comments on commit a90c944

Please sign in to comment.