624 lines
26 KiB
Diff
624 lines
26 KiB
Diff
From fcf500b87f0ddcd1fff0b9a0040b1be1b8a37321 Mon Sep 17 00:00:00 2001
|
|
Date: Fri, 29 Nov 2024 15:36:57 +0800
|
|
Subject: SA redact support password
|
|
|
|
---
|
|
src/hotspot/share/runtime/arguments.cpp | 16 ++--
|
|
src/hotspot/share/runtime/arguments.hpp | 5 -
|
|
src/hotspot/share/runtime/globals.hpp | 2 +-
|
|
src/hotspot/share/services/heapRedactor.cpp | 7 +-
|
|
src/hotspot/share/services/heapRedactor.hpp | 1 +
|
|
.../classes/sun/jvm/hotspot/SALauncher.java | 10 +-
|
|
.../classes/sun/jvm/hotspot/tools/JMap.java | 36 +++++++
|
|
.../hotspot/utilities/HeapHprofBinWriter.java | 75 ++++++++++++++-
|
|
.../jvm/hotspot/utilities/HeapRedactor.java | 30 +++---
|
|
.../share/classes/sun/tools/jmap/JMap.java | 96 +++++++++++++++----
|
|
10 files changed, 227 insertions(+), 51 deletions(-)
|
|
|
|
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
|
|
index 42b4f90f1..f24cabb11 100644
|
|
--- a/src/hotspot/share/runtime/arguments.cpp
|
|
+++ b/src/hotspot/share/runtime/arguments.cpp
|
|
@@ -121,8 +121,6 @@ bool Arguments::_has_jimage = false;
|
|
|
|
char* Arguments::_ext_dirs = NULL;
|
|
|
|
-char* Arguments::_heap_dump_redact_auth = NULL;
|
|
-
|
|
bool PathString::set_value(const char *value) {
|
|
if (_value != NULL) {
|
|
FreeHeap(_value);
|
|
@@ -3743,23 +3741,23 @@ jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args,
|
|
warning("Heap dump redacting did not setup properly, using wrong argument?");
|
|
vm_exit_during_initialization("Syntax error, expecting -XX:HeapDumpRedact=[off|names|basic|full|diyrules|annotation]",NULL);
|
|
}
|
|
+ continue;
|
|
}
|
|
|
|
// heapDump redact password
|
|
if(match_option(option, "-XX:RedactPassword=", &tail)) {
|
|
if(tail == NULL || strlen(tail) == 0) {
|
|
VerifyRedactPassword = false;
|
|
- jio_fprintf(defaultStream::output_stream(), "redact password is null, disable verify heap dump authority.\n");
|
|
} else {
|
|
- VerifyRedactPassword = true;
|
|
- size_t redact_password_len = strlen(tail);
|
|
- _heap_dump_redact_auth = NEW_C_HEAP_ARRAY(char, redact_password_len+1, mtArguments);
|
|
- memcpy(_heap_dump_redact_auth, tail, redact_password_len);
|
|
- _heap_dump_redact_auth[redact_password_len] = '\0';
|
|
- memset((void*)tail, '0', redact_password_len);
|
|
+ char* split_char = strstr(const_cast<char*>(tail), ",");
|
|
+ VerifyRedactPassword = !(split_char == NULL || strlen(split_char) < SALT_LEN);
|
|
+ }
|
|
+ if(!VerifyRedactPassword) {
|
|
+ jio_fprintf(defaultStream::output_stream(), "redact password is null or with bad format, disable verify heap dump authority.\n");
|
|
}
|
|
}
|
|
|
|
+
|
|
#ifndef PRODUCT
|
|
if (match_option(option, "-XX:+PrintFlagsWithComments")) {
|
|
JVMFlag::printFlags(tty, true);
|
|
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
|
|
index 6b9759906..cb2a04a2d 100644
|
|
--- a/src/hotspot/share/runtime/arguments.hpp
|
|
+++ b/src/hotspot/share/runtime/arguments.hpp
|
|
@@ -468,8 +468,6 @@ class Arguments : AllStatic {
|
|
char** base_archive_path,
|
|
char** top_archive_path) NOT_CDS_RETURN;
|
|
|
|
- static char* _heap_dump_redact_auth;
|
|
-
|
|
public:
|
|
// Parses the arguments, first phase
|
|
static jint parse(const JavaVMInitArgs* args);
|
|
@@ -555,9 +553,6 @@ class Arguments : AllStatic {
|
|
// Java launcher properties
|
|
static void process_sun_java_launcher_properties(JavaVMInitArgs* args);
|
|
|
|
- // heap dump redact password
|
|
- static const char* get_heap_dump_redact_auth() { return _heap_dump_redact_auth; }
|
|
-
|
|
// System properties
|
|
static void init_system_properties();
|
|
|
|
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
|
|
index b51e50ddf..680e78c04 100644
|
|
--- a/src/hotspot/share/runtime/globals.hpp
|
|
+++ b/src/hotspot/share/runtime/globals.hpp
|
|
@@ -568,7 +568,7 @@ const intx ObjectAlignmentInBytes = 8;
|
|
"verify authority for operating heapDump redact feature") \
|
|
\
|
|
product(ccstr, RedactPassword, NULL, \
|
|
- "authority for operating heapDump redact feature") \
|
|
+ "authority for operating heapDump redact feature, format {password,salt}, salt length >= 8") \
|
|
\
|
|
product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \
|
|
"Native memory tracking options") \
|
|
diff --git a/src/hotspot/share/services/heapRedactor.cpp b/src/hotspot/share/services/heapRedactor.cpp
|
|
index 0e7b0a97c..cfb5b3f82 100644
|
|
--- a/src/hotspot/share/services/heapRedactor.cpp
|
|
+++ b/src/hotspot/share/services/heapRedactor.cpp
|
|
@@ -170,12 +170,15 @@ void HeapRedactor::init(outputStream* out) {
|
|
* if HeapDumpRedact is NULL , jmap operation can not open redact feature without password
|
|
* if HeapDumpRedact is not NULL, jmap operation can not change redact level without password
|
|
**/
|
|
- if(Arguments::get_heap_dump_redact_auth() == NULL) {
|
|
+ char* split_char = NULL;
|
|
+ if(RedactPassword == NULL || (split_char = strstr(const_cast<char*>(RedactPassword), ",")) == NULL || strlen(split_char) < SALT_LEN) {
|
|
VerifyRedactPassword = false;
|
|
}
|
|
if(VerifyRedactPassword && !_use_sys_params) {
|
|
+ size_t auth_len = strlen(RedactPassword);
|
|
+ size_t suffix_len = strlen(split_char);
|
|
if(_redact_params.redact_password == NULL ||
|
|
- strcmp(_redact_params.redact_password, Arguments::get_heap_dump_redact_auth()) ) {
|
|
+ strncmp(_redact_params.redact_password, RedactPassword, auth_len-suffix_len) ) {
|
|
// no password or wrong password
|
|
_use_sys_params = true;
|
|
if(out != NULL) {
|
|
diff --git a/src/hotspot/share/services/heapRedactor.hpp b/src/hotspot/share/services/heapRedactor.hpp
|
|
index 790430507..e5a5bf440 100644
|
|
--- a/src/hotspot/share/services/heapRedactor.hpp
|
|
+++ b/src/hotspot/share/services/heapRedactor.hpp
|
|
@@ -32,6 +32,7 @@
|
|
#endif
|
|
|
|
#define MAX_MAP_FILE_LENGTH 1024
|
|
+#define SALT_LEN 9
|
|
|
|
enum HeapDumpRedactLevel {
|
|
REDACT_UNKNOWN,
|
|
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
|
|
index 291e483e0..91a432574 100644
|
|
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
|
|
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
|
|
@@ -322,7 +322,8 @@ public class SALauncher {
|
|
Map.entry("HeapDumpRedact=", "HeapDumpRedact"),
|
|
Map.entry("RedactMap=", "RedactMap"),
|
|
Map.entry("RedactMapFile=", "RedactMapFile"),
|
|
- Map.entry("RedactClassPath=", "RedactClassPath"));
|
|
+ Map.entry("RedactClassPath=", "RedactClassPath"),
|
|
+ Map.entry("RedactPassword", "RedactPassword"));
|
|
}
|
|
|
|
private static void runJMAP(String[] oldArgs) {
|
|
@@ -337,6 +338,7 @@ public class SALauncher {
|
|
String redactMap = newArgMap.get("RedactMap");
|
|
String redactMapFile = newArgMap.get("RedactMapFile");
|
|
String redactClassPath = newArgMap.get("RedactClassPath");
|
|
+ boolean hasRedactPassword = newArgMap.containsKey("RedactPassword");
|
|
if (!requestHeapdump && (dumpfile != null)) {
|
|
throw new IllegalArgumentException("Unexpected argument: dumpfile");
|
|
}
|
|
@@ -359,6 +361,9 @@ public class SALauncher {
|
|
if (redactClassPath != null) {
|
|
command += ",RedactClassPath=" + redactClassPath;
|
|
}
|
|
+ if(hasRedactPassword) {
|
|
+ command += ",RedactPassword";
|
|
+ }
|
|
newArgMap.put(command, null);
|
|
}
|
|
|
|
@@ -369,9 +374,12 @@ public class SALauncher {
|
|
newArgMap.remove("RedactMap");
|
|
newArgMap.remove("RedactMapFile");
|
|
newArgMap.remove("RedactClassPath");
|
|
+ newArgMap.remove("RedactPassword");
|
|
JMap.main(buildAttachArgs(newArgMap, false));
|
|
}
|
|
|
|
+
|
|
+
|
|
private static void runJINFO(String[] oldArgs) {
|
|
Map<String, String> longOptsMap = Map.of("exe=", "exe",
|
|
"core=", "core",
|
|
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
|
|
index e52cd1fb1..fbead3ce4 100644
|
|
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
|
|
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
|
|
@@ -25,6 +25,9 @@
|
|
package sun.jvm.hotspot.tools;
|
|
|
|
import java.io.*;
|
|
+import java.nio.CharBuffer;
|
|
+import java.util.regex.Pattern;
|
|
+
|
|
import sun.jvm.hotspot.debugger.JVMDebugger;
|
|
import sun.jvm.hotspot.utilities.*;
|
|
|
|
@@ -189,6 +192,9 @@ public class JMap extends Tool {
|
|
redactParams.setRedactMapFile(keyValue[1]);
|
|
} else if (keyValue[0].equals("RedactClassPath")) {
|
|
redactParams.setRedactClassPath(keyValue[1]);
|
|
+ } else if (keyValue[0].equals("RedactPassword")) {
|
|
+ redactParams.setRedactPassword(getRedactPassword());
|
|
+
|
|
} else {
|
|
System.err.println("unknown option:" + keyValue[0]);
|
|
|
|
@@ -226,6 +232,36 @@ public class JMap extends Tool {
|
|
jmap.execute(args);
|
|
}
|
|
|
|
+ private static CharBuffer getRedactPassword() {
|
|
+ CharBuffer redactPassword = CharBuffer.wrap("");
|
|
+ // heap dump may need a password
|
|
+ Console console = System.console();
|
|
+ char[] passwords = null;
|
|
+ if (console == null) {
|
|
+ return redactPassword;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ passwords = console.readPassword("redact authority password:");
|
|
+ } catch (Exception e) {
|
|
+ }
|
|
+ if(passwords == null) {
|
|
+ return redactPassword;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ CharBuffer cb = CharBuffer.wrap(passwords);
|
|
+ String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$";
|
|
+ if(!Pattern.matches(passwordPattern, cb)) {
|
|
+ return redactPassword;
|
|
+ }
|
|
+ redactPassword = cb;
|
|
+ } catch (Exception e) {
|
|
+ }
|
|
+
|
|
+ return redactPassword;
|
|
+ }
|
|
+
|
|
public boolean writeHeapHprofBin(String fileName, int gzLevel) {
|
|
try {
|
|
HeapHprofBinWriter hgw;
|
|
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
|
|
index e73b6f9a3..566d88646 100644
|
|
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
|
|
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
|
|
@@ -28,6 +28,10 @@ import java.io.*;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.channels.*;
|
|
+import java.nio.CharBuffer;
|
|
+import java.security.NoSuchAlgorithmException;
|
|
+import java.security.spec.InvalidKeySpecException;
|
|
+import java.security.spec.KeySpec;
|
|
import java.util.*;
|
|
import java.util.zip.*;
|
|
import sun.jvm.hotspot.debugger.*;
|
|
@@ -37,6 +41,10 @@ import sun.jvm.hotspot.runtime.*;
|
|
import sun.jvm.hotspot.classfile.*;
|
|
import sun.jvm.hotspot.gc.z.ZCollectedHeap;
|
|
|
|
+import javax.crypto.SecretKey;
|
|
+import javax.crypto.SecretKeyFactory;
|
|
+import javax.crypto.spec.PBEKeySpec;
|
|
+
|
|
/*
|
|
* This class writes Java heap in hprof binary format. This format is
|
|
* used by Heap Analysis Tool (HAT). The class is heavily influenced
|
|
@@ -386,6 +394,11 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
|
private static final long MAX_U4_VALUE = 0xFFFFFFFFL;
|
|
int serialNum = 1;
|
|
|
|
+ // encrypt
|
|
+ private static int SALT_MIN_LENGTH = 8;
|
|
+ private static int HASH_BIT_SIZE = 256;
|
|
+ private static int HASH_ITERATIONS_COUNT = 10000;
|
|
+
|
|
// Heap Redact
|
|
private HeapRedactor heapRedactor;
|
|
|
|
@@ -404,6 +417,10 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
|
return heapRedactor.getHeapDumpRedactLevel();
|
|
}
|
|
|
|
+ public Optional<CharBuffer> getHeapDumpRedactPassword() {
|
|
+ return heapRedactor == null ? Optional.empty() : Optional.ofNullable(heapRedactor.getRedactPassword());
|
|
+ }
|
|
+
|
|
private Optional<String> lookupRedactName(String name){
|
|
return heapRedactor == null ? Optional.empty() : heapRedactor.lookupRedactName(name);
|
|
}
|
|
@@ -454,10 +471,66 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
|
this.gzLevel = gzLevel;
|
|
}
|
|
|
|
+ private boolean checkPassword() {
|
|
+ Optional<String> redactAuthOption = getVMRedactParameter("RedactPassword");
|
|
+ String redactAuth = redactAuthOption.isPresent() ? redactAuthOption.get() : null;
|
|
+ boolean redactAuthFlag = true;
|
|
+ if(redactAuth != null) {
|
|
+ String[] auths = redactAuth.split(",");
|
|
+ if(auths.length != 2) {
|
|
+ return redactAuthFlag;
|
|
+ }
|
|
+
|
|
+ Optional<CharBuffer> passwordOption = getHeapDumpRedactPassword();
|
|
+ CharBuffer password = passwordOption.isPresent() ? passwordOption.get() : CharBuffer.wrap("");
|
|
+ char[] passwordChars = null;
|
|
+ try {
|
|
+ passwordChars = password.array();
|
|
+
|
|
+ byte[] saltBytes = auths[1].getBytes("UTF-8");
|
|
+ if(saltBytes.length < SALT_MIN_LENGTH) {
|
|
+ return redactAuthFlag;
|
|
+ }
|
|
+
|
|
+ String digestStr = getEncryptValue(passwordChars, saltBytes);
|
|
+ redactAuthFlag = auths[0].equals(digestStr);
|
|
+ } catch (Exception e) {
|
|
+ // ignore
|
|
+ redactAuthFlag = false;
|
|
+ } finally {
|
|
+ // clear all password
|
|
+ if(passwordChars != null) {
|
|
+ Arrays.fill(passwordChars, '0');
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return redactAuthFlag;
|
|
+ }
|
|
+
|
|
+ private String getEncryptValue(char[] passwordValue, byte[] saltBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
|
|
+ StringBuilder digestStrBuilder = new StringBuilder();
|
|
+
|
|
+ KeySpec spec = new PBEKeySpec(passwordValue, saltBytes, HASH_ITERATIONS_COUNT, HASH_BIT_SIZE);
|
|
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
+ SecretKey secretKey = secretKeyFactory.generateSecret(spec);
|
|
+ byte[] digestBytes = secretKey.getEncoded();
|
|
+ for (byte b : digestBytes) {
|
|
+ String hex = Integer.toHexString(0xff & b);
|
|
+ if (hex.length() == 1) {
|
|
+ digestStrBuilder.append('0');
|
|
+ }
|
|
+ digestStrBuilder.append(hex);
|
|
+ }
|
|
+ String digestStr = digestStrBuilder.toString();
|
|
+
|
|
+ return digestStr;
|
|
+ }
|
|
+
|
|
public synchronized void write(String fileName) throws IOException {
|
|
VM vm = VM.getVM();
|
|
|
|
- if(getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_UNKNOWN) {
|
|
+ if(getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_UNKNOWN || !checkPassword()) {
|
|
resetRedactParams();
|
|
}
|
|
|
|
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
|
|
index c2a916617..5c442b2bb 100644
|
|
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
|
|
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
|
|
@@ -30,6 +30,7 @@ import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.IOException;
|
|
+import java.nio.CharBuffer;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
@@ -51,7 +52,7 @@ public class HeapRedactor {
|
|
private HeapDumpRedactLevel redactLevel;
|
|
private Map<String, String> redactNameTable;
|
|
private Map<String, Map<String, String>> redactClassTable;
|
|
- private String redactClassFullName = null;
|
|
+ private String redactClassFullName = null;
|
|
private Map<Long, String> redactValueTable;
|
|
private RedactVectorNode headerNode;
|
|
private RedactVectorNode currentNode;
|
|
@@ -62,6 +63,7 @@ public class HeapRedactor {
|
|
public static final String REDACT_MAP_PREFIX = "RedactMap=";
|
|
public static final String REDACT_MAP_FILE_PREFIX = "RedactMapFile=";
|
|
public static final String REDACT_CLASS_PATH_PREFIX = "RedactClassPath=";
|
|
+ public static final String REDACT_PASSWORD_PREFIX = "RedactPassword=";
|
|
|
|
public static final String REDACT_UNKNOWN_STR = "UNKNOWN";
|
|
public static final String REDACT_OFF_STR = "OFF";
|
|
@@ -82,14 +84,6 @@ public class HeapRedactor {
|
|
public static final int PATH_MAX = 4096;
|
|
public static final int REDACT_VECTOR_SIZE = 1024;
|
|
|
|
- public HeapRedactor(String options) {
|
|
- redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN;
|
|
- redactNameTable = null;
|
|
- redactClassTable = null;
|
|
- redactValueTable = null;
|
|
- init(options);
|
|
- }
|
|
-
|
|
public HeapRedactor(RedactParams redactParams) {
|
|
this.redactParams = redactParams;
|
|
redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN;
|
|
@@ -167,6 +161,10 @@ public class HeapRedactor {
|
|
return redactParams.getRedactClassPath();
|
|
}
|
|
|
|
+ public CharBuffer getRedactPassword(){
|
|
+ return redactParams.getRedactPassword();
|
|
+ }
|
|
+
|
|
public Optional<Map<String, String>> getRedactRulesTable(String key) {
|
|
return Optional.ofNullable(redactClassTable == null ? null: redactClassTable.get(key));
|
|
}
|
|
@@ -218,7 +216,7 @@ public class HeapRedactor {
|
|
}
|
|
|
|
private RedactParams parseRedactOptions(String optionStr) {
|
|
- RedactParams params = new RedactParams(REDACT_OFF_OPTION, null, null, null);
|
|
+ RedactParams params = new RedactParams(REDACT_OFF_OPTION, null, null, null, null);
|
|
if (optionStr != null) {
|
|
String[] options = optionStr.split(",");
|
|
for (String option : options) {
|
|
@@ -321,16 +319,18 @@ public class HeapRedactor {
|
|
private String redactMap;
|
|
private String redactMapFile;
|
|
private String redactClassPath;
|
|
+ private CharBuffer redactPassword;
|
|
private boolean enableRedact = false;
|
|
|
|
public RedactParams() {
|
|
}
|
|
|
|
- public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath) {
|
|
+ public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath, CharBuffer redactPassword) {
|
|
this.heapDumpRedact = heapDumpRedact;
|
|
this.redactMap = redactMap;
|
|
this.redactMapFile = redactMapFile;
|
|
this.redactClassPath = redactClassPath;
|
|
+ this.redactPassword = redactPassword;
|
|
}
|
|
|
|
@Override
|
|
@@ -395,6 +395,14 @@ public class HeapRedactor {
|
|
this.redactClassPath = redactClassPath;
|
|
}
|
|
|
|
+ public CharBuffer getRedactPassword() {
|
|
+ return redactPassword;
|
|
+ }
|
|
+
|
|
+ public void setRedactPassword(CharBuffer redactPassword) {
|
|
+ this.redactPassword = redactPassword;
|
|
+ }
|
|
+
|
|
public static boolean checkLauncherHeapdumpRedactSupport(String value) {
|
|
String[] validValues = {REDACT_BASIC_OPTION, REDACT_NAME_OPTION, REDACT_FULL_OPTION, REDACT_DIYRULES_OPTION, REDACT_ANNOTATION_OPTION, REDACT_OFF_OPTION};
|
|
for (String validValue : validValues) {
|
|
diff --git a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
|
|
index ef4ea7152..6479863a6 100644
|
|
--- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
|
|
+++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
|
|
@@ -25,14 +25,17 @@
|
|
|
|
package sun.tools.jmap;
|
|
|
|
+import java.io.BufferedInputStream;
|
|
import java.io.Console;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
+import java.io.InputStreamReader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.nio.CharBuffer;
|
|
-import java.nio.charset.Charset;
|
|
-import java.security.MessageDigest;
|
|
+import java.security.NoSuchAlgorithmException;
|
|
+import java.security.spec.InvalidKeySpecException;
|
|
+import java.security.spec.KeySpec;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.regex.Pattern;
|
|
@@ -43,6 +46,10 @@ import com.sun.tools.attach.AttachNotSupportedException;
|
|
import sun.tools.attach.HotSpotVirtualMachine;
|
|
import sun.tools.common.ProcessArgumentMatcher;
|
|
|
|
+import javax.crypto.SecretKey;
|
|
+import javax.crypto.SecretKeyFactory;
|
|
+import javax.crypto.spec.PBEKeySpec;
|
|
+
|
|
/*
|
|
* This class is the main class for the JMap utility. It parses its arguments
|
|
* and decides if the command should be satisfied using the VM attach mechanism
|
|
@@ -51,6 +58,10 @@ import sun.tools.common.ProcessArgumentMatcher;
|
|
* options are mapped to SA tools.
|
|
*/
|
|
public class JMap {
|
|
+ // encrypt
|
|
+ private static int SALT_MIN_LENGTH = 8;
|
|
+ private static int HASH_BIT_SIZE = 256;
|
|
+ private static int HASH_ITERATIONS_COUNT = 10000;
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
if (args.length == 0) {
|
|
@@ -250,7 +261,7 @@ public class JMap {
|
|
} else if (subopt.startsWith("RedactClassPath")) {
|
|
redactParams.setRedactClassPath(subopt.substring("RedactClassPath=".length()));
|
|
} else if (subopt.startsWith("RedactPassword")) {
|
|
- redactPassword = getRedactPassword();
|
|
+ redactPassword = getRedactPassword(pid);
|
|
} else {
|
|
System.err.println("Fail: invalid option: '" + subopt + "'");
|
|
usage(1);
|
|
@@ -282,7 +293,7 @@ public class JMap {
|
|
}
|
|
}
|
|
|
|
- private static String getRedactPassword() {
|
|
+ private static String getRedactPassword(String pid) {
|
|
String redactPassword = ",RedactPassword=";
|
|
// heap dump may need a password
|
|
Console console = System.console();
|
|
@@ -300,42 +311,85 @@ public class JMap {
|
|
}
|
|
|
|
String digestStr = null;
|
|
- byte[] passwordBytes = null;
|
|
try {
|
|
CharBuffer cb = CharBuffer.wrap(passwords);
|
|
String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$";
|
|
if(!Pattern.matches(passwordPattern, cb)) {
|
|
return redactPassword;
|
|
}
|
|
- Charset cs = Charset.forName("UTF-8");
|
|
- passwordBytes= cs.encode(cb).array();
|
|
-
|
|
- StringBuilder digestStrBuilder = new StringBuilder();
|
|
- MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
|
- byte[] digestBytes = messageDigest.digest(passwordBytes);
|
|
- for(byte b : digestBytes) {
|
|
- String hex = Integer.toHexString(0xff & b);
|
|
- if(hex.length() == 1) {
|
|
- digestStrBuilder.append('0');
|
|
- }
|
|
- digestStrBuilder.append(hex);
|
|
+
|
|
+ String salt = getSalt(pid);
|
|
+ if(salt == null) {
|
|
+ return redactPassword;
|
|
+ }
|
|
+ byte[] saltBytes = salt.getBytes("UTF-8");
|
|
+ if(saltBytes.length < SALT_MIN_LENGTH) {
|
|
+ return redactPassword;
|
|
}
|
|
- digestStr = digestStrBuilder.toString();
|
|
+ digestStr = getEncryptValue(passwords, saltBytes);
|
|
} catch (Exception e) {
|
|
}finally {
|
|
// clear all password
|
|
if(passwords != null) {
|
|
Arrays.fill(passwords, '0');
|
|
}
|
|
- if(passwordBytes != null) {
|
|
- Arrays.fill(passwordBytes, (byte) 0);
|
|
- }
|
|
}
|
|
|
|
redactPassword += (digestStr == null ? "" : digestStr);
|
|
return redactPassword;
|
|
}
|
|
|
|
+ private static String getSalt(String pid) throws Exception {
|
|
+ String salt = null;
|
|
+ StringBuilder redactAuth = new StringBuilder();
|
|
+
|
|
+ VirtualMachine vm = VirtualMachine.attach(pid);
|
|
+ HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm;
|
|
+ String flag = "RedactPassword";
|
|
+ try (InputStream in = hvm.printFlag(flag); BufferedInputStream bis = new BufferedInputStream(in);
|
|
+ InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) {
|
|
+ char c[] = new char[256];
|
|
+ int n;
|
|
+ do {
|
|
+ n = isr.read(c);
|
|
+
|
|
+ if (n > 0) {
|
|
+ redactAuth.append(n == c.length ? c : Arrays.copyOf(c, n));
|
|
+ }
|
|
+ } while (n > 0);
|
|
+ }
|
|
+ vm.detach();
|
|
+
|
|
+ if(redactAuth.length() > 0) {
|
|
+ String[] auths = redactAuth.toString().split(",");
|
|
+ if(auths.length != 2) {
|
|
+ return salt;
|
|
+ }
|
|
+ return auths[1].trim();
|
|
+ }
|
|
+
|
|
+ return salt;
|
|
+ }
|
|
+
|
|
+ private static String getEncryptValue(char[] passwordValue, byte[] saltBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
|
|
+ StringBuilder digestStrBuilder = new StringBuilder();
|
|
+
|
|
+ KeySpec spec = new PBEKeySpec(passwordValue, saltBytes, HASH_ITERATIONS_COUNT, HASH_BIT_SIZE);
|
|
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
+ SecretKey secretKey = secretKeyFactory.generateSecret(spec);
|
|
+ byte[] digestBytes = secretKey.getEncoded();
|
|
+ for (byte b : digestBytes) {
|
|
+ String hex = Integer.toHexString(0xff & b);
|
|
+ if (hex.length() == 1) {
|
|
+ digestStrBuilder.append('0');
|
|
+ }
|
|
+ digestStrBuilder.append(hex);
|
|
+ }
|
|
+ String digestStr = digestStrBuilder.toString();
|
|
+
|
|
+ return digestStr;
|
|
+ }
|
|
+
|
|
private static void checkForUnsupportedOptions(String[] args) {
|
|
// Check arguments for -F, -m, and non-numeric value
|
|
// and warn the user that SA is not supported anymore
|
|
--
|
|
2.22.0
|
|
|