GUACAMOLE-641: Correct standard vs. custom field logic for complex retrievals.

When retrieving a contextual field like "passphrase", which does not
have a typed representation different from "password" or "hidden", the
contexts where the field's identity is truly known should be preferred
("password" field of a record with a "keypair" field, which MUST be the
key passphrase). When venturing outside well-known contexts, custom
fields should be preferred when their standard counterparts would
already have well-established meanings that differ from the requested
secret (again: "password" of a record with "keypair").

If this is not done, things like retrieving the private key from a
"Login" record fail, as one of the possible storage mechanisms for a
private key is a hidden or password field, which pulls the user's
password instead of their key. In this case, the correct behavior is
to pull the typed value ("keypair") if available, and use custom fields
ONLY otherwise, as those fields have labels that can establish context.
In no other case would it be reliable to assume that a hidden/password
field actually contains a private key.
This commit is contained in:
Michael Jumper
2022-01-21 15:23:41 -08:00
parent 1cfd2ee835
commit 46501f4b63

View File

@@ -310,38 +310,6 @@ public class KsmRecordService {
}
/**
* Returns the password associated with the given record and matching the
* given label pattern. Both standard and custom fields are searched. As
* standard fields do not have labels, the label pattern is ignored for
* standard fields. Only "Password" and "Hidden" field types are
* considered.
*
* @param record
* The record to retrieve the password from.
*
* @param labelPattern
* The pattern to match against the labels of custom fields, or null if
* no label pattern match should be performed.
*
* @return
* The password associated with the given record, or null if the record
* has no associated password or multiple passwords.
*/
private String getPassword(KeeperRecord record, Pattern labelPattern) {
Password passwordField = getField(record, Password.class, labelPattern);
if (passwordField != null)
return getSingleValue(passwordField.getValue());
HiddenField hiddenField = getField(record, HiddenField.class, labelPattern);
if (hiddenField != null)
return getSingleValue(hiddenField.getValue());
return null;
}
/**
* Returns the password associated with the given record. Both standard and
* custom fields are searched. Only "Password" and "Hidden" field types are
@@ -356,7 +324,17 @@ public class KsmRecordService {
* has no associated password.
*/
public String getPassword(KeeperRecord record) {
return getPassword(record, PASSWORD_LABEL_PATTERN);
Password passwordField = getField(record, Password.class, PASSWORD_LABEL_PATTERN);
if (passwordField != null)
return getSingleValue(passwordField.getValue());
HiddenField hiddenField = getField(record, HiddenField.class, PASSWORD_LABEL_PATTERN);
if (hiddenField != null)
return getSingleValue(hiddenField.getValue());
return null;
}
/**
@@ -381,8 +359,20 @@ public class KsmRecordService {
if (keyPairsField != null)
return getSingleValue(keyPairsField.getValue(), KeyPair::getPrivateKey);
// Fall back to general password/hidden fields if not found or ambiguous
return getPassword(record, PRIVATE_KEY_LABEL_PATTERN);
KeeperRecordData data = record.getData();
List<KeeperRecordField> custom = data.getCustom();
// Use password "private key" custom field as fallback ...
Password passwordField = getField(custom, Password.class, PRIVATE_KEY_LABEL_PATTERN);
if (passwordField != null)
return getSingleValue(passwordField.getValue());
// ... or hidden "private key" custom field
HiddenField hiddenField = getField(custom, HiddenField.class, PRIVATE_KEY_LABEL_PATTERN);
if (hiddenField != null)
return getSingleValue(hiddenField.getValue());
return null;
}
@@ -402,7 +392,37 @@ public class KsmRecordService {
* or null if there is no such passphrase associated with the record.
*/
public String getPassphrase(KeeperRecord record) {
return getPassword(record, PASSPHRASE_LABEL_PATTERN);
KeeperRecordData data = record.getData();
List<KeeperRecordField> fields = data.getFields();
List<KeeperRecordField> custom = data.getCustom();
// For records with a standard keypair field, the passphrase is the
// standard password field
if (getField(fields, KeyPairs.class, null) != null) {
Password passwordField = getField(fields, Password.class, null);
if (passwordField != null)
return getSingleValue(passwordField.getValue());
}
// For records WITHOUT a standard keypair field, the passphrase can
// only reasonably be a custom field (consider a "Login" record with
// a pair of custom hidden fields for the private key and passphrase:
// the standard password field of the "Login" record refers to the
// user's own password, if any, not the passphrase of their key)
// Use password "private key" custom field as fallback ...
Password passwordField = getField(custom, Password.class, PASSPHRASE_LABEL_PATTERN);
if (passwordField != null)
return getSingleValue(passwordField.getValue());
// ... or hidden "private key" custom field
HiddenField hiddenField = getField(custom, HiddenField.class, PASSPHRASE_LABEL_PATTERN);
if (hiddenField != null)
return getSingleValue(hiddenField.getValue());
return null;
}
}