Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/javax/management/security/HashedPasswordFileTest.java
41149 views
1
/*
2
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
/* @test
25
* @bug 5016517 8204661
26
* @summary Test Hashed passwords
27
* @library /test/lib
28
* @modules java.management
29
* jdk.management.agent/jdk.internal.agent
30
* @build HashedPasswordFileTest
31
* @run testng/othervm HashedPasswordFileTest
32
*
33
*/
34
35
import jdk.internal.agent.ConnectorAddressLink;
36
import jdk.test.lib.Utils;
37
import jdk.test.lib.process.ProcessTools;
38
import org.testng.Assert;
39
import org.testng.annotations.AfterClass;
40
import org.testng.annotations.Test;
41
42
import javax.management.MBeanServer;
43
import javax.management.remote.*;
44
import java.io.*;
45
import java.lang.management.ManagementFactory;
46
import java.nio.charset.StandardCharsets;
47
import java.nio.file.FileSystems;
48
import java.nio.file.Files;
49
import java.nio.file.attribute.PosixFilePermission;
50
import java.security.MessageDigest;
51
import java.security.NoSuchAlgorithmException;
52
import java.util.*;
53
import java.util.List;
54
import java.util.Set;
55
import java.util.concurrent.*;
56
57
@Test
58
public class HashedPasswordFileTest {
59
60
private final String[] randomWords = {"accost", "savoie", "bogart", "merest",
61
"azuela", "hoodie", "bursal", "lingua", "wincey", "trilby", "egesta",
62
"wester", "gilgai", "weinek", "ochone", "sanest", "gainst", "defang",
63
"ranket", "mayhem", "tagger", "timber", "eggcup", "mhren", "colloq",
64
"dreamy", "hattie", "rootle", "bloody", "helyne", "beater", "cosine",
65
"enmity", "outbox", "issuer", "lumina", "dekker", "vetoed", "dennis",
66
"strove", "gurnet", "talkie", "bennie", "behove", "coates", "shiloh",
67
"yemeni", "boleyn", "coaxal", "irne"};
68
69
private final String[] hashAlgs = {
70
"MD2",
71
"MD5",
72
"SHA-1",
73
"SHA-224",
74
"SHA-256",
75
"SHA-384",
76
"SHA-512/224",
77
"SHA-512/256",
78
"SHA3-224",
79
"SHA3-256",
80
"SHA3-384",
81
"SHA3-512"
82
};
83
84
private final Random random = Utils.getRandomInstance();
85
86
private JMXConnectorServer cs;
87
88
private String randomWord() {
89
int idx = random.nextInt(randomWords.length);
90
return randomWords[idx];
91
}
92
93
private String[] getHash(String algorithm, String password) {
94
try {
95
byte[] salt = new byte[64];
96
random.nextBytes(salt);
97
98
MessageDigest digest = MessageDigest.getInstance(algorithm);
99
digest.reset();
100
digest.update(salt);
101
byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
102
103
String saltStr = Base64.getEncoder().encodeToString(salt);
104
String hashStr = Base64.getEncoder().encodeToString(hash);
105
106
return new String[]{saltStr, hashStr};
107
} catch (NoSuchAlgorithmException ex) {
108
throw new RuntimeException(ex);
109
}
110
}
111
112
private String getPasswordFilePath() {
113
String testDir = System.getProperty("test.src");
114
String testFileName = "jmxremote.password";
115
return testDir + File.separator + testFileName;
116
}
117
118
private File createNewPasswordFile() throws IOException {
119
File file = new File(getPasswordFilePath());
120
if (file.exists()) {
121
file.delete();
122
}
123
file.createNewFile();
124
return file;
125
}
126
127
private Map<String, String> generateClearTextPasswordFile() throws IOException {
128
File file = createNewPasswordFile();
129
Map<String, String> props = new HashMap<>();
130
BufferedWriter br;
131
try (FileWriter fw = new FileWriter(file)) {
132
br = new BufferedWriter(fw);
133
int numentries = random.nextInt(5) + 3;
134
for (int i = 0; i < numentries; i++) {
135
String username;
136
do {
137
username = randomWord();
138
} while (props.get(username) != null);
139
String password = randomWord();
140
props.put(username, password);
141
br.write(username + " " + password + "\n");
142
}
143
br.flush();
144
}
145
br.close();
146
return props;
147
}
148
149
private boolean isPasswordFileHashed() throws IOException {
150
BufferedReader br;
151
boolean result;
152
try (FileReader fr = new FileReader(getPasswordFilePath())) {
153
br = new BufferedReader(fr);
154
result = br.lines().anyMatch(line -> {
155
if (line.startsWith("#")) {
156
return false;
157
}
158
String[] tokens = line.split("\\s+");
159
return tokens.length == 3 || tokens.length == 4;
160
});
161
}
162
br.close();
163
return result;
164
}
165
166
private Map<String, String> generateHashedPasswordFile() throws IOException {
167
File file = createNewPasswordFile();
168
Map<String, String> props = new HashMap<>();
169
BufferedWriter br;
170
try (FileWriter fw = new FileWriter(file)) {
171
br = new BufferedWriter(fw);
172
int numentries = random.nextInt(5) + 3;
173
for (int i = 0; i < numentries; i++) {
174
String username;
175
do {
176
username = randomWord();
177
} while (props.get(username) != null);
178
String password = randomWord();
179
String alg = hashAlgs[random.nextInt(hashAlgs.length)];
180
String[] b64str = getHash(alg, password);
181
br.write(username + " " + b64str[0] + " " + b64str[1] + " " + alg + "\n");
182
props.put(username, password);
183
}
184
br.flush();
185
}
186
br.close();
187
return props;
188
}
189
190
private JMXServiceURL createServerSide(boolean useHash)
191
throws IOException {
192
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
193
JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
194
195
HashMap<String, Object> env = new HashMap<>();
196
env.put("jmx.remote.x.password.file", getPasswordFilePath());
197
env.put("jmx.remote.x.password.toHashes", useHash ? "true" : "false");
198
cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
199
cs.start();
200
return cs.getAddress();
201
}
202
203
@Test
204
public void testClearTextPasswordFile() throws IOException {
205
Boolean[] bvals = new Boolean[]{true, false};
206
for (boolean bval : bvals) {
207
try {
208
Map<String, String> credentials = generateClearTextPasswordFile();
209
JMXServiceURL serverUrl = createServerSide(bval);
210
for (Map.Entry<String, String> entry : credentials.entrySet()) {
211
HashMap<String, Object> env = new HashMap<>();
212
env.put("jmx.remote.credentials",
213
new String[]{entry.getKey(), entry.getValue()});
214
try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
215
cc.getMBeanServerConnection();
216
}
217
}
218
Assert.assertEquals(isPasswordFileHashed(), bval);
219
} finally {
220
cs.stop();
221
}
222
}
223
}
224
225
@Test
226
public void testReadOnlyPasswordFile() throws IOException {
227
Boolean[] bvals = new Boolean[]{true, false};
228
for (boolean bval : bvals) {
229
try {
230
Map<String, String> credentials = generateClearTextPasswordFile();
231
File file = new File(getPasswordFilePath());
232
file.setReadOnly();
233
JMXServiceURL serverUrl = createServerSide(bval);
234
for (Map.Entry<String, String> entry : credentials.entrySet()) {
235
HashMap<String, Object> env = new HashMap<>();
236
env.put("jmx.remote.credentials",
237
new String[]{entry.getKey(), entry.getValue()});
238
try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
239
cc.getMBeanServerConnection();
240
}
241
}
242
Assert.assertEquals(isPasswordFileHashed(), false);
243
} finally {
244
cs.stop();
245
}
246
}
247
}
248
249
@Test
250
public void testHashedPasswordFile() throws IOException {
251
Boolean[] bvals = new Boolean[]{true, false};
252
for (boolean bval : bvals) {
253
try {
254
Map<String, String> credentials = generateHashedPasswordFile();
255
JMXServiceURL serverUrl = createServerSide(bval);
256
Assert.assertEquals(isPasswordFileHashed(), true);
257
for (Map.Entry<String, String> entry : credentials.entrySet()) {
258
HashMap<String, Object> env = new HashMap<>();
259
env.put("jmx.remote.credentials",
260
new String[]{entry.getKey(), entry.getValue()});
261
try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
262
cc.getMBeanServerConnection();
263
}
264
}
265
} finally {
266
cs.stop();
267
}
268
}
269
}
270
271
private static class SimpleJMXClient implements Callable {
272
private final JMXServiceURL url;
273
private final Map<String, String> credentials;
274
275
public SimpleJMXClient(JMXServiceURL url, Map<String, String> credentials) {
276
this.url = url;
277
this.credentials = credentials;
278
}
279
280
@Override
281
public Object call() throws Exception {
282
for (Map.Entry<String, String> entry : credentials.entrySet()) {
283
HashMap<String, Object> env = new HashMap<>();
284
env.put("jmx.remote.credentials",
285
new String[]{entry.getKey(), entry.getValue()});
286
try (JMXConnector cc = JMXConnectorFactory.connect(url, env)) {
287
cc.getMBeanServerConnection();
288
}
289
}
290
return null;
291
}
292
}
293
294
@Test
295
public void testMultipleClients() throws Throwable {
296
Map<String, String> credentials = generateClearTextPasswordFile();
297
JMXServiceURL serverUrl = createServerSide(true);
298
Assert.assertEquals(isPasswordFileHashed(), false);
299
// create random number of clients
300
int numClients = random.nextInt(20) + 10;
301
List<Future> futures = new ArrayList<>();
302
ExecutorService executor = Executors.newFixedThreadPool(numClients);
303
for (int i = 0; i < numClients; i++) {
304
Future future = executor.submit(new SimpleJMXClient(serverUrl, credentials));
305
futures.add(future);
306
}
307
try {
308
for (Future future : futures) {
309
future.get();
310
}
311
} catch (InterruptedException ex) {
312
Thread.currentThread().interrupt();
313
} catch (ExecutionException ex) {
314
throw ex.getCause();
315
} finally {
316
executor.shutdown();
317
}
318
319
Assert.assertEquals(isPasswordFileHashed(), true);
320
}
321
322
@Test
323
public void testPasswordChange() throws IOException {
324
try {
325
Map<String, String> credentials = generateClearTextPasswordFile();
326
JMXServiceURL serverUrl = createServerSide(true);
327
Assert.assertEquals(isPasswordFileHashed(), false);
328
329
for (Map.Entry<String, String> entry : credentials.entrySet()) {
330
HashMap<String, Object> env = new HashMap<>();
331
env.put("jmx.remote.credentials",
332
new String[]{entry.getKey(), entry.getValue()});
333
try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
334
cc.getMBeanServerConnection();
335
}
336
}
337
Assert.assertEquals(isPasswordFileHashed(), true);
338
339
// Read the file back. Add new entries. Change passwords for few
340
BufferedReader br = new BufferedReader(new FileReader(getPasswordFilePath()));
341
String line;
342
StringBuilder sbuild = new StringBuilder();
343
while ((line = br.readLine()) != null) {
344
if (line.trim().startsWith("#")) {
345
sbuild.append(line).append("\n");
346
continue;
347
}
348
349
// Change password for random entries
350
if (random.nextBoolean()) {
351
String[] tokens = line.split("\\s+");
352
if ((tokens.length == 4 || tokens.length == 3)) {
353
String password = randomWord();
354
credentials.put(tokens[0], password);
355
sbuild.append(tokens[0]).append(" ").append(password).append("\n");
356
}
357
} else {
358
sbuild.append(line).append("\n");
359
}
360
}
361
362
// Add new entries in clear
363
int newentries = random.nextInt(2) + 3;
364
for (int i = 0; i < newentries; i++) {
365
String username;
366
do {
367
username = randomWord();
368
} while (credentials.get(username) != null);
369
String password = randomWord();
370
credentials.put(username, password);
371
sbuild.append(username).append(" ").append(password).append("\n");
372
}
373
374
// Add new entries as a hash
375
int numentries = random.nextInt(2) + 3;
376
for (int i = 0; i < numentries; i++) {
377
String username;
378
do {
379
username = randomWord();
380
} while (credentials.get(username) != null);
381
String password = randomWord();
382
String alg = hashAlgs[random.nextInt(hashAlgs.length)];
383
String[] b64str = getHash(alg, password);
384
credentials.put(username, password);
385
sbuild.append(username).append(" ").append(b64str[0])
386
.append(" ").append(b64str[1]).append(" ")
387
.append(alg).append("\n");
388
}
389
390
try (BufferedWriter bw = new BufferedWriter(new FileWriter(getPasswordFilePath()))) {
391
bw.write(sbuild.toString());
392
}
393
394
for (Map.Entry<String, String> entry : credentials.entrySet()) {
395
HashMap<String, Object> env = new HashMap<>();
396
env.put("jmx.remote.credentials",
397
new String[]{entry.getKey(), entry.getValue()});
398
try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
399
cc.getMBeanServerConnection();
400
}
401
}
402
} finally {
403
cs.stop();
404
}
405
}
406
407
@Test
408
public void testDefaultAgent() throws Exception {
409
List<String> pbArgs = new ArrayList<>();
410
generateClearTextPasswordFile();
411
412
// This will run only on a POSIX compliant system
413
if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
414
return;
415
}
416
417
// Make sure only owner is able to read/write the file or else
418
// default agent will fail to start
419
File file = new File(getPasswordFilePath());
420
Set<PosixFilePermission> perms = new HashSet<>();
421
perms.add(PosixFilePermission.OWNER_READ);
422
perms.add(PosixFilePermission.OWNER_WRITE);
423
Files.setPosixFilePermissions(file.toPath(), perms);
424
425
pbArgs.add("-cp");
426
pbArgs.add(System.getProperty("test.class.path"));
427
428
pbArgs.add("-Dcom.sun.management.jmxremote.port=0");
429
pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true");
430
pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath());
431
pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false");
432
pbArgs.add("--add-exports");
433
pbArgs.add("jdk.management.agent/jdk.internal.agent=ALL-UNNAMED");
434
pbArgs.add(TestApp.class.getSimpleName());
435
436
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
437
pbArgs.toArray(new String[0]));
438
Process process = ProcessTools.startProcess(
439
TestApp.class.getSimpleName(),
440
pb);
441
442
if (process.waitFor() != 0) {
443
throw new RuntimeException("Test Failed : Error starting default agent");
444
}
445
Assert.assertEquals(isPasswordFileHashed(), true);
446
}
447
448
@Test
449
public void testDefaultAgentNoHash() throws Exception {
450
List<String> pbArgs = new ArrayList<>();
451
generateClearTextPasswordFile();
452
453
// This will run only on a POSIX compliant system
454
if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
455
return;
456
}
457
458
// Make sure only owner is able to read/write the file or else
459
// default agent will fail to start
460
File file = new File(getPasswordFilePath());
461
Set<PosixFilePermission> perms = new HashSet<>();
462
perms.add(PosixFilePermission.OWNER_READ);
463
perms.add(PosixFilePermission.OWNER_WRITE);
464
Files.setPosixFilePermissions(file.toPath(), perms);
465
466
pbArgs.add("-cp");
467
pbArgs.add(System.getProperty("test.class.path"));
468
469
pbArgs.add("-Dcom.sun.management.jmxremote.port=0");
470
pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true");
471
pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath());
472
pbArgs.add("-Dcom.sun.management.jmxremote.password.toHashes=false");
473
pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false");
474
pbArgs.add("--add-exports");
475
pbArgs.add("jdk.management.agent/jdk.internal.agent=ALL-UNNAMED");
476
pbArgs.add(TestApp.class.getSimpleName());
477
478
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
479
pbArgs.toArray(new String[0]));
480
Process process = ProcessTools.startProcess(
481
TestApp.class.getSimpleName(),
482
pb);
483
484
if (process.waitFor() != 0) {
485
throw new RuntimeException("Test Failed : Error starting default agent");
486
}
487
Assert.assertEquals(isPasswordFileHashed(), false);
488
}
489
490
@AfterClass
491
public void cleanUp() {
492
File file = new File(getPasswordFilePath());
493
if (file.exists()) {
494
file.delete();
495
}
496
}
497
}
498
499
class TestApp {
500
501
public static void main(String[] args) throws IOException {
502
try {
503
Map<String, String> propsMap = ConnectorAddressLink.importRemoteFrom(0);
504
String jmxServiceUrl = propsMap.get("sun.management.JMXConnectorServer.0.remoteAddress");
505
Map<String, Object> env = new HashMap<>(1);
506
// any dummy credentials will do. We just have to trigger password hashing
507
env.put("jmx.remote.credentials", new String[]{"a", "a"});
508
try (JMXConnector cc = JMXConnectorFactory.connect(new JMXServiceURL(jmxServiceUrl), env)) {
509
cc.getMBeanServerConnection();
510
}
511
} catch (SecurityException ex) {
512
// Catch authentication failure here
513
}
514
}
515
}
516
517