8316964: Security tools should not call System.exit

Reviewed-by: valeriep
This commit is contained in:
Weijun Wang 2023-10-24 18:32:01 +00:00
parent 1f2a80b78a
commit 1ddf826aea
8 changed files with 370 additions and 220 deletions

View File

@ -405,26 +405,38 @@ public final class Main {
collator.setStrength(Collator.PRIMARY);
}
private Main() { }
public static void main(String[] args) throws Exception {
Main kt = new Main();
kt.run(args, System.out);
int exitCode = kt.run(args, System.out);
if (exitCode != 0) {
System.exit(exitCode);
}
}
private void run(String[] args, PrintStream out) throws Exception {
private static class ExitException extends RuntimeException {
@java.io.Serial
static final long serialVersionUID = 0L;
private final int errorCode;
public ExitException(int errorCode) {
this.errorCode = errorCode;
}
}
public int run(String[] args, PrintStream out) throws Exception {
try {
args = parseArgs(args);
parseArgs(args);
if (command != null) {
doCommands(out);
}
} catch (ExitException ee) {
return ee.errorCode;
} catch (Exception e) {
System.out.println(rb.getString("keytool.error.") + e);
if (verbose) {
e.printStackTrace(System.out);
}
if (!debug) {
System.exit(1);
return 1;
} else {
throw e;
}
@ -441,6 +453,7 @@ public final class Main {
ksStream.close();
}
}
return 0;
}
/**
@ -5247,7 +5260,7 @@ public final class Main {
if (debug) {
throw new RuntimeException("NO BIG ERROR, SORRY");
} else {
System.exit(1);
throw new ExitException(1);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -92,14 +92,44 @@ public class Kinit {
*/
public static void main(String[] args) {
try {
Kinit self = new Kinit(args);
Kinit kinit = new Kinit();
int exitCode = kinit.run(args);
if (exitCode != 0) {
System.exit(exitCode);
}
catch (Exception e) {
String msg = null;
if (e instanceof KrbException) {
msg = ((KrbException)e).krbErrorMessage() + " " +
((KrbException)e).returnCodeMessage();
}
/**
* Run the Kinit command.
* @param args array of ticket request options.
* Available options are: -f, -p, -c, principal, password.
* @return the exit code
*/
public int run(String[] args) {
try {
if (args == null || args.length == 0) {
options = new KinitOptions();
} else {
options = new KinitOptions(args);
}
switch (options.action) {
case 0:
// Help, already displayed in new KinitOptions().
break;
case 1:
acquire();
break;
case 2:
renew();
break;
default:
throw new KrbException("kinit does not support action "
+ options.action);
}
} catch (Exception e) {
String msg;
if (e instanceof KrbException ke) {
msg = ke.krbErrorMessage() + " " + ke.returnCodeMessage();
} else {
msg = e.getMessage();
}
@ -109,37 +139,9 @@ public class Kinit {
System.out.println("Exception: " + e);
}
e.printStackTrace();
System.exit(-1);
}
return;
}
/**
* Constructs a new Kinit object.
* @param args array of ticket request options.
* Available options are: -f, -p, -c, principal, password.
* @exception IOException if an I/O error occurs.
* @exception RealmException if the Realm could not be instantiated.
* @exception KrbException if error occurs during Kerberos operation.
*/
private Kinit(String[] args)
throws IOException, RealmException, KrbException {
if (args == null || args.length == 0) {
options = new KinitOptions();
} else {
options = new KinitOptions(args);
}
switch (options.action) {
case 1:
acquire();
break;
case 2:
renew();
break;
default:
throw new KrbException("kinit does not support action "
+ options.action);
return -1;
}
return 0;
}
private void renew()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -46,7 +46,7 @@ import java.io.FileInputStream;
*/
class KinitOptions {
// 1. acquire, 2. renew, 3. validate
// 0. Help, 1. acquire, 2. renew, 3. validate
public int action = 1;
// forwardable and proxiable flags have two states:
@ -143,7 +143,8 @@ class KinitOptions {
// -help: legacy.
args[i].equalsIgnoreCase("-help")) {
printHelp();
System.exit(0);
action = 0;
return;
} else if (p == null) { // Haven't yet processed a "principal"
p = args[i];
try {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -78,115 +78,111 @@ public class Klist {
*/
public static void main(String[] args) {
Klist klist = new Klist();
int exitCode = klist.run(args);
if (exitCode != 0) {
System.exit(exitCode);
}
}
public int run(String[] args) {
if ((args == null) || (args.length == 0)) {
klist.action = 'c'; // default will list default credentials cache.
action = 'c'; // default will list default credentials cache.
} else {
klist.processArgs(args);
}
switch (klist.action) {
case 'c':
if (klist.name == null) {
klist.target = CredentialsCache.getInstance();
klist.name = CredentialsCache.cacheName();
} else
klist.target = CredentialsCache.getInstance(klist.name);
if (klist.target != null) {
klist.displayCache();
} else {
klist.displayMessage("Credentials cache");
System.exit(-1);
}
break;
case 'k':
KeyTab ktab = KeyTab.getInstance(klist.name);
if (ktab.isMissing()) {
System.out.println("KeyTab " + klist.name + " not found.");
System.exit(-1);
} else if (!ktab.isValid()) {
System.out.println("KeyTab " + klist.name
+ " format not supported.");
System.exit(-1);
}
klist.target = ktab;
klist.name = ktab.tabName();
klist.displayTab();
break;
default:
if (klist.name != null) {
klist.printHelp();
System.exit(-1);
} else {
klist.target = CredentialsCache.getInstance();
klist.name = CredentialsCache.cacheName();
if (klist.target != null) {
klist.displayCache();
} else {
klist.displayMessage("Credentials cache");
System.exit(-1);
}
}
}
}
/**
* Parses the command line arguments.
*/
void processArgs(String[] args) {
Character arg;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-?") ||
args[i].equals("-h") ||
args[i].equals("--help")) {
printHelp();
System.exit(0);
}
if ((args[i].length() >= 2) && (args[i].startsWith("-"))) {
arg = Character.valueOf(args[i].charAt(1));
switch (arg.charValue()) {
case 'c':
action = 'c';
break;
case 'k':
action = 'k';
break;
case 'a':
options[2] = 'a';
break;
case 'n':
options[3] = 'n';
break;
case 'f':
options[1] = 'f';
break;
case 'e':
options[0] = 'e';
break;
case 'K':
options[1] = 'K';
break;
case 't':
options[2] = 't';
break;
default:
Character arg;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-?") ||
args[i].equals("-h") ||
args[i].equals("--help")) {
printHelp();
System.exit(-1);
return 0;
}
} else {
if (!args[i].startsWith("-") && (i == args.length - 1)) {
// the argument is the last one.
name = args[i];
arg = null;
if ((args[i].length() >= 2) && (args[i].startsWith("-"))) {
arg = Character.valueOf(args[i].charAt(1));
switch (arg.charValue()) {
case 'c':
action = 'c';
break;
case 'k':
action = 'k';
break;
case 'a':
options[2] = 'a';
break;
case 'n':
options[3] = 'n';
break;
case 'f':
options[1] = 'f';
break;
case 'e':
options[0] = 'e';
break;
case 'K':
options[1] = 'K';
break;
case 't':
options[2] = 't';
break;
default:
System.out.println("Invalid argument: " + args[i]);
printHelp();
return -1;
}
} else {
printHelp(); // incorrect input format.
System.exit(-1);
if (!args[i].startsWith("-") && (i == args.length - 1)) {
// the argument is the last one.
name = args[i];
} else {
System.out.println("Invalid argument: " + args[i]);
printHelp(); // incorrect input format.
return -1;
}
}
}
}
switch (action) {
case 'c':
if (name == null) {
target = CredentialsCache.getInstance();
name = CredentialsCache.cacheName();
} else
target = CredentialsCache.getInstance(name);
if (target != null) {
return displayCache();
} else {
return displayError("Credentials cache");
}
case 'k':
KeyTab ktab = KeyTab.getInstance(name);
if (ktab.isMissing()) {
System.out.println("KeyTab " + name + " not found.");
return -1;
} else if (!ktab.isValid()) {
System.out.println("KeyTab " + name
+ " format not supported.");
return -1;
}
target = ktab;
name = ktab.tabName();
return displayTab();
default:
if (name != null) {
printHelp();
return -1;
} else {
target = CredentialsCache.getInstance();
name = CredentialsCache.cacheName();
if (target != null) {
return displayCache();
} else {
return displayError("Credentials cache");
}
}
}
}
void displayTab() {
int displayTab() {
KeyTab table = (KeyTab)target;
KeyTabEntry[] entries = table.getEntries();
if (entries.length == 0) {
@ -201,7 +197,7 @@ public class Klist {
entries.length + " entries found.\n");
for (int i = 0; i < entries.length; i++) {
System.out.println("[" + (i + 1) + "] " +
"Service principal: " +
"Service principal: " +
entries[i].getService().toString());
System.out.println("\t KVNO: " +
entries[i].getKey().getKeyVersionNumber());
@ -221,18 +217,19 @@ public class Klist {
}
}
}
return 0;
}
void displayCache() {
int displayCache() {
CredentialsCache cache = (CredentialsCache)target;
sun.security.krb5.internal.ccache.Credentials[] creds =
cache.getCredsList();
if (creds == null) {
System.out.println ("No credentials available in the cache " +
name);
System.exit(-1);
return -1;
}
System.out.println("\nCredentials cache: " + name);
System.out.println("\nCredentials cache: " + name);
String defaultPrincipal = cache.getPrimaryPrincipal().toString();
int num = creds.length;
@ -327,7 +324,7 @@ public class Klist {
if (DEBUG) {
e.printStackTrace();
}
System.exit(-1);
return -1;
}
}
} else {
@ -342,14 +339,17 @@ public class Klist {
System.out.println(" " + e);
}
}
return 0;
}
void displayMessage(String target) {
int displayError(String target) {
if (name == null) {
System.out.println("Default " + target + " not found.");
} else {
System.out.println(target + " " + name + " not found.");
}
return -1;
}
/**
* Reformats the date from the form -
@ -359,7 +359,7 @@ public class Klist {
* the day, mm is the minute within the hour,
* ss is the second within the minute, zzz is the time zone,
* and yyyy is the year.
* @param date the string form of Date object.
* @param kt the string form of Date object.
*/
private String format(KerberosTime kt) {
String date = kt.toDate().toString();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -73,52 +73,76 @@ public class Ktab {
*/
public static void main(String[] args) {
Ktab ktab = new Ktab();
int exitCode = ktab.run(args);
if (exitCode != 0) {
System.exit(exitCode);
}
}
private static class ExitException extends RuntimeException {
@java.io.Serial
static final long serialVersionUID = 0L;
private final int errorCode;
public ExitException(int errorCode) {
this.errorCode = errorCode;
}
}
public int run(String[] args) {
try {
run0(args);
return 0;
} catch (ExitException ee) {
return ee.errorCode;
}
}
private void run0(String[] args) throws ExitException {
if ((args.length == 1) &&
((args[0].equalsIgnoreCase("-?")) ||
(args[0].equalsIgnoreCase("-h")) ||
(args[0].equalsIgnoreCase("--help")) ||
// -help: legacy.
(args[0].equalsIgnoreCase("-help")))) {
ktab.printHelp();
System.exit(0);
printHelp();
return;
} else if ((args == null) || (args.length == 0)) {
ktab.action = 'l';
action = 'l';
} else {
ktab.processArgs(args);
processArgs(args);
}
ktab.table = KeyTab.getInstance(ktab.name);
if (ktab.table.isMissing() && ktab.action != 'a') {
if (ktab.name == null) {
table = KeyTab.getInstance(name);
if (table.isMissing() && action != 'a') {
if (name == null) {
System.out.println("No default key table exists.");
} else {
System.out.println("Key table " +
ktab.name + " does not exist.");
name + " does not exist.");
}
System.exit(-1);
throw new ExitException(-1);
}
if (!ktab.table.isValid()) {
if (ktab.name == null) {
if (!table.isValid()) {
if (name == null) {
System.out.println("The format of the default key table " +
" is incorrect.");
} else {
System.out.println("The format of key table " +
ktab.name + " is incorrect.");
name + " is incorrect.");
}
System.exit(-1);
throw new ExitException(-1);
}
switch (ktab.action) {
switch (action) {
case 'l':
ktab.listKt();
listKt();
break;
case 'a':
ktab.addEntry();
addEntry();
break;
case 'd':
ktab.deleteEntry();
deleteEntry();
break;
default:
ktab.error("A command must be provided");
error("A command must be provided");
}
}
@ -267,7 +291,7 @@ public class Ktab {
void addEntry() {
if (salt != null && fopt) {
System.err.println("-s and -f cannot coexist when adding a keytab entry.");
System.exit(-1);
throw new ExitException(-1);
}
PrincipalName pname = null;
try {
@ -276,7 +300,7 @@ public class Ktab {
System.err.println("Failed to add " + principal +
" to keytab.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
}
if (password == null) {
try {
@ -288,7 +312,7 @@ public class Ktab {
} catch (IOException e) {
System.err.println("Failed to read the password.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
}
}
@ -313,11 +337,11 @@ public class Ktab {
} catch (KrbException e) {
System.err.println("Failed to add " + principal + " to keytab.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
} catch (IOException e) {
System.err.println("Failed to save new entry.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
}
}
@ -399,22 +423,23 @@ public class Ktab {
System.out.flush();
answer = cis.readLine();
if (answer.equalsIgnoreCase("Y") ||
answer.equalsIgnoreCase("Yes"));
else {
answer.equalsIgnoreCase("Yes")) {
;
} else {
// no error, the user did not want to delete the entry
System.exit(0);
return;
}
}
} catch (KrbException e) {
System.err.println("Error occurred while deleting the entry. "+
"Deletion failed.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
} catch (IOException e) {
System.err.println("Error occurred while deleting the entry. "+
" Deletion failed.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
}
int count = table.deleteEntries(pname, etype, vDel);
@ -422,7 +447,7 @@ public class Ktab {
if (count == 0) {
System.err.println("No matched entry in the keytab. " +
"Deletion fails.");
System.exit(-1);
throw new ExitException(-1);
} else {
try {
table.save();
@ -430,7 +455,7 @@ public class Ktab {
System.err.println("Error occurs while saving the keytab. " +
"Deletion fails.");
e.printStackTrace();
System.exit(-1);
throw new ExitException(-1);
}
System.out.println("Done! " + count + " entries removed.");
}
@ -441,7 +466,7 @@ public class Ktab {
System.out.println("Error: " + error + ".");
}
printHelp();
System.exit(-1);
throw new ExitException(-1);
}
/**

View File

@ -135,7 +135,19 @@ public class Main {
// This is the entry that get launched by the security tool jarsigner.
public static void main(String args[]) throws Exception {
Main js = new Main();
js.run(args);
int exitCode = js.run(args);
if (exitCode != 0) {
System.exit(exitCode);
}
}
private static class ExitException extends RuntimeException {
@java.io.Serial
static final long serialVersionUID = 0L;
private final int errorCode;
public ExitException(int errorCode) {
this.errorCode = errorCode;
}
}
X509Certificate[] certChain; // signer's cert chain (when composing)
@ -230,13 +242,13 @@ public class Main {
PKIXBuilderParameters pkixParameters;
Set<X509Certificate> trustedCerts = new HashSet<>();
public void run(String args[]) {
public int run(String args[]) {
try {
args = parseArgs(args);
parseArgs(args);
// Try to load and install the specified providers
if (providers != null) {
for (String provName: providers) {
for (String provName : providers) {
try {
KeyStoreUtil.loadProviderByName(provName,
providerArgs.get(provName));
@ -263,7 +275,7 @@ public class Main {
} else {
cl = ClassLoader.getSystemClassLoader();
}
for (String provClass: providerClasses) {
for (String provClass : providerClasses) {
try {
KeyStoreUtil.loadProviderByClass(provClass,
providerArgs.get(provClass), cl);
@ -285,19 +297,9 @@ public class Main {
loadKeyStore(keystore, false);
} catch (Exception e) {
if ((keystore != null) || (storepass != null)) {
System.out.println(rb.getString("jarsigner.error.") +
e.getMessage());
if (debug) {
e.printStackTrace();
}
System.exit(1);
throw e;
}
}
/* if (debug) {
SignatureFileVerifier.setDebug(true);
ManifestEntryVerifier.setDebug(true);
}
*/
verifyJar(jarfile);
} else {
loadKeyStore(keystore, true);
@ -305,12 +307,14 @@ public class Main {
signJar(jarfile, alias);
}
} catch (ExitException ee) {
return ee.errorCode;
} catch (Exception e) {
System.out.println(rb.getString("jarsigner.error.") + e);
if (debug) {
e.printStackTrace();
}
System.exit(1);
return 1;
} finally {
// zero-out private key password
if (keypass != null) {
@ -343,10 +347,10 @@ public class Main {
if (tsaChainNotValidated) {
exitCode |= 64;
}
if (exitCode != 0) {
System.exit(exitCode);
}
return exitCode;
}
return 0;
}
/*
@ -612,12 +616,12 @@ public class Main {
static void usage() {
System.out.println();
System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
System.exit(1);
throw new ExitException(1);
}
static void doPrintVersion() {
System.out.println("jarsigner " + System.getProperty("java.version"));
System.exit(0);
throw new ExitException(0);
}
static void fullusage() {
@ -719,7 +723,7 @@ public class Main {
(".print.this.help.message"));
System.out.println();
System.exit(0);
throw new ExitException(0);
}
void verifyJar(String jarName)
@ -1105,19 +1109,11 @@ public class Main {
} else {
displayMessagesAndResult(false);
}
return;
} catch (Exception e) {
System.out.println(rb.getString("jarsigner.") + e);
if (debug) {
e.printStackTrace();
}
} finally { // close the resource
if (jf != null) {
jf.close();
}
}
System.exit(1);
}
private void displayMessagesAndResult(boolean isSigning) {
@ -2469,7 +2465,7 @@ public class Main {
void error(String message) {
System.out.println(rb.getString("jarsigner.")+message);
System.exit(1);
throw new ExitException(1);
}
@ -2478,7 +2474,7 @@ public class Main {
if (debug) {
e.printStackTrace();
}
System.exit(1);
throw new ExitException(1);
}
/**

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8316964
* @summary check exit code in kinit, klist, and ktab
* @requires os.family == "windows"
* @library /test/lib
* @modules java.security.jgss/sun.security.krb5.internal.tools
*/
import jdk.test.lib.Asserts;
import jdk.test.lib.Platform;
import jdk.test.lib.SecurityTools;
public class ExitOrNot {
private static final int BAD = Platform.isWindows() ? -1 : 255;
public static void main(String[] args) throws Exception {
// launching the tool still exits
SecurityTools.kinit("u@R p1 p2")
.shouldHaveExitValue(BAD);
SecurityTools.klist("-x")
.shouldHaveExitValue(BAD);
SecurityTools.ktab("-x")
.shouldHaveExitValue(BAD);
// calling the run() methods returns the exit code
Asserts.assertEQ(new sun.security.krb5.internal.tools.Kinit()
.run("u@R p1 p2".split(" ")), -1);
Asserts.assertEQ(new sun.security.krb5.internal.tools.Klist()
.run("-x".split(" ")), -1);
Asserts.assertEQ(new sun.security.krb5.internal.tools.Ktab()
.run("-x".split(" ")), -1);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8316964
* @summary check exit code in jarsigner and keytool
* @library /test/lib
* @modules java.base/sun.security.tools.keytool
* jdk.jartool/sun.security.tools.jarsigner
*/
import jdk.test.lib.Asserts;
import jdk.test.lib.SecurityTools;
public class ExitOrNot {
public static void main(String[] args) throws Exception {
// launching the tool still exits
SecurityTools.jarsigner("1 2 3")
.shouldHaveExitValue(1);
SecurityTools.keytool("-x")
.shouldHaveExitValue(1);
// calling the run() methods no longer
Asserts.assertEQ(new sun.security.tools.jarsigner.Main()
.run("1 2 3".split(" ")), 1);
Asserts.assertEQ(new sun.security.tools.keytool.Main()
.run("-x".split(" "), System.out), 1);
}
}